Merge pull request #11563 from bfredl/mark_madness

extmarks: mark sanity/madness
This commit is contained in:
Björn Linse
2020-01-16 15:31:05 +01:00
committed by GitHub
28 changed files with 3149 additions and 2419 deletions

View File

@@ -170,6 +170,14 @@ Boolean nvim_buf_attach(uint64_t channel_id,
}
cb.on_lines = v->data.luaref;
v->data.luaref = LUA_NOREF;
} else if (is_lua && strequal("_on_bytes", k.data)) {
// NB: undocumented, untested and incomplete interface!
if (v->type != kObjectTypeLuaRef) {
api_set_error(err, kErrorTypeValidation, "callback is not a function");
goto error;
}
cb.on_bytes = v->data.luaref;
v->data.luaref = LUA_NOREF;
} else if (is_lua && strequal("on_changedtick", k.data)) {
if (v->type != kObjectTypeLuaRef) {
api_set_error(err, kErrorTypeValidation, "callback is not a function");
@@ -201,6 +209,7 @@ Boolean nvim_buf_attach(uint64_t channel_id,
error:
// TODO(bfredl): ASAN build should check that the ref table is empty?
executor_free_luaref(cb.on_lines);
executor_free_luaref(cb.on_bytes);
executor_free_luaref(cb.on_changedtick);
executor_free_luaref(cb.on_detach);
return false;
@@ -639,7 +648,6 @@ void nvim_buf_set_lines(uint64_t channel_id,
(linenr_T)(end - 1),
MAXLNUM,
(long)extra,
false,
kExtmarkUndo);
changed_lines((linenr_T)start, 0, (linenr_T)end, (long)extra, true);
@@ -1119,12 +1127,12 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
return rv;
}
Extmark *extmark = extmark_from_id(buf, (uint64_t)ns_id, (uint64_t)id);
if (!extmark) {
ExtmarkInfo extmark = extmark_from_id(buf, (uint64_t)ns_id, (uint64_t)id);
if (extmark.row < 0) {
return rv;
}
ADD(rv, INTEGER_OBJ((Integer)extmark->line->lnum-1));
ADD(rv, INTEGER_OBJ((Integer)extmark->col-1));
ADD(rv, INTEGER_OBJ((Integer)extmark.row));
ADD(rv, INTEGER_OBJ((Integer)extmark.col));
return rv;
}
@@ -1204,43 +1212,39 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start,
if (limit == 0) {
return rv;
} else if (limit < 0) {
limit = INT64_MAX;
}
bool reverse = false;
linenr_T l_lnum;
int l_row;
colnr_T l_col;
if (!extmark_get_index_from_obj(buf, ns_id, start, &l_lnum, &l_col, err)) {
if (!extmark_get_index_from_obj(buf, ns_id, start, &l_row, &l_col, err)) {
return rv;
}
linenr_T u_lnum;
int u_row;
colnr_T u_col;
if (!extmark_get_index_from_obj(buf, ns_id, end, &u_lnum, &u_col, err)) {
if (!extmark_get_index_from_obj(buf, ns_id, end, &u_row, &u_col, err)) {
return rv;
}
if (l_lnum > u_lnum || (l_lnum == u_lnum && l_col > u_col)) {
if (l_row > u_row || (l_row == u_row && l_col > u_col)) {
reverse = true;
linenr_T tmp_lnum = l_lnum;
l_lnum = u_lnum;
u_lnum = tmp_lnum;
colnr_T tmp_col = l_col;
l_col = u_col;
u_col = tmp_col;
}
ExtmarkArray marks = extmark_get(buf, (uint64_t)ns_id, l_lnum, l_col, u_lnum,
ExtmarkArray marks = extmark_get(buf, (uint64_t)ns_id, l_row, l_col, u_row,
u_col, (int64_t)limit, reverse);
for (size_t i = 0; i < kv_size(marks); i++) {
Array mark = ARRAY_DICT_INIT;
Extmark *extmark = kv_A(marks, i);
ADD(mark, INTEGER_OBJ((Integer)extmark->mark_id));
ADD(mark, INTEGER_OBJ(extmark->line->lnum-1));
ADD(mark, INTEGER_OBJ(extmark->col-1));
ExtmarkInfo extmark = kv_A(marks, i);
ADD(mark, INTEGER_OBJ((Integer)extmark.mark_id));
ADD(mark, INTEGER_OBJ(extmark.row));
ADD(mark, INTEGER_OBJ(extmark.col));
ADD(rv, ARRAY_OBJ(mark));
}
@@ -1301,17 +1305,15 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer id,
}
uint64_t id_num;
if (id == 0) {
id_num = extmark_free_id_get(buf, (uint64_t)ns_id);
} else if (id > 0) {
if (id >= 0) {
id_num = (uint64_t)id;
} else {
api_set_error(err, kErrorTypeValidation, _("Invalid mark id"));
return 0;
}
extmark_set(buf, (uint64_t)ns_id, id_num,
(linenr_T)line+1, (colnr_T)col+1, kExtmarkUndo);
id_num = extmark_set(buf, (uint64_t)ns_id, id_num,
(int)line, (colnr_T)col, kExtmarkUndo);
return (Integer)id_num;
}
@@ -1339,7 +1341,7 @@ Boolean nvim_buf_del_extmark(Buffer buffer,
return false;
}
return extmark_del(buf, (uint64_t)ns_id, (uint64_t)id, kExtmarkUndo);
return extmark_del(buf, (uint64_t)ns_id, (uint64_t)id);
}
/// Adds a highlight to buffer.
@@ -1373,7 +1375,7 @@ Boolean nvim_buf_del_extmark(Buffer buffer,
/// @param[out] err Error details, if any
/// @return The ns_id that was used
Integer nvim_buf_add_highlight(Buffer buffer,
Integer ns_id,
Integer src_id,
String hl_group,
Integer line,
Integer col_start,
@@ -1398,14 +1400,31 @@ Integer nvim_buf_add_highlight(Buffer buffer,
col_end = MAXCOL;
}
uint64_t ns_id = src2ns(&src_id);
if (!(0 <= line && line < buf->b_ml.ml_line_count)) {
// safety check, we can't add marks outside the range
return src_id;
}
int hlg_id = 0;
if (hl_group.size > 0) {
hlg_id = syn_check_group((char_u *)hl_group.data, (int)hl_group.size);
} else {
return src_id;
}
ns_id = bufhl_add_hl(buf, (int)ns_id, hlg_id, (linenr_T)line+1,
(colnr_T)col_start+1, (colnr_T)col_end);
return ns_id;
int end_line = (int)line;
if (col_end == MAXCOL) {
col_end = 0;
end_line++;
}
ns_id = extmark_add_decoration(buf, ns_id, hlg_id,
(int)line, (colnr_T)col_start,
end_line, (colnr_T)col_end,
VIRTTEXT_EMPTY);
return src_id;
}
/// Clears namespaced objects (highlights, extmarks, virtual text) from
@@ -1439,12 +1458,9 @@ void nvim_buf_clear_namespace(Buffer buffer,
if (line_end < 0 || line_end > MAXLNUM) {
line_end = MAXLNUM;
}
bufhl_clear_line_range(buf, (int)ns_id, (int)line_start+1, (int)line_end);
extmark_clear(buf, ns_id == -1 ? 0 : (uint64_t)ns_id,
(linenr_T)line_start+1,
(linenr_T)line_end,
kExtmarkUndo);
extmark_clear(buf, (ns_id < 0 ? 0 : (uint64_t)ns_id),
(int)line_start, 0,
(int)line_end-1, MAXCOL);
}
/// Clears highlights and virtual text from namespace and range of lines
@@ -1467,6 +1483,43 @@ void nvim_buf_clear_highlight(Buffer buffer,
nvim_buf_clear_namespace(buffer, ns_id, line_start, line_end, err);
}
static VirtText parse_virt_text(Array chunks, Error *err)
{
VirtText virt_text = KV_INITIAL_VALUE;
for (size_t i = 0; i < chunks.size; i++) {
if (chunks.items[i].type != kObjectTypeArray) {
api_set_error(err, kErrorTypeValidation, "Chunk is not an array");
goto free_exit;
}
Array chunk = chunks.items[i].data.array;
if (chunk.size == 0 || chunk.size > 2
|| chunk.items[0].type != kObjectTypeString
|| (chunk.size == 2 && chunk.items[1].type != kObjectTypeString)) {
api_set_error(err, kErrorTypeValidation,
"Chunk is not an array with one or two strings");
goto free_exit;
}
String str = chunk.items[0].data.string;
char *text = transstr(str.size > 0 ? str.data : ""); // allocates
int hl_id = 0;
if (chunk.size == 2) {
String hl = chunk.items[1].data.string;
if (hl.size > 0) {
hl_id = syn_check_group((char_u *)hl.data, (int)hl.size);
}
}
kv_push(virt_text, ((VirtTextChunk){ .text = text, .hl_id = hl_id }));
}
return virt_text;
free_exit:
clear_virttext(&virt_text);
return virt_text;
}
/// Set the virtual text (annotation) for a buffer line.
///
@@ -1496,7 +1549,7 @@ void nvim_buf_clear_highlight(Buffer buffer,
/// @param[out] err Error details, if any
/// @return The ns_id that was used
Integer nvim_buf_set_virtual_text(Buffer buffer,
Integer ns_id,
Integer src_id,
Integer line,
Array chunks,
Dictionary opts,
@@ -1518,41 +1571,26 @@ Integer nvim_buf_set_virtual_text(Buffer buffer,
return 0;
}
VirtText virt_text = KV_INITIAL_VALUE;
for (size_t i = 0; i < chunks.size; i++) {
if (chunks.items[i].type != kObjectTypeArray) {
api_set_error(err, kErrorTypeValidation, "Chunk is not an array");
goto free_exit;
}
Array chunk = chunks.items[i].data.array;
if (chunk.size == 0 || chunk.size > 2
|| chunk.items[0].type != kObjectTypeString
|| (chunk.size == 2 && chunk.items[1].type != kObjectTypeString)) {
api_set_error(err, kErrorTypeValidation,
"Chunk is not an array with one or two strings");
goto free_exit;
}
uint64_t ns_id = src2ns(&src_id);
String str = chunk.items[0].data.string;
char *text = transstr(str.size > 0 ? str.data : ""); // allocates
int hl_id = 0;
if (chunk.size == 2) {
String hl = chunk.items[1].data.string;
if (hl.size > 0) {
hl_id = syn_check_group((char_u *)hl.data, (int)hl.size);
}
}
kv_push(virt_text, ((VirtTextChunk){ .text = text, .hl_id = hl_id }));
VirtText virt_text = parse_virt_text(chunks, err);
if (ERROR_SET(err)) {
return 0;
}
ns_id = bufhl_add_virt_text(buf, (int)ns_id, (linenr_T)line+1,
virt_text);
return ns_id;
free_exit:
kv_destroy(virt_text);
return 0;
VirtText *existing = extmark_find_virttext(buf, (int)line, ns_id);
if (existing) {
clear_virttext(existing);
*existing = virt_text;
return src_id;
}
extmark_add_decoration(buf, ns_id, 0,
(int)line, 0, -1, -1,
virt_text);
return src_id;
}
/// Get the virtual text (annotation) for a buffer line.
@@ -1570,7 +1608,7 @@ free_exit:
/// @param line Line to get the virtual text from (zero-indexed)
/// @param[out] err Error details, if any
/// @return List of virtual text chunks
Array nvim_buf_get_virtual_text(Buffer buffer, Integer lnum, Error *err)
Array nvim_buf_get_virtual_text(Buffer buffer, Integer line, Error *err)
FUNC_API_SINCE(7)
{
Array chunks = ARRAY_DICT_INIT;
@@ -1580,20 +1618,20 @@ Array nvim_buf_get_virtual_text(Buffer buffer, Integer lnum, Error *err)
return chunks;
}
if (lnum < 0 || lnum >= MAXLNUM) {
if (line < 0 || line >= MAXLNUM) {
api_set_error(err, kErrorTypeValidation, "Line number outside range");
return chunks;
}
BufhlLine *lineinfo = bufhl_tree_ref(&buf->b_bufhl_info, (linenr_T)(lnum + 1),
false);
if (!lineinfo) {
VirtText *virt_text = extmark_find_virttext(buf, (int)line, 0);
if (!virt_text) {
return chunks;
}
for (size_t i = 0; i < lineinfo->virt_text.size; i++) {
for (size_t i = 0; i < virt_text->size; i++) {
Array chunk = ARRAY_DICT_INIT;
VirtTextChunk *vtc = &lineinfo->virt_text.items[i];
VirtTextChunk *vtc = &virt_text->items[i];
ADD(chunk, STRING_OBJ(cstr_to_string(vtc->text)));
if (vtc->hl_id > 0) {
ADD(chunk, STRING_OBJ(cstr_to_string(
@@ -1605,6 +1643,59 @@ Array nvim_buf_get_virtual_text(Buffer buffer, Integer lnum, Error *err)
return chunks;
}
Integer nvim__buf_add_decoration(Buffer buffer, Integer ns_id, String hl_group,
Integer start_row, Integer start_col,
Integer end_row, Integer end_col,
Array virt_text,
Error *err)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
if (!buf) {
return 0;
}
if (!ns_initialized((uint64_t)ns_id)) {
api_set_error(err, kErrorTypeValidation, _("Invalid ns_id"));
return 0;
}
if (start_row < 0 || start_row >= MAXLNUM || end_row > MAXCOL) {
api_set_error(err, kErrorTypeValidation, "Line number outside range");
return 0;
}
if (start_col < 0 || start_col > MAXCOL || end_col > MAXCOL) {
api_set_error(err, kErrorTypeValidation, "Column value outside range");
return 0;
}
if (end_row < 0 || end_col < 0) {
end_row = -1;
end_col = -1;
}
if (start_row >= buf->b_ml.ml_line_count
|| end_row >= buf->b_ml.ml_line_count) {
// safety check, we can't add marks outside the range
return 0;
}
int hlg_id = 0;
if (hl_group.size > 0) {
hlg_id = syn_check_group((char_u *)hl_group.data, (int)hl_group.size);
}
VirtText vt = parse_virt_text(virt_text, err);
if (ERROR_SET(err)) {
return 0;
}
uint64_t mark_id = extmark_add_decoration(buf, (uint64_t)ns_id, hlg_id,
(int)start_row, (colnr_T)start_col,
(int)end_row, (colnr_T)end_col, vt);
return (Integer)mark_id;
}
Dictionary nvim__buf_stats(Buffer buffer, Error *err)
{
Dictionary rv = ARRAY_DICT_INIT;
@@ -1626,6 +1717,16 @@ Dictionary nvim__buf_stats(Buffer buffer, Error *err)
// this exists to debug issues
PUT(rv, "dirty_bytes", INTEGER_OBJ((Integer)buf->deleted_bytes));
u_header_T *uhp = NULL;
if (buf->b_u_curhead != NULL) {
uhp = buf->b_u_curhead;
} else if (buf->b_u_newhead) {
uhp = buf->b_u_newhead;
}
if (uhp) {
PUT(rv, "uhp_extmark_size", INTEGER_OBJ((Integer)kv_size(uhp->uh_extmark)));
}
return rv;
}

View File

@@ -1511,61 +1511,6 @@ ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf)
return mappings;
}
// Returns an extmark given an id or a positional index
// If throw == true then an error will be raised if nothing
// was found
// Returns NULL if something went wrong
Extmark *extmark_from_id_or_pos(Buffer buffer, Integer ns, Object id,
Error *err, bool throw)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
if (!buf) {
return NULL;
}
Extmark *extmark = NULL;
if (id.type == kObjectTypeArray) {
if (id.data.array.size != 2) {
api_set_error(err, kErrorTypeValidation,
_("Position must have 2 elements"));
return NULL;
}
linenr_T row = (linenr_T)id.data.array.items[0].data.integer;
colnr_T col = (colnr_T)id.data.array.items[1].data.integer;
if (row < 1 || col < 1) {
if (throw) {
api_set_error(err, kErrorTypeValidation, _("Row and column MUST be > 0"));
}
return NULL;
}
extmark = extmark_from_pos(buf, (uint64_t)ns, row, col);
} else if (id.type != kObjectTypeInteger) {
if (throw) {
api_set_error(err, kErrorTypeValidation,
_("Mark id must be an int or [row, col]"));
}
return NULL;
} else if (id.data.integer < 0) {
if (throw) {
api_set_error(err, kErrorTypeValidation, _("Mark id must be positive"));
}
return NULL;
} else {
extmark = extmark_from_id(buf,
(uint64_t)ns,
(uint64_t)id.data.integer);
}
if (!extmark) {
if (throw) {
api_set_error(err, kErrorTypeValidation, _("Mark doesn't exist"));
}
return NULL;
}
return extmark;
}
// Is the Namespace in use?
bool ns_initialized(uint64_t ns)
{
@@ -1584,29 +1529,29 @@ bool ns_initialized(uint64_t ns)
/// @param[out] colnr extmark column
///
/// @return true if the extmark was found, else false
bool extmark_get_index_from_obj(buf_T *buf, Integer ns, Object obj, linenr_T
*lnum, colnr_T *colnr, Error *err)
bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, int
*row, colnr_T *col, Error *err)
{
// Check if it is mark id
if (obj.type == kObjectTypeInteger) {
Integer id = obj.data.integer;
if (id == 0) {
*lnum = 1;
*colnr = 1;
*row = 0;
*col = 0;
return true;
} else if (id == -1) {
*lnum = MAXLNUM;
*colnr = MAXCOL;
*row = MAXLNUM;
*col = MAXCOL;
return true;
} else if (id < 0) {
api_set_error(err, kErrorTypeValidation, _("Mark id must be positive"));
return false;
}
Extmark *extmark = extmark_from_id(buf, (uint64_t)ns, (uint64_t)id);
if (extmark) {
*lnum = extmark->line->lnum;
*colnr = extmark->col;
ExtmarkInfo extmark = extmark_from_id(buf, (uint64_t)ns_id, (uint64_t)id);
if (extmark.row >= 0) {
*row = extmark.row;
*col = extmark.col;
return true;
} else {
api_set_error(err, kErrorTypeValidation, _("No mark with requested id"));
@@ -1623,10 +1568,10 @@ bool extmark_get_index_from_obj(buf_T *buf, Integer ns, Object obj, linenr_T
_("Position must have 2 integer elements"));
return false;
}
Integer line = pos.items[0].data.integer;
Integer col = pos.items[1].data.integer;
*lnum = (linenr_T)(line >= 0 ? line + 1 : MAXLNUM);
*colnr = (colnr_T)(col >= 0 ? col + 1 : MAXCOL);
Integer pos_row = pos.items[0].data.integer;
Integer pos_col = pos.items[1].data.integer;
*row = (int)(pos_row >= 0 ? pos_row : MAXLNUM);
*col = (colnr_T)(pos_col >= 0 ? pos_col : MAXCOL);
return true;
} else {
api_set_error(err, kErrorTypeValidation,

View File

@@ -81,12 +81,6 @@
#include "nvim/os/input.h"
#include "nvim/buffer_updates.h"
typedef enum {
kBLSUnchanged = 0,
kBLSChanged = 1,
kBLSDeleted = 2,
} BufhlLineStatus;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "buffer.c.generated.h"
#endif
@@ -818,7 +812,6 @@ static void free_buffer_stuff(buf_T *buf, int free_flags)
uc_clear(&buf->b_ucmds); // clear local user commands
buf_delete_signs(buf, (char_u *)"*"); // delete any signs
extmark_free_all(buf); // delete any extmarks
bufhl_clear_all(buf); // delete any highligts
map_clear_int(buf, MAP_ALL_MODES, true, false); // clear local mappings
map_clear_int(buf, MAP_ALL_MODES, true, true); // clear local abbrevs
XFREE_CLEAR(buf->b_start_fenc);
@@ -5342,430 +5335,6 @@ int buf_signcols(buf_T *buf)
return buf->b_signcols;
}
// bufhl: plugin highlights associated with a buffer
/// Get reference to line in kbtree_t
///
/// @param b the three
/// @param line the linenumber to lookup
/// @param put if true, put a new line when not found
/// if false, return NULL when not found
BufhlLine *bufhl_tree_ref(BufhlInfo *b, linenr_T line, bool put)
{
BufhlLine t = BUFHLLINE_INIT(line);
// kp_put() only works if key is absent, try get first
BufhlLine **pp = kb_get(bufhl, b, &t);
if (pp) {
return *pp;
} else if (!put) {
return NULL;
}
BufhlLine *p = xmalloc(sizeof(*p));
*p = (BufhlLine)BUFHLLINE_INIT(line);
kb_put(bufhl, b, p);
return p;
}
/// Adds a highlight to buffer.
///
/// Unlike matchaddpos() highlights follow changes to line numbering (as lines
/// are inserted/removed above the highlighted line), like signs and marks do.
///
/// When called with "src_id" set to 0, a unique source id is generated and
/// returned. Succesive calls can pass it in as "src_id" to add new highlights
/// to the same source group. All highlights in the same group can be cleared
/// at once. If the highlight never will be manually deleted pass in -1 for
/// "src_id"
///
/// if "hl_id" or "lnum" is invalid no highlight is added, but a new src_id
/// is still returned.
///
/// @param buf The 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 Id of the highlight group to use
/// @param lnum The line to highlight
/// @param col_start First column to highlight
/// @param col_end The last column to highlight,
/// or -1 to highlight to end of line
/// @return The src_id that was used
int bufhl_add_hl(buf_T *buf,
int src_id,
int hl_id,
linenr_T lnum,
colnr_T col_start,
colnr_T col_end)
{
if (src_id == 0) {
src_id = (int)nvim_create_namespace((String)STRING_INIT);
}
if (hl_id <= 0) {
// no highlight group or invalid line, just return src_id
return src_id;
}
BufhlLine *lineinfo = bufhl_tree_ref(&buf->b_bufhl_info, lnum, true);
BufhlItem *hlentry = kv_pushp(lineinfo->items);
hlentry->src_id = src_id;
hlentry->hl_id = hl_id;
hlentry->start = col_start;
hlentry->stop = col_end;
if (0 < lnum && lnum <= buf->b_ml.ml_line_count) {
redraw_buf_line_later(buf, lnum);
}
return src_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;
for (linenr_T lnum = pos_start.lnum; lnum <= pos_end.lnum; lnum ++) {
if (pos_start.lnum < lnum && lnum < pos_end.lnum) {
hl_start = offset;
hl_end = MAXCOL;
} else if (lnum == pos_start.lnum && lnum < pos_end.lnum) {
hl_start = pos_start.col + offset + 1;
hl_end = MAXCOL;
} else if (pos_start.lnum < lnum && lnum == pos_end.lnum) {
hl_start = offset;
hl_end = pos_end.col + offset;
} else if (pos_start.lnum == lnum && pos_end.lnum == lnum) {
hl_start = pos_start.col + offset + 1;
hl_end = pos_end.col + offset;
}
(void)bufhl_add_hl(buf, src_id, hl_id, lnum, hl_start, hl_end);
}
}
int bufhl_add_virt_text(buf_T *buf,
int src_id,
linenr_T lnum,
VirtText virt_text)
{
if (src_id == 0) {
src_id = (int)nvim_create_namespace((String)STRING_INIT);
}
BufhlLine *lineinfo = bufhl_tree_ref(&buf->b_bufhl_info, lnum, true);
bufhl_clear_virttext(&lineinfo->virt_text);
if (kv_size(virt_text) > 0) {
lineinfo->virt_text_src = src_id;
lineinfo->virt_text = virt_text;
} else {
lineinfo->virt_text_src = 0;
// currently not needed, but allow a future caller with
// 0 size and non-zero capacity
kv_destroy(virt_text);
}
if (0 < lnum && lnum <= buf->b_ml.ml_line_count) {
redraw_buf_line_later(buf, lnum);
}
return src_id;
}
static void bufhl_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;
}
/// Clear bufhl highlights from a given source group and range of lines.
///
/// @param buf The buffer to remove highlights from
/// @param src_id highlight source group to clear, or -1 to clear all groups.
/// @param line_start first line to clear
/// @param line_end last line to clear or MAXLNUM to clear to end of file.
void bufhl_clear_line_range(buf_T *buf,
int src_id,
linenr_T line_start,
linenr_T line_end)
{
// TODO(bfredl): implement kb_itr_interval to jump directly to the first line
kbitr_t(bufhl) itr;
BufhlLine *l, t = BUFHLLINE_INIT(line_start);
if (!kb_itr_get(bufhl, &buf->b_bufhl_info, &t, &itr)) {
kb_itr_next(bufhl, &buf->b_bufhl_info, &itr);
}
for (; kb_itr_valid(&itr); kb_itr_next(bufhl, &buf->b_bufhl_info, &itr)) {
l = kb_itr_key(&itr);
linenr_T line = l->line;
if (line > line_end) {
break;
}
if (line_start <= line) {
BufhlLineStatus status = bufhl_clear_line(l, src_id, line);
if (status != kBLSUnchanged) {
redraw_buf_line_later(buf, line);
}
if (status == kBLSDeleted) {
kb_del_itr(bufhl, &buf->b_bufhl_info, &itr);
xfree(l);
}
}
}
}
/// Clear bufhl highlights from a given source group and given line
///
/// @param bufhl_info The highlight info for the buffer
/// @param src_id Highlight source group to clear, or -1 to clear all groups.
/// @param lnum Linenr where the highlight should be cleared
static BufhlLineStatus bufhl_clear_line(BufhlLine *lineinfo, int src_id,
linenr_T lnum)
{
BufhlLineStatus changed = kBLSUnchanged;
size_t oldsize = kv_size(lineinfo->items);
if (src_id < 0) {
kv_size(lineinfo->items) = 0;
} else {
size_t newidx = 0;
for (size_t i = 0; i < kv_size(lineinfo->items); i++) {
if (kv_A(lineinfo->items, i).src_id != src_id) {
if (i != newidx) {
kv_A(lineinfo->items, newidx) = kv_A(lineinfo->items, i);
}
newidx++;
}
}
kv_size(lineinfo->items) = newidx;
}
if (kv_size(lineinfo->items) != oldsize) {
changed = kBLSChanged;
}
if (kv_size(lineinfo->virt_text) != 0
&& (src_id < 0 || src_id == lineinfo->virt_text_src)) {
bufhl_clear_virttext(&lineinfo->virt_text);
lineinfo->virt_text_src = 0;
changed = kBLSChanged;
}
if (kv_size(lineinfo->items) == 0 && kv_size(lineinfo->virt_text) == 0) {
kv_destroy(lineinfo->items);
return kBLSDeleted;
}
return changed;
}
/// Remove all highlights and free the highlight data
void bufhl_clear_all(buf_T *buf)
{
bufhl_clear_line_range(buf, -1, 1, MAXLNUM);
kb_destroy(bufhl, (&buf->b_bufhl_info));
kb_init(&buf->b_bufhl_info);
kv_destroy(buf->b_bufhl_move_space);
kv_init(buf->b_bufhl_move_space);
}
/// Adjust a placed highlight for inserted/deleted lines.
void bufhl_mark_adjust(buf_T* buf,
linenr_T line1,
linenr_T line2,
long amount,
long amount_after,
bool end_temp)
{
kbitr_t(bufhl) itr;
BufhlLine *l, t = BUFHLLINE_INIT(line1);
if (end_temp && amount < 0) {
// Move all items from b_bufhl_move_space to the btree.
for (size_t i = 0; i < kv_size(buf->b_bufhl_move_space); i++) {
l = kv_A(buf->b_bufhl_move_space, i);
l->line += amount;
kb_put(bufhl, &buf->b_bufhl_info, l);
}
kv_size(buf->b_bufhl_move_space) = 0;
return;
}
if (!kb_itr_get(bufhl, &buf->b_bufhl_info, &t, &itr)) {
kb_itr_next(bufhl, &buf->b_bufhl_info, &itr);
}
for (; kb_itr_valid(&itr); kb_itr_next(bufhl, &buf->b_bufhl_info, &itr)) {
l = kb_itr_key(&itr);
if (l->line >= line1 && l->line <= line2) {
if (end_temp && amount > 0) {
kb_del_itr(bufhl, &buf->b_bufhl_info, &itr);
kv_push(buf->b_bufhl_move_space, l);
}
if (amount == MAXLNUM) {
if (bufhl_clear_line(l, -1, l->line) == kBLSDeleted) {
kb_del_itr(bufhl, &buf->b_bufhl_info, &itr);
xfree(l);
} else {
assert(false);
}
} else {
l->line += amount;
}
} else if (l->line > line2) {
if (amount_after == 0) {
break;
}
l->line += amount_after;
}
}
}
/// 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
///
/// @param buf The buffer handle
/// @param lnum The line number
/// @param[out] info The highligts for the line
/// @return true if there was highlights to display
bool bufhl_start_line(buf_T *buf, linenr_T lnum, BufhlLineInfo *info)
{
BufhlLine *lineinfo = bufhl_tree_ref(&buf->b_bufhl_info, lnum, false);
if (!lineinfo) {
return false;
}
info->valid_to = -1;
info->line = lineinfo;
return true;
}
/// get highlighting at column col
///
/// It is is assumed this will be called with
/// non-decreasing column nrs, so that it is
/// possible to only recalculate highlights
/// at endpoints.
///
/// @param info The info returned by bufhl_start_line
/// @param col The column to get the attr for
/// @return The highilight attr to display at the column
int bufhl_get_attr(BufhlLineInfo *info, colnr_T col)
{
if (col <= info->valid_to) {
return info->current;
}
int attr = 0;
info->valid_to = MAXCOL;
for (size_t i = 0; i < kv_size(info->line->items); i++) {
BufhlItem entry = kv_A(info->line->items, i);
if (entry.start <= col && col <= entry.stop) {
int entry_attr = syn_id2attr(entry.hl_id);
attr = hl_combine_attr(attr, entry_attr);
if (entry.stop < info->valid_to) {
info->valid_to = entry.stop;
}
} else if (col < entry.start && entry.start-1 < info->valid_to) {
info->valid_to = entry.start-1;
}
}
info->current = attr;
return attr;
}
/*
* Set 'buflisted' for curbuf to "on" and trigger autocommands if it changed.
*/

View File

@@ -42,6 +42,8 @@ typedef struct {
#include "nvim/map.h"
// for kvec
#include "nvim/lib/kvec.h"
// for marktree
#include "nvim/marktree.h"
#define GETFILE_SUCCESS(x) ((x) <= 0)
#define MODIFIABLE(buf) (buf->b_p_ma)
@@ -109,15 +111,10 @@ typedef uint16_t disptick_T; // display tick type
#include "nvim/syntax_defs.h"
// for signlist_T
#include "nvim/sign_defs.h"
// for bufhl_*_T
#include "nvim/bufhl_defs.h"
#include "nvim/os/fs_defs.h" // for FileID
#include "nvim/terminal.h" // for Terminal
#include "nvim/lib/kbtree.h"
#include "nvim/mark_extended.h"
/*
* The taggy struct is used to store the information about a :tag command.
*/
@@ -461,11 +458,15 @@ typedef TV_DICTITEM_STRUCT(sizeof("changedtick")) ChangedtickDictItem;
typedef struct {
LuaRef on_lines;
LuaRef on_bytes;
LuaRef on_changedtick;
LuaRef on_detach;
bool utf_sizes;
} BufUpdateCallbacks;
#define BUF_UPDATE_CALLBACKS_INIT { LUA_NOREF, LUA_NOREF, LUA_NOREF, false }
#define BUF_UPDATE_CALLBACKS_INIT { LUA_NOREF, LUA_NOREF, LUA_NOREF, \
LUA_NOREF, false }
EXTERN int curbuf_splice_pending INIT(= 0);
#define BUF_HAS_QF_ENTRY 1
#define BUF_HAS_LL_ENTRY 2
@@ -804,13 +805,9 @@ struct file_buffer {
int b_mapped_ctrl_c; // modes where CTRL-C is mapped
BufhlInfo b_bufhl_info; // buffer stored highlights
kvec_t(BufhlLine *) b_bufhl_move_space; // temporary space for highlights
PMap(uint64_t) *b_extmark_ns; // extmark namespaces
kbtree_t(extmarklines) b_extlines; // extmarks
kvec_t(ExtmarkLine *) b_extmark_move_space; // temp space for extmarks
MarkTree b_marktree[1];
Map(uint64_t, ExtmarkItem) *b_extmark_index;
Map(uint64_t, ExtmarkNs) *b_extmark_ns; // extmark namespaces
// array of channel_id:s which have asked to receive updates for this
// buffer.

View File

@@ -281,6 +281,54 @@ void buf_updates_send_changes(buf_T *buf,
kv_size(buf->update_callbacks) = j;
}
void buf_updates_send_splice(buf_T *buf,
linenr_T start_line, colnr_T start_col,
linenr_T oldextent_line, colnr_T oldextent_col,
linenr_T newextent_line, colnr_T newextent_col)
{
if (!buf_updates_active(buf)) {
return;
}
// notify each of the active callbakcs
size_t j = 0;
for (size_t i = 0; i < kv_size(buf->update_callbacks); i++) {
BufUpdateCallbacks cb = kv_A(buf->update_callbacks, i);
bool keep = true;
if (cb.on_bytes != LUA_NOREF) {
Array args = ARRAY_DICT_INIT;
Object items[8];
args.size = 8;
args.items = items;
// the first argument is always the buffer handle
args.items[0] = BUFFER_OBJ(buf->handle);
// next argument is b:changedtick
args.items[1] = INTEGER_OBJ(buf_get_changedtick(buf));
args.items[2] = INTEGER_OBJ(start_line);
args.items[3] = INTEGER_OBJ(start_col);
args.items[4] = INTEGER_OBJ(oldextent_line);
args.items[5] = INTEGER_OBJ(oldextent_col);
args.items[6] = INTEGER_OBJ(newextent_line);
args.items[7] = INTEGER_OBJ(newextent_col);
textlock++;
Object res = executor_exec_lua_cb(cb.on_bytes, "bytes", args, true, NULL);
textlock--;
if (res.type == kObjectTypeBoolean && res.data.boolean == true) {
free_update_callbacks(cb);
keep = false;
}
}
if (keep) {
kv_A(buf->update_callbacks, j++) = kv_A(buf->update_callbacks, i);
}
}
kv_size(buf->update_callbacks) = j;
}
void buf_updates_changedtick(buf_T *buf)
{
// notify each of the active channels

View File

@@ -1,41 +0,0 @@
#ifndef NVIM_BUFHL_DEFS_H
#define NVIM_BUFHL_DEFS_H
#include "nvim/pos.h"
#include "nvim/lib/kvec.h"
#include "nvim/lib/kbtree.h"
// bufhl: buffer specific highlighting
typedef struct {
int src_id;
int hl_id; // highlight group
colnr_T start; // first column to highlight
colnr_T stop; // last column to highlight
} BufhlItem;
typedef struct {
char *text;
int hl_id;
} VirtTextChunk;
typedef kvec_t(VirtTextChunk) VirtText;
typedef struct {
linenr_T line;
kvec_t(BufhlItem) items;
int virt_text_src;
VirtText virt_text;
} BufhlLine;
#define BUFHLLINE_INIT(l) { l, KV_INITIAL_VALUE, 0, KV_INITIAL_VALUE }
typedef struct {
BufhlLine *line;
int current;
colnr_T valid_to;
} BufhlLineInfo;
#define BUFHL_CMP(a, b) ((int)(((a)->line - (b)->line)))
KBTREE_INIT(bufhl, BufhlLine *, BUFHL_CMP, 10) // -V512
typedef kbtree_t(bufhl) BufhlInfo;
#endif // NVIM_BUFHL_DEFS_H

View File

@@ -363,15 +363,10 @@ void changed_bytes(linenr_T lnum, colnr_T col)
///
/// 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)
static void inserted_bytes(linenr_T lnum, colnr_T col, int old, int new)
{
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);
if (curbuf_splice_pending == 0) {
extmark_splice(curbuf, (int)lnum-1, col, 0, old, 0, new, kExtmarkUndo);
}
changed_bytes(lnum, col);
@@ -391,7 +386,10 @@ void appended_lines_mark(linenr_T lnum, long count)
// Skip mark_adjust when adding a line after the last one, there can't
// be marks there. But it's still needed in diff mode.
if (lnum + count < curbuf->b_ml.ml_line_count || curwin->w_p_diff) {
mark_adjust(lnum + 1, (linenr_T)MAXLNUM, count, 0L, false, kExtmarkUndo);
mark_adjust(lnum + 1, (linenr_T)MAXLNUM, count, 0L, kExtmarkUndo);
} else {
extmark_adjust(curbuf, lnum + 1, (linenr_T)MAXLNUM, count, 0L,
kExtmarkUndo);
}
changed_lines(lnum + 1, 0, lnum + 1, count, true);
}
@@ -409,7 +407,7 @@ void deleted_lines(linenr_T lnum, long count)
/// be triggered to display the cursor.
void deleted_lines_mark(linenr_T lnum, long count)
{
mark_adjust(lnum, (linenr_T)(lnum + count - 1), (long)MAXLNUM, -count, false,
mark_adjust(lnum, (linenr_T)(lnum + count - 1), (long)MAXLNUM, -count,
kExtmarkUndo);
changed_lines(lnum, 0, lnum + count, -count, true);
}
@@ -648,7 +646,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
inserted_bytes(lnum, (colnr_T)col, (int)(newlen - oldlen));
inserted_bytes(lnum, (colnr_T)col, (int)oldlen, (int)newlen);
// If we're in Insert or Replace mode and 'showmatch' is set, then briefly
// show the match for right parens and braces.
@@ -694,7 +692,7 @@ void ins_str(char_u *s)
assert(bytes >= 0);
memmove(newp + col + newlen, oldp + col, (size_t)bytes);
ml_replace(lnum, newp, false);
inserted_bytes(lnum, col, newlen);
inserted_bytes(lnum, col, 0, newlen);
curwin->w_cursor.col += newlen;
}
@@ -815,7 +813,7 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
}
// mark the buffer as changed and prepare for displaying
inserted_bytes(lnum, col, -count);
inserted_bytes(lnum, col, count, 0);
return OK;
}
@@ -1583,6 +1581,7 @@ int open_line(
end_comment_pending = NUL; // turns out there was no leader
}
curbuf_splice_pending++;
old_cursor = curwin->w_cursor;
if (dir == BACKWARD) {
curwin->w_cursor.lnum--;
@@ -1597,7 +1596,7 @@ int open_line(
// be marks there. But still needed in diff mode.
if (curwin->w_cursor.lnum + 1 < curbuf->b_ml.ml_line_count
|| curwin->w_p_diff) {
mark_adjust(curwin->w_cursor.lnum + 1, (linenr_T)MAXLNUM, 1L, 0L, false,
mark_adjust(curwin->w_cursor.lnum + 1, (linenr_T)MAXLNUM, 1L, 0L,
kExtmarkUndo);
}
did_append = true;
@@ -1638,7 +1637,7 @@ int open_line(
// it. It gets restored at the function end.
curbuf->b_p_pi = true;
} else {
(void)set_indent(newindent, SIN_INSERT);
(void)set_indent(newindent, SIN_INSERT|SIN_NOMARK);
}
less_cols -= curwin->w_cursor.col;
@@ -1687,12 +1686,13 @@ int open_line(
if (flags & OPENLINE_MARKFIX) {
mark_col_adjust(curwin->w_cursor.lnum,
curwin->w_cursor.col + less_cols_off,
1L, (long)-less_cols, 0, kExtmarkNOOP);
1L, (long)-less_cols, 0);
}
// Always move extmarks - Here we move only the line where the
// cursor is, the previous mark_adjust takes care of the lines after
extmark_col_adjust(curbuf, lnum, mincol, 1L, (long)-less_cols,
kExtmarkUndo);
int cols_added = mincol-1+less_cols_off-less_cols;
extmark_splice(curbuf, (int)lnum-1, mincol-1, 0, less_cols_off,
1, cols_added, kExtmarkUndo);
} else {
changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col);
}
@@ -1704,7 +1704,10 @@ int open_line(
}
if (did_append) {
changed_lines(curwin->w_cursor.lnum, 0, curwin->w_cursor.lnum, 1L, true);
extmark_splice(curbuf, (int)curwin->w_cursor.lnum-1,
0, 0, 0, 1, 0, kExtmarkUndo);
}
curbuf_splice_pending--;
curwin->w_cursor.col = newcol;
curwin->w_cursor.coladd = 0;

View File

@@ -2711,7 +2711,7 @@ void ex_diffgetput(exarg_T *eap)
// Adjust marks. This will change the following entries!
if (added != 0) {
mark_adjust(lnum, lnum + count - 1, (long)MAXLNUM, (long)added, false,
mark_adjust(lnum, lnum + count - 1, (long)MAXLNUM, (long)added,
kExtmarkUndo);
if (curwin->w_cursor.lnum >= lnum) {
// Adjust the cursor position if it's in/after the changed

View File

@@ -1826,11 +1826,14 @@ change_indent (
/* We only put back the new line up to the cursor */
new_line[curwin->w_cursor.col] = NUL;
int new_col = curwin->w_cursor.col;
// Put back original line
ml_replace(curwin->w_cursor.lnum, orig_line, false);
curwin->w_cursor.col = orig_col;
curbuf_splice_pending++;
/* Backspace from cursor to start of line */
backspace_until_column(0);
@@ -1838,13 +1841,16 @@ change_indent (
ins_bytes(new_line);
xfree(new_line);
}
// change_indent seems to bec called twice, this combination only triggers
// once for both calls
if (new_cursor_col - vcol != 0) {
extmark_col_adjust(curbuf, curwin->w_cursor.lnum, 0, 0, amount,
kExtmarkUndo);
curbuf_splice_pending--;
// TODO(bfredl): test for crazy edge cases, like we stand on a TAB or
// something? does this even do the right text change then?
int delta = orig_col - new_col;
extmark_splice(curbuf, curwin->w_cursor.lnum-1, new_col,
0, delta < 0 ? -delta : 0,
0, delta > 0 ? delta : 0,
kExtmarkUndo);
}
}

View File

@@ -14,6 +14,7 @@
#include <math.h>
#include "nvim/api/private/defs.h"
#include "nvim/api/vim.h"
#include "nvim/api/buffer.h"
#include "nvim/log.h"
#include "nvim/vim.h"
@@ -659,10 +660,10 @@ void ex_sort(exarg_T *eap)
deleted = (long)(count - (lnum - eap->line2));
if (deleted > 0) {
mark_adjust(eap->line2 - deleted, eap->line2, (long)MAXLNUM, -deleted,
false, kExtmarkUndo);
kExtmarkUndo);
msgmore(-deleted);
} else if (deleted < 0) {
mark_adjust(eap->line2, MAXLNUM, -deleted, 0L, false, kExtmarkUndo);
mark_adjust(eap->line2, MAXLNUM, -deleted, 0L, kExtmarkUndo);
}
if (change_occurred || deleted != 0) {
changed_lines(eap->line1, 0, eap->line2 + 1, -deleted, true);
@@ -875,12 +876,10 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
* their final destination at the new text position -- webb
*/
last_line = curbuf->b_ml.ml_line_count;
mark_adjust_nofold(line1, line2, last_line - line2, 0L, true, kExtmarkNoUndo);
extmark_adjust(curbuf, line1, line2, last_line - line2, 0L, kExtmarkNoUndo,
true);
mark_adjust_nofold(line1, line2, last_line - line2, 0L, kExtmarkNOOP);
changed_lines(last_line - num_lines + 1, 0, last_line + 1, num_lines, false);
if (dest >= line2) {
mark_adjust_nofold(line2 + 1, dest, -num_lines, 0L, false, kExtmarkNoUndo);
mark_adjust_nofold(line2 + 1, dest, -num_lines, 0L, kExtmarkNOOP);
FOR_ALL_TAB_WINDOWS(tab, win) {
if (win->w_buffer == curbuf) {
foldMoveRange(&win->w_folds, line1, line2, dest);
@@ -889,8 +888,7 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
curbuf->b_op_start.lnum = dest - num_lines + 1;
curbuf->b_op_end.lnum = dest;
} else {
mark_adjust_nofold(dest + 1, line1 - 1, num_lines, 0L, false,
kExtmarkNoUndo);
mark_adjust_nofold(dest + 1, line1 - 1, num_lines, 0L, kExtmarkNOOP);
FOR_ALL_TAB_WINDOWS(tab, win) {
if (win->w_buffer == curbuf) {
foldMoveRange(&win->w_folds, dest + 1, line1 - 1, line2);
@@ -901,9 +899,15 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
}
curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
mark_adjust_nofold(last_line - num_lines + 1, last_line,
-(last_line - dest - extra), 0L, true, kExtmarkNoUndo);
-(last_line - dest - extra), 0L, kExtmarkNOOP);
// extmarks are handled separately
int size = line2-line1+1;
int off = dest >= line2 ? -size : 0;
extmark_move_region(curbuf, line1-1, 0,
line2-line1+1, 0,
dest+off, 0, kExtmarkUndo);
u_extmark_move(curbuf, line1, line2, last_line, dest, num_lines, extra);
changed_lines(last_line - num_lines + 1, 0, last_line + 1, -extra, false);
// send update regarding the new lines that were added
@@ -1285,16 +1289,19 @@ static void do_filter(
if (do_in) {
if (cmdmod.keepmarks || vim_strchr(p_cpo, CPO_REMMARK) == NULL) {
// TODO(bfredl): Currently not active for extmarks. What would we
// do if columns don't match, assume added/deleted bytes at the
// end of each line?
if (read_linecount >= linecount) {
// move all marks from old lines to new lines
mark_adjust(line1, line2, linecount, 0L, false, kExtmarkUndo);
mark_adjust(line1, line2, linecount, 0L, kExtmarkNOOP);
} else {
// move marks from old lines to new lines, delete marks
// that are in deleted lines
mark_adjust(line1, line1 + read_linecount - 1, linecount, 0L, false,
kExtmarkUndo);
mark_adjust(line1 + read_linecount, line2, MAXLNUM, 0L, false,
kExtmarkUndo);
mark_adjust(line1, line1 + read_linecount - 1, linecount, 0L,
kExtmarkNOOP);
mark_adjust(line1 + read_linecount, line2, MAXLNUM, 0L,
kExtmarkNOOP);
}
}
@@ -3222,186 +3229,6 @@ static char_u *sub_parse_flags(char_u *cmd, subflags_T *subflags,
return cmd;
}
static void extmark_move_regmatch_single(lpos_T startpos,
lpos_T endpos,
linenr_T lnum,
int sublen)
{
colnr_T mincol;
colnr_T endcol;
colnr_T col_amount;
mincol = startpos.col + 1;
endcol = endpos.col + 1;
// There are cases such as :s/^/x/ where this happens
// a delete is simply not required.
if (mincol + 1 <= endcol) {
extmark_col_adjust_delete(curbuf,
lnum, mincol + 1, endcol, kExtmarkUndo, 0);
}
// Insert, sublen seems to be the value we need but + 1...
col_amount = sublen - 1;
extmark_col_adjust(curbuf, lnum, mincol, 0, col_amount, kExtmarkUndo);
}
static void extmark_move_regmatch_multi(ExtmarkSubMulti s, long i)
{
colnr_T mincol;
mincol = s.startpos.col + 1;
linenr_T n_u_lnum = s.lnum + s.endpos.lnum - s.startpos.lnum;
colnr_T n_after_newline_in_pat = s.endpos.col;
colnr_T n_before_newline_in_pat = mincol - s.cm_start.col;
long n_after_newline_in_sub;
if (!s.newline_in_sub) {
n_after_newline_in_sub = s.cm_end.col - s.cm_start.col;
} else {
n_after_newline_in_sub = s.cm_end.col;
}
if (s.newline_in_pat && !s.newline_in_sub) {
// -- Delete Pattern --
// 1. Move marks in the pattern
mincol = s.startpos.col + 1;
linenr_T u_lnum = n_u_lnum;
assert(n_u_lnum == u_lnum);
extmark_copy_and_place(curbuf,
s.lnum, mincol,
u_lnum, n_after_newline_in_pat,
s.lnum, mincol,
kExtmarkUndo, true, NULL);
// 2. Move marks on last newline
mincol = mincol - n_before_newline_in_pat;
extmark_col_adjust(curbuf,
u_lnum,
n_after_newline_in_pat + 1,
-s.newline_in_pat,
mincol - n_after_newline_in_pat,
kExtmarkUndo);
// Take care of the lines after
extmark_adjust(curbuf,
u_lnum,
u_lnum,
MAXLNUM,
-s.newline_in_pat,
kExtmarkUndo,
false);
// 1. first insert the text in the substitutaion
extmark_col_adjust(curbuf,
s.lnum,
mincol + 1,
s.newline_in_sub,
n_after_newline_in_sub,
kExtmarkUndo);
} else {
// The data in sub_obj is as if the substituons above had already taken
// place. For our extmarks they haven't as we work from the bottom of the
// buffer up. Readjust the data.
n_u_lnum = s.lnum + s.endpos.lnum - s.startpos.lnum;
n_u_lnum = n_u_lnum - s.lnum_added;
// adjusted = L - (i-1)N
// where L = lnum value, N= lnum_added and i = iteration
linenr_T a_l_lnum = s.cm_start.lnum - ((i -1) * s.lnum_added);
linenr_T a_u_lnum = a_l_lnum + s.endpos.lnum;
assert(s.startpos.lnum == 0);
mincol = s.startpos.col + 1;
if (!s.newline_in_pat && s.newline_in_sub) {
// -- Delete Pattern --
// 1. Move marks in the pattern
extmark_col_adjust_delete(curbuf,
a_l_lnum,
mincol + 1,
s.endpos.col + 1,
kExtmarkUndo,
s.eol);
extmark_adjust(curbuf,
a_u_lnum + 1,
MAXLNUM,
(long)s.newline_in_sub,
0,
kExtmarkUndo,
false);
// 3. Insert
extmark_col_adjust(curbuf,
a_l_lnum,
mincol,
s.newline_in_sub,
(long)-mincol + 1 + n_after_newline_in_sub,
kExtmarkUndo);
} else if (s.newline_in_pat && s.newline_in_sub) {
if (s.lnum_added >= 0) {
linenr_T u_col = n_after_newline_in_pat == 0
? 1 : n_after_newline_in_pat;
extmark_copy_and_place(curbuf,
a_l_lnum, mincol,
a_u_lnum, u_col,
a_l_lnum, mincol,
kExtmarkUndo, true, NULL);
// 2. Move marks on last newline
mincol = mincol - (colnr_T)n_before_newline_in_pat;
extmark_col_adjust(curbuf,
a_u_lnum,
(colnr_T)(n_after_newline_in_pat + 1),
-s.newline_in_pat,
mincol - n_after_newline_in_pat,
kExtmarkUndo);
// TODO(timeyyy): nothing to do here if lnum_added = 0
extmark_adjust(curbuf,
a_u_lnum + 1,
MAXLNUM,
(long)s.lnum_added,
0,
kExtmarkUndo,
false);
extmark_col_adjust(curbuf,
a_l_lnum,
mincol + 1,
s.newline_in_sub,
(long)-mincol + n_after_newline_in_sub,
kExtmarkUndo);
} else {
mincol = s.startpos.col + 1;
a_l_lnum = s.startpos.lnum + 1;
a_u_lnum = s.endpos.lnum + 1;
extmark_copy_and_place(curbuf,
a_l_lnum, mincol,
a_u_lnum, n_after_newline_in_pat,
a_l_lnum, mincol,
kExtmarkUndo, true, NULL);
// 2. Move marks on last newline
mincol = mincol - (colnr_T)n_before_newline_in_pat;
extmark_col_adjust(curbuf,
a_u_lnum,
(colnr_T)(n_after_newline_in_pat + 1),
-s.newline_in_pat,
mincol - n_after_newline_in_pat,
kExtmarkUndo);
extmark_adjust(curbuf,
a_u_lnum,
a_u_lnum,
MAXLNUM,
s.lnum_added,
kExtmarkUndo,
false);
// 3. Insert
extmark_col_adjust(curbuf,
a_l_lnum,
mincol + 1,
s.newline_in_sub,
(long)-mincol + n_after_newline_in_sub,
kExtmarkUndo);
}
}
}
}
/// Perform a substitution from line eap->line1 to line eap->line2 using the
/// command pointed to by eap->arg which should be of the form:
@@ -3449,11 +3276,6 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
int save_ma = 0;
int save_b_changed = curbuf->b_changed;
bool preview = (State & CMDPREVIEW);
extmark_sub_multi_vec_t extmark_sub_multi = KV_INITIAL_VALUE;
extmark_sub_single_vec_t extmark_sub_single = KV_INITIAL_VALUE;
linenr_T no_of_lines_changed = 0;
linenr_T newline_in_pat = 0;
linenr_T newline_in_sub = 0;
// inccommand tests fail without this check
if (!preview) {
@@ -4010,9 +3832,11 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
goto skip;
}
// 3. Substitute the string. During 'inccommand' preview only do this if
// there is a replace pattern.
if (!preview || has_second_delim) {
long lnum_start = lnum; // save the start lnum
save_ma = curbuf->b_p_ma;
if (subflags.do_count) {
// prevent accidentally changing the buffer by a function
@@ -4060,7 +3884,8 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
// Finally, at this point we can know where the match actually will
// start in the new text
current_match.start.col = new_end - new_start;
int start_col = new_end - new_start;
current_match.start.col = start_col;
(void)vim_regsub_multi(&regmatch,
sub_firstlnum - regmatch.startpos[0].lnum,
@@ -4092,8 +3917,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
*p1 = NUL; // truncate up to the CR
ml_append(lnum - 1, new_start,
(colnr_T)(p1 - new_start + 1), false);
mark_adjust(lnum + 1, (linenr_T)MAXLNUM, 1L, 0L, false,
kExtmarkNOOP);
mark_adjust(lnum + 1, (linenr_T)MAXLNUM, 1L, 0L, kExtmarkNOOP);
if (subflags.do_ask) {
appended_lines(lnum - 1, 1L);
@@ -4117,45 +3941,21 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
p1 += (*mb_ptr2len)(p1) - 1;
}
}
current_match.end.col = STRLEN(new_start);
size_t new_endcol = STRLEN(new_start);
current_match.end.col = new_endcol;
current_match.end.lnum = lnum;
}
// Adjust extmarks, by delete and then insert
if (!preview) {
newline_in_pat = (regmatch.endpos[0].lnum
- regmatch.startpos[0].lnum);
newline_in_sub = current_match.end.lnum - current_match.start.lnum;
if (newline_in_pat || newline_in_sub) {
ExtmarkSubMulti sub_multi;
no_of_lines_changed = newline_in_sub - newline_in_pat;
sub_multi.newline_in_pat = newline_in_pat;
sub_multi.newline_in_sub = newline_in_sub;
sub_multi.lnum = lnum;
sub_multi.lnum_added = no_of_lines_changed;
sub_multi.cm_start = current_match.start;
sub_multi.cm_end = current_match.end;
sub_multi.startpos = regmatch.startpos[0];
sub_multi.endpos = regmatch.endpos[0];
sub_multi.eol = extmark_eol_col(curbuf, lnum);
kv_push(extmark_sub_multi, sub_multi);
// Collect information required for moving extmarks WITHOUT \n, \r
} else {
no_of_lines_changed = 0;
if (regmatch.startpos[0].col != -1) {
ExtmarkSubSingle sub_single;
sub_single.sublen = sublen;
sub_single.lnum = lnum;
sub_single.startpos = regmatch.startpos[0];
sub_single.endpos = regmatch.endpos[0];
kv_push(extmark_sub_single, sub_single);
// TODO(bfredl): adjust in preview, because decorations?
// this has some robustness issues, will look into later.
if (!preview) {
lpos_T start = regmatch.startpos[0], end = regmatch.endpos[0];
int matchcols = end.col - ((end.lnum == start.lnum)
? start.col : 0);
int subcols = new_endcol - ((lnum == lnum_start) ? start_col : 0);
extmark_splice(curbuf, lnum_start-1, start_col,
end.lnum-start.lnum, matchcols,
lnum-lnum_start, subcols, kExtmarkUndo);
}
}
}
@@ -4225,7 +4025,7 @@ skip:
ml_delete(lnum, false);
}
mark_adjust(lnum, lnum + nmatch_tl - 1,
(long)MAXLNUM, -nmatch_tl, false, kExtmarkNOOP);
(long)MAXLNUM, -nmatch_tl, kExtmarkNOOP);
if (subflags.do_ask) {
deleted_lines(lnum, nmatch_tl);
}
@@ -4387,7 +4187,7 @@ skip:
} else if (*p_icm != NUL && pat != NULL) {
if (pre_src_id == 0) {
// Get a unique new src_id, saved in a static
pre_src_id = bufhl_add_hl(NULL, 0, -1, 0, 0, 0);
pre_src_id = (int)nvim_create_namespace((String)STRING_INIT);
}
if (pre_hl_id == 0) {
pre_hl_id = syn_check_group((char_u *)S_LEN("Substitute"));
@@ -4396,40 +4196,11 @@ skip:
preview_buf = show_sub(eap, old_cursor, &preview_lines,
pre_hl_id, pre_src_id);
if (subsize > 0) {
bufhl_clear_line_range(orig_buf, pre_src_id, eap->line1,
kv_last(preview_lines.subresults).end.lnum);
extmark_clear(orig_buf, pre_src_id, eap->line1-1, 0,
kv_last(preview_lines.subresults).end.lnum-1, MAXCOL);
}
}
}
if (newline_in_pat || newline_in_sub) {
long n = (long)kv_size(extmark_sub_multi);
ExtmarkSubMulti sub_multi;
if (no_of_lines_changed < 0) {
for (i = 0; i < n; i++) {
sub_multi = kv_A(extmark_sub_multi, i);
extmark_move_regmatch_multi(sub_multi, i);
}
} else {
// Move extmarks in reverse order to avoid moving marks we just moved...
for (i = 0; i < n; i++) {
sub_multi = kv_Z(extmark_sub_multi, i);
extmark_move_regmatch_multi(sub_multi, n - i);
}
}
kv_destroy(extmark_sub_multi);
} else {
long n = (long)kv_size(extmark_sub_single);
ExtmarkSubSingle sub_single;
for (i = 0; i < n; i++) {
sub_single = kv_Z(extmark_sub_single, i);
extmark_move_regmatch_single(sub_single.startpos,
sub_single.endpos,
sub_single.lnum,
sub_single.sublen);
}
kv_destroy(extmark_sub_single);
}
kv_destroy(preview_lines.subresults);

View File

@@ -22,6 +22,7 @@
#include "nvim/func_attr.h"
#include "nvim/indent.h"
#include "nvim/buffer_updates.h"
#include "nvim/mark_extended.h"
#include "nvim/mark.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
@@ -1610,6 +1611,7 @@ static void foldAddMarker(linenr_T lnum, const char_u *marker, size_t markerlen)
// Allocate a new line: old-line + 'cms'-start + marker + 'cms'-end
line = ml_get(lnum);
size_t line_len = STRLEN(line);
size_t added = 0;
if (u_save(lnum - 1, lnum + 1) == OK) {
// Check if the line ends with an unclosed comment
@@ -1619,12 +1621,19 @@ static void foldAddMarker(linenr_T lnum, const char_u *marker, size_t markerlen)
// Append the marker to the end of the line
if (p == NULL || line_is_comment) {
STRLCPY(newline + line_len, marker, markerlen + 1);
added = markerlen;
} else {
STRCPY(newline + line_len, cms);
memcpy(newline + line_len + (p - cms), marker, markerlen);
STRCPY(newline + line_len + (p - cms) + markerlen, p + 2);
added = markerlen + STRLEN(cms)-2;
}
ml_replace(lnum, newline, false);
if (added) {
extmark_splice(curbuf, (int)lnum-1, (int)line_len,
0, 0,
0, (int)added, kExtmarkUndo);
}
}
}
@@ -1692,6 +1701,9 @@ static void foldDelMarker(linenr_T lnum, char_u *marker, size_t markerlen)
memcpy(newline, line, (size_t)(p - line));
STRCPY(newline + (p - line), p + len);
ml_replace(lnum, newline, false);
extmark_splice(curbuf, (int)lnum-1, (int)(p - line),
0, (int)len,
0, 0, kExtmarkUndo);
}
break;
}

View File

@@ -13,6 +13,7 @@
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/mark.h"
#include "nvim/mark_extended.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/misc1.h"
@@ -88,6 +89,7 @@ int get_indent_str(char_u *ptr, int ts, int list)
// SIN_CHANGED: call changed_bytes() if the line was changed.
// SIN_INSERT: insert the indent in front of the line.
// SIN_UNDO: save line for undo before changing it.
// SIN_NOMARK: don't move extmarks (because just after ml_append or something)
// @param size measured in spaces
// Returns true if the line was changed.
int set_indent(int size, int flags)
@@ -205,6 +207,7 @@ int set_indent(int size, int flags)
// If 'preserveindent' and 'expandtab' are both set keep the original
// characters and allocate accordingly. We will fill the rest with spaces
// after the if (!curbuf->b_p_et) below.
int skipcols = 0; // number of columns (in bytes) that were presved
if (orig_char_len != -1) {
int newline_size; // = orig_char_len + size - ind_done + line_len
STRICT_ADD(orig_char_len, size, &newline_size, int);
@@ -219,6 +222,7 @@ int set_indent(int size, int flags)
ind_len = orig_char_len + todo;
p = oldline;
s = newline;
skipcols = orig_char_len;
while (orig_char_len > 0) {
*s++ = *p++;
@@ -263,6 +267,7 @@ int set_indent(int size, int flags)
ind_done++;
}
*s++ = *p++;
skipcols++;
}
// Fill to next tabstop with a tab, if possible.
@@ -290,6 +295,13 @@ int set_indent(int size, int flags)
// Replace the line (unless undo fails).
if (!(flags & SIN_UNDO) || (u_savesub(curwin->w_cursor.lnum) == OK)) {
ml_replace(curwin->w_cursor.lnum, newline, false);
if (!(flags & SIN_NOMARK)) {
extmark_splice(curbuf,
(int)curwin->w_cursor.lnum-1, skipcols,
0, (int)(p-oldline) - skipcols,
0, (int)(s-newline) - skipcols,
kExtmarkUndo);
}
if (flags & SIN_CHANGED) {
changed_bytes(curwin->w_cursor.lnum, 0);

View File

@@ -3,10 +3,11 @@
#include "nvim/vim.h"
/* flags for set_indent() */
#define SIN_CHANGED 1 /* call changed_bytes() when line changed */
#define SIN_INSERT 2 /* insert indent before existing text */
#define SIN_UNDO 4 /* save line for undo before changing it */
// flags for set_indent()
#define SIN_CHANGED 1 // call changed_bytes() when line changed
#define SIN_INSERT 2 // insert indent before existing text
#define SIN_UNDO 4 // save line for undo before changing it
#define SIN_NOMARK 8 // don't adjust extmarks
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "indent.h.generated.h"

View File

@@ -44,7 +44,8 @@
#define INITIALIZER(T, U) T##_##U##_initializer
#define INITIALIZER_DECLARE(T, U, ...) const U INITIALIZER(T, U) = __VA_ARGS__
#define DEFAULT_INITIALIZER {0}
#define DEFAULT_INITIALIZER { 0 }
#define SSIZE_INITIALIZER { -1 }
#define MAP_IMPL(T, U, ...) \
INITIALIZER_DECLARE(T, U, __VA_ARGS__); \
@@ -178,10 +179,16 @@ MAP_IMPL(int, int, DEFAULT_INITIALIZER)
MAP_IMPL(cstr_t, ptr_t, DEFAULT_INITIALIZER)
MAP_IMPL(ptr_t, ptr_t, DEFAULT_INITIALIZER)
MAP_IMPL(uint64_t, ptr_t, DEFAULT_INITIALIZER)
MAP_IMPL(uint64_t, ssize_t, SSIZE_INITIALIZER)
MAP_IMPL(uint64_t, uint64_t, DEFAULT_INITIALIZER)
#define EXTMARK_NS_INITIALIZER { 0, 0 }
MAP_IMPL(uint64_t, ExtmarkNs, EXTMARK_NS_INITIALIZER)
#define KVEC_INITIALIZER { .size = 0, .capacity = 0, .items = NULL }
#define EXTMARK_ITEM_INITIALIZER { 0, 0, 0, KVEC_INITIALIZER }
MAP_IMPL(uint64_t, ExtmarkItem, EXTMARK_ITEM_INITIALIZER)
MAP_IMPL(handle_T, ptr_t, DEFAULT_INITIALIZER)
#define MSGPACK_HANDLER_INITIALIZER { .fn = NULL, .fast = false }
MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER)
#define KVEC_INITIALIZER { .size = 0, .capacity = 0, .items = NULL }
MAP_IMPL(HlEntry, int, DEFAULT_INITIALIZER)
MAP_IMPL(String, handle_T, 0)

View File

@@ -4,9 +4,9 @@
#include <stdbool.h>
#include "nvim/map_defs.h"
#include "nvim/mark_extended_defs.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/dispatch.h"
#include "nvim/bufhl_defs.h"
#include "nvim/highlight_defs.h"
#if defined(__NetBSD__)
@@ -38,6 +38,18 @@ MAP_DECLS(int, int)
MAP_DECLS(cstr_t, ptr_t)
MAP_DECLS(ptr_t, ptr_t)
MAP_DECLS(uint64_t, ptr_t)
MAP_DECLS(uint64_t, ssize_t)
MAP_DECLS(uint64_t, uint64_t)
// NB: this is the only way to define a struct both containing and contained
// in a map...
typedef struct ExtmarkNs { // For namespacing extmarks
Map(uint64_t, uint64_t) *map; // For fast lookup
uint64_t free_id; // For automatically assigning id's
} ExtmarkNs;
MAP_DECLS(uint64_t, ExtmarkNs)
MAP_DECLS(uint64_t, ExtmarkItem)
MAP_DECLS(handle_T, ptr_t)
MAP_DECLS(String, MsgpackRpcRequestHandler)
MAP_DECLS(HlEntry, int)
@@ -53,6 +65,8 @@ MAP_DECLS(String, handle_T)
#define map_del(T, U) map_##T##_##U##_del
#define map_clear(T, U) map_##T##_##U##_clear
#define map_size(map) ((map)->table->size)
#define pmap_new(T) map_new(T, ptr_t)
#define pmap_free(T) map_free(T, ptr_t)
#define pmap_get(T) map_get(T, ptr_t)

View File

@@ -20,6 +20,7 @@
#include "nvim/ex_cmds.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/mark_extended.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
@@ -915,10 +916,9 @@ void mark_adjust(linenr_T line1,
linenr_T line2,
long amount,
long amount_after,
bool end_temp,
ExtmarkOp op)
{
mark_adjust_internal(line1, line2, amount, amount_after, true, end_temp, op);
mark_adjust_internal(line1, line2, amount, amount_after, true, op);
}
// mark_adjust_nofold() does the same as mark_adjust() but without adjusting
@@ -927,15 +927,15 @@ void mark_adjust(linenr_T line1,
// calling foldMarkAdjust() with arguments line1, line2, amount, amount_after,
// for an example of why this may be necessary, see do_move().
void mark_adjust_nofold(linenr_T line1, linenr_T line2, long amount,
long amount_after, bool end_temp,
long amount_after,
ExtmarkOp op)
{
mark_adjust_internal(line1, line2, amount, amount_after, false, end_temp, op);
mark_adjust_internal(line1, line2, amount, amount_after, false, op);
}
static void mark_adjust_internal(linenr_T line1, linenr_T line2,
long amount, long amount_after,
bool adjust_folds, bool end_temp,
bool adjust_folds,
ExtmarkOp op)
{
int i;
@@ -991,9 +991,8 @@ static void mark_adjust_internal(linenr_T line1, linenr_T line2,
}
sign_mark_adjust(line1, line2, amount, amount_after);
bufhl_mark_adjust(curbuf, line1, line2, amount, amount_after, end_temp);
if (op != kExtmarkNOOP) {
extmark_adjust(curbuf, line1, line2, amount, amount_after, op, end_temp);
extmark_adjust(curbuf, line1, line2, amount, amount_after, op);
}
}
@@ -1106,7 +1105,7 @@ static void mark_adjust_internal(linenr_T line1, linenr_T line2,
// cursor is inside them.
void mark_col_adjust(
linenr_T lnum, colnr_T mincol, long lnum_amount, long col_amount,
int spaces_removed, ExtmarkOp op)
int spaces_removed)
{
int i;
int fnum = curbuf->b_fnum;
@@ -1126,13 +1125,6 @@ void mark_col_adjust(
col_adjust(&(namedfm[i].fmark.mark));
}
// Extmarks
if (op != kExtmarkNOOP) {
// TODO(timeyyy): consider spaces_removed? (behave like a delete)
extmark_col_adjust(curbuf, lnum, mincol, lnum_amount, col_amount,
kExtmarkUndo);
}
/* last Insert position */
col_adjust(&(curbuf->b_last_insert.mark));

View File

@@ -6,6 +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/memory.h"
#include "nvim/pos.h"
#include "nvim/os/time.h"

File diff suppressed because it is too large Load Diff

View File

@@ -1,236 +1,57 @@
#ifndef NVIM_MARK_EXTENDED_H
#define NVIM_MARK_EXTENDED_H
#include "nvim/buffer_defs.h"
#include "nvim/mark_extended_defs.h"
#include "nvim/buffer_defs.h" // for buf_T
#include "nvim/marktree.h"
EXTERN int extmark_splice_pending INIT(= 0);
// Macro Documentation: FOR_ALL_?
// Search exclusively using the range values given.
// Use MAXCOL/MAXLNUM for the start and end of the line/col.
// The ns parameter: Unless otherwise stated, this is only a starting point
// for the btree to searched in, the results being itterated over will
// still contain extmarks from other namespaces.
typedef struct
{
uint64_t ns_id;
uint64_t mark_id;
int row;
colnr_T col;
} ExtmarkInfo;
// see FOR_ALL_? for documentation
#define FOR_ALL_EXTMARKLINES(buf, l_lnum, u_lnum, code)\
kbitr_t(extmarklines) itr;\
ExtmarkLine t;\
t.lnum = l_lnum;\
if (!kb_itr_get(extmarklines, &buf->b_extlines, &t, &itr)) { \
kb_itr_next(extmarklines, &buf->b_extlines, &itr);\
}\
ExtmarkLine *extmarkline;\
for (; kb_itr_valid(&itr); kb_itr_next(extmarklines, \
&buf->b_extlines, &itr)) { \
extmarkline = kb_itr_key(&itr);\
if (extmarkline->lnum > u_lnum) { \
break;\
}\
code;\
}
typedef kvec_t(ExtmarkInfo) ExtmarkArray;
// see FOR_ALL_? for documentation
#define FOR_ALL_EXTMARKLINES_PREV(buf, l_lnum, u_lnum, code)\
kbitr_t(extmarklines) itr;\
ExtmarkLine t;\
t.lnum = u_lnum;\
if (!kb_itr_get(extmarklines, &buf->b_extlines, &t, &itr)) { \
kb_itr_prev(extmarklines, &buf->b_extlines, &itr);\
}\
ExtmarkLine *extmarkline;\
for (; kb_itr_valid(&itr); kb_itr_prev(extmarklines, \
&buf->b_extlines, &itr)) { \
extmarkline = kb_itr_key(&itr);\
if (extmarkline->lnum < l_lnum) { \
break;\
}\
code;\
}
// see FOR_ALL_? for documentation
#define FOR_ALL_EXTMARKS(buf, ns, l_lnum, l_col, u_lnum, u_col, code)\
kbitr_t(markitems) mitr;\
Extmark mt;\
mt.ns_id = ns;\
mt.mark_id = 0;\
mt.line = NULL;\
FOR_ALL_EXTMARKLINES(buf, l_lnum, u_lnum, { \
mt.col = (extmarkline->lnum != l_lnum) ? MINCOL : l_col;\
if (!kb_itr_get(markitems, &extmarkline->items, mt, &mitr)) { \
kb_itr_next(markitems, &extmarkline->items, &mitr);\
} \
Extmark *extmark;\
for (; \
kb_itr_valid(&mitr); \
kb_itr_next(markitems, &extmarkline->items, &mitr)) { \
extmark = &kb_itr_key(&mitr);\
if (extmark->line->lnum == u_lnum \
&& extmark->col > u_col) { \
break;\
}\
code;\
}\
})
// see FOR_ALL_? for documentation
#define FOR_ALL_EXTMARKS_PREV(buf, ns, l_lnum, l_col, u_lnum, u_col, code)\
kbitr_t(markitems) mitr;\
Extmark mt;\
mt.mark_id = sizeof(uint64_t);\
mt.ns_id = ns;\
FOR_ALL_EXTMARKLINES_PREV(buf, l_lnum, u_lnum, { \
mt.col = (extmarkline->lnum != u_lnum) ? MAXCOL : u_col;\
if (!kb_itr_get(markitems, &extmarkline->items, mt, &mitr)) { \
kb_itr_prev(markitems, &extmarkline->items, &mitr);\
} \
Extmark *extmark;\
for (; \
kb_itr_valid(&mitr); \
kb_itr_prev(markitems, &extmarkline->items, &mitr)) { \
extmark = &kb_itr_key(&mitr);\
if (extmark->line->lnum == l_lnum \
&& extmark->col < l_col) { \
break;\
}\
code;\
}\
})
#define FOR_ALL_EXTMARKS_IN_LINE(items, l_col, u_col, code)\
kbitr_t(markitems) mitr;\
Extmark mt;\
mt.ns_id = 0;\
mt.mark_id = 0;\
mt.line = NULL;\
mt.col = l_col;\
colnr_T extmarkline_u_col = u_col;\
if (!kb_itr_get(markitems, &items, mt, &mitr)) { \
kb_itr_next(markitems, &items, &mitr);\
} \
Extmark *extmark;\
for (; kb_itr_valid(&mitr); kb_itr_next(markitems, &items, &mitr)) { \
extmark = &kb_itr_key(&mitr);\
if (extmark->col > extmarkline_u_col) { \
break;\
}\
code;\
}
typedef struct ExtmarkNs { // For namespacing extmarks
PMap(uint64_t) *map; // For fast lookup
uint64_t free_id; // For automatically assigning id's
} ExtmarkNs;
typedef kvec_t(Extmark *) ExtmarkArray;
// 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;
// adjust line numbers only, corresponding to mark_adjust call
typedef struct {
linenr_T line1;
linenr_T line2;
long amount;
long amount_after;
} Adjust;
// adjust columns after split/join line, like mark_col_adjust
typedef struct {
linenr_T lnum;
colnr_T mincol;
long col_amount;
long lnum_amount;
} ColAdjust;
// delete the columns between mincol and endcol
typedef struct {
linenr_T lnum;
colnr_T mincol;
colnr_T endcol;
int eol;
} ColAdjustDelete;
int start_row;
colnr_T start_col;
int oldextent_row;
colnr_T oldextent_col;
int newextent_row;
colnr_T newextent_col;
} ExtmarkSplice;
// adjust linenumbers after :move operation
// adjust marks after :move operation
typedef struct {
linenr_T line1;
linenr_T line2;
linenr_T last_line;
linenr_T dest;
linenr_T num_lines;
linenr_T extra;
} AdjustMove;
// TODO(bfredl): reconsider if we really should track mark creation/updating
// itself, these are not really "edit" operation.
// extmark was created
typedef struct {
uint64_t ns_id;
uint64_t mark_id;
linenr_T lnum;
colnr_T col;
} ExtmarkSet;
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 ns_id;
uint64_t mark_id;
linenr_T old_lnum;
uint64_t mark; // raw mark id of the marktree
int old_row;
colnr_T old_col;
linenr_T lnum;
int row;
colnr_T col;
} ExtmarkUpdate;
// copied mark before deletion (as operation is destructive)
typedef struct {
uint64_t ns_id;
uint64_t mark_id;
linenr_T lnum;
colnr_T col;
} ExtmarkCopy;
// also used as part of :move operation? probably can be simplified to one
// event.
typedef struct {
linenr_T l_lnum;
colnr_T l_col;
linenr_T u_lnum;
colnr_T u_col;
linenr_T p_lnum;
colnr_T p_col;
} ExtmarkCopyPlace;
// extmark was cleared.
// TODO(bfredl): same reconsideration as for ExtmarkSet/ExtmarkUpdate
typedef struct {
uint64_t ns_id;
linenr_T l_lnum;
linenr_T u_lnum;
} ExtmarkClear;
} ExtmarkSavePos;
typedef enum {
kLineAdjust,
kColAdjust,
kColAdjustDelete,
kAdjustMove,
kExtmarkSet,
kExtmarkDel,
kExtmarkSplice,
kExtmarkMove,
kExtmarkUpdate,
kExtmarkCopy,
kExtmarkCopyPlace,
kExtmarkSavePos,
kExtmarkClear,
} UndoObjectType;
@@ -238,42 +59,32 @@ typedef enum {
struct undo_object {
UndoObjectType type;
union {
Adjust adjust;
ColAdjust col_adjust;
ColAdjustDelete col_adjust_delete;
AdjustMove move;
ExtmarkSet set;
ExtmarkUpdate update;
ExtmarkCopy copy;
ExtmarkCopyPlace copy_place;
ExtmarkClear clear;
ExtmarkSplice splice;
ExtmarkMove move;
ExtmarkSavePos savepos;
} data;
};
// For doing move of extmarks in substitutions
typedef struct {
lpos_T startpos;
lpos_T endpos;
linenr_T lnum;
int sublen;
} ExtmarkSubSingle;
int start_row;
int start_col;
int end_row;
int end_col;
int attr_id;
VirtText *virt_text;
} HlRange;
// For doing move of extmarks in substitutions
typedef struct {
lpos_T startpos;
lpos_T endpos;
linenr_T lnum;
linenr_T newline_in_pat;
linenr_T newline_in_sub;
linenr_T lnum_added;
lpos_T cm_start; // start of the match
lpos_T cm_end; // end of the match
int eol; // end of the match
} ExtmarkSubMulti;
MarkTreeIter itr[1];
kvec_t(HlRange) active;
int top_row;
int row;
int col_until;
int current;
VirtText *virt_text;
} DecorationState;
typedef kvec_t(ExtmarkSubSingle) extmark_sub_single_vec_t;
typedef kvec_t(ExtmarkSubMulti) extmark_sub_multi_vec_t;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "mark_extended.h.generated.h"

View File

@@ -2,53 +2,36 @@
#define NVIM_MARK_EXTENDED_DEFS_H
#include "nvim/pos.h" // for colnr_T
#include "nvim/map.h" // for uint64_t
#include "nvim/lib/kbtree.h"
#include "nvim/lib/kvec.h"
struct ExtmarkLine;
typedef struct {
char *text;
int hl_id;
} VirtTextChunk;
typedef struct Extmark
typedef kvec_t(VirtTextChunk) VirtText;
#define VIRTTEXT_EMPTY ((VirtText)KV_INITIAL_VALUE)
typedef struct
{
uint64_t ns_id;
uint64_t mark_id;
struct ExtmarkLine *line;
colnr_T col;
} Extmark;
// We only need to compare columns as rows are stored in a different tree.
// Marks are ordered by: position, namespace, mark_id
// This improves moving marks but slows down all other use cases (searches)
static inline int extmark_cmp(Extmark a, Extmark b)
{
int cmp = kb_generic_cmp(a.col, b.col);
if (cmp != 0) {
return cmp;
}
cmp = kb_generic_cmp(a.ns_id, b.ns_id);
if (cmp != 0) {
return cmp;
}
return kb_generic_cmp(a.mark_id, b.mark_id);
}
#define markitems_cmp(a, b) (extmark_cmp((a), (b)))
KBTREE_INIT(markitems, Extmark, markitems_cmp, 10)
typedef struct ExtmarkLine
{
linenr_T lnum;
kbtree_t(markitems) items;
} ExtmarkLine;
#define EXTMARKLINE_CMP(a, b) (kb_generic_cmp((a)->lnum, (b)->lnum))
KBTREE_INIT(extmarklines, ExtmarkLine *, EXTMARKLINE_CMP, 10)
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

1196
src/nvim/marktree.c Normal file

File diff suppressed because it is too large Load Diff

76
src/nvim/marktree.h Normal file
View File

@@ -0,0 +1,76 @@
#ifndef NVIM_MARKTREE_H
#define NVIM_MARKTREE_H
#include <stdint.h>
#include "nvim/map.h"
#include "nvim/garray.h"
#define MT_MAX_DEPTH 20
#define MT_BRANCH_FACTOR 10
typedef struct {
int32_t row;
int32_t col;
} mtpos_t;
typedef struct {
int32_t row;
int32_t col;
uint64_t id;
bool right_gravity;
} mtmark_t;
typedef struct mtnode_s mtnode_t;
typedef struct {
int oldcol;
int i;
} iterstate_t;
typedef struct {
mtpos_t pos;
int lvl;
mtnode_t *node;
int i;
iterstate_t s[MT_MAX_DEPTH];
} MarkTreeIter;
// Internal storage
//
// NB: actual marks have id > 0, so we can use (row,col,0) pseudo-key for
// "space before (row,col)"
typedef struct {
mtpos_t pos;
uint64_t id;
} mtkey_t;
struct mtnode_s {
int32_t n;
int32_t level;
// TODO(bfredl): we could consider having a only-sometimes-valid
// index into parent for faster "chached" lookup.
mtnode_t *parent;
mtkey_t key[2 * MT_BRANCH_FACTOR - 1];
mtnode_t *ptr[];
};
// TODO(bfredl): the iterator is pretty much everpresent, make it part of the
// tree struct itself?
typedef struct {
mtnode_t *root;
size_t n_keys, n_nodes;
uint64_t next_id;
// TODO(bfredl): the pointer to node could be part of the larger
// Map(uint64_t, ExtmarkItem) essentially;
PMap(uint64_t) *id2node;
} MarkTree;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "marktree.h.generated.h"
#endif
#define MARKTREE_PAIRED_FLAG (((uint64_t)1) << 1)
#define MARKTREE_END_FLAG (((uint64_t)1) << 0)
#endif // NVIM_MARKTREE_H

View File

@@ -31,6 +31,7 @@
#include "nvim/indent.h"
#include "nvim/log.h"
#include "nvim/mark.h"
#include "nvim/mark_extended.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
@@ -307,15 +308,6 @@ void shift_line(
change_indent(INDENT_SET, count, false, NUL, call_changed_bytes);
} else {
(void)set_indent(count, call_changed_bytes ? SIN_CHANGED : 0);
colnr_T mincol = (curwin->w_cursor.col + 1) -p_sw;
colnr_T col_amount = left ? -p_sw : p_sw;
extmark_col_adjust(curbuf,
curwin->w_cursor.lnum,
mincol,
0,
col_amount,
kExtmarkUndo);
}
}
@@ -352,6 +344,8 @@ static void shift_block(oparg_T *oap, int amount)
char_u *const oldp = get_cursor_line_ptr();
int startcol, oldlen, newlen;
if (!left) {
/*
* 1. Get start vcol
@@ -361,6 +355,7 @@ static void shift_block(oparg_T *oap, int amount)
*/
total += bd.pre_whitesp; // all virtual WS up to & incl a split TAB
colnr_T ws_vcol = bd.start_vcol - bd.pre_whitesp;
char_u * old_textstart = bd.textstart;
if (bd.startspaces) {
if (has_mbyte) {
if ((*mb_ptr2len)(bd.textstart) == 1) {
@@ -387,14 +382,19 @@ static void shift_block(oparg_T *oap, int amount)
j = ((ws_vcol % p_ts) + total) % p_ts; /* number of spp */
else
j = total;
/* if we're splitting a TAB, allow for it */
bd.textcol -= bd.pre_whitesp_c - (bd.startspaces != 0);
// if we're splitting a TAB, allow for it
int col_pre = bd.pre_whitesp_c - (bd.startspaces != 0);
bd.textcol -= col_pre;
const int len = (int)STRLEN(bd.textstart) + 1;
int col = bd.textcol + i +j + len;
assert(col >= 0);
newp = (char_u *)xmalloc((size_t)col);
memset(newp, NUL, (size_t)col);
memmove(newp, oldp, (size_t)bd.textcol);
startcol = bd.textcol;
oldlen = (int)(bd.textstart-old_textstart) + col_pre;
newlen = i+j;
memset(newp + bd.textcol, TAB, (size_t)i);
memset(newp + bd.textcol + i, ' ', (size_t)j);
/* the end */
@@ -478,7 +478,10 @@ static void shift_block(oparg_T *oap, int amount)
// - the rest of the line, pointed to by non_white.
new_line_len = verbatim_diff + fill + STRLEN(non_white) + 1;
newp = (char_u *) xmalloc(new_line_len);
newp = (char_u *)xmalloc(new_line_len);
startcol = (int)verbatim_diff;
oldlen = bd.textcol + (int)(non_white - bd.textstart) - (int)verbatim_diff;
newlen = (int)fill;
memmove(newp, oldp, verbatim_diff);
memset(newp + verbatim_diff, ' ', fill);
STRMOVE(newp + verbatim_diff + fill, non_white);
@@ -486,13 +489,12 @@ static void shift_block(oparg_T *oap, int amount)
// replace the line
ml_replace(curwin->w_cursor.lnum, newp, false);
changed_bytes(curwin->w_cursor.lnum, (colnr_T)bd.textcol);
extmark_splice(curbuf, (int)curwin->w_cursor.lnum-1, startcol,
0, oldlen, 0, newlen,
kExtmarkUndo);
State = oldstate;
curwin->w_cursor.col = oldcol;
p_ri = old_p_ri;
colnr_T col_amount = left ? -p_sw : p_sw;
extmark_col_adjust(curbuf, curwin->w_cursor.lnum,
curwin->w_cursor.col, 0, col_amount, kExtmarkUndo);
}
/*
@@ -561,6 +563,7 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def
// copy up to shifted part
memmove(newp, oldp, (size_t)offset);
oldp += offset;
int startcol = offset;
// insert pre-padding
memset(newp + offset, ' ', (size_t)spaces);
@@ -569,6 +572,7 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def
memmove(newp + offset + spaces, s, s_len);
offset += (int)s_len;
int skipped = 0;
if (spaces && !bdp->is_short) {
// insert post-padding
memset(newp + offset + spaces, ' ', (size_t)(p_ts - spaces));
@@ -576,6 +580,7 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def
oldp++;
// We allowed for that TAB, remember this now
count++;
skipped = 1;
}
if (spaces > 0)
@@ -583,6 +588,9 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def
STRMOVE(newp + offset, oldp);
ml_replace(lnum, newp, false);
extmark_splice(curbuf, (int)lnum-1, startcol,
0, skipped,
0, offset-startcol, kExtmarkUndo);
if (lnum == oap->end.lnum) {
/* Set "']" mark to the end of the block instead of the end of
@@ -642,14 +650,6 @@ void op_reindent(oparg_T *oap, Indenter how)
first_changed = curwin->w_cursor.lnum;
}
last_changed = curwin->w_cursor.lnum;
// Adjust extmarks
extmark_col_adjust(curbuf,
curwin->w_cursor.lnum,
0, // mincol
0, // lnum_amount
amount, // col_amount
kExtmarkUndo);
}
}
++curwin->w_cursor.lnum;
@@ -1517,6 +1517,11 @@ int op_delete(oparg_T *oap)
STRMOVE(newp + bd.textcol + bd.startspaces + bd.endspaces, oldp);
// replace the line
ml_replace(lnum, newp, false);
extmark_splice(curbuf, (int)lnum-1, bd.textcol,
0, bd.textlen,
0, bd.startspaces+bd.endspaces,
kExtmarkUndo);
}
check_cursor_col();
@@ -1633,6 +1638,8 @@ int op_delete(oparg_T *oap)
(linenr_T)(curwin->w_cursor.lnum + oap->line_count)) == FAIL)
return FAIL;
curbuf_splice_pending++;
pos_T startpos = curwin->w_cursor; // start position for delete
truncate_line(true); // delete from cursor to end of line
curpos = curwin->w_cursor; // remember curwin->w_cursor
@@ -1646,6 +1653,9 @@ int op_delete(oparg_T *oap)
oap->op_type == OP_DELETE && !oap->is_VIsual);
curwin->w_cursor = curpos; // restore curwin->w_cursor
(void)do_join(2, false, false, false, false);
curbuf_splice_pending--;
extmark_splice(curbuf, (int)startpos.lnum-1, startpos.col,
(int)oap->line_count-1, n, 0, 0, kExtmarkUndo);
}
}
@@ -1660,19 +1670,6 @@ setmarks:
}
curbuf->b_op_start = oap->start;
// TODO(timeyyy): refactor: Move extended marks
// + 1 to change to buf mode,
// and + 1 because we only move marks after the deleted col
colnr_T mincol = oap->start.col + 1 + 1;
colnr_T endcol;
if (oap->motion_type == kMTBlockWise) {
// TODO(timeyyy): refactor extmark_col_adjust to take lnumstart, lnum_end ?
endcol = bd.end_vcol + 1;
for (lnum = curwin->w_cursor.lnum; lnum <= oap->end.lnum; lnum++) {
extmark_col_adjust_delete(curbuf, lnum, mincol, endcol,
kExtmarkUndo, 0);
}
}
return OK;
}
@@ -1695,8 +1692,11 @@ static void mb_adjust_opend(oparg_T *oap)
*/
static inline void pbyte(pos_T lp, int c)
{
assert(c <= UCHAR_MAX);
*(ml_get_buf(curbuf, lp.lnum, true) + lp.col) = (char_u)c;
assert(c <= UCHAR_MAX);
*(ml_get_buf(curbuf, lp.lnum, true) + lp.col) = (char_u)c;
if (!curbuf_splice_pending) {
extmark_splice(curbuf, (int)lp.lnum-1, lp.col, 0, 1, 0, 1, kExtmarkUndo);
}
}
// Replace the character under the cursor with "c".
@@ -1817,6 +1817,7 @@ int op_replace(oparg_T *oap, int c)
size_t after_p_len = 0;
int col = oldlen - bd.textcol - bd.textlen + 1;
assert(col >= 0);
int newrows = 0, newcols = 0;
if (had_ctrl_v_cr || (c != '\r' && c != '\n')) {
// strlen(newp) at this point
int newp_len = bd.textcol + bd.startspaces;
@@ -1829,21 +1830,27 @@ int op_replace(oparg_T *oap, int c)
newp_len += bd.endspaces;
// copy the part after the changed part
memmove(newp + newp_len, oldp, (size_t)col);
}
}
newcols = newp_len - bd.textcol;
} else {
// Replacing with \r or \n means splitting the line.
after_p_len = (size_t)col;
after_p = (char_u *)xmalloc(after_p_len);
memmove(after_p, oldp, after_p_len);
newrows = 1;
}
// replace the line
ml_replace(curwin->w_cursor.lnum, newp, false);
linenr_T baselnum = curwin->w_cursor.lnum;
if (after_p != NULL) {
ml_append(curwin->w_cursor.lnum++, after_p, (int)after_p_len, false);
appended_lines_mark(curwin->w_cursor.lnum, 1L);
oap->end.lnum++;
xfree(after_p);
}
extmark_splice(curbuf, (int)baselnum-1, bd.textcol,
0, bd.textlen,
newrows, newcols, kExtmarkUndo);
}
} else {
// Characterwise or linewise motion replace.
@@ -1856,6 +1863,8 @@ int op_replace(oparg_T *oap, int c)
} else if (!oap->inclusive)
dec(&(oap->end));
// TODO(bfredl): we could batch all the splicing
// done on the same line, at least
while (ltoreq(curwin->w_cursor, oap->end)) {
n = gchar_cursor();
if (n != NUL) {
@@ -2262,10 +2271,6 @@ void op_insert(oparg_T *oap, long count1)
xfree(ins_text);
}
}
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);
}
}
/*
@@ -2380,6 +2385,9 @@ int op_change(oparg_T *oap)
oldp += bd.textcol;
STRMOVE(newp + offset, oldp);
ml_replace(linenr, newp, false);
extmark_splice(curbuf, (int)linenr-1, bd.textcol,
0, 0,
0, vpos.coladd+(int)ins_len, kExtmarkUndo);
}
}
check_cursor();
@@ -2735,28 +2743,6 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
recursive = false;
}
static void extmarks_do_put(int dir,
size_t totlen,
MotionType y_type,
linenr_T lnum,
colnr_T col)
{
// adjust extmarks
colnr_T col_amount = (colnr_T)(dir == FORWARD ? totlen-1 : totlen);
// Move extmark with char put
if (y_type == kMTCharWise) {
extmark_col_adjust(curbuf, lnum, col, 0, col_amount, kExtmarkUndo);
// Move extmark with blockwise put
} else if (y_type == kMTBlockWise) {
for (lnum = curbuf->b_op_start.lnum;
lnum <= curbuf->b_op_end.lnum;
lnum++) {
extmark_col_adjust(curbuf, lnum, col, 0, col_amount, kExtmarkUndo);
}
}
}
/*
* Put contents of register "regname" into the text.
* Caller must check "regname" to be valid!
@@ -3176,6 +3162,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
assert(columns >= 0);
memmove(ptr, oldp + bd.textcol + delcount, (size_t)columns);
ml_replace(curwin->w_cursor.lnum, newp, false);
extmark_splice(curbuf, (int)curwin->w_cursor.lnum-1, bd.textcol,
0, delcount,
0, (int)totlen,
kExtmarkUndo);
++curwin->w_cursor.lnum;
if (i == 0)
@@ -3277,6 +3267,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
if (totlen && (restart_edit != 0 || (flags & PUT_CURSEND)))
++curwin->w_cursor.col;
changed_bytes(lnum, col);
extmark_splice(curbuf, (int)lnum-1, col,
0, 0,
0, (int)totlen, kExtmarkUndo);
} else {
// Insert at least one line. When y_type is kMTCharWise, break the first
// line in two.
@@ -3332,13 +3325,22 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
first_indent = FALSE;
} else if ((indent = get_indent() + indent_diff) < 0)
indent = 0;
(void)set_indent(indent, 0);
(void)set_indent(indent, SIN_NOMARK);
curwin->w_cursor = old_pos;
/* remember how many chars were removed */
if (cnt == count && i == y_size - 1)
lendiff -= (int)STRLEN(ml_get(lnum));
}
}
if (y_type == kMTCharWise) {
extmark_splice(curbuf, (int)new_cursor.lnum-1, col, 0, 0,
(int)y_size-1, (int)STRLEN(y_array[y_size-1]),
kExtmarkUndo);
} else if (y_type == kMTLineWise && flags & PUT_LINE_SPLIT) {
extmark_splice(curbuf, (int)new_cursor.lnum-1, col, 0, 0,
(int)y_size+1, 0, kExtmarkUndo);
}
}
error:
@@ -3352,8 +3354,10 @@ error:
// can't be marks there.
if (curbuf->b_op_start.lnum + (y_type == kMTCharWise) - 1 + nr_lines
< curbuf->b_ml.ml_line_count) {
ExtmarkOp kind = (y_type == kMTLineWise && !(flags & PUT_LINE_SPLIT))
? kExtmarkUndo : kExtmarkNOOP;
mark_adjust(curbuf->b_op_start.lnum + (y_type == kMTCharWise),
(linenr_T)MAXLNUM, nr_lines, 0L, false, kExtmarkUndo);
(linenr_T)MAXLNUM, nr_lines, 0L, kind);
}
// note changed text for displaying and folding
@@ -3415,9 +3419,7 @@ end:
/* If the cursor is past the end of the line put it at the end. */
adjust_cursor_eol();
extmarks_do_put(dir, totlen, y_type, lnum, col);
}
} // NOLINT(readability/fn_size)
/*
* When the cursor is on the NUL past the end of the line and it should not be
@@ -3779,6 +3781,13 @@ int do_join(size_t count,
}
}
}
if (t > 0 && curbuf_splice_pending == 0) {
extmark_splice(curbuf, (int)curwin->w_cursor.lnum-1, sumsize,
1, (int)(curr- curr_start),
0, spaces[t],
kExtmarkUndo);
}
currsize = (int)STRLEN(curr);
sumsize += currsize + spaces[t];
endcurr1 = endcurr2 = NUL;
@@ -3814,6 +3823,8 @@ int do_join(size_t count,
* should not really be a problem.
*/
curbuf_splice_pending++;
for (t = (linenr_T)count - 1;; t--) {
cend -= currsize;
memmove(cend, curr, (size_t)currsize);
@@ -3830,8 +3841,7 @@ int do_join(size_t count,
long lnum_amount = (linenr_T)-t;
long col_amount = (long)(cend - newp - spaces_removed);
mark_col_adjust(lnum, mincol, lnum_amount, col_amount, spaces_removed,
kExtmarkUndo);
mark_col_adjust(lnum, mincol, lnum_amount, col_amount, spaces_removed);
if (t == 0) {
break;
@@ -3867,6 +3877,7 @@ int do_join(size_t count,
curwin->w_cursor.lnum++;
del_lines((long)count - 1, false);
curwin->w_cursor.lnum = t;
curbuf_splice_pending--;
/*
* Set the cursor column:
@@ -4265,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, kExtmarkNOOP);
(long)-next_leader_len, 0);
} 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);
mark_col_adjust(curwin->w_cursor.lnum,
(colnr_T)0, 0L, (long)-indent, 0, kExtmarkNOOP);
(colnr_T)0, 0L, (long)-indent, 0);
}
}
curwin->w_cursor.lnum--;

View File

@@ -87,6 +87,7 @@
#include "nvim/highlight.h"
#include "nvim/main.h"
#include "nvim/mark.h"
#include "nvim/mark_extended.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
@@ -622,6 +623,9 @@ bool win_cursorline_standout(const win_T *wp)
|| (wp->w_p_cole > 0 && (VIsual_active || !conceal_cursor_line(wp)));
}
static DecorationState decorations;
bool decorations_active = false;
/*
* Update a single window.
*
@@ -1221,6 +1225,7 @@ static void win_update(win_T *wp)
: (wp->w_topline + wp->w_height_inner));
args.items[0] = WINDOW_OBJ(wp->handle);
args.items[1] = BUFFER_OBJ(buf->handle);
// TODO(bfredl): we are not using this, but should be first drawn line?
args.items[2] = INTEGER_OBJ(wp->w_topline-1);
args.items[3] = INTEGER_OBJ(knownmax);
// TODO(bfredl): we could allow this callback to change mod_top, mod_bot.
@@ -1232,6 +1237,8 @@ static void win_update(win_T *wp)
}
}
decorations_active = extmark_decorations_reset(buf, &decorations);
for (;; ) {
/* stop updating when reached the end of the window (check for _past_
* the end of the window is at the end of the loop) */
@@ -2250,8 +2257,7 @@ win_line (
int prev_c1 = 0; // first composing char for prev_c
bool search_attr_from_match = false; // if search_attr is from :match
BufhlLineInfo bufhl_info; // bufhl data for this line
bool has_bufhl = false; // this buffer has highlight matches
bool has_decorations = false; // this buffer has decorations
bool do_virttext = false; // draw virtual text for this line
/* draw_state: items that are drawn in sequence: */
@@ -2315,14 +2321,12 @@ win_line (
}
}
if (bufhl_start_line(wp->w_buffer, lnum, &bufhl_info)) {
if (kv_size(bufhl_info.line->items)) {
has_bufhl = true;
if (decorations_active) {
has_decorations = extmark_decorations_line(wp->w_buffer, lnum-1,
&decorations);
if (has_decorations) {
extra_check = true;
}
if (kv_size(bufhl_info.line->virt_text)) {
do_virttext = true;
}
}
// Check for columns to display for 'colorcolumn'.
@@ -3515,19 +3519,25 @@ win_line (
char_attr = hl_combine_attr(spell_attr, char_attr);
}
if (has_bufhl && v > 0) {
int bufhl_attr = bufhl_get_attr(&bufhl_info, (colnr_T)v);
if (bufhl_attr != 0) {
if (has_decorations && v > 0) {
int extmark_attr = extmark_decorations_col(wp->w_buffer, (colnr_T)v-1,
&decorations);
if (extmark_attr != 0) {
if (!attr_pri) {
char_attr = hl_combine_attr(char_attr, bufhl_attr);
char_attr = hl_combine_attr(char_attr, extmark_attr);
} else {
char_attr = hl_combine_attr(bufhl_attr, char_attr);
char_attr = hl_combine_attr(extmark_attr, char_attr);
}
}
}
// TODO(bfredl): luahl should reuse the "active decorations" buffer
if (buf->b_luahl && v > 0 && v < (long)lua_attr_bufsize+1) {
char_attr = hl_combine_attr(char_attr, lua_attr_buf[v-1]);
if (!attr_pri) {
char_attr = hl_combine_attr(char_attr, lua_attr_buf[v-1]);
} else {
char_attr = hl_combine_attr(lua_attr_buf[v-1], char_attr);
}
}
if (wp->w_buffer->terminal) {
@@ -4008,6 +4018,19 @@ win_line (
if (draw_color_col)
draw_color_col = advance_color_col(VCOL_HLC, &color_cols);
VirtText virt_text = KV_INITIAL_VALUE;
if (luatext) {
kv_push(virt_text, ((VirtTextChunk){ .text = luatext, .hl_id = 0 }));
do_virttext = true;
} else if (has_decorations) {
VirtText *vp = extmark_decorations_virt_text(wp->w_buffer,
&decorations);
if (vp) {
virt_text = *vp;
do_virttext = true;
}
}
if (((wp->w_p_cuc
&& (int)wp->w_virtcol >= VCOL_HLC - eol_hl_off
&& (int)wp->w_virtcol <
@@ -4018,14 +4041,6 @@ win_line (
int rightmost_vcol = 0;
int i;
VirtText virt_text;
if (luatext) {
virt_text = (VirtText)KV_INITIAL_VALUE;
kv_push(virt_text, ((VirtTextChunk){ .text = luatext, .hl_id = 0 }));
} else {
virt_text = do_virttext ? bufhl_info.line->virt_text
: (VirtText)KV_INITIAL_VALUE;
}
size_t virt_pos = 0;
LineState s = LINE_STATE((char_u *)"");
int virt_attr = 0;

View File

@@ -2244,7 +2244,7 @@ static void u_undoredo(int undo, bool do_buf_event)
// Adjust marks
if (oldsize != newsize) {
mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
(long)newsize - (long)oldsize, false, kExtmarkNOOP);
(long)newsize - (long)oldsize, kExtmarkNOOP);
if (curbuf->b_op_start.lnum > top + oldsize) {
curbuf->b_op_start.lnum += newsize - oldsize;
}