extmark: review changes

This commit is contained in:
Björn Linse 2019-11-09 12:41:50 +01:00
parent a9065a5051
commit 18a8b702c0
17 changed files with 732 additions and 804 deletions

View File

@ -148,14 +148,3 @@ See `LICENSE` for details.
[Gentoo]: https://packages.gentoo.org/packages/app-editors/neovim
<!-- vim: set tw=80: -->
CC=clang make functionaltest CMAKE_EXTRA_FLAGS="-DCLANG_ASAN_UBSAN=ON" TEST_FILE=test/functional/api TEST_TAG=extmarks
CC=clang make functionaltest CMAKE_EXTRA_FLAGS="-DCLANG_ASAN_UBSAN=ON" TEST_FILE=test/functional/ui/mouse_spec.lua TEST_TAG=blah
CC=clang make functionaltest CMAKE_EXTRA_FLAGS="-DCLANG_ASAN_UBSAN=ON"
CC=clang make functionaltest CMAKE_EXTRA_FLAGS="-DCLANG_ASAN_UBSAN=OFF" TEST_FILE=test/functional/api/mark_extended_spec.lua TEST_TAG=broken
# TODO debug in container with clion...
https://github.com/shuhaoliu/docker-clion-dev

View File

@ -439,34 +439,40 @@ Example: create a float with scratch buffer: >
>
==============================================================================
Buffer extended marks *api-extended-marks*
Extended marks *api-extended-marks*
An extended mark represents a buffer annotation that remains logically
stationary even as the buffer changes. They could be used to represent cursors,
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 row 1, column three.
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_mark(0, :g:mark_ns, 0, 1, 3)`
`let g:mark_id = nvim_buf_set_extmark(0, g:mark_ns, 0, 0, 2)`
Note: the mark id was randomly generated because we used an inital value of 0
Passing in id=0 creates a new mark and returns the id. we can look-up a mark
by its id:
`echo nvim_buf_lookup_mark(0, g:mark_ns, g:mark_id)`
=> [1, 1, 3]
`echo nvim_buf_get_marks(0, g:mark_ns, [1, 1], [1, 3], -1, 0)`
=> [[1, 1, 3]]
`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_unset_mark()| function.
to remove an extended mark, use the |nvim_buf_del_extmark()| function.
The namepsace ensures you only ever work with the extended marks you mean to.
Calling set and unset of marks will not automatically prop a new undo.
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*
@ -1479,22 +1485,6 @@ nvim_select_popupmenu_item({item}, {insert}, {finish}, {opts})
nvim__inspect_cell({grid}, {row}, {col}) *nvim__inspect_cell()*
TODO: Documentation
*nvim_init_mark_ns()*
nvim_init_mark_ns({namespace})
Create a new namepsace for holding extended marks. The id
of the namespace is returned, this namespace id is required
for using the rest of the extended marks api.
Parameters:~
{namespace} String name to be assigned to the namespace
*nvim_mark_get_ns_ids()*
nvim_mark_get_ns_ids()
Returns a list of extended mark namespaces.
Pairs of ids and string names are returned.
An empty list will be returned if no namespaces are set.
==============================================================================
Buffer Functions *api-buffer*
@ -1793,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})

View File

@ -1003,16 +1003,16 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err)
return rv;
}
/// Returns position info for a given extmark id
/// 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 namespace,
ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
Integer id, Error *err)
FUNC_API_SINCE(6)
FUNC_API_SINCE(7)
{
Array rv = ARRAY_DICT_INIT;
@ -1022,13 +1022,13 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer namespace,
return rv;
}
if (!ns_initialized((uint64_t)namespace)) {
api_set_error(err, kErrorTypeValidation, _("Invalid mark namespace"));
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)namespace,
(uint64_t)ns_id,
(uint64_t)id);
if (!extmark) {
return rv;
@ -1052,16 +1052,17 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer namespace,
/// first marks prior to a given position.
///
/// @param buffer The buffer handle
/// @param namespace An id returned previously from nvim_create_namespace
/// @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 amount Maximum number of marks to return or -1 for all marks found
/// /// @param[out] err Details of an error that may have occurred
/// @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, Integer amount,
Object start, Object end, Dictionary opts,
Error *err)
FUNC_API_SINCE(6)
FUNC_API_SINCE(7)
{
Array rv = ARRAY_DICT_INIT;
@ -1071,9 +1072,26 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id,
}
if (!ns_initialized((uint64_t)ns_id)) {
api_set_error(err, kErrorTypeValidation, _("Invalid mark namespace"));
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;
@ -1122,20 +1140,30 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id,
return rv;
}
/// Create or update a namespaced mark at a position
/// 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 for next free id
/// @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 nsmark_id for a new mark, or 0 for an update
/// @return the id of the extmark.
Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer id,
Integer line, Integer col, Error *err)
FUNC_API_SINCE(6)
Integer line, Integer col,
Dictionary opts, Error *err)
FUNC_API_SINCE(7)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
if (!buf) {
@ -1143,7 +1171,12 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer id,
}
if (!ns_initialized((uint64_t)ns_id)) {
api_set_error(err, kErrorTypeValidation, _("Invalid mark namespace"));
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;
}
@ -1172,16 +1205,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer id,
return 0;
}
bool new = extmark_set(buf, (uint64_t)ns_id, id_num,
(linenr_T)line+1,
(colnr_T)col+1,
kExtmarkUndo);
extmark_set(buf, (uint64_t)ns_id, id_num,
(linenr_T)line+1, (colnr_T)col+1, kExtmarkUndo);
if (new) {
return (Integer)id_num;
} else {
return 0;
}
return (Integer)id_num;
}
/// Remove an extmark
@ -1190,12 +1217,12 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer id,
/// @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 no extmarks found
/// @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(6)
FUNC_API_SINCE(7)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@ -1203,7 +1230,7 @@ Boolean nvim_buf_del_extmark(Buffer buffer,
return false;
}
if (!ns_initialized((uint64_t)ns_id)) {
api_set_error(err, kErrorTypeValidation, _("Invalid mark namespace"));
api_set_error(err, kErrorTypeValidation, _("Invalid ns_id"));
return false;
}

View File

@ -1575,12 +1575,14 @@ bool ns_initialized(uint64_t ns)
return ns < (uint64_t)next_namespace_id;
}
// Extmarks may be queried from position or name or even special names
// in the future such as "cursor". This macro sets the line and col
// to make the extmark functions recognize what's required
//
// *lnum: linenr_T, lnum to be set
// *col: colnr_T, col to be set
/// 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)

View File

@ -809,7 +809,7 @@ struct file_buffer {
kvec_t(BufhlLine *) b_bufhl_move_space; // temporary space for highlights
PMap(uint64_t) *b_extmark_ns; // extmark namespaces
kbtree_t(extlines) b_extlines; // extmarks
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

View File

@ -106,8 +106,6 @@ typedef struct {
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_cmds.c.generated.h"
#include "mark_extended.h"
#endif
/// ":ascii" and "ga" implementation

View File

@ -184,7 +184,6 @@ 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)
MAP_IMPL(uint64_t, cstr_t, DEFAULT_INITIALIZER)
/// Deletes a key:value pair from a string:pointer map, and frees the

View File

@ -42,7 +42,6 @@ MAP_DECLS(handle_T, ptr_t)
MAP_DECLS(String, MsgpackRpcRequestHandler)
MAP_DECLS(HlEntry, int)
MAP_DECLS(String, handle_T)
MAP_DECLS(uint64_t, cstr_t)
#define map_new(T, U) map_##T##_##U##_new
#define map_free(T, U) map_##T##_##U##_free

View File

@ -1,8 +1,8 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// Implements extended marks for plugins
// Each Mark exists in a btree of lines containing btrees of columns.
// Implements extended marks for plugins. Each mark exists in a btree of
// lines containing btrees of columns.
//
// The btree provides efficent range lookups.
// A map of pointers to the marks is used for fast lookup by mark id.
@ -17,17 +17,17 @@
// copy extmarks is for the area being effected by a delete.
//
// Marks live in namespaces that allow plugins/users to segregate marks
// from other users, namespaces have to be initialized before usage
// from other users.
//
// For possible ideas for efficency improvements see:
// http://blog.atom.io/2015/06/16/optimizing-an-important-atom-primitive.html
// TODO(bfredl): These ideas could be used for an enhanced btree, which
// wouldn't need separate line and column layers.
// Other implementations exist in gtk and tk toolkits.
//
// Deleting marks only happens explicitly extmark_del, deleteing over a
// range of marks will only move the marks.
//
// deleting on a mark will leave it in that same position unless it is on
// the eol of the line.
// Deleting marks only happens when explicitly calling extmark_del, deleteing
// over a range of marks will only move the marks. Deleting on a mark will
// leave it in same position unless it is on the EOL of a line.
#include <assert.h>
#include "nvim/vim.h"
@ -50,23 +50,19 @@
///
/// must not be used during iteration!
/// @returns whether a new mark was created
int extmark_set(buf_T *buf,
uint64_t ns,
uint64_t id,
linenr_T lnum,
colnr_T col,
ExtmarkOp op)
int extmark_set(buf_T *buf, uint64_t ns, uint64_t id,
linenr_T lnum, colnr_T col, ExtmarkOp op)
{
ExtendedMark *extmark = extmark_from_id(buf, ns, id);
if (!extmark) {
extmark_create(buf, ns, id, lnum, col, op);
return true;
} else {
ExtMarkLine *extline = extmark->line;
ExtMarkLine *extmarkline = extmark->line;
extmark_update(extmark, buf, ns, id, lnum, col, op, NULL);
if (kb_size(&extline->items) == 0) {
kb_del(extlines, &buf->b_extlines, extline);
extline_free(extline);
if (kb_size(&extmarkline->items) == 0) {
kb_del(extmarklines, &buf->b_extlines, extmarkline);
extmarkline_free(extmarkline);
}
return false;
}
@ -74,10 +70,7 @@ int extmark_set(buf_T *buf,
// Remove an extmark
// Returns 0 on missing id
int extmark_del(buf_T *buf,
uint64_t ns,
uint64_t id,
ExtmarkOp op)
int extmark_del(buf_T *buf, uint64_t ns, uint64_t id, ExtmarkOp op)
{
ExtendedMark *extmark = extmark_from_id(buf, ns, id);
if (!extmark) {
@ -88,11 +81,8 @@ int extmark_del(buf_T *buf,
// Free extmarks in a ns between lines
// if ns = 0, it means clear all namespaces
void extmark_clear(buf_T *buf,
uint64_t ns,
linenr_T l_lnum,
linenr_T u_lnum,
ExtmarkOp undo)
void extmark_clear(buf_T *buf, uint64_t ns,
linenr_T l_lnum, linenr_T u_lnum, ExtmarkOp undo)
{
if (!buf->b_extmark_ns) {
return;
@ -115,7 +105,7 @@ void extmark_clear(buf_T *buf,
}
FOR_ALL_EXTMARKLINES(buf, l_lnum, u_lnum, {
FOR_ALL_EXTMARKS_IN_LINE(extline->items, 0, MAXCOL, {
FOR_ALL_EXTMARKS_IN_LINE(extmarkline->items, 0, MAXCOL, {
if (extmark->ns_id == ns || all_ns) {
marks_cleared = true;
if (all_ns) {
@ -124,12 +114,12 @@ void extmark_clear(buf_T *buf,
ns_obj = pmap_get(uint64_t)(buf->b_extmark_ns, ns);
}
pmap_del(uint64_t)(ns_obj->map, extmark->mark_id);
kb_del_itr(markitems, &extline->items, &mitr);
kb_del_itr(markitems, &extmarkline->items, &mitr);
}
});
if (kb_size(&extline->items) == 0) {
kb_del_itr(extlines, &buf->b_extlines, &itr);
extline_free(extline);
if (kb_size(&extmarkline->items) == 0) {
kb_del_itr(extmarklines, &buf->b_extlines, &itr);
extmarkline_free(extmarkline);
}
});
@ -145,14 +135,10 @@ void extmark_clear(buf_T *buf,
// will be searched to the start, or end
// dir can be set to control the order of the array
// amount = amount of marks to find or -1 for all
ExtmarkArray extmark_get(buf_T *buf,
uint64_t ns,
linenr_T l_lnum,
colnr_T l_col,
linenr_T u_lnum,
colnr_T u_col,
int64_t amount,
bool reverse)
ExtmarkArray extmark_get(buf_T *buf, uint64_t ns,
linenr_T l_lnum, colnr_T l_col,
linenr_T u_lnum, colnr_T u_col,
int64_t amount, bool reverse)
{
ExtmarkArray array = KV_INITIAL_VALUE;
// Find all the marks
@ -178,12 +164,8 @@ ExtmarkArray extmark_get(buf_T *buf,
return array;
}
static void extmark_create(buf_T *buf,
uint64_t ns,
uint64_t id,
linenr_T lnum,
colnr_T col,
ExtmarkOp op)
static void extmark_create(buf_T *buf, uint64_t ns, uint64_t id,
linenr_T lnum, colnr_T col, ExtmarkOp op)
{
if (!buf->b_extmark_ns) {
buf->b_extmark_ns = pmap_new(uint64_t)();
@ -198,13 +180,13 @@ static void extmark_create(buf_T *buf,
}
// Create or get a line
ExtMarkLine *extline = extline_ref(buf, lnum, true);
ExtMarkLine *extmarkline = extmarkline_ref(buf, lnum, true);
// Create and put mark on the line
extmark_put(col, id, extline, ns);
extmark_put(col, id, extmarkline, ns);
// Marks do not have stable address so we have to look them up
// by using the line instead of the mark
pmap_put(uint64_t)(ns_obj->map, id, extline);
pmap_put(uint64_t)(ns_obj->map, id, extmarkline);
if (op != kExtmarkNoUndo) {
u_extmark_set(buf, ns, id, lnum, col, kExtmarkSet);
}
@ -215,14 +197,10 @@ static void extmark_create(buf_T *buf,
// update the position of an extmark
// to update while iterating pass the markitems itr
static void extmark_update(ExtendedMark *extmark,
buf_T *buf,
uint64_t ns,
uint64_t id,
linenr_T lnum,
colnr_T col,
ExtmarkOp op,
kbitr_t(markitems) *mitr)
static void extmark_update(ExtendedMark *extmark, buf_T *buf,
uint64_t ns, uint64_t id,
linenr_T lnum, colnr_T col,
ExtmarkOp op, kbitr_t(markitems) *mitr)
{
assert(op != kExtmarkNOOP);
if (op != kExtmarkNoUndo) {
@ -232,7 +210,7 @@ static void extmark_update(ExtendedMark *extmark,
ExtMarkLine *old_line = extmark->line;
// Move the mark to a new line and update column
if (old_line->lnum != lnum) {
ExtMarkLine *ref_line = extline_ref(buf, lnum, true);
ExtMarkLine *ref_line = extmarkline_ref(buf, lnum, true);
extmark_put(col, id, ref_line, ns);
// Update the hashmap
ExtmarkNs *ns_obj = pmap_get(uint64_t)(buf->b_extmark_ns, ns);
@ -272,12 +250,12 @@ static int extmark_delete(ExtendedMark *extmark,
pmap_del(uint64_t)(ns_obj->map, id);
// Remove the mark mark from the line
ExtMarkLine *extline = extmark->line;
kb_del(markitems, &extline->items, *extmark);
ExtMarkLine *extmarkline = extmark->line;
kb_del(markitems, &extmarkline->items, *extmark);
// Remove the line if there are no more marks in the line
if (kb_size(&extline->items) == 0) {
kb_del(extlines, &buf->b_extlines, extline);
extline_free(extline);
if (kb_size(&extmarkline->items) == 0) {
kb_del(extmarklines, &buf->b_extlines, extmarkline);
extmarkline_free(extmarkline);
}
return true;
}
@ -292,12 +270,12 @@ ExtendedMark *extmark_from_id(buf_T *buf, uint64_t ns, uint64_t id)
if (!ns_obj || !kh_size(ns_obj->map->table)) {
return NULL;
}
ExtMarkLine *extline = pmap_get(uint64_t)(ns_obj->map, id);
if (!extline) {
ExtMarkLine *extmarkline = pmap_get(uint64_t)(ns_obj->map, id);
if (!extmarkline) {
return NULL;
}
FOR_ALL_EXTMARKS_IN_LINE(extline->items, 0, MAXCOL, {
FOR_ALL_EXTMARKS_IN_LINE(extmarkline->items, 0, MAXCOL, {
if (extmark->ns_id == ns
&& extmark->mark_id == id) {
return extmark;
@ -354,8 +332,8 @@ void extmark_free_all(buf_T *buf)
ExtmarkNs *ns_obj;
FOR_ALL_EXTMARKLINES(buf, 1, MAXLNUM, {
kb_del_itr(extlines, &buf->b_extlines, &itr);
extline_free(extline);
kb_del_itr(extmarklines, &buf->b_extlines, &itr);
extmarkline_free(extmarkline);
})
map_foreach(buf->b_extmark_ns, ns, ns_obj, {
@ -365,9 +343,10 @@ void extmark_free_all(buf_T *buf)
});
pmap_free(uint64_t)(buf->b_extmark_ns);
buf->b_extmark_ns = NULL;
// k?_init called to set pointers to NULL
kb_destroy(extlines, (&buf->b_extlines));
kb_destroy(extmarklines, (&buf->b_extlines));
kb_init(&buf->b_extlines);
kv_destroy(buf->b_extmark_move_space);
@ -376,14 +355,10 @@ void extmark_free_all(buf_T *buf)
// Save info for undo/redo of set marks
static void u_extmark_set(buf_T *buf,
uint64_t ns,
uint64_t id,
linenr_T lnum,
colnr_T col,
UndoObjectType undo_type)
static void u_extmark_set(buf_T *buf, uint64_t ns, uint64_t id,
linenr_T lnum, colnr_T col, UndoObjectType undo_type)
{
u_header_T *uhp = force_get_undo_header(buf);
u_header_T *uhp = u_force_get_undo_header(buf);
if (!uhp) {
return;
}
@ -401,15 +376,11 @@ static void u_extmark_set(buf_T *buf,
}
// Save info for undo/redo of deleted marks
static void u_extmark_update(buf_T *buf,
uint64_t ns,
uint64_t id,
linenr_T old_lnum,
colnr_T old_col,
linenr_T lnum,
colnr_T col)
static void u_extmark_update(buf_T *buf, uint64_t ns, uint64_t id,
linenr_T old_lnum, colnr_T old_col,
linenr_T lnum, colnr_T col)
{
u_header_T *uhp = force_get_undo_header(buf);
u_header_T *uhp = u_force_get_undo_header(buf);
if (!uhp) {
return;
}
@ -431,13 +402,10 @@ static void u_extmark_update(buf_T *buf,
// - Instead of 1 undo object for each char inserted,
// we create 1 undo objet for all text inserted before the user hits esc
// Return True if we compacted else False
static bool u_compact_col_adjust(buf_T *buf,
linenr_T lnum,
colnr_T mincol,
long lnum_amount,
long col_amount)
static bool u_compact_col_adjust(buf_T *buf, linenr_T lnum, colnr_T mincol,
long lnum_amount, long col_amount)
{
u_header_T *uhp = force_get_undo_header(buf);
u_header_T *uhp = u_force_get_undo_header(buf);
if (!uhp) {
return false;
}
@ -472,13 +440,10 @@ static bool u_compact_col_adjust(buf_T *buf,
}
// Save col_adjust info so we can undo/redo
void u_extmark_col_adjust(buf_T *buf,
linenr_T lnum,
colnr_T mincol,
long lnum_amount,
long col_amount)
void u_extmark_col_adjust(buf_T *buf, linenr_T lnum, colnr_T mincol,
long lnum_amount, long col_amount)
{
u_header_T *uhp = force_get_undo_header(buf);
u_header_T *uhp = u_force_get_undo_header(buf);
if (!uhp) {
return;
}
@ -498,13 +463,10 @@ void u_extmark_col_adjust(buf_T *buf,
}
// Save col_adjust_delete info so we can undo/redo
void u_extmark_col_adjust_delete(buf_T *buf,
linenr_T lnum,
colnr_T mincol,
colnr_T endcol,
int eol)
void u_extmark_col_adjust_delete(buf_T *buf, linenr_T lnum,
colnr_T mincol, colnr_T endcol, int eol)
{
u_header_T *uhp = force_get_undo_header(buf);
u_header_T *uhp = u_force_get_undo_header(buf);
if (!uhp) {
return;
}
@ -522,13 +484,10 @@ void u_extmark_col_adjust_delete(buf_T *buf,
}
// Save adjust info so we can undo/redo
static void u_extmark_adjust(buf_T * buf,
linenr_T line1,
linenr_T line2,
long amount,
long amount_after)
static void u_extmark_adjust(buf_T * buf, linenr_T line1, linenr_T line2,
long amount, long amount_after)
{
u_header_T *uhp = force_get_undo_header(buf);
u_header_T *uhp = u_force_get_undo_header(buf);
if (!uhp) {
return;
}
@ -546,15 +505,11 @@ static void u_extmark_adjust(buf_T * buf,
}
// save info to undo/redo a :move
void u_extmark_move(buf_T *buf,
linenr_T line1,
linenr_T line2,
linenr_T last_line,
linenr_T dest,
linenr_T num_lines,
void u_extmark_move(buf_T *buf, linenr_T line1, linenr_T line2,
linenr_T last_line, linenr_T dest, linenr_T num_lines,
linenr_T extra)
{
u_header_T *uhp = force_get_undo_header(buf);
u_header_T *uhp = u_force_get_undo_header(buf);
if (!uhp) {
return;
}
@ -577,14 +532,11 @@ void u_extmark_move(buf_T *buf,
// the operation. This will do nothing on redo, enforces correct position when
// undo.
// if ns = 0, it means copy all namespaces
void u_extmark_copy(buf_T *buf,
uint64_t ns,
linenr_T l_lnum,
colnr_T l_col,
linenr_T u_lnum,
colnr_T u_col)
void u_extmark_copy(buf_T *buf, uint64_t ns,
linenr_T l_lnum, colnr_T l_col,
linenr_T u_lnum, colnr_T u_col)
{
u_header_T *uhp = force_get_undo_header(buf);
u_header_T *uhp = u_force_get_undo_header(buf);
if (!uhp) {
return;
}
@ -608,14 +560,11 @@ void u_extmark_copy(buf_T *buf,
}
void u_extmark_copy_place(buf_T *buf,
linenr_T l_lnum,
colnr_T l_col,
linenr_T u_lnum,
colnr_T u_col,
linenr_T p_lnum,
colnr_T p_col)
linenr_T l_lnum, colnr_T l_col,
linenr_T u_lnum, colnr_T u_col,
linenr_T p_lnum, colnr_T p_col)
{
u_header_T *uhp = force_get_undo_header(buf);
u_header_T *uhp = u_force_get_undo_header(buf);
if (!uhp) {
return;
}
@ -635,12 +584,10 @@ void u_extmark_copy_place(buf_T *buf,
}
// Save info for undo/redo of extmark_clear
static void u_extmark_clear(buf_T *buf,
uint64_t ns,
linenr_T l_lnum,
linenr_T u_lnum)
static void u_extmark_clear(buf_T *buf, uint64_t ns,
linenr_T l_lnum, linenr_T u_lnum)
{
u_header_T *uhp = force_get_undo_header(buf);
u_header_T *uhp = u_force_get_undo_header(buf);
if (!uhp) {
return;
}
@ -843,105 +790,35 @@ static void apply_undo_move(ExtmarkUndoObject undo_info, bool undo)
if (undo) {
if (dest >= line2) {
extmark_adjust(curbuf,
dest - num_lines + 1,
dest,
last_line - dest + num_lines - 1,
0L,
kExtmarkNoUndo,
extmark_adjust(curbuf, dest - num_lines + 1, dest,
last_line - dest + num_lines - 1, 0L, kExtmarkNoUndo,
true);
extmark_adjust(curbuf,
dest - line2,
dest - line1,
dest - line2,
0L,
kExtmarkNoUndo,
false);
extmark_adjust(curbuf, dest - line2, dest - line1,
dest - line2, 0L, kExtmarkNoUndo, false);
} else {
extmark_adjust(curbuf,
line1-num_lines,
line2-num_lines,
last_line - (line1-num_lines),
0L,
kExtmarkNoUndo,
true);
extmark_adjust(curbuf,
(line1-num_lines) + 1,
(line2-num_lines) + 1,
-num_lines,
0L,
kExtmarkNoUndo,
false);
extmark_adjust(curbuf, line1-num_lines, line2-num_lines,
last_line - (line1-num_lines), 0L, kExtmarkNoUndo, true);
extmark_adjust(curbuf, (line1-num_lines) + 1, (line2-num_lines) + 1,
-num_lines, 0L, kExtmarkNoUndo, false);
}
extmark_adjust(curbuf,
last_line,
last_line + num_lines - 1,
line1 - last_line,
0L,
kExtmarkNoUndo,
true);
extmark_adjust(curbuf, last_line, last_line + num_lines - 1,
line1 - last_line, 0L, kExtmarkNoUndo, true);
// redo
} else {
extmark_adjust(curbuf,
line1,
line2,
last_line - line2,
0L,
kExtmarkNoUndo,
true);
extmark_adjust(curbuf, line1, line2,
last_line - line2, 0L, kExtmarkNoUndo, true);
if (dest >= line2) {
extmark_adjust(curbuf,
line2 + 1,
dest,
-num_lines,
0L,
kExtmarkNoUndo,
false);
extmark_adjust(curbuf, line2 + 1, dest,
-num_lines, 0L, kExtmarkNoUndo, false);
} else {
extmark_adjust(curbuf,
dest + 1,
line1 - 1,
num_lines,
0L,
kExtmarkNoUndo,
false);
extmark_adjust(curbuf, dest + 1, line1 - 1,
num_lines, 0L, kExtmarkNoUndo, false);
}
extmark_adjust(curbuf,
last_line - num_lines + 1,
last_line,
-(last_line - dest - extra),
0L,
kExtmarkNoUndo,
true);
extmark_adjust(curbuf, last_line - num_lines + 1, last_line,
-(last_line - dest - extra), 0L, kExtmarkNoUndo, true);
}
}
// for anything other than deletes
// Return, desired col amount where the adjustment should take place
// (not taking) eol into account
static long update_constantly(colnr_T _, colnr_T __, long col_amount)
{
return col_amount;
}
// for deletes,
// Return, desired col amount where the adjustment should take place
// (not taking) eol into account
static long update_variably(colnr_T mincol, colnr_T current, long endcol)
{
colnr_T start_effected_range = mincol - 1;
long col_amount;
// When mark inside range
if (current < endcol) {
col_amount = -(current - start_effected_range);
// Mark outside of range
} else {
// -1 because a delete of width 0 should still move marks
col_amount = -(endcol - mincol) - 1;
}
return col_amount;
}
/// Get the column position for EOL on a line
///
@ -960,23 +837,37 @@ colnr_T extmark_eol_col(buf_T *buf, linenr_T lnum)
// returns true if something was moved otherwise false
static bool extmark_col_adjust_impl(buf_T *buf, linenr_T lnum,
colnr_T mincol, long lnum_amount,
long (*calc_amount)(colnr_T, colnr_T, long),
long func_arg)
bool for_delete,
long update_col)
{
bool marks_exist = false;
colnr_T *cp;
long col_amount;
ExtMarkLine *extline = extline_ref(buf, lnum, false);
if (!extline) {
ExtMarkLine *extmarkline = extmarkline_ref(buf, lnum, false);
if (!extmarkline) {
return false;
}
FOR_ALL_EXTMARKS_IN_LINE(extline->items, mincol, MAXCOL, {
FOR_ALL_EXTMARKS_IN_LINE(extmarkline->items, mincol, MAXCOL, {
marks_exist = true;
cp = &(extmark->col);
col_amount = (*calc_amount)(mincol, *cp, func_arg);
// Calculate desired col amount where the adjustment should take place
// (not taking) eol into account
long col_amount;
if (for_delete) {
if (extmark->col < update_col) {
// When mark inside range
colnr_T start_effected_range = mincol - 1;
col_amount = -(extmark->col - start_effected_range);
} else {
// Mark outside of range
// -1 because a delete of width 0 should still move marks
col_amount = -(update_col - mincol) - 1;
}
} else {
// for anything other than deletes
col_amount = update_col;
}
// No update required for this guy
if (col_amount == 0 && lnum_amount == 0) {
continue;
@ -984,23 +875,22 @@ static bool extmark_col_adjust_impl(buf_T *buf, linenr_T lnum,
// Set mark to start of line
if (col_amount < 0
&& *cp <= (colnr_T)-col_amount) { // TODO(timeyyy): does mark.c
// need this line?
&& extmark->col <= (colnr_T)-col_amount) {
extmark_update(extmark, buf, extmark->ns_id, extmark->mark_id,
extline->lnum + lnum_amount,
extmarkline->lnum + lnum_amount,
1, kExtmarkNoUndo, &mitr);
// Update the mark
} else {
// Note: The undo is handled by u_extmark_col_adjust, NoUndo here
extmark_update(extmark, buf, extmark->ns_id, extmark->mark_id,
extline->lnum + lnum_amount,
*cp + (colnr_T)col_amount, kExtmarkNoUndo, &mitr);
extmarkline->lnum + lnum_amount,
extmark->col + (colnr_T)col_amount, kExtmarkNoUndo, &mitr);
}
})
if (kb_size(&extline->items) == 0) {
kb_del(extlines, &buf->b_extlines, extline);
extline_free(extline);
if (kb_size(&extmarkline->items) == 0) {
kb_del(extmarklines, &buf->b_extlines, extmarkline);
extmarkline_free(extmarkline);
}
return marks_exist;
@ -1019,7 +909,7 @@ void extmark_col_adjust(buf_T *buf, linenr_T lnum,
assert(col_amount > INT_MIN && col_amount <= INT_MAX);
bool marks_moved = extmark_col_adjust_impl(buf, lnum, mincol, lnum_amount,
&update_constantly, col_amount);
false, col_amount);
if (undo == kExtmarkUndo && marks_moved) {
u_extmark_col_adjust(buf, lnum, mincol, lnum_amount, col_amount);
@ -1038,17 +928,6 @@ void extmark_col_adjust_delete(buf_T *buf, linenr_T lnum,
ExtmarkOp undo, int _eol)
{
colnr_T start_effected_range = mincol;
// TODO(timeyyy): understand why this has to be uncommented out for the
// tests to pass.. shouldn't this be required?
// why is this even being called if it's not neeed?
// Some tests in the vim suite were hitting the assertion that was here.
// functional/ui/mouse_spec/
// We don't know what to do in this case so just bail!
// if (start_effected_range <= endcol) {
// return;
// }
bool marks_moved;
if (undo == kExtmarkUndo) {
@ -1058,7 +937,7 @@ void extmark_col_adjust_delete(buf_T *buf, linenr_T lnum,
}
marks_moved = extmark_col_adjust_impl(buf, lnum, mincol, 0,
&update_variably, (long)endcol);
true, (long)endcol);
// Deletes at the end of the line have different behaviour than the normal
// case when deleted.
@ -1071,7 +950,7 @@ void extmark_col_adjust_delete(buf_T *buf, linenr_T lnum,
}
FOR_ALL_EXTMARKS(buf, 1, lnum, eol, lnum, -1, {
extmark_update(extmark, buf, extmark->ns_id, extmark->mark_id,
extline->lnum, (colnr_T)eol, kExtmarkNoUndo, &mitr);
extmarkline->lnum, (colnr_T)eol, kExtmarkNoUndo, &mitr);
})
// Record the undo for the actual move
@ -1092,12 +971,12 @@ void extmark_adjust(buf_T *buf,
ExtMarkLine *_extline;
// btree needs to be kept ordered to work, so far only :move requires this
// 2nd call with end_temp = unpack the lines from the temp position
// 2nd call with end_temp = true unpack the lines from the temp position
if (end_temp && amount < 0) {
for (size_t i = 0; i < kv_size(buf->b_extmark_move_space); i++) {
_extline = kv_A(buf->b_extmark_move_space, i);
_extline->lnum += amount;
kb_put(extlines, &buf->b_extlines, _extline);
kb_put(extmarklines, &buf->b_extlines, _extline);
}
kv_size(buf->b_extmark_move_space) = 0;
return;
@ -1108,11 +987,12 @@ void extmark_adjust(buf_T *buf,
linenr_T adj_start = line1;
if (amount == MAXLNUM) {
// Careful! marks from deleted region can end up on en extisting extline
// Careful! marks from deleted region can end up on en extisting extmarkline
// that is goinig to be adjusted to the target position.
linenr_T join_num = line1 - amount_after;
ExtMarkLine *joinline = join_num > line2 \
? extline_ref(buf, join_num, false) : NULL;
ExtMarkLine *joinline = (join_num > line2
? extmarkline_ref(buf, join_num, false) : NULL);
// extmark_adjust is already redoable, the copy should only be for undo
marks_exist = extmark_copy_and_place(curbuf,
line1, 1,
@ -1123,12 +1003,12 @@ void extmark_adjust(buf_T *buf,
}
FOR_ALL_EXTMARKLINES(buf, adj_start, MAXLNUM, {
marks_exist = true;
lp = &(extline->lnum);
lp = &(extmarkline->lnum);
if (*lp <= line2) {
// 1st call with end_temp = true, store the lines in a temp position
if (end_temp && amount > 0) {
kb_del_itr_extlines(&buf->b_extlines, &itr);
kv_push(buf->b_extmark_move_space, extline);
kb_del_itr_extmarklines(&buf->b_extlines, &itr);
kv_push(buf->b_extmark_move_space, extmarkline);
}
*lp += amount;
@ -1147,14 +1027,10 @@ void extmark_adjust(buf_T *buf,
/// if part of a larger iteration we can't delete, then the caller
/// must check for empty lines.
bool extmark_copy_and_place(buf_T *buf,
linenr_T l_lnum,
colnr_T l_col,
linenr_T u_lnum,
colnr_T u_col,
linenr_T p_lnum,
colnr_T p_col,
ExtmarkOp undo,
bool delete,
linenr_T l_lnum, colnr_T l_col,
linenr_T u_lnum, colnr_T u_col,
linenr_T p_lnum, colnr_T p_col,
ExtmarkOp undo, bool delete,
ExtMarkLine *destline)
{
@ -1166,17 +1042,17 @@ bool extmark_copy_and_place(buf_T *buf,
// Move extmarks to their final position
// Careful: if we move items within the same line, we might change order of
// marks within the same extline. Too keep it simple, first delete all items
// from the extline and put them back in the right order.
// marks within the same extmarkline. Too keep it simple, first delete all
// items from the extmarkline and put them back in the right order.
FOR_ALL_EXTMARKLINES(buf, l_lnum, u_lnum, {
kvec_t(ExtendedMark) temp_space = KV_INITIAL_VALUE;
bool same_line = extline == destline;
FOR_ALL_EXTMARKS_IN_LINE(extline->items,
(extline->lnum > l_lnum) ? 0 : l_col,
(extline->lnum < u_lnum) ? MAXCOL : u_col, {
bool same_line = extmarkline == destline;
FOR_ALL_EXTMARKS_IN_LINE(extmarkline->items,
(extmarkline->lnum > l_lnum) ? 0 : l_col,
(extmarkline->lnum < u_lnum) ? MAXCOL : u_col, {
if (!destline) {
destline = extline_ref(buf, p_lnum, true);
same_line = extline == destline;
destline = extmarkline_ref(buf, p_lnum, true);
same_line = extmarkline == destline;
}
marks_moved = true;
if (!same_line) {
@ -1188,17 +1064,17 @@ bool extmark_copy_and_place(buf_T *buf,
kv_push(temp_space, *extmark);
}
// Delete old mark
kb_del_itr(markitems, &extline->items, &mitr);
kb_del_itr(markitems, &extmarkline->items, &mitr);
})
if (same_line) {
for (size_t i = 0; i < kv_size(temp_space); i++) {
ExtendedMark mark = kv_A(temp_space, i);
extmark_put(p_col, mark.mark_id, extline, mark.ns_id);
extmark_put(p_col, mark.mark_id, extmarkline, mark.ns_id);
}
kv_destroy(temp_space);
} else if (delete && kb_size(&extline->items) == 0) {
kb_del_itr(extlines, &buf->b_extlines, &itr);
extline_free(extline);
} else if (delete && kb_size(&extmarkline->items) == 0) {
kb_del_itr(extmarklines, &buf->b_extlines, &itr);
extmarkline_free(extmarkline);
}
})
@ -1211,13 +1087,13 @@ bool extmark_copy_and_place(buf_T *buf,
}
// Get reference to line in kbtree_t, allocating it if neccessary.
ExtMarkLine *extline_ref(buf_T *buf, linenr_T lnum, bool put)
ExtMarkLine *extmarkline_ref(buf_T *buf, linenr_T lnum, bool put)
{
kbtree_t(extlines) *b = &buf->b_extlines;
kbtree_t(extmarklines) *b = &buf->b_extlines;
ExtMarkLine t, **pp;
t.lnum = lnum;
pp = kb_get(extlines, b, &t);
pp = kb_get(extmarklines, b, &t);
if (!pp) {
if (!put) {
return NULL;
@ -1225,34 +1101,32 @@ ExtMarkLine *extline_ref(buf_T *buf, linenr_T lnum, bool put)
ExtMarkLine *p = xcalloc(sizeof(ExtMarkLine), 1);
p->lnum = lnum;
// p->items zero initialized
kb_put(extlines, b, p);
kb_put(extmarklines, b, p);
return p;
}
// Return existing
return *pp;
}
void extline_free(ExtMarkLine *extline)
void extmarkline_free(ExtMarkLine *extmarkline)
{
kb_destroy(markitems, (&extline->items));
xfree(extline);
kb_destroy(markitems, (&extmarkline->items));
xfree(extmarkline);
}
/// Put an extmark into a line,
///
/// caller must ensure combination of id and ns_id isn't in use.
void extmark_put(colnr_T col,
uint64_t id,
ExtMarkLine *extline,
uint64_t ns)
void extmark_put(colnr_T col, uint64_t id,
ExtMarkLine *extmarkline, uint64_t ns)
{
ExtendedMark t;
t.col = col;
t.mark_id = id;
t.line = extline;
t.line = extmarkline;
t.ns_id = ns;
kbtree_t(markitems) *b = &(extline->items);
kbtree_t(markitems) *b = &(extmarkline->items);
// kb_put requries the key to not be there
assert(!kb_getp(markitems, b, &t));

View File

@ -14,16 +14,17 @@
// see FOR_ALL_? for documentation
#define FOR_ALL_EXTMARKLINES(buf, l_lnum, u_lnum, code)\
kbitr_t(extlines) itr;\
kbitr_t(extmarklines) itr;\
ExtMarkLine t;\
t.lnum = l_lnum;\
if (!kb_itr_get(extlines, &buf->b_extlines, &t, &itr)) { \
kb_itr_next(extlines, &buf->b_extlines, &itr);\
if (!kb_itr_get(extmarklines, &buf->b_extlines, &t, &itr)) { \
kb_itr_next(extmarklines, &buf->b_extlines, &itr);\
}\
ExtMarkLine *extline;\
for (; kb_itr_valid(&itr); kb_itr_next(extlines, &buf->b_extlines, &itr)) { \
extline = kb_itr_key(&itr);\
if (extline->lnum > u_lnum) { \
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;\
@ -31,16 +32,17 @@
// see FOR_ALL_? for documentation
#define FOR_ALL_EXTMARKLINES_PREV(buf, l_lnum, u_lnum, code)\
kbitr_t(extlines) itr;\
kbitr_t(extmarklines) itr;\
ExtMarkLine t;\
t.lnum = u_lnum;\
if (!kb_itr_get(extlines, &buf->b_extlines, &t, &itr)) { \
kb_itr_prev(extlines, &buf->b_extlines, &itr);\
if (!kb_itr_get(extmarklines, &buf->b_extlines, &t, &itr)) { \
kb_itr_prev(extmarklines, &buf->b_extlines, &itr);\
}\
ExtMarkLine *extline;\
for (; kb_itr_valid(&itr); kb_itr_prev(extlines, &buf->b_extlines, &itr)) { \
extline = kb_itr_key(&itr);\
if (extline->lnum < l_lnum) { \
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;\
@ -54,14 +56,14 @@
mt.mark_id = 0;\
mt.line = NULL;\
FOR_ALL_EXTMARKLINES(buf, l_lnum, u_lnum, { \
mt.col = (extline->lnum != l_lnum) ? MINCOL : l_col;\
if (!kb_itr_get(markitems, &extline->items, mt, &mitr)) { \
kb_itr_next(markitems, &extline->items, &mitr);\
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, &extline->items, &mitr)) { \
kb_itr_next(markitems, &extmarkline->items, &mitr)) { \
extmark = &kb_itr_key(&mitr);\
if (extmark->line->lnum == u_lnum \
&& extmark->col > u_col) { \
@ -79,14 +81,14 @@
mt.mark_id = sizeof(uint64_t);\
mt.ns_id = ns;\
FOR_ALL_EXTMARKLINES_PREV(buf, l_lnum, u_lnum, { \
mt.col = (extline->lnum != u_lnum) ? MAXCOL : u_col;\
if (!kb_itr_get(markitems, &extline->items, mt, &mitr)) { \
kb_itr_prev(markitems, &extline->items, &mitr);\
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, &extline->items, &mitr)) { \
kb_itr_prev(markitems, &extmarkline->items, &mitr)) { \
extmark = &kb_itr_key(&mitr);\
if (extmark->line->lnum == l_lnum \
&& extmark->col < l_col) { \
@ -104,14 +106,14 @@
mt.mark_id = 0;\
mt.line = NULL;\
mt.col = l_col;\
colnr_T extline_u_col = u_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 > extline_u_col) { \
if (extmark->col > extmarkline_u_col) { \
break;\
}\
code;\
@ -125,7 +127,6 @@ typedef struct ExtmarkNs { // For namespacing extmarks
typedef kvec_t(ExtendedMark *) ExtmarkArray;
typedef kvec_t(ExtMarkLine *) ExtlineArray;
// Undo/redo extmarks
@ -138,6 +139,7 @@ typedef enum {
} ExtmarkOp;
// adjust line numbers only, corresponding to mark_adjust call
typedef struct {
linenr_T line1;
linenr_T line2;
@ -145,6 +147,7 @@ typedef struct {
long amount_after;
} Adjust;
// adjust columns after split/join line, like mark_col_adjust
typedef struct {
linenr_T lnum;
colnr_T mincol;
@ -152,6 +155,7 @@ typedef struct {
long lnum_amount;
} ColAdjust;
// delete the columns between mincol and endcol
typedef struct {
linenr_T lnum;
colnr_T mincol;
@ -159,6 +163,7 @@ typedef struct {
int eol;
} ColAdjustDelete;
// adjust linenumbers after :move operation
typedef struct {
linenr_T line1;
linenr_T line2;
@ -168,6 +173,9 @@ typedef struct {
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;
@ -175,6 +183,7 @@ typedef struct {
colnr_T col;
} ExtmarkSet;
// extmark was updated
typedef struct {
uint64_t ns_id;
uint64_t mark_id;
@ -184,6 +193,7 @@ typedef struct {
colnr_T col;
} ExtmarkUpdate;
// copied mark before deletion (as operation is destructive)
typedef struct {
uint64_t ns_id;
uint64_t mark_id;
@ -191,6 +201,8 @@ typedef struct {
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;
@ -200,6 +212,8 @@ typedef struct {
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;
@ -220,6 +234,7 @@ typedef enum {
kExtmarkClear,
} UndoObjectType;
// TODO(bfredl): reduce the number of undo action types
struct undo_object {
UndoObjectType type;
union {

View File

@ -43,8 +43,8 @@ typedef struct ExtMarkLine
kbtree_t(markitems) items;
} ExtMarkLine;
#define extline_cmp(a, b) (kb_generic_cmp((a)->lnum, (b)->lnum))
KBTREE_INIT(extlines, ExtMarkLine *, extline_cmp, 10)
#define EXTMARKLINE_CMP(a, b) (kb_generic_cmp((a)->lnum, (b)->lnum))
KBTREE_INIT(extmarklines, ExtMarkLine *, EXTMARKLINE_CMP, 10)
typedef struct undo_object ExtmarkUndoObject;

View File

@ -127,30 +127,6 @@ static char opchars[][3] =
{ Ctrl_X, NUL, false }, // OP_NR_SUB
};
char *nvim_lltoa(int64_t val, int base)
{
static char buf[64] = { 0 };
int i = 62;
int sign = (val < 0);
if (sign) {
val = -val;
}
if (val == 0) {
return "0";
}
for (; val && i ; i--, val /= base) {
buf[i] = "0123456789abcdef"[val % base];
}
if (sign) {
buf[i--] = '-';
}
return &buf[i+1];
}
/*
* Translate a command name into an operator type.
* Must only be called with a valid operator name!
@ -332,13 +308,8 @@ void shift_line(
} else {
(void)set_indent(count, call_changed_bytes ? SIN_CHANGED : 0);
colnr_T col_amount;
colnr_T mincol = (curwin->w_cursor.col + 1) -p_sw;
if (left) {
col_amount = -p_sw;
} else {
col_amount = p_sw;
}
colnr_T col_amount = left ? -p_sw : p_sw;
extmark_col_adjust(curbuf,
curwin->w_cursor.lnum,
mincol,
@ -519,10 +490,7 @@ static void shift_block(oparg_T *oap, int amount)
curwin->w_cursor.col = oldcol;
p_ri = old_p_ri;
colnr_T col_amount = p_sw;
if (left) {
col_amount = -col_amount;
}
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);
}
@ -1669,7 +1637,7 @@ int op_delete(oparg_T *oap)
curpos = curwin->w_cursor; // remember curwin->w_cursor
curwin->w_cursor.lnum++;
del_lines(oap->line_count - 2, true);
del_lines(oap->line_count - 2, false);
// delete from start of line until op_end
n = (oap->end.col + 1 - !oap->inclusive);
@ -1715,12 +1683,7 @@ setmarks:
lnum = curwin->w_cursor.lnum;
if (oap->is_VIsual == false) {
// for some reason we required this :/
endcol = endcol - 1;
// for some reason we required this :/
if (endcol < mincol) {
endcol = mincol;
}
endcol = MAX(endcol - 1, mincol);
}
extmark_col_adjust_delete(curbuf, lnum, mincol, endcol, kExtmarkUndo, 0);
}
@ -2787,8 +2750,6 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
}
// Function length couldn't be over 500 lines..
static void extmarks_do_put(int dir,
size_t totlen,
MotionType y_type,
@ -2796,12 +2757,7 @@ static void extmarks_do_put(int dir,
colnr_T col)
{
// adjust extmarks
colnr_T col_amount;
if (dir == FORWARD) {
col_amount = (colnr_T)(totlen-1);
} else {
col_amount = (colnr_T)totlen;
}
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);
@ -3869,11 +3825,6 @@ int do_join(size_t count,
* should not really be a problem.
*/
linenr_T lnum;
colnr_T mincol;
long lnum_amount;
long col_amount;
for (t = (linenr_T)count - 1;; t--) {
cend -= currsize;
memmove(cend, curr, (size_t)currsize);
@ -3885,10 +3836,10 @@ 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]);
lnum = curwin->w_cursor.lnum + t;
mincol = (colnr_T)0;
lnum_amount = (linenr_T)-t;
col_amount = (long)(cend - newp - spaces_removed);
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);
@ -4675,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
@ -4984,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--;
@ -4998,15 +4948,15 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
curbuf->b_op_end.col--;
}
if (did_change) {
// 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(nvim_lltoa((int64_t)n, 10));
col_amount = negative ? col_amount + 1 : col_amount;
long col_amount = (long)STRLEN(buf1);
extmark_col_adjust(curbuf,
pos->lnum,
startpos.col + 1,
@ -5016,6 +4966,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
}
theend:
xfree(buf1);
if (visual) {
curwin->w_cursor = save_cursor;
} else if (did_change) {

View File

@ -2849,7 +2849,6 @@ u_freeentries(
u_freeentry(uep, uep->ue_size);
}
// TODO(timeyyy): is this the correct place? ...
kv_destroy(uhp->uh_extmark);
#ifdef U_DEBUG
@ -3049,7 +3048,7 @@ list_T *u_eval_tree(const u_header_T *const first_uhp)
// 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 *force_get_undo_header(buf_T *buf)
u_header_T *u_force_get_undo_header(buf_T *buf)
{
u_header_T *uhp = NULL;
if (buf->b_u_curhead != NULL) {
@ -3064,8 +3063,8 @@ u_header_T *force_get_undo_header(buf_T *buf)
uhp = buf->b_u_curhead;
if (!uhp) {
uhp = buf->b_u_newhead;
if (get_undolevel() > 0) {
assert(uhp);
if (get_undolevel() > 0 && !uhp) {
abort();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -8,22 +8,22 @@ local expect = helpers.expect
describe('multi-line regexp', function()
setup(clear)
it('is working #fail', function()
it('is working', function()
insert([[
1 aa
bb
cc
2 dd
ee
3 ef
gh
4 ij
5 a8
8b c9
9d
6 e7
77f
xxxxx]])
1 aa
bb
cc
2 dd
ee
3 ef
gh
4 ij
5 a8
8b c9
9d
6 e7
77f
xxxxx]])
-- Test if replacing a line break works with a back reference
feed([[:/^1/,/^2/s/\n\(.\)/ \1/<cr>]])

View File

@ -131,7 +131,7 @@ describe(":substitute, inccommand=split interactivity", function()
end)
end)
describe(":substitute, 'inccommand' preserves #inc", function()
describe(":substitute, 'inccommand' preserves", function()
before_each(clear)
it('listed buffers (:ls)', function()
@ -293,7 +293,7 @@ describe(":substitute, 'inccommand' preserves #inc", function()
end)
describe(":substitute, 'inccommand' preserves undo #inc", function()
describe(":substitute, 'inccommand' preserves undo", function()
local cases = { "", "split", "nosplit" }
local substrings = {
@ -1962,7 +1962,7 @@ describe(":substitute", function()
clear()
end)
it("inccommand=split, highlights multiline substitutions #inc2", function()
it("inccommand=split, highlights multiline substitutions", function()
common_setup(screen, "split", multiline_text)
feed("gg")
@ -2024,7 +2024,7 @@ describe(":substitute", function()
]])
end)
it("inccommand=nosplit, highlights multiline substitutions #inc2", function()
it("inccommand=nosplit, highlights multiline substitutions", function()
common_setup(screen, "nosplit", multiline_text)
feed("gg")
@ -2117,7 +2117,7 @@ describe(":substitute", function()
]])
end)
it("inccommand=split, with \\zs #inc", function()
it("inccommand=split, with \\zs", function()
common_setup(screen, "split", multiline_text)
feed("gg")
@ -2141,7 +2141,7 @@ describe(":substitute", function()
]])
end)
it("inccommand=nosplit, with \\zs #inc", function()
it("inccommand=nosplit, with \\zs", function()
common_setup(screen, "nosplit", multiline_text)
feed("gg")
@ -2212,7 +2212,7 @@ describe(":substitute", function()
]])
end)
it("inccommand=split, contraction of lines #inc2", function()
it("inccommand=split, contraction of lines", function()
local text = [[
T T123 T T123 T2T TT T23423424
x
@ -2261,7 +2261,7 @@ describe(":substitute", function()
]])
end)
it("inccommand=nosplit, contraction of lines #inc2", function()
it("inccommand=nosplit, contraction of lines", function()
local text = [[
T T123 T T123 T2T TT T23423424
x

View File

@ -817,6 +817,7 @@ describe('ui/mouse/input', function()
feed_command('syntax match NonText "cats" conceal cchar=X')
feed_command('syntax match NonText "x" conceal cchar=>')
-- First column is there to retain the tabs.
insert([[
|Section *t1*
| *t2* *t3* *t4*