mirror of
https://github.com/neovim/neovim.git
synced 2025-10-17 07:16:09 +00:00
feat(api): named marks set, get, delete #15346
Adds the following API functions. - nvim_buf_set_mark(buf, name, line, col) * Set marks in a buffer. - nvim_buf_del_mark(buf, name) * Delete a mark that belongs to buffer. - nvim_del_mark(name) * Delete a global mark. - nvim_get_mark(name) * Get a global mark. Tests: - Adds test to all the new api functions, and adds more for the existing nvim_buf_get_mark. * Tests include failure cases. Documentation: - Adds documentation for all the new functions, and improves the existing fucntion docs.
This commit is contained in:
@@ -1108,14 +1108,97 @@ Boolean nvim_buf_is_valid(Buffer buffer)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Return a tuple (row,col) representing the position of the named mark.
|
||||
/// Deletes a named mark in the buffer. See |mark-motions|.
|
||||
///
|
||||
/// @note only deletes marks set in the buffer, if the mark is not set
|
||||
/// in the buffer it will return false.
|
||||
/// @param buffer Buffer to set the mark on
|
||||
/// @param name Mark name
|
||||
/// @return true if the mark was deleted, else false.
|
||||
/// @see |nvim_buf_set_mark()|
|
||||
/// @see |nvim_del_mark()|
|
||||
Boolean nvim_buf_del_mark(Buffer buffer, String name, Error *err)
|
||||
FUNC_API_SINCE(8)
|
||||
{
|
||||
bool res = false;
|
||||
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||
|
||||
if (!buf) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (name.size != 1) {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"Mark name must be a single character");
|
||||
return res;
|
||||
}
|
||||
|
||||
pos_T *pos = getmark_buf(buf, *name.data, false);
|
||||
|
||||
// pos point to NULL when there's no mark with name
|
||||
if (pos == NULL) {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid mark name: '%c'",
|
||||
*name.data);
|
||||
return res;
|
||||
}
|
||||
|
||||
// pos->lnum is 0 when the mark is not valid in the buffer, or is not set.
|
||||
if (pos->lnum != 0) {
|
||||
// since the mark belongs to the buffer delete it.
|
||||
res = set_mark(buf, name, 0, 0, err);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Sets a named mark in the given buffer, all marks are allowed
|
||||
/// file/uppercase, visual, last change, etc. See |mark-motions|.
|
||||
///
|
||||
/// Marks are (1,0)-indexed. |api-indexing|
|
||||
///
|
||||
/// @note Passing 0 as line deletes the mark
|
||||
///
|
||||
/// @param buffer Buffer to set the mark on
|
||||
/// @param name Mark name
|
||||
/// @param line Line number
|
||||
/// @param col Column/row number
|
||||
/// @return true if the mark was set, else false.
|
||||
/// @see |nvim_buf_del_mark()|
|
||||
/// @see |nvim_buf_get_mark()|
|
||||
Boolean nvim_buf_set_mark(Buffer buffer, String name,
|
||||
Integer line, Integer col, Error *err)
|
||||
FUNC_API_SINCE(8)
|
||||
{
|
||||
bool res = false;
|
||||
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||
|
||||
if (!buf) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (name.size != 1) {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"Mark name must be a single character");
|
||||
return res;
|
||||
}
|
||||
|
||||
res = set_mark(buf, name, line, col, err);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Returns a tuple (row,col) representing the position of the named mark. See
|
||||
/// |mark-motions|.
|
||||
///
|
||||
/// Marks are (1,0)-indexed. |api-indexing|
|
||||
///
|
||||
/// @param buffer Buffer handle, or 0 for current buffer
|
||||
/// @param name Mark name
|
||||
/// @param[out] err Error details, if any
|
||||
/// @return (row, col) tuple
|
||||
/// @return (row, col) tuple, (0, 0) if the mark is not set, or is an
|
||||
/// uppercase/file mark set in another buffer.
|
||||
/// @see |nvim_buf_set_mark()|
|
||||
/// @see |nvim_buf_del_mark()|
|
||||
ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err)
|
||||
FUNC_API_SINCE(1)
|
||||
{
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#include "nvim/lua/executor.h"
|
||||
#include "nvim/map.h"
|
||||
#include "nvim/map_defs.h"
|
||||
#include "nvim/mark.h"
|
||||
#include "nvim/memline.h"
|
||||
#include "nvim/memory.h"
|
||||
#include "nvim/msgpack_rpc/helpers.h"
|
||||
@@ -1671,3 +1672,42 @@ void api_free_keydict(void *dict, KeySetLink *table)
|
||||
}
|
||||
}
|
||||
|
||||
/// Set a named mark
|
||||
/// buffer and mark name must be validated already
|
||||
/// @param buffer Buffer to set the mark on
|
||||
/// @param name Mark name
|
||||
/// @param line Line number
|
||||
/// @param col Column/row number
|
||||
/// @return true if the mark was set, else false
|
||||
bool set_mark(buf_T *buf, String name, Integer line, Integer col, Error *err)
|
||||
{
|
||||
buf = buf == NULL ? curbuf : buf;
|
||||
// If line == 0 the marks is being deleted
|
||||
bool res = false;
|
||||
bool deleting = false;
|
||||
if (line == 0) {
|
||||
col = 0;
|
||||
deleting = true;
|
||||
} else {
|
||||
if (col > MAXCOL) {
|
||||
api_set_error(err, kErrorTypeValidation, "Column value outside range");
|
||||
return res;
|
||||
}
|
||||
if (line < 1 || line > buf->b_ml.ml_line_count) {
|
||||
api_set_error(err, kErrorTypeValidation, "Line value outside range");
|
||||
return res;
|
||||
}
|
||||
}
|
||||
pos_T pos = { line, (int)col, (int)col };
|
||||
res = setmark_pos(*name.data, &pos, buf->handle);
|
||||
if (!res) {
|
||||
if (deleting) {
|
||||
api_set_error(err, kErrorTypeException,
|
||||
"Failed to delete named mark: %c", *name.data);
|
||||
} else {
|
||||
api_set_error(err, kErrorTypeException,
|
||||
"Failed to set named mark: %c", *name.data);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@@ -2779,3 +2779,106 @@ void nvim_set_decoration_provider(Integer ns_id, DictionaryOf(LuaRef) opts, Erro
|
||||
error:
|
||||
decor_provider_clear(p);
|
||||
}
|
||||
|
||||
/// Deletes a uppercase/file named mark. See |mark-motions|.
|
||||
///
|
||||
/// @note fails with error if a lowercase or buffer local named mark is used.
|
||||
/// @param name Mark name
|
||||
/// @return true if the mark was deleted, else false.
|
||||
/// @see |nvim_buf_del_mark()|
|
||||
/// @see |nvim_get_mark()|
|
||||
Boolean nvim_del_mark(String name, Error *err)
|
||||
FUNC_API_SINCE(8)
|
||||
{
|
||||
bool res = false;
|
||||
if (name.size != 1) {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"Mark name must be a single character");
|
||||
return res;
|
||||
}
|
||||
// Only allow file/uppercase marks
|
||||
// TODO(muniter): Refactor this ASCII_ISUPPER macro to a proper function
|
||||
if (ASCII_ISUPPER(*name.data) || ascii_isdigit(*name.data)) {
|
||||
res = set_mark(NULL, name, 0, 0, err);
|
||||
} else {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"Only file/uppercase marks allowed, invalid mark name: '%c'",
|
||||
*name.data);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Return a tuple (row, col, buffer, buffername) representing the position of
|
||||
/// the uppercase/file named mark. See |mark-motions|.
|
||||
///
|
||||
/// Marks are (1,0)-indexed. |api-indexing|
|
||||
///
|
||||
/// @note fails with error if a lowercase or buffer local named mark is used.
|
||||
/// @param name Mark name
|
||||
/// @return 4-tuple (row, col, buffer, buffername), (0, 0, 0, '') if the mark is
|
||||
/// not set.
|
||||
/// @see |nvim_buf_set_mark()|
|
||||
/// @see |nvim_del_mark()|
|
||||
Array nvim_get_mark(String name, Error *err)
|
||||
FUNC_API_SINCE(8)
|
||||
{
|
||||
Array rv = ARRAY_DICT_INIT;
|
||||
|
||||
if (name.size != 1) {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"Mark name must be a single character");
|
||||
return rv;
|
||||
} else if (!(ASCII_ISUPPER(*name.data) || ascii_isdigit(*name.data))) {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"Only file/uppercase marks allowed, invalid mark name: '%c'",
|
||||
*name.data);
|
||||
return rv;
|
||||
}
|
||||
|
||||
xfmark_T mark = get_global_mark(*name.data);
|
||||
pos_T pos = mark.fmark.mark;
|
||||
bool allocated = false;
|
||||
int bufnr;
|
||||
char *filename;
|
||||
|
||||
// Marks are from an open buffer it fnum is non zero
|
||||
if (mark.fmark.fnum != 0) {
|
||||
bufnr = mark.fmark.fnum;
|
||||
filename = (char *)buflist_nr2name(bufnr, true, true);
|
||||
allocated = true;
|
||||
// Marks comes from shada
|
||||
} else {
|
||||
filename = (char *)mark.fname;
|
||||
bufnr = 0;
|
||||
}
|
||||
|
||||
bool exists = filename != NULL;
|
||||
Integer row;
|
||||
Integer col;
|
||||
|
||||
if (!exists || pos.lnum <= 0) {
|
||||
if (allocated) {
|
||||
xfree(filename);
|
||||
allocated = false;
|
||||
}
|
||||
filename = "";
|
||||
bufnr = 0;
|
||||
row = 0;
|
||||
col = 0;
|
||||
} else {
|
||||
row = pos.lnum;
|
||||
col = pos.col;
|
||||
}
|
||||
|
||||
ADD(rv, INTEGER_OBJ(row));
|
||||
ADD(rv, INTEGER_OBJ(col));
|
||||
ADD(rv, INTEGER_OBJ(bufnr));
|
||||
ADD(rv, STRING_OBJ(cstr_to_string(filename)));
|
||||
|
||||
if (allocated) {
|
||||
xfree(filename);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user