commit
122426966e
|
@ -438,6 +438,42 @@ Example: create a float with scratch buffer: >
|
|||
call nvim_win_set_option(win, 'winhl', 'Normal:MyHighlight')
|
||||
>
|
||||
|
||||
==============================================================================
|
||||
Extended marks *api-extended-marks*
|
||||
|
||||
An extended mark (extmark) represents a buffer annotation that follows
|
||||
movements as the buffer changes. They could be used to represent cursors,
|
||||
folds, misspelled words, and anything else that needs to track a logical
|
||||
location in the buffer over time.
|
||||
|
||||
Example:
|
||||
|
||||
We will set an extmark at the first row and third column. As the API is zero-
|
||||
indexed, use row and column counts 0 and 2:
|
||||
|
||||
`let g:mark_ns = nvim_create_namespace('myplugin')`
|
||||
`let g:mark_id = nvim_buf_set_extmark(0, g:mark_ns, 0, 0, 2)`
|
||||
|
||||
Passing in id=0 creates a new mark and returns the id. we can look-up a mark
|
||||
by its id:
|
||||
|
||||
`echo nvim_buf_get_extmark_by_id(0, g:mark_ns, g:mark_id)`
|
||||
=> [0, 2]
|
||||
|
||||
Or we can look-up all marks in a buffer for our namespace (or by a range):
|
||||
`echo nvim_buf_get_extmarks(0, g:mark_ns, 0, -1, -1)`
|
||||
=> [[1, 0, 2]]
|
||||
|
||||
Deleting the text all around an extended mark does not remove it. If you want
|
||||
to remove an extended mark, use the |nvim_buf_del_extmark()| function.
|
||||
|
||||
The namespace ensures your plugin doesn't have to deal with extmarks created
|
||||
by another plugin.
|
||||
|
||||
Mark positions changed by an edit will be restored on undo/redo. Creating and
|
||||
deleting marks doesn't count as a buffer change on itself, i e new undo
|
||||
states will not be created only for marks.
|
||||
|
||||
==============================================================================
|
||||
Global Functions *api-global*
|
||||
|
||||
|
@ -1747,6 +1783,86 @@ nvim_buf_get_mark({buffer}, {name}) *nvim_buf_get_mark()*
|
|||
Return: ~
|
||||
(row, col) tuple
|
||||
|
||||
*nvim_buf_get_extmark_by_id()*
|
||||
nvim_buf_get_extmark_by_id({buffer}, {ns_id}, {id})
|
||||
Returns position for a given extmark id
|
||||
|
||||
Parameters: ~
|
||||
{buffer} The buffer handle
|
||||
{namespace} a identifier returned previously with
|
||||
nvim_create_namespace
|
||||
{id} the extmark id
|
||||
|
||||
Return: ~
|
||||
(row, col) tuple or empty list () if extmark id was absent
|
||||
|
||||
*nvim_buf_get_extmarks()*
|
||||
nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts})
|
||||
List extmarks in a range (inclusive)
|
||||
|
||||
range ends can be specified as (row, col) tuples, as well as
|
||||
extmark ids in the same namespace. In addition, 0 and -1 works
|
||||
as shorthands for (0,0) and (-1,-1) respectively, so that all
|
||||
marks in the buffer can be quieried as:
|
||||
|
||||
all_marks = nvim_buf_get_extmarks(0, my_ns, 0, -1, -1)
|
||||
|
||||
If end is a lower position than start, then the range will be
|
||||
traversed backwards. This is mostly used with limited amount,
|
||||
to be able to get the first marks prior to a given position.
|
||||
|
||||
Parameters: ~
|
||||
{buffer} The buffer handle
|
||||
{ns_id} An id returned previously from
|
||||
nvim_create_namespace
|
||||
{lower} One of: extmark id, (row, col) or 0, -1 for
|
||||
buffer ends
|
||||
{upper} One of: extmark id, (row, col) or 0, -1 for
|
||||
buffer ends
|
||||
{opts} additional options. Supports the keys:
|
||||
• amount: Maximum number of marks to return •
|
||||
|
||||
Return: ~
|
||||
[[nsmark_id, row, col], ...]
|
||||
|
||||
*nvim_buf_set_extmark()*
|
||||
nvim_buf_set_extmark({buffer}, {ns_id}, {id}, {line}, {col}, {opts})
|
||||
Create or update an extmark at a position
|
||||
|
||||
If an invalid namespace is given, an error will be raised.
|
||||
|
||||
To create a new extmark, pass in id=0. The new extmark id will
|
||||
be returned. To move an existing mark, pass in its id.
|
||||
|
||||
It is also allowed to create a new mark by passing in a
|
||||
previously unused id, but the caller must then keep track of
|
||||
existing and unused ids itself. This is mainly useful over
|
||||
RPC, to avoid needing to wait for the return value.
|
||||
|
||||
Parameters: ~
|
||||
{buffer} The buffer handle
|
||||
{ns_id} a identifier returned previously with
|
||||
nvim_create_namespace
|
||||
{id} The extmark's id or 0 to create a new mark.
|
||||
{row} The row to set the extmark to.
|
||||
{col} The column to set the extmark to.
|
||||
{opts} Optional parameters. Currently not used.
|
||||
|
||||
Return: ~
|
||||
the id of the extmark.
|
||||
|
||||
nvim_buf_del_extmark({buffer}, {ns_id}, {id}) *nvim_buf_del_extmark()*
|
||||
Remove an extmark
|
||||
|
||||
Parameters: ~
|
||||
{buffer} The buffer handle
|
||||
{ns_id} a identifier returned previously with
|
||||
nvim_create_namespace
|
||||
{id} The extmarks's id
|
||||
|
||||
Return: ~
|
||||
true on success, false if the extmark was not found.
|
||||
|
||||
*nvim_buf_add_highlight()*
|
||||
nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line},
|
||||
{col_start}, {col_end})
|
||||
|
|
|
@ -23,7 +23,10 @@
|
|||
#include "nvim/memory.h"
|
||||
#include "nvim/misc1.h"
|
||||
#include "nvim/ex_cmds.h"
|
||||
#include "nvim/map_defs.h"
|
||||
#include "nvim/map.h"
|
||||
#include "nvim/mark.h"
|
||||
#include "nvim/mark_extended.h"
|
||||
#include "nvim/fileio.h"
|
||||
#include "nvim/move.h"
|
||||
#include "nvim/syntax.h"
|
||||
|
@ -544,7 +547,8 @@ void nvim_buf_set_lines(uint64_t channel_id,
|
|||
(linenr_T)(end - 1),
|
||||
MAXLNUM,
|
||||
(long)extra,
|
||||
false);
|
||||
false,
|
||||
kExtmarkUndo);
|
||||
|
||||
changed_lines((linenr_T)start, 0, (linenr_T)end, (long)extra, true);
|
||||
fix_cursor((linenr_T)start, (linenr_T)end, (linenr_T)extra);
|
||||
|
@ -999,6 +1003,240 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err)
|
|||
return rv;
|
||||
}
|
||||
|
||||
/// Returns position for a given extmark id
|
||||
///
|
||||
/// @param buffer The buffer handle
|
||||
/// @param namespace a identifier returned previously with nvim_create_namespace
|
||||
/// @param id the extmark id
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return (row, col) tuple or empty list () if extmark id was absent
|
||||
ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
|
||||
Integer id, Error *err)
|
||||
FUNC_API_SINCE(7)
|
||||
{
|
||||
Array rv = ARRAY_DICT_INIT;
|
||||
|
||||
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||
|
||||
if (!buf) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!ns_initialized((uint64_t)ns_id)) {
|
||||
api_set_error(err, kErrorTypeValidation, _("Invalid ns_id"));
|
||||
return rv;
|
||||
}
|
||||
|
||||
ExtendedMark *extmark = extmark_from_id(buf,
|
||||
(uint64_t)ns_id,
|
||||
(uint64_t)id);
|
||||
if (!extmark) {
|
||||
return rv;
|
||||
}
|
||||
ADD(rv, INTEGER_OBJ((Integer)extmark->line->lnum-1));
|
||||
ADD(rv, INTEGER_OBJ((Integer)extmark->col-1));
|
||||
return rv;
|
||||
}
|
||||
|
||||
/// List extmarks in a range (inclusive)
|
||||
///
|
||||
/// range ends can be specified as (row, col) tuples, as well as extmark
|
||||
/// ids in the same namespace. In addition, 0 and -1 works as shorthands
|
||||
/// for (0,0) and (-1,-1) respectively, so that all marks in the buffer can be
|
||||
/// quieried as:
|
||||
///
|
||||
/// all_marks = nvim_buf_get_extmarks(0, my_ns, 0, -1, -1)
|
||||
///
|
||||
/// If end is a lower position than start, then the range will be traversed
|
||||
/// backwards. This is mostly used with limited amount, to be able to get the
|
||||
/// first marks prior to a given position.
|
||||
///
|
||||
/// @param buffer The buffer handle
|
||||
/// @param ns_id An id returned previously from nvim_create_namespace
|
||||
/// @param lower One of: extmark id, (row, col) or 0, -1 for buffer ends
|
||||
/// @param upper One of: extmark id, (row, col) or 0, -1 for buffer ends
|
||||
/// @param opts additional options. Supports the keys:
|
||||
/// - amount: Maximum number of marks to return
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return [[nsmark_id, row, col], ...]
|
||||
Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id,
|
||||
Object start, Object end, Dictionary opts,
|
||||
Error *err)
|
||||
FUNC_API_SINCE(7)
|
||||
{
|
||||
Array rv = ARRAY_DICT_INIT;
|
||||
|
||||
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||
if (!buf) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!ns_initialized((uint64_t)ns_id)) {
|
||||
api_set_error(err, kErrorTypeValidation, _("Invalid ns_id"));
|
||||
return rv;
|
||||
}
|
||||
Integer amount = -1;
|
||||
|
||||
for (size_t i = 0; i < opts.size; i++) {
|
||||
String k = opts.items[i].key;
|
||||
Object *v = &opts.items[i].value;
|
||||
if (strequal("amount", k.data)) {
|
||||
if (v->type != kObjectTypeInteger) {
|
||||
api_set_error(err, kErrorTypeValidation, "amount is not an integer");
|
||||
return rv;
|
||||
}
|
||||
amount = v->data.integer;
|
||||
v->data.integer = LUA_NOREF;
|
||||
} else {
|
||||
api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
if (amount == 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
bool reverse = false;
|
||||
|
||||
linenr_T l_lnum;
|
||||
colnr_T l_col;
|
||||
if (!set_extmark_index_from_obj(buf, ns_id, start, &l_lnum, &l_col, err)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
linenr_T u_lnum;
|
||||
colnr_T u_col;
|
||||
if (!set_extmark_index_from_obj(buf, ns_id, end, &u_lnum, &u_col, err)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (l_lnum > u_lnum || (l_lnum == u_lnum && 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, u_col, (int64_t)amount,
|
||||
reverse);
|
||||
|
||||
for (size_t i = 0; i < kv_size(marks); i++) {
|
||||
Array mark = ARRAY_DICT_INIT;
|
||||
ExtendedMark *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));
|
||||
ADD(rv, ARRAY_OBJ(mark));
|
||||
}
|
||||
|
||||
kv_destroy(marks);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/// Create or update an extmark at a position
|
||||
///
|
||||
/// If an invalid namespace is given, an error will be raised.
|
||||
///
|
||||
/// To create a new extmark, pass in id=0. The new extmark id will be
|
||||
/// returned. To move an existing mark, pass in its id.
|
||||
///
|
||||
/// It is also allowed to create a new mark by passing in a previously unused
|
||||
/// id, but the caller must then keep track of existing and unused ids itself.
|
||||
/// This is mainly useful over RPC, to avoid needing to wait for the return
|
||||
/// value.
|
||||
///
|
||||
/// @param buffer The buffer handle
|
||||
/// @param ns_id a identifier returned previously with nvim_create_namespace
|
||||
/// @param id The extmark's id or 0 to create a new mark.
|
||||
/// @param row The row to set the extmark to.
|
||||
/// @param col The column to set the extmark to.
|
||||
/// @param opts Optional parameters. Currently not used.
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return the id of the extmark.
|
||||
Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer id,
|
||||
Integer line, Integer col,
|
||||
Dictionary opts, Error *err)
|
||||
FUNC_API_SINCE(7)
|
||||
{
|
||||
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 (opts.size > 0) {
|
||||
api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t len = 0;
|
||||
if (line < 0 || line > buf->b_ml.ml_line_count) {
|
||||
api_set_error(err, kErrorTypeValidation, "line value outside range");
|
||||
return 0;
|
||||
} else if (line < buf->b_ml.ml_line_count) {
|
||||
len = STRLEN(ml_get_buf(curbuf, (linenr_T)line+1, false));
|
||||
}
|
||||
|
||||
if (col == -1) {
|
||||
col = (Integer)len;
|
||||
} else if (col < -1 || col > (Integer)len) {
|
||||
api_set_error(err, kErrorTypeValidation, "col value outside range");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t id_num;
|
||||
if (id == 0) {
|
||||
id_num = extmark_free_id_get(buf, (uint64_t)ns_id);
|
||||
} else 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);
|
||||
|
||||
return (Integer)id_num;
|
||||
}
|
||||
|
||||
/// Remove an extmark
|
||||
///
|
||||
/// @param buffer The buffer handle
|
||||
/// @param ns_id a identifier returned previously with nvim_create_namespace
|
||||
/// @param id The extmarks's id
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return true on success, false if the extmark was not found.
|
||||
Boolean nvim_buf_del_extmark(Buffer buffer,
|
||||
Integer ns_id,
|
||||
Integer id,
|
||||
Error *err)
|
||||
FUNC_API_SINCE(7)
|
||||
{
|
||||
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||
|
||||
if (!buf) {
|
||||
return false;
|
||||
}
|
||||
if (!ns_initialized((uint64_t)ns_id)) {
|
||||
api_set_error(err, kErrorTypeValidation, _("Invalid ns_id"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return extmark_del(buf, (uint64_t)ns_id, (uint64_t)id, kExtmarkUndo);
|
||||
}
|
||||
|
||||
/// Adds a highlight to buffer.
|
||||
///
|
||||
/// Useful for plugins that dynamically generate highlights to a buffer
|
||||
|
@ -1097,6 +1335,10 @@ void nvim_buf_clear_namespace(Buffer buffer,
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// Clears highlights and virtual text from namespace and range of lines
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "nvim/api/private/helpers.h"
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/api/private/handle.h"
|
||||
#include "nvim/api/vim.h"
|
||||
#include "nvim/msgpack_rpc/helpers.h"
|
||||
#include "nvim/lua/executor.h"
|
||||
#include "nvim/ascii.h"
|
||||
|
@ -23,6 +24,7 @@
|
|||
#include "nvim/eval/typval.h"
|
||||
#include "nvim/map_defs.h"
|
||||
#include "nvim/map.h"
|
||||
#include "nvim/mark_extended.h"
|
||||
#include "nvim/option.h"
|
||||
#include "nvim/option_defs.h"
|
||||
#include "nvim/version.h"
|
||||
|
@ -1505,3 +1507,131 @@ 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
|
||||
ExtendedMark *extmark_from_id_or_pos(Buffer buffer,
|
||||
Integer namespace,
|
||||
Object id,
|
||||
Error *err,
|
||||
bool throw)
|
||||
{
|
||||
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||
|
||||
if (!buf) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ExtendedMark *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)namespace, 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)namespace,
|
||||
(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)
|
||||
{
|
||||
if (ns < 1) {
|
||||
return false;
|
||||
}
|
||||
return ns < (uint64_t)next_namespace_id;
|
||||
}
|
||||
|
||||
/// Get line and column from extmark object
|
||||
///
|
||||
/// Extmarks may be queried from position or name or even special names
|
||||
/// in the future such as "cursor". This function sets the line and col
|
||||
/// to make the extmark functions recognize what's required
|
||||
///
|
||||
/// @param[out] lnum lnum to be set
|
||||
/// @param[out] colnr col to be set
|
||||
bool set_extmark_index_from_obj(buf_T *buf, Integer namespace,
|
||||
Object obj, linenr_T *lnum, colnr_T *colnr,
|
||||
Error *err)
|
||||
{
|
||||
// Check if it is mark id
|
||||
if (obj.type == kObjectTypeInteger) {
|
||||
Integer id = obj.data.integer;
|
||||
if (id == 0) {
|
||||
*lnum = 1;
|
||||
*colnr = 1;
|
||||
return true;
|
||||
} else if (id == -1) {
|
||||
*lnum = MAXLNUM;
|
||||
*colnr = MAXCOL;
|
||||
return true;
|
||||
} else if (id < 0) {
|
||||
api_set_error(err, kErrorTypeValidation, _("Mark id must be positive"));
|
||||
return false;
|
||||
}
|
||||
|
||||
ExtendedMark *extmark = extmark_from_id(buf, (uint64_t)namespace,
|
||||
(uint64_t)id);
|
||||
if (extmark) {
|
||||
*lnum = extmark->line->lnum;
|
||||
*colnr = extmark->col;
|
||||
return true;
|
||||
} else {
|
||||
api_set_error(err, kErrorTypeValidation, _("No mark with requested id"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if it is a position
|
||||
} else if (obj.type == kObjectTypeArray) {
|
||||
Array pos = obj.data.array;
|
||||
if (pos.size != 2
|
||||
|| pos.items[0].type != kObjectTypeInteger
|
||||
|| pos.items[1].type != kObjectTypeInteger) {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
_("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);
|
||||
return true;
|
||||
} else {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
_("Position must be a mark id Integer or position Array"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "nvim/ops.h"
|
||||
#include "nvim/option.h"
|
||||
#include "nvim/state.h"
|
||||
#include "nvim/mark_extended.h"
|
||||
#include "nvim/syntax.h"
|
||||
#include "nvim/getchar.h"
|
||||
#include "nvim/os/input.h"
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
#include "nvim/indent_c.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"
|
||||
|
@ -816,6 +817,7 @@ 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
|
||||
|
@ -5496,6 +5498,7 @@ void bufhl_clear_line_range(buf_T *buf,
|
|||
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)) {
|
||||
|
|
|
@ -115,6 +115,9 @@ typedef uint16_t disptick_T; // display tick type
|
|||
#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.
|
||||
*/
|
||||
|
@ -805,6 +808,10 @@ struct file_buffer {
|
|||
|
||||
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
|
||||
|
||||
// array of channel_id:s which have asked to receive updates for this
|
||||
// buffer.
|
||||
kvec_t(uint64_t) update_channels;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "nvim/indent.h"
|
||||
#include "nvim/indent_c.h"
|
||||
#include "nvim/mark.h"
|
||||
#include "nvim/mark_extended.h"
|
||||
#include "nvim/memline.h"
|
||||
#include "nvim/misc1.h"
|
||||
#include "nvim/move.h"
|
||||
|
@ -372,7 +373,7 @@ 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);
|
||||
mark_adjust(lnum + 1, (linenr_T)MAXLNUM, count, 0L, false, kExtmarkUndo);
|
||||
}
|
||||
changed_lines(lnum + 1, 0, lnum + 1, count, true);
|
||||
}
|
||||
|
@ -390,7 +391,8 @@ 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, false,
|
||||
kExtmarkUndo);
|
||||
changed_lines(lnum, 0, lnum + count, -count, true);
|
||||
}
|
||||
|
||||
|
@ -951,6 +953,9 @@ int open_line(
|
|||
bool did_append; // appended a new line
|
||||
int saved_pi = curbuf->b_p_pi; // copy of preserveindent setting
|
||||
|
||||
linenr_T lnum = curwin->w_cursor.lnum;
|
||||
colnr_T mincol = curwin->w_cursor.col + 1;
|
||||
|
||||
// make a copy of the current line so we can mess with it
|
||||
char_u *saved_line = vim_strsave(get_cursor_line_ptr());
|
||||
|
||||
|
@ -1574,7 +1579,8 @@ 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, false,
|
||||
kExtmarkUndo);
|
||||
}
|
||||
did_append = true;
|
||||
} else {
|
||||
|
@ -1663,8 +1669,12 @@ 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);
|
||||
1L, (long)-less_cols, 0, kExtmarkNOOP);
|
||||
}
|
||||
// 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);
|
||||
} else {
|
||||
changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col);
|
||||
}
|
||||
|
|
|
@ -2690,7 +2690,8 @@ 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, false,
|
||||
kExtmarkUndo);
|
||||
if (curwin->w_cursor.lnum >= lnum) {
|
||||
// Adjust the cursor position if it's in/after the changed
|
||||
// lines.
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "nvim/indent.h"
|
||||
#include "nvim/indent_c.h"
|
||||
#include "nvim/main.h"
|
||||
#include "nvim/mark_extended.h"
|
||||
#include "nvim/mbyte.h"
|
||||
#include "nvim/memline.h"
|
||||
#include "nvim/memory.h"
|
||||
|
@ -1837,6 +1838,13 @@ change_indent (
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -5587,6 +5595,9 @@ insertchar (
|
|||
do_digraph(buf[i-1]); /* may be the start of a digraph */
|
||||
buf[i] = NUL;
|
||||
ins_str(buf);
|
||||
extmark_col_adjust(curbuf, curwin->w_cursor.lnum,
|
||||
(colnr_T)(curwin->w_cursor.col + 1), 0,
|
||||
(long)STRLEN(buf), kExtmarkUndo);
|
||||
if (flags & INSCHAR_CTRLV) {
|
||||
redo_literal(*buf);
|
||||
i = 1;
|
||||
|
@ -5597,6 +5608,9 @@ insertchar (
|
|||
} else {
|
||||
int cc;
|
||||
|
||||
extmark_col_adjust(curbuf, curwin->w_cursor.lnum,
|
||||
(colnr_T)(curwin->w_cursor.col + 1), 0,
|
||||
1, kExtmarkUndo);
|
||||
if ((cc = utf_char2len(c)) > 1) {
|
||||
char_u buf[MB_MAXBYTES + 1];
|
||||
|
||||
|
@ -5606,10 +5620,11 @@ insertchar (
|
|||
AppendCharToRedobuff(c);
|
||||
} else {
|
||||
ins_char(c);
|
||||
if (flags & INSCHAR_CTRLV)
|
||||
if (flags & INSCHAR_CTRLV) {
|
||||
redo_literal(c);
|
||||
else
|
||||
} else {
|
||||
AppendCharToRedobuff(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6891,8 +6906,9 @@ static void mb_replace_pop_ins(int cc)
|
|||
for (i = 1; i < n; ++i)
|
||||
buf[i] = replace_pop();
|
||||
ins_bytes_len(buf, n);
|
||||
} else
|
||||
} else {
|
||||
ins_char(cc);
|
||||
}
|
||||
|
||||
if (enc_utf8)
|
||||
/* Handle composing chars. */
|
||||
|
@ -8002,9 +8018,9 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
|
|||
Insstart_orig.col = curwin->w_cursor.col;
|
||||
}
|
||||
|
||||
if (State & VREPLACE_FLAG)
|
||||
if (State & VREPLACE_FLAG) {
|
||||
ins_char(' ');
|
||||
else {
|
||||
} else {
|
||||
ins_str((char_u *)" ");
|
||||
if ((State & REPLACE_FLAG))
|
||||
replace_push(NUL);
|
||||
|
@ -8482,8 +8498,17 @@ static bool ins_tab(void)
|
|||
} else { // otherwise use "tabstop"
|
||||
temp = (int)curbuf->b_p_ts;
|
||||
}
|
||||
|
||||
temp -= get_nolist_virtcol() % temp;
|
||||
|
||||
// Move extmarks
|
||||
extmark_col_adjust(curbuf,
|
||||
curwin->w_cursor.lnum,
|
||||
curwin->w_cursor.col,
|
||||
0,
|
||||
temp,
|
||||
kExtmarkUndo);
|
||||
|
||||
/*
|
||||
* Insert the first space with ins_char(). It will delete one char in
|
||||
* replace mode. Insert the rest with ins_str(); it will not delete any
|
||||
|
@ -8491,12 +8516,13 @@ static bool ins_tab(void)
|
|||
*/
|
||||
ins_char(' ');
|
||||
while (--temp > 0) {
|
||||
if (State & VREPLACE_FLAG)
|
||||
if (State & VREPLACE_FLAG) {
|
||||
ins_char(' ');
|
||||
else {
|
||||
} else {
|
||||
ins_str((char_u *)" ");
|
||||
if (State & REPLACE_FLAG) /* no char replaced */
|
||||
if (State & REPLACE_FLAG) { // no char replaced
|
||||
replace_push(NUL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "nvim/buffer_updates.h"
|
||||
#include "nvim/main.h"
|
||||
#include "nvim/mark.h"
|
||||
#include "nvim/mark_extended.h"
|
||||
#include "nvim/mbyte.h"
|
||||
#include "nvim/memline.h"
|
||||
#include "nvim/message.h"
|
||||
|
@ -658,10 +659,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);
|
||||
false, kExtmarkUndo);
|
||||
msgmore(-deleted);
|
||||
} else if (deleted < 0) {
|
||||
mark_adjust(eap->line2, MAXLNUM, -deleted, 0L, false);
|
||||
mark_adjust(eap->line2, MAXLNUM, -deleted, 0L, false, kExtmarkUndo);
|
||||
}
|
||||
if (change_occurred || deleted != 0) {
|
||||
changed_lines(eap->line1, 0, eap->line2 + 1, -deleted, true);
|
||||
|
@ -874,10 +875,12 @@ 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);
|
||||
mark_adjust_nofold(line1, line2, last_line - line2, 0L, true, kExtmarkNoUndo);
|
||||
extmark_adjust(curbuf, line1, line2, last_line - line2, 0L, kExtmarkNoUndo,
|
||||
true);
|
||||
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);
|
||||
mark_adjust_nofold(line2 + 1, dest, -num_lines, 0L, false, kExtmarkNoUndo);
|
||||
FOR_ALL_TAB_WINDOWS(tab, win) {
|
||||
if (win->w_buffer == curbuf) {
|
||||
foldMoveRange(&win->w_folds, line1, line2, dest);
|
||||
|
@ -886,7 +889,8 @@ 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);
|
||||
mark_adjust_nofold(dest + 1, line1 - 1, num_lines, 0L, false,
|
||||
kExtmarkNoUndo);
|
||||
FOR_ALL_TAB_WINDOWS(tab, win) {
|
||||
if (win->w_buffer == curbuf) {
|
||||
foldMoveRange(&win->w_folds, dest + 1, line1 - 1, line2);
|
||||
|
@ -897,7 +901,9 @@ 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);
|
||||
-(last_line - dest - extra), 0L, true, kExtmarkNoUndo);
|
||||
|
||||
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
|
||||
|
@ -1281,12 +1287,14 @@ static void do_filter(
|
|||
if (cmdmod.keepmarks || vim_strchr(p_cpo, CPO_REMMARK) == NULL) {
|
||||
if (read_linecount >= linecount) {
|
||||
// move all marks from old lines to new lines
|
||||
mark_adjust(line1, line2, linecount, 0L, false);
|
||||
mark_adjust(line1, line2, linecount, 0L, false, kExtmarkUndo);
|
||||
} 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);
|
||||
mark_adjust(line1 + read_linecount, line2, MAXLNUM, 0L, false);
|
||||
mark_adjust(line1, line1 + read_linecount - 1, linecount, 0L, false,
|
||||
kExtmarkUndo);
|
||||
mark_adjust(line1 + read_linecount, line2, MAXLNUM, 0L, false,
|
||||
kExtmarkUndo);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3214,6 +3222,189 @@ 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;
|
||||
linenr_T u_lnum;
|
||||
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;
|
||||
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;
|
||||
u_lnum = n_u_lnum;
|
||||
|
||||
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:
|
||||
///
|
||||
|
@ -3260,6 +3451,17 @@ 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;
|
||||
|
||||
// inccomand tests fail without this check
|
||||
if (!preview) {
|
||||
// Requried for Undo to work for nsmarks,
|
||||
u_save_cursor();
|
||||
}
|
||||
|
||||
if (!global_busy) {
|
||||
sub_nsubs = 0;
|
||||
|
@ -3418,6 +3620,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
|
|||
// Check for a match on each line.
|
||||
// If preview: limit to max('cmdwinheight', viewport).
|
||||
linenr_T line2 = eap->line2;
|
||||
|
||||
for (linenr_T lnum = eap->line1;
|
||||
lnum <= line2 && !got_quit && !aborting()
|
||||
&& (!preview || preview_lines.lines_needed <= (linenr_T)p_cwh
|
||||
|
@ -3876,6 +4079,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
|
|||
|
||||
ADJUST_SUB_FIRSTLNUM();
|
||||
|
||||
|
||||
// Now the trick is to replace CTRL-M chars with a real line
|
||||
// break. This would make it impossible to insert a CTRL-M in
|
||||
// the text. The line break can be avoided by preceding the
|
||||
|
@ -3890,7 +4094,9 @@ 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);
|
||||
mark_adjust(lnum + 1, (linenr_T)MAXLNUM, 1L, 0L, false,
|
||||
kExtmarkNOOP);
|
||||
|
||||
if (subflags.do_ask) {
|
||||
appended_lines(lnum - 1, 1L);
|
||||
} else {
|
||||
|
@ -3917,6 +4123,44 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 4. If subflags.do_all is set, find next match.
|
||||
// Prevent endless loop with patterns that match empty
|
||||
// strings, e.g. :s/$/pat/g or :s/[a-z]* /(&)/g.
|
||||
|
@ -3983,7 +4227,7 @@ skip:
|
|||
ml_delete(lnum, false);
|
||||
}
|
||||
mark_adjust(lnum, lnum + nmatch_tl - 1,
|
||||
(long)MAXLNUM, -nmatch_tl, false);
|
||||
(long)MAXLNUM, -nmatch_tl, false, kExtmarkNOOP);
|
||||
if (subflags.do_ask) {
|
||||
deleted_lines(lnum, nmatch_tl);
|
||||
}
|
||||
|
@ -4159,6 +4403,35 @@ skip:
|
|||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
|
|
|
@ -25,6 +25,12 @@
|
|||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// Gotchas
|
||||
// -------
|
||||
//
|
||||
// if you delete from a kbtree while iterating over it you must use
|
||||
// kb_del_itr and not kb_del otherwise the iterator might point to freed memory.
|
||||
|
||||
#ifndef NVIM_LIB_KBTREE_H
|
||||
#define NVIM_LIB_KBTREE_H
|
||||
|
||||
|
|
|
@ -905,9 +905,10 @@ void mark_adjust(linenr_T line1,
|
|||
linenr_T line2,
|
||||
long amount,
|
||||
long amount_after,
|
||||
bool end_temp)
|
||||
bool end_temp,
|
||||
ExtmarkOp op)
|
||||
{
|
||||
mark_adjust_internal(line1, line2, amount, amount_after, true, end_temp);
|
||||
mark_adjust_internal(line1, line2, amount, amount_after, true, end_temp, op);
|
||||
}
|
||||
|
||||
// mark_adjust_nofold() does the same as mark_adjust() but without adjusting
|
||||
|
@ -916,14 +917,16 @@ 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, bool end_temp,
|
||||
ExtmarkOp op)
|
||||
{
|
||||
mark_adjust_internal(line1, line2, amount, amount_after, false, end_temp);
|
||||
mark_adjust_internal(line1, line2, amount, amount_after, false, end_temp, 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, bool end_temp,
|
||||
ExtmarkOp op)
|
||||
{
|
||||
int i;
|
||||
int fnum = curbuf->b_fnum;
|
||||
|
@ -979,6 +982,9 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
/* previous context mark */
|
||||
|
@ -1090,7 +1096,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)
|
||||
int spaces_removed, ExtmarkOp op)
|
||||
{
|
||||
int i;
|
||||
int fnum = curbuf->b_fnum;
|
||||
|
@ -1110,6 +1116,13 @@ 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));
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,282 @@
|
|||
#ifndef NVIM_MARK_EXTENDED_H
|
||||
#define NVIM_MARK_EXTENDED_H
|
||||
|
||||
#include "nvim/mark_extended_defs.h"
|
||||
#include "nvim/buffer_defs.h" // for buf_T
|
||||
|
||||
|
||||
// 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.
|
||||
|
||||
// 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;\
|
||||
}
|
||||
|
||||
// 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;\
|
||||
ExtendedMark 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);\
|
||||
} \
|
||||
ExtendedMark *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;\
|
||||
ExtendedMark 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);\
|
||||
} \
|
||||
ExtendedMark *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;\
|
||||
ExtendedMark 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);\
|
||||
} \
|
||||
ExtendedMark *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(ExtendedMark *) 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;
|
||||
|
||||
// adjust linenumbers 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;
|
||||
|
||||
// extmark was updated
|
||||
typedef struct {
|
||||
uint64_t ns_id;
|
||||
uint64_t mark_id;
|
||||
linenr_T old_lnum;
|
||||
colnr_T old_col;
|
||||
linenr_T lnum;
|
||||
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;
|
||||
|
||||
|
||||
typedef enum {
|
||||
kLineAdjust,
|
||||
kColAdjust,
|
||||
kColAdjustDelete,
|
||||
kAdjustMove,
|
||||
kExtmarkSet,
|
||||
kExtmarkDel,
|
||||
kExtmarkUpdate,
|
||||
kExtmarkCopy,
|
||||
kExtmarkCopyPlace,
|
||||
kExtmarkClear,
|
||||
} UndoObjectType;
|
||||
|
||||
// TODO(bfredl): reduce the number of undo action types
|
||||
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;
|
||||
} data;
|
||||
};
|
||||
|
||||
|
||||
// For doing move of extmarks in substitutions
|
||||
typedef struct {
|
||||
lpos_T startpos;
|
||||
lpos_T endpos;
|
||||
linenr_T lnum;
|
||||
int sublen;
|
||||
} ExtmarkSubSingle;
|
||||
|
||||
// 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;
|
||||
|
||||
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"
|
||||
#endif
|
||||
|
||||
#endif // NVIM_MARK_EXTENDED_H
|
|
@ -0,0 +1,54 @@
|
|||
#ifndef NVIM_MARK_EXTENDED_DEFS_H
|
||||
#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 ExtendedMark
|
||||
{
|
||||
uint64_t ns_id;
|
||||
uint64_t mark_id;
|
||||
struct ExtMarkLine *line;
|
||||
colnr_T col;
|
||||
} ExtendedMark;
|
||||
|
||||
|
||||
// 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(ExtendedMark a, ExtendedMark 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, ExtendedMark, 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)
|
||||
|
||||
|
||||
typedef struct undo_object ExtmarkUndoObject;
|
||||
typedef kvec_t(ExtmarkUndoObject) extmark_undo_vec_t;
|
||||
|
||||
|
||||
#endif // NVIM_MARK_EXTENDED_DEFS_H
|
|
@ -30,7 +30,6 @@
|
|||
#include "nvim/indent_c.h"
|
||||
#include "nvim/buffer_updates.h"
|
||||
#include "nvim/main.h"
|
||||
#include "nvim/mark.h"
|
||||
#include "nvim/mbyte.h"
|
||||
#include "nvim/memline.h"
|
||||
#include "nvim/memory.h"
|
||||
|
|
138
src/nvim/ops.c
138
src/nvim/ops.c
|
@ -49,6 +49,7 @@
|
|||
#include "nvim/undo.h"
|
||||
#include "nvim/macros.h"
|
||||
#include "nvim/window.h"
|
||||
#include "nvim/lib/kvec.h"
|
||||
#include "nvim/os/input.h"
|
||||
#include "nvim/os/time.h"
|
||||
|
||||
|
@ -306,6 +307,15 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -479,6 +489,10 @@ static void shift_block(oparg_T *oap, int amount)
|
|||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -623,10 +637,19 @@ void op_reindent(oparg_T *oap, Indenter how)
|
|||
amount = how(); /* get the indent for this line */
|
||||
|
||||
if (amount >= 0 && set_indent(amount, SIN_UNDO)) {
|
||||
/* did change the indent, call changed_lines() later */
|
||||
if (first_changed == 0)
|
||||
// did change the indent, call changed_lines() later
|
||||
if (first_changed == 0) {
|
||||
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;
|
||||
|
@ -1621,6 +1644,8 @@ int op_delete(oparg_T *oap)
|
|||
curwin->w_cursor.col = 0;
|
||||
(void)del_bytes((colnr_T)n, !virtual_op,
|
||||
oap->op_type == OP_DELETE && !oap->is_VIsual);
|
||||
extmark_col_adjust(curbuf, curwin->w_cursor.lnum,
|
||||
(colnr_T)0, 0L, (long)-n, kExtmarkUndo);
|
||||
curwin->w_cursor = curpos; // restore curwin->w_cursor
|
||||
(void)do_join(2, false, false, false, false);
|
||||
}
|
||||
|
@ -1632,10 +1657,36 @@ setmarks:
|
|||
if (oap->motion_type == kMTBlockWise) {
|
||||
curbuf->b_op_end.lnum = oap->end.lnum;
|
||||
curbuf->b_op_end.col = oap->start.col;
|
||||
} else
|
||||
} else {
|
||||
curbuf->b_op_end = oap->start;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
// Delete characters within one line,
|
||||
// The case with multiple lines is handled by do_join
|
||||
} else if (oap->motion_type == kMTCharWise && oap->line_count == 1) {
|
||||
// + 1 to change to buf mode, then plus 1 to fit function requirements
|
||||
endcol = oap->end.col + 1 + 1;
|
||||
|
||||
lnum = curwin->w_cursor.lnum;
|
||||
if (oap->is_VIsual == false) {
|
||||
endcol = MAX(endcol - 1, mincol);
|
||||
}
|
||||
extmark_col_adjust_delete(curbuf, lnum, mincol, endcol, kExtmarkUndo, 0);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
@ -2031,8 +2082,8 @@ bool swapchar(int op_type, pos_T *pos)
|
|||
pos_T sp = curwin->w_cursor;
|
||||
|
||||
curwin->w_cursor = *pos;
|
||||
/* don't use del_char(), it also removes composing chars */
|
||||
del_bytes(utf_ptr2len(get_cursor_pos_ptr()), FALSE, FALSE);
|
||||
// don't use del_char(), it also removes composing chars
|
||||
del_bytes(utf_ptr2len(get_cursor_pos_ptr()), false, false);
|
||||
ins_char(nc);
|
||||
curwin->w_cursor = sp;
|
||||
} else {
|
||||
|
@ -2105,8 +2156,9 @@ void op_insert(oparg_T *oap, long count1)
|
|||
* values in "bd". */
|
||||
if (u_save_cursor() == FAIL)
|
||||
return;
|
||||
for (i = 0; i < bd.endspaces; i++)
|
||||
for (i = 0; i < bd.endspaces; i++) {
|
||||
ins_char(' ');
|
||||
}
|
||||
bd.textlen += bd.endspaces;
|
||||
}
|
||||
} else {
|
||||
|
@ -2224,6 +2276,10 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2694,6 +2750,27 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
|
|||
}
|
||||
|
||||
|
||||
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!
|
||||
|
@ -2708,8 +2785,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
|
|||
char_u *oldp;
|
||||
int yanklen;
|
||||
size_t totlen = 0; // init for gcc
|
||||
linenr_T lnum;
|
||||
colnr_T col;
|
||||
linenr_T lnum = 0;
|
||||
colnr_T col = 0;
|
||||
size_t i; // index in y_array[]
|
||||
MotionType y_type;
|
||||
size_t y_size;
|
||||
|
@ -3286,11 +3363,11 @@ error:
|
|||
curbuf->b_op_start.lnum++;
|
||||
}
|
||||
// Skip mark_adjust when adding lines after the last one, there
|
||||
// can't be marks there. But still needed in diff mode.
|
||||
// can't be marks there.
|
||||
if (curbuf->b_op_start.lnum + (y_type == kMTCharWise) - 1 + nr_lines
|
||||
< curbuf->b_ml.ml_line_count || curwin->w_p_diff) {
|
||||
< curbuf->b_ml.ml_line_count) {
|
||||
mark_adjust(curbuf->b_op_start.lnum + (y_type == kMTCharWise),
|
||||
(linenr_T)MAXLNUM, nr_lines, 0L, false);
|
||||
(linenr_T)MAXLNUM, nr_lines, 0L, false, kExtmarkUndo);
|
||||
}
|
||||
|
||||
// note changed text for displaying and folding
|
||||
|
@ -3352,6 +3429,8 @@ 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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3745,6 +3824,7 @@ int do_join(size_t count,
|
|||
* column. This is not Vi compatible, but Vi deletes the marks, thus that
|
||||
* should not really be a problem.
|
||||
*/
|
||||
|
||||
for (t = (linenr_T)count - 1;; t--) {
|
||||
cend -= currsize;
|
||||
memmove(cend, curr, (size_t)currsize);
|
||||
|
@ -3756,12 +3836,18 @@ int do_join(size_t count,
|
|||
// If deleting more spaces than adding, the cursor moves no more than
|
||||
// what is added if it is inside these spaces.
|
||||
const int spaces_removed = (int)((curr - curr_start) - spaces[t]);
|
||||
linenr_T lnum = curwin->w_cursor.lnum + t;
|
||||
colnr_T mincol = (colnr_T)0;
|
||||
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(curwin->w_cursor.lnum + t, (colnr_T)0, (linenr_T)-t,
|
||||
(long)(cend - newp - spaces_removed), spaces_removed);
|
||||
if (t == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t - 1));
|
||||
if (remove_comments)
|
||||
curr += comments[t - 1];
|
||||
|
@ -3769,6 +3855,7 @@ int do_join(size_t count,
|
|||
curr = skipwhite(curr);
|
||||
currsize = (int)STRLEN(curr);
|
||||
}
|
||||
|
||||
ml_replace(curwin->w_cursor.lnum, newp, false);
|
||||
|
||||
if (setmark) {
|
||||
|
@ -4189,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);
|
||||
(long)-next_leader_len, 0, kExtmarkUndo);
|
||||
} 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);
|
||||
(colnr_T)0, 0L, (long)-indent, 0, kExtmarkUndo);
|
||||
}
|
||||
}
|
||||
curwin->w_cursor.lnum--;
|
||||
|
@ -4539,7 +4626,7 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd)
|
|||
int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
|
||||
{
|
||||
int col;
|
||||
char_u *buf1;
|
||||
char_u *buf1 = NULL;
|
||||
char_u buf2[NUMBUFLEN];
|
||||
int pre; // 'X' or 'x': hex; '0': octal; 'B' or 'b': bin
|
||||
static bool hexupper = false; // 0xABC
|
||||
|
@ -4848,7 +4935,6 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
|
|||
*ptr = NUL;
|
||||
STRCAT(buf1, buf2);
|
||||
ins_str(buf1); // insert the new number
|
||||
xfree(buf1);
|
||||
endpos = curwin->w_cursor;
|
||||
if (curwin->w_cursor.col) {
|
||||
curwin->w_cursor.col--;
|
||||
|
@ -4862,7 +4948,25 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
|
|||
curbuf->b_op_end.col--;
|
||||
}
|
||||
|
||||
// if buf1 wasn't allocated, only a singe ASCII char was changed in-place.
|
||||
if (did_change && buf1 != NULL) {
|
||||
extmark_col_adjust_delete(curbuf,
|
||||
pos->lnum,
|
||||
startpos.col + 2,
|
||||
endpos.col + 1 + length,
|
||||
kExtmarkUndo,
|
||||
0);
|
||||
long col_amount = (long)STRLEN(buf1);
|
||||
extmark_col_adjust(curbuf,
|
||||
pos->lnum,
|
||||
startpos.col + 1,
|
||||
0,
|
||||
col_amount,
|
||||
kExtmarkUndo);
|
||||
}
|
||||
|
||||
theend:
|
||||
xfree(buf1);
|
||||
if (visual) {
|
||||
curwin->w_cursor = save_cursor;
|
||||
} else if (did_change) {
|
||||
|
|
|
@ -14,6 +14,10 @@ typedef int colnr_T;
|
|||
enum { MAXLNUM = 0x7fffffff };
|
||||
/// Maximal column number, 31 bits
|
||||
enum { MAXCOL = 0x7fffffff };
|
||||
// Minimum line number
|
||||
enum { MINLNUM = 1 };
|
||||
// minimum column number
|
||||
enum { MINCOL = 1 };
|
||||
|
||||
/*
|
||||
* position in file or buffer
|
||||
|
|
|
@ -91,7 +91,9 @@
|
|||
#include "nvim/fileio.h"
|
||||
#include "nvim/fold.h"
|
||||
#include "nvim/buffer_updates.h"
|
||||
#include "nvim/pos.h" // MAXLNUM
|
||||
#include "nvim/mark.h"
|
||||
#include "nvim/mark_extended.h"
|
||||
#include "nvim/memline.h"
|
||||
#include "nvim/message.h"
|
||||
#include "nvim/misc1.h"
|
||||
|
@ -106,6 +108,7 @@
|
|||
#include "nvim/types.h"
|
||||
#include "nvim/os/os.h"
|
||||
#include "nvim/os/time.h"
|
||||
#include "nvim/lib/kvec.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "undo.c.generated.h"
|
||||
|
@ -384,6 +387,7 @@ int u_savecommon(linenr_T top, linenr_T bot, linenr_T newbot, int reload)
|
|||
* up the undo info when out of memory.
|
||||
*/
|
||||
uhp = xmalloc(sizeof(u_header_T));
|
||||
kv_init(uhp->uh_extmark);
|
||||
#ifdef U_DEBUG
|
||||
uhp->uh_magic = UH_MAGIC;
|
||||
#endif
|
||||
|
@ -2249,10 +2253,10 @@ static void u_undoredo(int undo, bool do_buf_event)
|
|||
xfree((char_u *)uep->ue_array);
|
||||
}
|
||||
|
||||
/* adjust marks */
|
||||
// Adjust marks
|
||||
if (oldsize != newsize) {
|
||||
mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
|
||||
(long)newsize - (long)oldsize, false);
|
||||
(long)newsize - (long)oldsize, false, kExtmarkNOOP);
|
||||
if (curbuf->b_op_start.lnum > top + oldsize) {
|
||||
curbuf->b_op_start.lnum += newsize - oldsize;
|
||||
}
|
||||
|
@ -2285,6 +2289,23 @@ static void u_undoredo(int undo, bool do_buf_event)
|
|||
newlist = uep;
|
||||
}
|
||||
|
||||
// Adjust Extmarks
|
||||
ExtmarkUndoObject undo_info;
|
||||
if (undo) {
|
||||
for (i = (int)kv_size(curhead->uh_extmark) - 1; i > -1; i--) {
|
||||
undo_info = kv_A(curhead->uh_extmark, i);
|
||||
extmark_apply_undo(undo_info, undo);
|
||||
}
|
||||
// redo
|
||||
} else {
|
||||
for (i = 0; i < (int)kv_size(curhead->uh_extmark); i++) {
|
||||
undo_info = kv_A(curhead->uh_extmark, i);
|
||||
extmark_apply_undo(undo_info, undo);
|
||||
}
|
||||
}
|
||||
// finish Adjusting extmarks
|
||||
|
||||
|
||||
curhead->uh_entry = newlist;
|
||||
curhead->uh_flags = new_flags;
|
||||
if ((old_flags & UH_EMPTYBUF) && BUFEMPTY()) {
|
||||
|
@ -2828,6 +2849,8 @@ u_freeentries(
|
|||
u_freeentry(uep, uep->ue_size);
|
||||
}
|
||||
|
||||
kv_destroy(uhp->uh_extmark);
|
||||
|
||||
#ifdef U_DEBUG
|
||||
uhp->uh_magic = 0;
|
||||
#endif
|
||||
|
@ -3022,3 +3045,28 @@ list_T *u_eval_tree(const u_header_T *const first_uhp)
|
|||
|
||||
return list;
|
||||
}
|
||||
|
||||
// Given the buffer, Return the undo header. If none is set, set one first.
|
||||
// NULL will be returned if e.g undolevels = -1 (undo disabled)
|
||||
u_header_T *u_force_get_undo_header(buf_T *buf)
|
||||
{
|
||||
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;
|
||||
}
|
||||
// Create the first undo header for the buffer
|
||||
if (!uhp) {
|
||||
// TODO(timeyyy): there would be a better way to do this!
|
||||
u_save_cursor();
|
||||
uhp = buf->b_u_curhead;
|
||||
if (!uhp) {
|
||||
uhp = buf->b_u_newhead;
|
||||
if (get_undolevel() > 0 && !uhp) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
return uhp;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <time.h> // for time_t
|
||||
|
||||
#include "nvim/pos.h"
|
||||
#include "nvim/mark_extended_defs.h"
|
||||
#include "nvim/mark_defs.h"
|
||||
|
||||
typedef struct u_header u_header_T;
|
||||
|
@ -56,14 +57,15 @@ struct u_header {
|
|||
u_entry_T *uh_getbot_entry; /* pointer to where ue_bot must be set */
|
||||
pos_T uh_cursor; /* cursor position before saving */
|
||||
long uh_cursor_vcol;
|
||||
int uh_flags; /* see below */
|
||||
fmark_T uh_namedm[NMARKS]; /* marks before undo/after redo */
|
||||
visualinfo_T uh_visual; /* Visual areas before undo/after redo */
|
||||
time_t uh_time; /* timestamp when the change was made */
|
||||
long uh_save_nr; /* set when the file was saved after the
|
||||
changes in this block */
|
||||
int uh_flags; // see below
|
||||
fmark_T uh_namedm[NMARKS]; // marks before undo/after redo
|
||||
extmark_undo_vec_t uh_extmark; // info to move extmarks
|
||||
visualinfo_T uh_visual; // Visual areas before undo/after redo
|
||||
time_t uh_time; // timestamp when the change was made
|
||||
long uh_save_nr; // set when the file was saved after the
|
||||
// changes in this block
|
||||
#ifdef U_DEBUG
|
||||
int uh_magic; /* magic number to check allocation */
|
||||
int uh_magic; // magic number to check allocation
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue