Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] - Extended Marks #5031

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,14 @@ 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
46 changes: 46 additions & 0 deletions runtime/doc/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,36 @@ Example: create a float with scratch buffer: >
call nvim_win_set_option(win, 'winhl', 'Normal:MyHighlight')
>

==============================================================================
Buffer 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,
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.

`let g:mark_ns = nvim_create_namespace('myplugin')`
`let g:mark_id = nvim_buf_set_mark(0, :g:mark_ns, 0, 1, 3)`

Note: the mark id was randomly generated because we used an inital value of 0

`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]]

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.

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.


==============================================================================
Global Functions *api-global*

Expand Down Expand Up @@ -1449,6 +1479,22 @@ 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})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function does not exists.
And nvim_create_namespace() API is already added in neovim.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

api.txt is autogenerated. we can just delete this file from the PR.

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()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nvim_buf_get_extmarks()?

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*
Expand Down
217 changes: 216 additions & 1 deletion src/nvim/api/buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -529,7 +532,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);
Expand Down Expand Up @@ -984,6 +988,213 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err)
return rv;
}

/// Returns position info 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,
Integer id, Error *err)
FUNC_API_SINCE(6)
{
Array rv = ARRAY_DICT_INIT;

buf_T *buf = find_buffer_by_handle(buffer, err);

if (!buf) {
return rv;
}

if (!ns_initialized((uint64_t)namespace)) {
api_set_error(err, kErrorTypeValidation, _("Invalid mark namespace"));
return rv;
}

ExtendedMark *extmark = extmark_from_id(buf,
(uint64_t)namespace,
(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 namespace 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
/// @return [[nsmark_id, row, col], ...]
Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id,
Object start, Object end, Integer amount,
Error *err)
FUNC_API_SINCE(6)
{
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 mark namespace"));
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));
}
Copy link
Member

@bfredl bfredl Apr 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kv_destroy(extmarks_in_range) (done)


kv_destroy(marks);
return rv;
}

/// Create or update a namespaced mark at a position
///
/// If an invalid namespace is given, an error will be raised.
///
/// @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 row The row to set the extmark to.
/// @param col The column to set the extmark to.
/// @param[out] err Details of an error that may have occurred
/// @return the nsmark_id for a new mark, or 0 for an update
Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer id,
Integer line, Integer col, Error *err)
FUNC_API_SINCE(6)
{
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 mark namespace"));
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;
}

bool new = 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;
}
}

/// 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 no extmarks found
Boolean nvim_buf_del_extmark(Buffer buffer,
Integer ns_id,
Integer id,
Error *err)
FUNC_API_SINCE(6)
{
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 mark namespace"));
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
Expand Down Expand Up @@ -1082,6 +1293,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
Expand Down
Loading