mirror of
https://github.com/neovim/neovim.git
synced 2025-10-08 19:06:31 +00:00
Introduce nvim namespace: Move files.
Move files from src/ to src/nvim/. - src/nvim/ becomes the new root dir for nvim executable sources. - src/libnvim/ is planned to become root dir of the neovim library.
This commit is contained in:
413
src/nvim/api/buffer.c
Normal file
413
src/nvim/api/buffer.c
Normal file
@@ -0,0 +1,413 @@
|
||||
// Much of this code was adapted from 'if_py_both.h' from the original
|
||||
// vim source
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "api/buffer.h"
|
||||
#include "api/helpers.h"
|
||||
#include "api/defs.h"
|
||||
#include "../vim.h"
|
||||
#include "../buffer.h"
|
||||
#include "memline.h"
|
||||
#include "memory.h"
|
||||
#include "misc1.h"
|
||||
#include "misc2.h"
|
||||
#include "ex_cmds.h"
|
||||
#include "mark.h"
|
||||
#include "fileio.h"
|
||||
#include "move.h"
|
||||
#include "../window.h"
|
||||
#include "undo.h"
|
||||
|
||||
// Find a window that contains "buf" and switch to it.
|
||||
// If there is no such window, use the current window and change "curbuf".
|
||||
// Caller must initialize save_curbuf to NULL.
|
||||
// restore_win_for_buf() MUST be called later!
|
||||
static void switch_to_win_for_buf(buf_T *buf,
|
||||
win_T **save_curwinp,
|
||||
tabpage_T **save_curtabp,
|
||||
buf_T **save_curbufp);
|
||||
|
||||
static void restore_win_for_buf(win_T *save_curwin,
|
||||
tabpage_T *save_curtab,
|
||||
buf_T *save_curbuf);
|
||||
|
||||
// Check if deleting lines made the cursor position invalid.
|
||||
// Changed the lines from "lo" to "hi" and added "extra" lines (negative if
|
||||
// deleted).
|
||||
static void fix_cursor(linenr_T lo, linenr_T hi, linenr_T extra);
|
||||
|
||||
// Normalizes 0-based indexes to buffer line numbers
|
||||
static int64_t normalize_index(buf_T *buf, int64_t index);
|
||||
|
||||
int64_t buffer_get_length(Buffer buffer, Error *err)
|
||||
{
|
||||
buf_T *buf = find_buffer(buffer, err);
|
||||
|
||||
if (!buf) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return buf->b_ml.ml_line_count;
|
||||
}
|
||||
|
||||
String buffer_get_line(Buffer buffer, int64_t index, Error *err)
|
||||
{
|
||||
String rv = {.size = 0};
|
||||
StringArray slice = buffer_get_slice(buffer, index, index, true, true, err);
|
||||
|
||||
if (slice.size) {
|
||||
rv = slice.items[0];
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void buffer_set_line(Buffer buffer, int64_t index, String line, Error *err)
|
||||
{
|
||||
StringArray array = {.items = &line, .size = 1};
|
||||
buffer_set_slice(buffer, index, index, true, true, array, err);
|
||||
}
|
||||
|
||||
void buffer_del_line(Buffer buffer, int64_t index, Error *err)
|
||||
{
|
||||
StringArray array = {.size = 0};
|
||||
buffer_set_slice(buffer, index, index, true, true, array, err);
|
||||
}
|
||||
|
||||
StringArray buffer_get_slice(Buffer buffer,
|
||||
int64_t start,
|
||||
int64_t end,
|
||||
bool include_start,
|
||||
bool include_end,
|
||||
Error *err)
|
||||
{
|
||||
StringArray rv = {.size = 0};
|
||||
buf_T *buf = find_buffer(buffer, err);
|
||||
|
||||
if (!buf) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
start = normalize_index(buf, start) + (include_start ? 0 : 1);
|
||||
end = normalize_index(buf, end) + (include_end ? 1 : 0);
|
||||
|
||||
if (start >= end) {
|
||||
// Return 0-length array
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv.size = end - start;
|
||||
rv.items = xmalloc(sizeof(String) * rv.size);
|
||||
|
||||
for (uint32_t i = 0; i < rv.size; i++) {
|
||||
rv.items[i].data = xstrdup((char *)ml_get_buf(buf, start + i, false));
|
||||
rv.items[i].size = strlen(rv.items[i].data);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void buffer_set_slice(Buffer buffer,
|
||||
int64_t start,
|
||||
int64_t end,
|
||||
bool include_start,
|
||||
bool include_end,
|
||||
StringArray replacement,
|
||||
Error *err)
|
||||
{
|
||||
buf_T *buf = find_buffer(buffer, err);
|
||||
|
||||
if (!buf) {
|
||||
return;
|
||||
}
|
||||
|
||||
start = normalize_index(buf, start) + (include_start ? 0 : 1);
|
||||
end = normalize_index(buf, end) + (include_end ? 1 : 0);
|
||||
|
||||
if (start > end) {
|
||||
set_api_error("start > end", err);
|
||||
return;
|
||||
}
|
||||
|
||||
buf_T *save_curbuf = NULL;
|
||||
win_T *save_curwin = NULL;
|
||||
tabpage_T *save_curtab = NULL;
|
||||
uint32_t new_len = replacement.size;
|
||||
uint32_t old_len = end - start;
|
||||
uint32_t i;
|
||||
int32_t extra = 0; // lines added to text, can be negative
|
||||
char **lines;
|
||||
|
||||
if (new_len == 0) {
|
||||
// avoid allocating zero bytes
|
||||
lines = NULL;
|
||||
} else {
|
||||
lines = xcalloc(sizeof(char *), new_len);
|
||||
}
|
||||
|
||||
for (i = 0; i < new_len; i++) {
|
||||
String l = replacement.items[i];
|
||||
lines[i] = xstrndup(l.data, l.size);
|
||||
}
|
||||
|
||||
try_start();
|
||||
switch_to_win_for_buf(buf, &save_curwin, &save_curtab, &save_curbuf);
|
||||
|
||||
if (u_save(start - 1, end) == FAIL) {
|
||||
set_api_error("Cannot save undo information", err);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// If the size of the range is reducing (ie, new_len < old_len) we
|
||||
// need to delete some old_len. We do this at the start, by
|
||||
// repeatedly deleting line "start".
|
||||
for (i = 0; new_len < old_len && i < old_len - new_len; i++) {
|
||||
if (ml_delete(start, false) == FAIL) {
|
||||
set_api_error("Cannot delete line", err);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
extra -= i;
|
||||
|
||||
// For as long as possible, replace the existing old_len with the
|
||||
// new old_len. This is a more efficient operation, as it requires
|
||||
// less memory allocation and freeing.
|
||||
for (i = 0; i < old_len && i < new_len; i++) {
|
||||
if (ml_replace(start + i, (char_u *)lines[i], false) == FAIL) {
|
||||
set_api_error("Cannot replace line", err);
|
||||
goto cleanup;
|
||||
}
|
||||
// Mark lines that haven't been passed to the buffer as they need
|
||||
// to be freed later
|
||||
lines[i] = NULL;
|
||||
}
|
||||
|
||||
// Now we may need to insert the remaining new old_len
|
||||
while (i < new_len) {
|
||||
if (ml_append(start + i - 1, (char_u *)lines[i], 0, false) == FAIL) {
|
||||
set_api_error("Cannot insert line", err);
|
||||
goto cleanup;
|
||||
}
|
||||
// Same as with replacing
|
||||
lines[i] = NULL;
|
||||
i++;
|
||||
extra++;
|
||||
}
|
||||
|
||||
// Adjust marks. Invalidate any which lie in the
|
||||
// changed range, and move any in the remainder of the buffer.
|
||||
// Only adjust marks if we managed to switch to a window that holds
|
||||
// the buffer, otherwise line numbers will be invalid.
|
||||
if (save_curbuf == NULL) {
|
||||
mark_adjust(start, end - 1, MAXLNUM, extra);
|
||||
}
|
||||
|
||||
changed_lines(start, 0, end, extra);
|
||||
|
||||
if (buf == curbuf) {
|
||||
fix_cursor(start, end, extra);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
for (uint32_t i = 0; i < new_len; i++) {
|
||||
if (lines[i] != NULL) {
|
||||
free(lines[i]);
|
||||
}
|
||||
}
|
||||
|
||||
free(lines);
|
||||
restore_win_for_buf(save_curwin, save_curtab, save_curbuf);
|
||||
try_end(err);
|
||||
}
|
||||
|
||||
Object buffer_get_var(Buffer buffer, String name, Error *err)
|
||||
{
|
||||
Object rv;
|
||||
buf_T *buf = find_buffer(buffer, err);
|
||||
|
||||
if (!buf) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return dict_get_value(buf->b_vars, name, err);
|
||||
}
|
||||
|
||||
Object buffer_set_var(Buffer buffer, String name, Object value, Error *err)
|
||||
{
|
||||
Object rv;
|
||||
buf_T *buf = find_buffer(buffer, err);
|
||||
|
||||
if (!buf) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return dict_set_value(buf->b_vars, name, value, err);
|
||||
}
|
||||
|
||||
Object buffer_get_option(Buffer buffer, String name, Error *err)
|
||||
{
|
||||
Object rv;
|
||||
buf_T *buf = find_buffer(buffer, err);
|
||||
|
||||
if (!buf) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return get_option_from(buf, SREQ_BUF, name, err);
|
||||
}
|
||||
|
||||
void buffer_set_option(Buffer buffer, String name, Object value, Error *err)
|
||||
{
|
||||
buf_T *buf = find_buffer(buffer, err);
|
||||
|
||||
if (!buf) {
|
||||
return;
|
||||
}
|
||||
|
||||
set_option_to(buf, SREQ_BUF, name, value, err);
|
||||
}
|
||||
|
||||
String buffer_get_name(Buffer buffer, Error *err)
|
||||
{
|
||||
String rv = {.size = 0, .data = ""};
|
||||
buf_T *buf = find_buffer(buffer, err);
|
||||
|
||||
if (!buf || buf->b_ffname == NULL) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv.data = xstrdup((char *)buf->b_ffname);
|
||||
rv.size = strlen(rv.data);
|
||||
return rv;
|
||||
}
|
||||
|
||||
void buffer_set_name(Buffer buffer, String name, Error *err)
|
||||
{
|
||||
buf_T *buf = find_buffer(buffer, err);
|
||||
|
||||
if (!buf) {
|
||||
return;
|
||||
}
|
||||
|
||||
aco_save_T aco;
|
||||
int ren_ret;
|
||||
char *val = xstrndup(name.data, name.size);
|
||||
|
||||
try_start();
|
||||
// Using aucmd_*: autocommands will be executed by rename_buffer
|
||||
aucmd_prepbuf(&aco, buf);
|
||||
ren_ret = rename_buffer((char_u *)val);
|
||||
aucmd_restbuf(&aco);
|
||||
|
||||
if (try_end(err)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ren_ret == FAIL) {
|
||||
set_api_error("failed to rename buffer", err);
|
||||
}
|
||||
}
|
||||
|
||||
bool buffer_is_valid(Buffer buffer)
|
||||
{
|
||||
Error stub = {.set = false};
|
||||
return find_buffer(buffer, &stub) != NULL;
|
||||
}
|
||||
|
||||
void buffer_insert(Buffer buffer, int64_t index, StringArray lines, Error *err)
|
||||
{
|
||||
buffer_set_slice(buffer, index, index, false, true, lines, err);
|
||||
}
|
||||
|
||||
Position buffer_get_mark(Buffer buffer, String name, Error *err)
|
||||
{
|
||||
Position rv;
|
||||
buf_T *buf = find_buffer(buffer, err);
|
||||
|
||||
if (!buf) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (name.size != 0) {
|
||||
set_api_error("mark name must be a single character", err);
|
||||
return rv;
|
||||
}
|
||||
|
||||
pos_T *posp;
|
||||
buf_T *savebuf;
|
||||
char mark = *name.data;
|
||||
|
||||
try_start();
|
||||
switch_buffer(&savebuf, buf);
|
||||
posp = getmark(mark, false);
|
||||
restore_buffer(savebuf);
|
||||
|
||||
if (try_end(err)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (posp == NULL) {
|
||||
set_api_error("invalid mark name", err);
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv.row = posp->lnum;
|
||||
rv.col = posp->col;
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void switch_to_win_for_buf(buf_T *buf,
|
||||
win_T **save_curwinp,
|
||||
tabpage_T **save_curtabp,
|
||||
buf_T **save_curbufp)
|
||||
{
|
||||
win_T *wp;
|
||||
tabpage_T *tp;
|
||||
|
||||
if (find_win_for_buf(buf, &wp, &tp) == FAIL
|
||||
|| switch_win(save_curwinp, save_curtabp, wp, tp, true) == FAIL)
|
||||
switch_buffer(save_curbufp, buf);
|
||||
}
|
||||
|
||||
static void restore_win_for_buf(win_T *save_curwin,
|
||||
tabpage_T *save_curtab,
|
||||
buf_T *save_curbuf)
|
||||
{
|
||||
if (save_curbuf == NULL) {
|
||||
restore_win(save_curwin, save_curtab, true);
|
||||
} else {
|
||||
restore_buffer(save_curbuf);
|
||||
}
|
||||
}
|
||||
|
||||
static void fix_cursor(linenr_T lo, linenr_T hi, linenr_T extra)
|
||||
{
|
||||
if (curwin->w_cursor.lnum >= lo) {
|
||||
// Adjust the cursor position if it's in/after the changed
|
||||
// lines.
|
||||
if (curwin->w_cursor.lnum >= hi) {
|
||||
curwin->w_cursor.lnum += extra;
|
||||
check_cursor_col();
|
||||
} else if (extra < 0) {
|
||||
curwin->w_cursor.lnum = lo;
|
||||
check_cursor();
|
||||
} else {
|
||||
check_cursor_col();
|
||||
}
|
||||
changed_cline_bef_curs();
|
||||
}
|
||||
invalidate_botline();
|
||||
}
|
||||
|
||||
static int64_t normalize_index(buf_T *buf, int64_t index)
|
||||
{
|
||||
// Fix if < 0
|
||||
index = index < 0 ? buf->b_ml.ml_line_count + index : index;
|
||||
// Convert the index to a vim line number
|
||||
index++;
|
||||
// Fix if > line_count
|
||||
index = index > buf->b_ml.ml_line_count ? buf->b_ml.ml_line_count : index;
|
||||
return index;
|
||||
}
|
145
src/nvim/api/buffer.h
Normal file
145
src/nvim/api/buffer.h
Normal file
@@ -0,0 +1,145 @@
|
||||
#ifndef NEOVIM_API_BUFFER_H
|
||||
#define NEOVIM_API_BUFFER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "api/defs.h"
|
||||
|
||||
/// Gets the buffer line count
|
||||
///
|
||||
/// @param buffer The buffer handle
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return The line count
|
||||
int64_t buffer_get_length(Buffer buffer, Error *err);
|
||||
|
||||
/// Gets a buffer line
|
||||
///
|
||||
/// @param buffer The buffer handle
|
||||
/// @param index The line index
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return The line string
|
||||
String buffer_get_line(Buffer buffer, int64_t index, Error *err);
|
||||
|
||||
/// Sets a buffer line
|
||||
///
|
||||
/// @param buffer The buffer handle
|
||||
/// @param index The line index
|
||||
/// @param line The new line.
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
void buffer_set_line(Buffer buffer, int64_t index, String line, Error *err);
|
||||
|
||||
/// Deletes a buffer line
|
||||
///
|
||||
/// @param buffer The buffer handle
|
||||
/// @param index The line index
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
void buffer_del_line(Buffer buffer, int64_t index, Error *err);
|
||||
|
||||
/// Retrieves a line range from the buffer
|
||||
///
|
||||
/// @param buffer The buffer handle
|
||||
/// @param start The first line index
|
||||
/// @param end The last line index
|
||||
/// @param include_start True if the slice includes the `start` parameter
|
||||
/// @param include_end True if the slice includes the `end` parameter
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return An array of lines
|
||||
StringArray buffer_get_slice(Buffer buffer,
|
||||
int64_t start,
|
||||
int64_t end,
|
||||
bool include_start,
|
||||
bool include_end,
|
||||
Error *err);
|
||||
|
||||
/// Replaces a line range on the buffer
|
||||
///
|
||||
/// @param buffer The buffer handle
|
||||
/// @param start The first line index
|
||||
/// @param end The last line index
|
||||
/// @param include_start True if the slice includes the `start` parameter
|
||||
/// @param include_end True if the slice includes the `end` parameter
|
||||
/// @param lines An array of lines to use as replacement(A 0-length array
|
||||
/// will simply delete the line range)
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
void buffer_set_slice(Buffer buffer,
|
||||
int64_t start,
|
||||
int64_t end,
|
||||
bool include_start,
|
||||
bool include_end,
|
||||
StringArray replacement,
|
||||
Error *err);
|
||||
|
||||
/// Gets a buffer variable
|
||||
///
|
||||
/// @param buffer The buffer handle
|
||||
/// @param name The variable name
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return The variable value
|
||||
Object buffer_get_var(Buffer buffer, String name, Error *err);
|
||||
|
||||
/// Sets a buffer variable. Passing 'nil' as value deletes the variable.
|
||||
///
|
||||
/// @param buffer The buffer handle
|
||||
/// @param name The variable name
|
||||
/// @param value The variable value
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return The old value
|
||||
Object buffer_set_var(Buffer buffer, String name, Object value, Error *err);
|
||||
|
||||
/// Gets a buffer option value
|
||||
///
|
||||
/// @param buffer The buffer handle
|
||||
/// @param name The option name
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return The option value
|
||||
Object buffer_get_option(Buffer buffer, String name, Error *err);
|
||||
|
||||
/// Sets a buffer option value. Passing 'nil' as value deletes the option(only
|
||||
/// works if there's a global fallback)
|
||||
///
|
||||
/// @param buffer The buffer handle
|
||||
/// @param name The option name
|
||||
/// @param value The option value
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
void buffer_set_option(Buffer buffer, String name, Object value, Error *err);
|
||||
|
||||
/// Gets the full file name for the buffer
|
||||
///
|
||||
/// @param buffer The buffer handle
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return The buffer name
|
||||
String buffer_get_name(Buffer buffer, Error *err);
|
||||
|
||||
/// Sets the full file name for a buffer
|
||||
///
|
||||
/// @param buffer The buffer handle
|
||||
/// @param name The buffer name
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
void buffer_set_name(Buffer buffer, String name, Error *err);
|
||||
|
||||
/// Checks if a buffer is valid
|
||||
///
|
||||
/// @param buffer The buffer handle
|
||||
/// @return true if the buffer is valid, false otherwise
|
||||
bool buffer_is_valid(Buffer buffer);
|
||||
|
||||
/// Inserts a sequence of lines to a buffer at a certain index
|
||||
///
|
||||
/// @param buffer The buffer handle
|
||||
/// @param lnum Insert the lines before `lnum`. If negative, it will append
|
||||
/// to the end of the buffer.
|
||||
/// @param lines An array of lines
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
void buffer_insert(Buffer buffer, int64_t index, StringArray lines, Error *err);
|
||||
|
||||
/// Return a tuple (row,col) representing the position of the named mark
|
||||
///
|
||||
/// @param buffer The buffer handle
|
||||
/// @param name The mark's name
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return The (row, col) tuple
|
||||
Position buffer_get_mark(Buffer buffer, String name, Error *err);
|
||||
|
||||
#endif // NEOVIM_API_BUFFER_H
|
||||
|
74
src/nvim/api/defs.h
Normal file
74
src/nvim/api/defs.h
Normal file
@@ -0,0 +1,74 @@
|
||||
#ifndef NEOVIM_API_DEFS_H
|
||||
#define NEOVIM_API_DEFS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
// Basic types
|
||||
typedef struct {
|
||||
char msg[256];
|
||||
bool set;
|
||||
} Error;
|
||||
|
||||
typedef struct {
|
||||
char *data;
|
||||
size_t size;
|
||||
} String;
|
||||
|
||||
typedef uint16_t Buffer;
|
||||
typedef uint16_t Window;
|
||||
typedef uint16_t Tabpage;
|
||||
|
||||
typedef struct object Object;
|
||||
|
||||
typedef struct {
|
||||
String *items;
|
||||
size_t size;
|
||||
} StringArray;
|
||||
|
||||
typedef struct {
|
||||
uint16_t row, col;
|
||||
} Position;
|
||||
|
||||
typedef struct {
|
||||
Object *items;
|
||||
size_t size;
|
||||
} Array;
|
||||
|
||||
typedef struct key_value_pair KeyValuePair;
|
||||
|
||||
typedef struct {
|
||||
KeyValuePair *items;
|
||||
size_t size;
|
||||
} Dictionary;
|
||||
|
||||
typedef enum {
|
||||
kObjectTypeNil,
|
||||
kObjectTypeBool,
|
||||
kObjectTypeInt,
|
||||
kObjectTypeFloat,
|
||||
kObjectTypeString,
|
||||
kObjectTypeArray,
|
||||
kObjectTypeDictionary
|
||||
} ObjectType;
|
||||
|
||||
struct object {
|
||||
ObjectType type;
|
||||
union {
|
||||
bool boolean;
|
||||
int64_t integer;
|
||||
double floating_point;
|
||||
String string;
|
||||
Array array;
|
||||
Dictionary dictionary;
|
||||
} data;
|
||||
};
|
||||
|
||||
struct key_value_pair {
|
||||
String key;
|
||||
Object value;
|
||||
};
|
||||
|
||||
|
||||
#endif // NEOVIM_API_DEFS_H
|
||||
|
578
src/nvim/api/helpers.c
Normal file
578
src/nvim/api/helpers.c
Normal file
@@ -0,0 +1,578 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "api/helpers.h"
|
||||
#include "api/defs.h"
|
||||
#include "../vim.h"
|
||||
#include "../buffer.h"
|
||||
#include "../window.h"
|
||||
#include "memory.h"
|
||||
#include "eval.h"
|
||||
#include "option.h"
|
||||
#include "option_defs.h"
|
||||
|
||||
#include "lib/khash.h"
|
||||
|
||||
#if defined(ARCH_64)
|
||||
typedef uint64_t ptr_int_t;
|
||||
KHASH_SET_INIT_INT64(Lookup)
|
||||
#elif defined(ARCH_32)
|
||||
typedef uint32_t ptr_int_t;
|
||||
KHASH_SET_INIT_INT(Lookup)
|
||||
#endif
|
||||
|
||||
/// Recursion helper for the `vim_to_object`. This uses a pointer table
|
||||
/// to avoid infinite recursion due to cyclic references
|
||||
///
|
||||
/// @param obj The source object
|
||||
/// @param lookup Lookup table containing pointers to all processed objects
|
||||
/// @return The converted value
|
||||
static Object vim_to_object_rec(typval_T *obj, khash_t(Lookup) *lookup);
|
||||
|
||||
static bool object_to_vim(Object obj, typval_T *tv, Error *err);
|
||||
|
||||
static void set_option_value_for(char *key,
|
||||
int numval,
|
||||
char *stringval,
|
||||
int opt_flags,
|
||||
int opt_type,
|
||||
void *from,
|
||||
Error *err);
|
||||
|
||||
static void set_option_value_err(char *key,
|
||||
int numval,
|
||||
char *stringval,
|
||||
int opt_flags,
|
||||
Error *err);
|
||||
|
||||
void try_start()
|
||||
{
|
||||
++trylevel;
|
||||
}
|
||||
|
||||
bool try_end(Error *err)
|
||||
{
|
||||
--trylevel;
|
||||
|
||||
// Without this it stops processing all subsequent VimL commands and
|
||||
// generates strange error messages if I e.g. try calling Test() in a
|
||||
// cycle
|
||||
did_emsg = false;
|
||||
|
||||
if (got_int) {
|
||||
if (did_throw) {
|
||||
// If we got an interrupt, discard the current exception
|
||||
discard_current_exception();
|
||||
}
|
||||
|
||||
set_api_error("Keyboard interrupt", err);
|
||||
got_int = false;
|
||||
} else if (msg_list != NULL && *msg_list != NULL) {
|
||||
int should_free;
|
||||
char *msg = (char *)get_exception_string(*msg_list,
|
||||
ET_ERROR,
|
||||
NULL,
|
||||
&should_free);
|
||||
strncpy(err->msg, msg, sizeof(err->msg));
|
||||
err->set = true;
|
||||
free_global_msglist();
|
||||
|
||||
if (should_free) {
|
||||
free(msg);
|
||||
}
|
||||
} else if (did_throw) {
|
||||
set_api_error((char *)current_exception->value, err);
|
||||
}
|
||||
|
||||
return err->set;
|
||||
}
|
||||
|
||||
Object dict_get_value(dict_T *dict, String key, Error *err)
|
||||
{
|
||||
Object rv;
|
||||
hashitem_T *hi;
|
||||
dictitem_T *di;
|
||||
char *k = xstrndup(key.data, key.size);
|
||||
hi = hash_find(&dict->dv_hashtab, (uint8_t *)k);
|
||||
free(k);
|
||||
|
||||
if (HASHITEM_EMPTY(hi)) {
|
||||
set_api_error("Key not found", err);
|
||||
return rv;
|
||||
}
|
||||
|
||||
di = dict_lookup(hi);
|
||||
rv = vim_to_object(&di->di_tv);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
Object dict_set_value(dict_T *dict, String key, Object value, Error *err)
|
||||
{
|
||||
Object rv = {.type = kObjectTypeNil};
|
||||
|
||||
if (dict->dv_lock) {
|
||||
set_api_error("Dictionary is locked", err);
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (key.size == 0) {
|
||||
set_api_error("Empty dictionary keys aren't allowed", err);
|
||||
return rv;
|
||||
}
|
||||
|
||||
dictitem_T *di = dict_find(dict, (uint8_t *)key.data, key.size);
|
||||
|
||||
if (value.type == kObjectTypeNil) {
|
||||
// Delete the key
|
||||
if (di == NULL) {
|
||||
// Doesn't exist, fail
|
||||
set_api_error("Key doesn't exist", err);
|
||||
} else {
|
||||
// Return the old value
|
||||
rv = vim_to_object(&di->di_tv);
|
||||
// Delete the entry
|
||||
hashitem_T *hi = hash_find(&dict->dv_hashtab, di->di_key);
|
||||
hash_remove(&dict->dv_hashtab, hi);
|
||||
dictitem_free(di);
|
||||
}
|
||||
} else {
|
||||
// Update the key
|
||||
typval_T tv;
|
||||
|
||||
// Convert the object to a vimscript type in the temporary variable
|
||||
if (!object_to_vim(value, &tv, err)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (di == NULL) {
|
||||
// Need to create an entry
|
||||
char *k = xstrndup(key.data, key.size);
|
||||
di = dictitem_alloc((uint8_t *)k);
|
||||
free(k);
|
||||
dict_add(dict, di);
|
||||
} else {
|
||||
// Return the old value
|
||||
clear_tv(&di->di_tv);
|
||||
}
|
||||
|
||||
// Update the value
|
||||
copy_tv(&tv, &di->di_tv);
|
||||
// Clear the temporary variable
|
||||
clear_tv(&tv);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
Object get_option_from(void *from, int type, String name, Error *err)
|
||||
{
|
||||
Object rv = {.type = kObjectTypeNil};
|
||||
|
||||
if (name.size == 0) {
|
||||
set_api_error("Empty option name", err);
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Return values
|
||||
int64_t numval;
|
||||
char *stringval = NULL;
|
||||
// copy the option name into 0-delimited string
|
||||
char *key = xstrndup(name.data, name.size);
|
||||
int flags = get_option_value_strict(key, &numval, &stringval, type, from);
|
||||
free(key);
|
||||
|
||||
if (!flags) {
|
||||
set_api_error("invalid option name", err);
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (flags & SOPT_BOOL) {
|
||||
rv.type = kObjectTypeBool;
|
||||
rv.data.boolean = numval ? true : false;
|
||||
} else if (flags & SOPT_NUM) {
|
||||
rv.type = kObjectTypeInt;
|
||||
rv.data.integer = numval;
|
||||
} else if (flags & SOPT_STRING) {
|
||||
if (stringval) {
|
||||
rv.type = kObjectTypeString;
|
||||
rv.data.string.data = stringval;
|
||||
rv.data.string.size = strlen(stringval);
|
||||
} else {
|
||||
set_api_error(N_("Unable to get option value"), err);
|
||||
}
|
||||
} else {
|
||||
set_api_error(N_("internal error: unknown option type"), err);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void set_option_to(void *to, int type, String name, Object value, Error *err)
|
||||
{
|
||||
if (name.size == 0) {
|
||||
set_api_error("Empty option name", err);
|
||||
return;
|
||||
}
|
||||
|
||||
char *key = xstrndup(name.data, name.size);
|
||||
int flags = get_option_value_strict(key, NULL, NULL, type, to);
|
||||
|
||||
if (flags == 0) {
|
||||
set_api_error("invalid option name", err);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (value.type == kObjectTypeNil) {
|
||||
if (type == SREQ_GLOBAL) {
|
||||
set_api_error("unable to unset option", err);
|
||||
goto cleanup;
|
||||
} else if (!(flags & SOPT_GLOBAL)) {
|
||||
set_api_error("cannot unset option that doesn't have a global value",
|
||||
err);
|
||||
goto cleanup;
|
||||
} else {
|
||||
unset_global_local_option(key, to);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
int opt_flags = (type ? OPT_LOCAL : OPT_GLOBAL);
|
||||
|
||||
if (flags & SOPT_BOOL) {
|
||||
if (value.type != kObjectTypeBool) {
|
||||
set_api_error("option requires a boolean value", err);
|
||||
goto cleanup;
|
||||
}
|
||||
bool val = value.data.boolean;
|
||||
set_option_value_for(key, val, NULL, opt_flags, type, to, err);
|
||||
|
||||
} else if (flags & SOPT_NUM) {
|
||||
if (value.type != kObjectTypeInt) {
|
||||
set_api_error("option requires an integer value", err);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
int val = value.data.integer;
|
||||
set_option_value_for(key, val, NULL, opt_flags, type, to, err);
|
||||
} else {
|
||||
if (value.type != kObjectTypeString) {
|
||||
set_api_error("option requires a string value", err);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
char *val = xstrndup(value.data.string.data, value.data.string.size);
|
||||
set_option_value_for(key, 0, val, opt_flags, type, to, err);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(key);
|
||||
}
|
||||
|
||||
Object vim_to_object(typval_T *obj)
|
||||
{
|
||||
Object rv;
|
||||
// We use a lookup table to break out of cyclic references
|
||||
khash_t(Lookup) *lookup = kh_init(Lookup);
|
||||
rv = vim_to_object_rec(obj, lookup);
|
||||
// Free the table
|
||||
kh_destroy(Lookup, lookup);
|
||||
return rv;
|
||||
}
|
||||
|
||||
buf_T *find_buffer(Buffer buffer, Error *err)
|
||||
{
|
||||
buf_T *buf = buflist_findnr(buffer);
|
||||
|
||||
if (buf == NULL) {
|
||||
set_api_error("Invalid buffer id", err);
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
win_T * find_window(Window window, Error *err)
|
||||
{
|
||||
tabpage_T *tp;
|
||||
win_T *wp;
|
||||
|
||||
FOR_ALL_TAB_WINDOWS(tp, wp) {
|
||||
if (!--window) {
|
||||
return wp;
|
||||
}
|
||||
}
|
||||
|
||||
set_api_error("Invalid window id", err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tabpage_T * find_tab(Tabpage tabpage, Error *err)
|
||||
{
|
||||
tabpage_T *rv = find_tabpage(tabpage);
|
||||
|
||||
if (!rv) {
|
||||
set_api_error("Invalid tabpage id", err);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static bool object_to_vim(Object obj, typval_T *tv, Error *err)
|
||||
{
|
||||
tv->v_type = VAR_UNKNOWN;
|
||||
tv->v_lock = 0;
|
||||
|
||||
switch (obj.type) {
|
||||
case kObjectTypeNil:
|
||||
tv->v_type = VAR_NUMBER;
|
||||
tv->vval.v_number = 0;
|
||||
break;
|
||||
|
||||
case kObjectTypeBool:
|
||||
tv->v_type = VAR_NUMBER;
|
||||
tv->vval.v_number = obj.data.boolean;
|
||||
break;
|
||||
|
||||
case kObjectTypeInt:
|
||||
tv->v_type = VAR_NUMBER;
|
||||
tv->vval.v_number = obj.data.integer;
|
||||
break;
|
||||
|
||||
case kObjectTypeFloat:
|
||||
tv->v_type = VAR_FLOAT;
|
||||
tv->vval.v_float = obj.data.floating_point;
|
||||
break;
|
||||
|
||||
case kObjectTypeString:
|
||||
tv->v_type = VAR_STRING;
|
||||
tv->vval.v_string = (uint8_t *)xstrndup(obj.data.string.data,
|
||||
obj.data.string.size);
|
||||
break;
|
||||
|
||||
case kObjectTypeArray:
|
||||
tv->v_type = VAR_LIST;
|
||||
tv->vval.v_list = list_alloc();
|
||||
|
||||
for (uint32_t i = 0; i < obj.data.array.size; i++) {
|
||||
Object item = obj.data.array.items[i];
|
||||
listitem_T *li = listitem_alloc();
|
||||
|
||||
if (!object_to_vim(item, &li->li_tv, err)) {
|
||||
// cleanup
|
||||
listitem_free(li);
|
||||
list_free(tv->vval.v_list, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
list_append(tv->vval.v_list, li);
|
||||
}
|
||||
tv->vval.v_list->lv_refcount++;
|
||||
break;
|
||||
|
||||
case kObjectTypeDictionary:
|
||||
tv->v_type = VAR_DICT;
|
||||
tv->vval.v_dict = dict_alloc();
|
||||
|
||||
for (uint32_t i = 0; i < obj.data.dictionary.size; i++) {
|
||||
KeyValuePair item = obj.data.dictionary.items[i];
|
||||
String key = item.key;
|
||||
|
||||
if (key.size == 0) {
|
||||
set_api_error("Empty dictionary keys aren't allowed", err);
|
||||
// cleanup
|
||||
dict_free(tv->vval.v_dict, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
char *k = xstrndup(key.data, key.size);
|
||||
dictitem_T *di = dictitem_alloc((uint8_t *)k);
|
||||
free(k);
|
||||
|
||||
if (!object_to_vim(item.value, &di->di_tv, err)) {
|
||||
// cleanup
|
||||
dictitem_free(di);
|
||||
dict_free(tv->vval.v_dict, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
dict_add(tv->vval.v_dict, di);
|
||||
}
|
||||
tv->vval.v_dict->dv_refcount++;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static Object vim_to_object_rec(typval_T *obj, khash_t(Lookup) *lookup)
|
||||
{
|
||||
Object rv = {.type = kObjectTypeNil};
|
||||
|
||||
if (obj->v_type == VAR_LIST || obj->v_type == VAR_DICT) {
|
||||
int ret;
|
||||
// Container object, add it to the lookup table
|
||||
kh_put(Lookup, lookup, (ptr_int_t)obj, &ret);
|
||||
if (!ret) {
|
||||
// It's already present, meaning we alredy processed it so just return
|
||||
// nil instead.
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
switch (obj->v_type) {
|
||||
case VAR_STRING:
|
||||
if (obj->vval.v_string != NULL) {
|
||||
rv.type = kObjectTypeString;
|
||||
rv.data.string.data = xstrdup((char *)obj->vval.v_string);
|
||||
rv.data.string.size = strlen(rv.data.string.data);
|
||||
}
|
||||
break;
|
||||
|
||||
case VAR_NUMBER:
|
||||
rv.type = kObjectTypeInt;
|
||||
rv.data.integer = obj->vval.v_number;
|
||||
break;
|
||||
|
||||
case VAR_FLOAT:
|
||||
rv.type = kObjectTypeFloat;
|
||||
rv.data.floating_point = obj->vval.v_float;
|
||||
break;
|
||||
|
||||
case VAR_LIST:
|
||||
{
|
||||
list_T *list = obj->vval.v_list;
|
||||
listitem_T *item;
|
||||
|
||||
if (list != NULL) {
|
||||
rv.type = kObjectTypeArray;
|
||||
rv.data.array.size = list->lv_len;
|
||||
rv.data.array.items = xmalloc(list->lv_len * sizeof(Object));
|
||||
|
||||
uint32_t i = 0;
|
||||
for (item = list->lv_first; item != NULL; item = item->li_next) {
|
||||
rv.data.array.items[i] = vim_to_object_rec(&item->li_tv, lookup);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VAR_DICT:
|
||||
{
|
||||
dict_T *dict = obj->vval.v_dict;
|
||||
hashtab_T *ht;
|
||||
uint64_t todo;
|
||||
hashitem_T *hi;
|
||||
dictitem_T *di;
|
||||
|
||||
if (dict != NULL) {
|
||||
ht = &obj->vval.v_dict->dv_hashtab;
|
||||
todo = ht->ht_used;
|
||||
rv.type = kObjectTypeDictionary;
|
||||
|
||||
// Count items
|
||||
rv.data.dictionary.size = 0;
|
||||
for (hi = ht->ht_array; todo > 0; ++hi) {
|
||||
if (!HASHITEM_EMPTY(hi)) {
|
||||
todo--;
|
||||
rv.data.dictionary.size++;
|
||||
}
|
||||
}
|
||||
|
||||
rv.data.dictionary.items =
|
||||
xmalloc(rv.data.dictionary.size * sizeof(KeyValuePair));
|
||||
todo = ht->ht_used;
|
||||
uint32_t i = 0;
|
||||
|
||||
// Convert all
|
||||
for (hi = ht->ht_array; todo > 0; ++hi) {
|
||||
if (!HASHITEM_EMPTY(hi)) {
|
||||
di = dict_lookup(hi);
|
||||
// Convert key
|
||||
rv.data.dictionary.items[i].key.data =
|
||||
xstrdup((char *)hi->hi_key);
|
||||
rv.data.dictionary.items[i].key.size =
|
||||
strlen((char *)hi->hi_key);
|
||||
// Convert value
|
||||
rv.data.dictionary.items[i].value =
|
||||
vim_to_object_rec(&di->di_tv, lookup);
|
||||
todo--;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
static void set_option_value_for(char *key,
|
||||
int numval,
|
||||
char *stringval,
|
||||
int opt_flags,
|
||||
int opt_type,
|
||||
void *from,
|
||||
Error *err)
|
||||
{
|
||||
win_T *save_curwin = NULL;
|
||||
tabpage_T *save_curtab = NULL;
|
||||
buf_T *save_curbuf = NULL;
|
||||
|
||||
try_start();
|
||||
switch (opt_type)
|
||||
{
|
||||
case SREQ_WIN:
|
||||
if (switch_win(&save_curwin, &save_curtab, (win_T *)from,
|
||||
win_find_tabpage((win_T *)from), false) == FAIL)
|
||||
{
|
||||
if (try_end(err)) {
|
||||
return;
|
||||
}
|
||||
set_api_error("problem while switching windows", err);
|
||||
return;
|
||||
}
|
||||
set_option_value_err(key, numval, stringval, opt_flags, err);
|
||||
restore_win(save_curwin, save_curtab, true);
|
||||
break;
|
||||
case SREQ_BUF:
|
||||
switch_buffer(&save_curbuf, (buf_T *)from);
|
||||
set_option_value_err(key, numval, stringval, opt_flags, err);
|
||||
restore_buffer(save_curbuf);
|
||||
break;
|
||||
case SREQ_GLOBAL:
|
||||
set_option_value_err(key, numval, stringval, opt_flags, err);
|
||||
break;
|
||||
}
|
||||
|
||||
if (err->set) {
|
||||
return;
|
||||
}
|
||||
|
||||
try_end(err);
|
||||
}
|
||||
|
||||
|
||||
static void set_option_value_err(char *key,
|
||||
int numval,
|
||||
char *stringval,
|
||||
int opt_flags,
|
||||
Error *err)
|
||||
{
|
||||
char *errmsg;
|
||||
|
||||
if ((errmsg = (char *)set_option_value((uint8_t *)key,
|
||||
numval,
|
||||
(uint8_t *)stringval,
|
||||
opt_flags)))
|
||||
{
|
||||
if (try_end(err)) {
|
||||
return;
|
||||
}
|
||||
|
||||
set_api_error(errmsg, err);
|
||||
}
|
||||
}
|
90
src/nvim/api/helpers.h
Normal file
90
src/nvim/api/helpers.h
Normal file
@@ -0,0 +1,90 @@
|
||||
#ifndef NEOVIM_API_HELPERS_H
|
||||
#define NEOVIM_API_HELPERS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "api/defs.h"
|
||||
#include "../vim.h"
|
||||
|
||||
#define set_api_error(message, err) \
|
||||
do { \
|
||||
strncpy(err->msg, message, sizeof(err->msg)); \
|
||||
err->set = true; \
|
||||
} while (0)
|
||||
|
||||
/// Start block that may cause vimscript exceptions
|
||||
void try_start(void);
|
||||
|
||||
/// End try block, set the error message if any and return true if an error
|
||||
/// occurred.
|
||||
///
|
||||
/// @param err Pointer to the stack-allocated error object
|
||||
/// @return true if an error occurred
|
||||
bool try_end(Error *err);
|
||||
|
||||
/// Recursively expands a vimscript value in a dict
|
||||
///
|
||||
/// @param dict The vimscript dict
|
||||
/// @param key The key
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
Object dict_get_value(dict_T *dict, String key, Error *err);
|
||||
|
||||
/// Set a value in a dict. Objects are recursively expanded into their
|
||||
/// vimscript equivalents. Passing 'nil' as value deletes the key.
|
||||
///
|
||||
/// @param dict The vimscript dict
|
||||
/// @param key The key
|
||||
/// @param value The new value
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return the old value, if any
|
||||
Object dict_set_value(dict_T *dict, String key, Object value, Error *err);
|
||||
|
||||
/// Gets the value of a global or local(buffer, window) option.
|
||||
///
|
||||
/// @param from If `type` is `SREQ_WIN` or `SREQ_BUF`, this must be a pointer
|
||||
/// to the window or buffer.
|
||||
/// @param type One of `SREQ_GLOBAL`, `SREQ_WIN` or `SREQ_BUF`
|
||||
/// @param name The option name
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return the option value
|
||||
Object get_option_from(void *from, int type, String name, Error *err);
|
||||
|
||||
/// Sets the value of a global or local(buffer, window) option.
|
||||
///
|
||||
/// @param to If `type` is `SREQ_WIN` or `SREQ_BUF`, this must be a pointer
|
||||
/// to the window or buffer.
|
||||
/// @param type One of `SREQ_GLOBAL`, `SREQ_WIN` or `SREQ_BUF`
|
||||
/// @param name The option name
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
void set_option_to(void *to, int type, String name, Object value, Error *err);
|
||||
|
||||
/// Convert a vim object to an `Object` instance, recursively expanding
|
||||
/// Arrays/Dictionaries.
|
||||
///
|
||||
/// @param obj The source object
|
||||
/// @return The converted value
|
||||
Object vim_to_object(typval_T *obj);
|
||||
|
||||
/// Finds the pointer for a window number
|
||||
///
|
||||
/// @param window the window number
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return the window pointer
|
||||
buf_T *find_buffer(Buffer buffer, Error *err);
|
||||
|
||||
/// Finds the pointer for a window number
|
||||
///
|
||||
/// @param window the window number
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return the window pointer
|
||||
win_T * find_window(Window window, Error *err);
|
||||
|
||||
/// Finds the pointer for a tabpage number
|
||||
///
|
||||
/// @param tabpage the tabpage number
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return the tabpage pointer
|
||||
tabpage_T * find_tab(Tabpage tabpage, Error *err);
|
||||
|
||||
#endif // NEOVIM_API_HELPERS_H
|
||||
|
88
src/nvim/api/tabpage.c
Normal file
88
src/nvim/api/tabpage.c
Normal file
@@ -0,0 +1,88 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "api/tabpage.h"
|
||||
#include "api/vim.h"
|
||||
#include "api/defs.h"
|
||||
#include "api/helpers.h"
|
||||
|
||||
int64_t tabpage_get_window_count(Tabpage tabpage, Error *err)
|
||||
{
|
||||
uint64_t rv = 0;
|
||||
tabpage_T *tab = find_tab(tabpage, err);
|
||||
|
||||
if (!tab) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
tabpage_T *tp;
|
||||
win_T *wp;
|
||||
|
||||
FOR_ALL_TAB_WINDOWS(tp, wp) {
|
||||
if (tp != tab) {
|
||||
break;
|
||||
}
|
||||
rv++;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
Object tabpage_get_var(Tabpage tabpage, String name, Error *err)
|
||||
{
|
||||
Object rv;
|
||||
tabpage_T *tab = find_tab(tabpage, err);
|
||||
|
||||
if (!tab) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return dict_get_value(tab->tp_vars, name, err);
|
||||
}
|
||||
|
||||
Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err)
|
||||
{
|
||||
Object rv;
|
||||
tabpage_T *tab = find_tab(tabpage, err);
|
||||
|
||||
if (!tab) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return dict_set_value(tab->tp_vars, name, value, err);
|
||||
}
|
||||
|
||||
Window tabpage_get_window(Tabpage tabpage, Error *err)
|
||||
{
|
||||
Window rv = 0;
|
||||
tabpage_T *tab = find_tab(tabpage, err);
|
||||
|
||||
if (!tab) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (tab == curtab) {
|
||||
return vim_get_current_window();
|
||||
} else {
|
||||
tabpage_T *tp;
|
||||
win_T *wp;
|
||||
rv = 1;
|
||||
|
||||
FOR_ALL_TAB_WINDOWS(tp, wp) {
|
||||
if (tp == tab && wp == tab->tp_curwin) {
|
||||
return rv;
|
||||
}
|
||||
rv++;
|
||||
}
|
||||
// There should always be a current window for a tabpage
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
bool tabpage_is_valid(Tabpage tabpage)
|
||||
{
|
||||
Error stub = {.set = false};
|
||||
return find_tab(tabpage, &stub) != NULL;
|
||||
}
|
||||
|
47
src/nvim/api/tabpage.h
Normal file
47
src/nvim/api/tabpage.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef NEOVIM_API_TABPAGE_H
|
||||
#define NEOVIM_API_TABPAGE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "api/defs.h"
|
||||
|
||||
/// Gets the number of windows in a tabpage
|
||||
///
|
||||
/// @param tabpage The tabpage
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return The number of windows in `tabpage`
|
||||
int64_t tabpage_get_window_count(Tabpage tabpage, Error *err);
|
||||
|
||||
/// Gets a tabpage variable
|
||||
///
|
||||
/// @param tabpage The tab page handle
|
||||
/// @param name The variable name
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return The variable value
|
||||
Object tabpage_get_var(Tabpage tabpage, String name, Error *err);
|
||||
|
||||
/// Sets a tabpage variable. Passing 'nil' as value deletes the variable.
|
||||
///
|
||||
/// @param tabpage handle
|
||||
/// @param name The variable name
|
||||
/// @param value The variable value
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return The tab page handle
|
||||
Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err);
|
||||
|
||||
/// Gets the current window in a tab page
|
||||
///
|
||||
/// @param tabpage The tab page handle
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return The Window handle
|
||||
Window tabpage_get_window(Tabpage tabpage, Error *err);
|
||||
|
||||
/// Checks if a tab page is valid
|
||||
///
|
||||
/// @param tabpage The tab page handle
|
||||
/// @return true if the tab page is valid, false otherwise
|
||||
bool tabpage_is_valid(Tabpage tabpage);
|
||||
|
||||
#endif // NEOVIM_API_TABPAGE_H
|
||||
|
321
src/nvim/api/vim.c
Normal file
321
src/nvim/api/vim.c
Normal file
@@ -0,0 +1,321 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "api/vim.h"
|
||||
#include "api/helpers.h"
|
||||
#include "api/defs.h"
|
||||
#include "api/buffer.h"
|
||||
#include "../vim.h"
|
||||
#include "../buffer.h"
|
||||
#include "../window.h"
|
||||
#include "types.h"
|
||||
#include "ascii.h"
|
||||
#include "ex_docmd.h"
|
||||
#include "screen.h"
|
||||
#include "memory.h"
|
||||
#include "message.h"
|
||||
#include "eval.h"
|
||||
#include "misc2.h"
|
||||
|
||||
#define LINE_BUFFER_SIZE 4096
|
||||
|
||||
/// Writes a message to vim output or error buffer. The string is split
|
||||
/// and flushed after each newline. Incomplete lines are kept for writing
|
||||
/// later.
|
||||
///
|
||||
/// @param message The message to write
|
||||
/// @param to_err True if it should be treated as an error message(use
|
||||
/// `emsg` instead of `msg` to print each line)
|
||||
static void write_msg(String message, bool to_err);
|
||||
|
||||
void vim_push_keys(String str)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
void vim_command(String str, Error *err)
|
||||
{
|
||||
// We still use 0-terminated strings, so we must convert.
|
||||
char *cmd_str = xstrndup(str.data, str.size);
|
||||
// Run the command
|
||||
try_start();
|
||||
do_cmdline_cmd((char_u *)cmd_str);
|
||||
free(cmd_str);
|
||||
update_screen(VALID);
|
||||
try_end(err);
|
||||
}
|
||||
|
||||
Object vim_eval(String str, Error *err)
|
||||
{
|
||||
Object rv;
|
||||
char *expr_str = xstrndup(str.data, str.size);
|
||||
// Evaluate the expression
|
||||
try_start();
|
||||
typval_T *expr_result = eval_expr((char_u *)expr_str, NULL);
|
||||
free(expr_str);
|
||||
|
||||
if (!try_end(err)) {
|
||||
// No errors, convert the result
|
||||
rv = vim_to_object(expr_result);
|
||||
}
|
||||
|
||||
// Free the vim object
|
||||
free_tv(expr_result);
|
||||
return rv;
|
||||
}
|
||||
|
||||
int64_t vim_strwidth(String str)
|
||||
{
|
||||
return mb_string2cells((char_u *)str.data, str.size);
|
||||
}
|
||||
|
||||
StringArray vim_list_runtime_paths(void)
|
||||
{
|
||||
StringArray rv = {.size = 0};
|
||||
uint8_t *rtp = p_rtp;
|
||||
|
||||
if (*rtp == NUL) {
|
||||
// No paths
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Count the number of paths in rtp
|
||||
while (*rtp != NUL) {
|
||||
if (*rtp == ',') {
|
||||
rv.size++;
|
||||
}
|
||||
rtp++;
|
||||
}
|
||||
|
||||
// index
|
||||
uint32_t i = 0;
|
||||
// Allocate memory for the copies
|
||||
rv.items = xmalloc(sizeof(String) * rv.size);
|
||||
// reset the position
|
||||
rtp = p_rtp;
|
||||
// Start copying
|
||||
while (*rtp != NUL) {
|
||||
rv.items[i].data = xmalloc(MAXPATHL);
|
||||
// Copy the path from 'runtimepath' to rv.items[i]
|
||||
rv.items[i].size = copy_option_part(&rtp,
|
||||
(char_u *)rv.items[i].data,
|
||||
MAXPATHL,
|
||||
",");
|
||||
i++;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void vim_change_directory(String dir, Error *err)
|
||||
{
|
||||
char string[MAXPATHL];
|
||||
strncpy(string, dir.data, dir.size);
|
||||
|
||||
try_start();
|
||||
|
||||
if (vim_chdir((char_u *)string)) {
|
||||
if (!try_end(err)) {
|
||||
set_api_error("failed to change directory", err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
post_chdir(false);
|
||||
try_end(err);
|
||||
}
|
||||
|
||||
String vim_get_current_line(Error *err)
|
||||
{
|
||||
return buffer_get_line(curbuf->b_fnum, curwin->w_cursor.lnum - 1, err);
|
||||
}
|
||||
|
||||
void vim_set_current_line(String line, Error *err)
|
||||
{
|
||||
buffer_set_line(curbuf->b_fnum, curwin->w_cursor.lnum - 1, line, err);
|
||||
}
|
||||
|
||||
void vim_del_current_line(Error *err)
|
||||
{
|
||||
buffer_del_line(curbuf->b_fnum, curwin->w_cursor.lnum - 1, err);
|
||||
}
|
||||
|
||||
Object vim_get_var(String name, Error *err)
|
||||
{
|
||||
return dict_get_value(&globvardict, name, err);
|
||||
}
|
||||
|
||||
Object vim_set_var(String name, Object value, Error *err)
|
||||
{
|
||||
return dict_set_value(&globvardict, name, value, err);
|
||||
}
|
||||
|
||||
Object vim_get_vvar(String name, Error *err)
|
||||
{
|
||||
return dict_get_value(&vimvardict, name, err);
|
||||
}
|
||||
|
||||
Object vim_get_option(String name, Error *err)
|
||||
{
|
||||
return get_option_from(NULL, SREQ_GLOBAL, name, err);
|
||||
}
|
||||
|
||||
void vim_set_option(String name, Object value, Error *err)
|
||||
{
|
||||
set_option_to(NULL, SREQ_GLOBAL, name, value, err);
|
||||
}
|
||||
|
||||
void vim_out_write(String str)
|
||||
{
|
||||
write_msg(str, false);
|
||||
}
|
||||
|
||||
void vim_err_write(String str)
|
||||
{
|
||||
write_msg(str, true);
|
||||
}
|
||||
|
||||
int64_t vim_get_buffer_count(void)
|
||||
{
|
||||
buf_T *b = firstbuf;
|
||||
uint64_t n = 0;
|
||||
|
||||
while (b) {
|
||||
n++;
|
||||
b = b->b_next;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
Buffer vim_get_current_buffer(void)
|
||||
{
|
||||
return curbuf->b_fnum;
|
||||
}
|
||||
|
||||
void vim_set_current_buffer(Buffer buffer, Error *err)
|
||||
{
|
||||
try_start();
|
||||
if (do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buffer, 0) == FAIL) {
|
||||
if (try_end(err)) {
|
||||
return;
|
||||
}
|
||||
|
||||
char msg[256];
|
||||
snprintf(msg, sizeof(msg), "failed to switch to buffer %d", (int)buffer);
|
||||
set_api_error(msg, err);
|
||||
return;
|
||||
}
|
||||
|
||||
try_end(err);
|
||||
}
|
||||
|
||||
int64_t vim_get_window_count(void)
|
||||
{
|
||||
tabpage_T *tp;
|
||||
win_T *wp;
|
||||
uint64_t rv = 0;
|
||||
|
||||
FOR_ALL_TAB_WINDOWS(tp, wp) {
|
||||
rv++;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
Window vim_get_current_window(void)
|
||||
{
|
||||
tabpage_T *tp;
|
||||
win_T *wp;
|
||||
Window rv = 1;
|
||||
|
||||
FOR_ALL_TAB_WINDOWS(tp, wp) {
|
||||
if (wp == curwin) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv++;
|
||||
}
|
||||
|
||||
// There should always be a current window
|
||||
abort();
|
||||
}
|
||||
|
||||
void vim_set_current_window(Window window, Error *err)
|
||||
{
|
||||
win_T *win = find_window(window, err);
|
||||
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
|
||||
try_start();
|
||||
win_goto(win);
|
||||
|
||||
if (win != curwin) {
|
||||
if (try_end(err)) {
|
||||
return;
|
||||
}
|
||||
set_api_error("did not switch to the specified window", err);
|
||||
return;
|
||||
}
|
||||
|
||||
try_end(err);
|
||||
}
|
||||
|
||||
int64_t vim_get_tabpage_count(void)
|
||||
{
|
||||
tabpage_T *tp = first_tabpage;
|
||||
uint64_t rv = 0;
|
||||
|
||||
while (tp != NULL) {
|
||||
tp = tp->tp_next;
|
||||
rv++;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
Tabpage vim_get_current_tabpage(void)
|
||||
{
|
||||
Tabpage rv = 1;
|
||||
tabpage_T *t;
|
||||
|
||||
for (t = first_tabpage; t != NULL && t != curtab; t = t->tp_next) {
|
||||
rv++;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void vim_set_current_tabpage(Tabpage tabpage, Error *err)
|
||||
{
|
||||
try_start();
|
||||
goto_tabpage(tabpage);
|
||||
try_end(err);
|
||||
}
|
||||
|
||||
static void write_msg(String message, bool to_err)
|
||||
{
|
||||
static int pos = 0;
|
||||
static char line_buf[LINE_BUFFER_SIZE];
|
||||
|
||||
for (uint32_t i = 0; i < message.size; i++) {
|
||||
if (message.data[i] == NL || pos == LINE_BUFFER_SIZE - 1) {
|
||||
// Flush line
|
||||
line_buf[pos] = NUL;
|
||||
if (to_err) {
|
||||
emsg((uint8_t *)line_buf);
|
||||
} else {
|
||||
msg((uint8_t *)line_buf);
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
line_buf[pos++] = message.data[i];
|
||||
}
|
||||
}
|
158
src/nvim/api/vim.h
Normal file
158
src/nvim/api/vim.h
Normal file
@@ -0,0 +1,158 @@
|
||||
#ifndef NEOVIM_API_VIM_H
|
||||
#define NEOVIM_API_VIM_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "api/defs.h"
|
||||
|
||||
/// Send keys to vim input buffer, simulating user input.
|
||||
///
|
||||
/// @param str The keys to send
|
||||
void vim_push_keys(String str);
|
||||
|
||||
/// Executes an ex-mode command str
|
||||
///
|
||||
/// @param str The command str
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
void vim_command(String str, Error *err);
|
||||
|
||||
/// Evaluates the expression str using the vim internal expression
|
||||
/// evaluator (see |expression|).
|
||||
/// Dictionaries and lists are recursively expanded.
|
||||
///
|
||||
/// @param str The expression str
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return The expanded object
|
||||
Object vim_eval(String str, Error *err);
|
||||
|
||||
/// Calculates the number of display cells `str` occupies, tab is counted as
|
||||
/// one cell.
|
||||
///
|
||||
/// @param str Some text
|
||||
/// @return The number of cells
|
||||
int64_t vim_strwidth(String str);
|
||||
|
||||
/// Returns a list of paths contained in 'runtimepath'
|
||||
///
|
||||
/// @return The list of paths
|
||||
StringArray vim_list_runtime_paths(void);
|
||||
|
||||
/// Changes vim working directory
|
||||
///
|
||||
/// @param dir The new working directory
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
void vim_change_directory(String dir, Error *err);
|
||||
|
||||
/// Return the current line
|
||||
///
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return The current line string
|
||||
String vim_get_current_line(Error *err);
|
||||
|
||||
/// Delete the current line
|
||||
///
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
void vim_del_current_line(Error *err);
|
||||
|
||||
/// Sets the current line
|
||||
///
|
||||
/// @param line The line contents
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
void vim_set_current_line(String line, Error *err);
|
||||
|
||||
/// Gets a global variable
|
||||
///
|
||||
/// @param name The variable name
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return The variable value
|
||||
Object vim_get_var(String name, Error *err);
|
||||
|
||||
/// Sets a global variable. Passing 'nil' as value deletes the variable.
|
||||
///
|
||||
/// @param name The variable name
|
||||
/// @param value The variable value
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return the old value if any
|
||||
Object vim_set_var(String name, Object value, Error *err);
|
||||
|
||||
/// Gets a vim variable
|
||||
///
|
||||
/// @param name The variable name
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return The variable value
|
||||
Object vim_get_vvar(String name, Error *err);
|
||||
|
||||
/// Get an option value string
|
||||
///
|
||||
/// @param name The option name
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return The option value
|
||||
Object vim_get_option(String name, Error *err);
|
||||
|
||||
/// Sets an option value
|
||||
///
|
||||
/// @param name The option name
|
||||
/// @param value The new option value
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
void vim_set_option(String name, Object value, Error *err);
|
||||
|
||||
/// Write a message to vim output buffer
|
||||
///
|
||||
/// @param str The message
|
||||
void vim_out_write(String str);
|
||||
|
||||
/// Write a message to vim error buffer
|
||||
///
|
||||
/// @param str The message
|
||||
void vim_err_write(String str);
|
||||
|
||||
/// Gets the number of buffers
|
||||
///
|
||||
/// @return The number of buffers
|
||||
int64_t vim_get_buffer_count(void);
|
||||
|
||||
/// Return the current buffer
|
||||
///
|
||||
/// @reqturn The buffer handle
|
||||
Buffer vim_get_current_buffer(void);
|
||||
|
||||
/// Sets the current buffer
|
||||
///
|
||||
/// @param id The buffer handle
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
void vim_set_current_buffer(Buffer buffer, Error *err);
|
||||
|
||||
/// Gets the number of windows
|
||||
///
|
||||
/// @return The number of windows
|
||||
int64_t vim_get_window_count(void);
|
||||
|
||||
/// Return the current window
|
||||
///
|
||||
/// @return The window handle
|
||||
Window vim_get_current_window(void);
|
||||
|
||||
/// Sets the current window
|
||||
///
|
||||
/// @param handle The window handle
|
||||
void vim_set_current_window(Window window, Error *err);
|
||||
|
||||
/// Gets the number of tab pages
|
||||
///
|
||||
/// @return The number of tab pages
|
||||
int64_t vim_get_tabpage_count(void);
|
||||
|
||||
/// Return the current tab page
|
||||
///
|
||||
/// @return The tab page handle
|
||||
Tabpage vim_get_current_tabpage(void);
|
||||
|
||||
/// Sets the current tab page
|
||||
///
|
||||
/// @param handle The tab page handle
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
void vim_set_current_tabpage(Tabpage tabpage, Error *err);
|
||||
|
||||
#endif // NEOVIM_API_VIM_H
|
||||
|
184
src/nvim/api/window.c
Normal file
184
src/nvim/api/window.c
Normal file
@@ -0,0 +1,184 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "api/window.h"
|
||||
#include "api/defs.h"
|
||||
#include "api/helpers.h"
|
||||
#include "../vim.h"
|
||||
#include "../window.h"
|
||||
#include "screen.h"
|
||||
#include "misc2.h"
|
||||
|
||||
|
||||
Buffer window_get_buffer(Window window, Error *err)
|
||||
{
|
||||
win_T *win = find_window(window, err);
|
||||
|
||||
if (!win) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return win->w_buffer->b_fnum;
|
||||
}
|
||||
|
||||
Position window_get_cursor(Window window, Error *err)
|
||||
{
|
||||
Position rv = {.row = 0, .col = 0};
|
||||
win_T *win = find_window(window, err);
|
||||
|
||||
if (win) {
|
||||
rv.row = win->w_cursor.lnum;
|
||||
rv.col = win->w_cursor.col;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void window_set_cursor(Window window, Position pos, Error *err)
|
||||
{
|
||||
win_T *win = find_window(window, err);
|
||||
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pos.row <= 0 || pos.row > win->w_buffer->b_ml.ml_line_count) {
|
||||
set_api_error("cursor position outside buffer", err);
|
||||
return;
|
||||
}
|
||||
|
||||
win->w_cursor.lnum = pos.row;
|
||||
win->w_cursor.col = pos.col;
|
||||
win->w_cursor.coladd = 0;
|
||||
// When column is out of range silently correct it.
|
||||
check_cursor_col_win(win);
|
||||
update_screen(VALID);
|
||||
}
|
||||
|
||||
int64_t window_get_height(Window window, Error *err)
|
||||
{
|
||||
win_T *win = find_window(window, err);
|
||||
|
||||
if (!win) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return win->w_height;
|
||||
}
|
||||
|
||||
void window_set_height(Window window, int64_t height, Error *err)
|
||||
{
|
||||
win_T *win = find_window(window, err);
|
||||
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
|
||||
win_T *savewin = curwin;
|
||||
curwin = win;
|
||||
try_start();
|
||||
win_setheight(height);
|
||||
curwin = savewin;
|
||||
try_end(err);
|
||||
}
|
||||
|
||||
int64_t window_get_width(Window window, Error *err)
|
||||
{
|
||||
win_T *win = find_window(window, err);
|
||||
|
||||
if (!win) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return win->w_width;
|
||||
}
|
||||
|
||||
void window_set_width(Window window, int64_t width, Error *err)
|
||||
{
|
||||
win_T *win = find_window(window, err);
|
||||
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
|
||||
win_T *savewin = curwin;
|
||||
curwin = win;
|
||||
try_start();
|
||||
win_setwidth(width);
|
||||
curwin = savewin;
|
||||
try_end(err);
|
||||
}
|
||||
|
||||
Object window_get_var(Window window, String name, Error *err)
|
||||
{
|
||||
Object rv;
|
||||
win_T *win = find_window(window, err);
|
||||
|
||||
if (!win) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return dict_get_value(win->w_vars, name, err);
|
||||
}
|
||||
|
||||
Object window_set_var(Window window, String name, Object value, Error *err)
|
||||
{
|
||||
Object rv;
|
||||
win_T *win = find_window(window, err);
|
||||
|
||||
if (!win) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return dict_set_value(win->w_vars, name, value, err);
|
||||
}
|
||||
|
||||
Object window_get_option(Window window, String name, Error *err)
|
||||
{
|
||||
Object rv;
|
||||
win_T *win = find_window(window, err);
|
||||
|
||||
if (!win) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return get_option_from(win, SREQ_WIN, name, err);
|
||||
}
|
||||
|
||||
void window_set_option(Window window, String name, Object value, Error *err)
|
||||
{
|
||||
win_T *win = find_window(window, err);
|
||||
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
|
||||
set_option_to(win, SREQ_WIN, name, value, err);
|
||||
}
|
||||
|
||||
Position window_get_position(Window window, Error *err)
|
||||
{
|
||||
Position rv;
|
||||
win_T *win = find_window(window, err);
|
||||
|
||||
if (win) {
|
||||
rv.col = win->w_wincol;
|
||||
rv.row = win->w_winrow;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
Tabpage window_get_tabpage(Window window, Error *err)
|
||||
{
|
||||
set_api_error("Not implemented", err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool window_is_valid(Window window)
|
||||
{
|
||||
Error stub = {.set = false};
|
||||
return find_window(window, &stub) != NULL;
|
||||
}
|
||||
|
115
src/nvim/api/window.h
Normal file
115
src/nvim/api/window.h
Normal file
@@ -0,0 +1,115 @@
|
||||
#ifndef NEOVIM_API_WINDOW_H
|
||||
#define NEOVIM_API_WINDOW_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "api/defs.h"
|
||||
|
||||
/// Gets the current buffer in a window
|
||||
///
|
||||
/// @param window The window handle
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return The buffer handle
|
||||
Buffer window_get_buffer(Window window, Error *err);
|
||||
|
||||
/// Gets the cursor position in the window
|
||||
///
|
||||
/// @param window The window handle
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return the (row, col) tuple
|
||||
Position window_get_cursor(Window window, Error *err);
|
||||
|
||||
/// Sets the cursor position in the window
|
||||
///
|
||||
/// @param window The window handle
|
||||
/// @param pos the (row, col) tuple representing the new position
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
void window_set_cursor(Window window, Position pos, Error *err);
|
||||
|
||||
/// Gets the window height
|
||||
///
|
||||
/// @param window The window handle
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return the height in rows
|
||||
int64_t window_get_height(Window window, Error *err);
|
||||
|
||||
/// Sets the window height. This will only succeed if the screen is split
|
||||
/// horizontally.
|
||||
///
|
||||
/// @param window The window handle
|
||||
/// @param height the new height in rows
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
void window_set_height(Window window, int64_t height, Error *err);
|
||||
|
||||
/// Gets the window width
|
||||
///
|
||||
/// @param window The window handle
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return the width in columns
|
||||
int64_t window_get_width(Window window, Error *err);
|
||||
|
||||
/// Sets the window width. This will only succeed if the screen is split
|
||||
/// vertically.
|
||||
///
|
||||
/// @param window The window handle
|
||||
/// @param width the new width in columns
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
void window_set_width(Window window, int64_t width, Error *err);
|
||||
|
||||
/// Gets a window variable
|
||||
///
|
||||
/// @param window The window handle
|
||||
/// @param name The variable name
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return The variable value
|
||||
Object window_get_var(Window window, String name, Error *err);
|
||||
|
||||
/// Sets a window variable. Passing 'nil' as value deletes the variable.
|
||||
///
|
||||
/// @param window The window handle
|
||||
/// @param name The variable name
|
||||
/// @param value The variable value
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return The old value
|
||||
Object window_set_var(Window window, String name, Object value, Error *err);
|
||||
|
||||
/// Gets a window option value
|
||||
///
|
||||
/// @param window The window handle
|
||||
/// @param name The option name
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return The option value
|
||||
Object window_get_option(Window window, String name, Error *err);
|
||||
|
||||
/// Sets a window option value. Passing 'nil' as value deletes the option(only
|
||||
/// works if there's a global fallback)
|
||||
///
|
||||
/// @param window The window handle
|
||||
/// @param name The option name
|
||||
/// @param value The option value
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
void window_set_option(Window window, String name, Object value, Error *err);
|
||||
|
||||
/// Gets the window position in display cells. First position is zero.
|
||||
///
|
||||
/// @param window The window handle
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return The (row, col) tuple with the window position
|
||||
Position window_get_position(Window window, Error *err);
|
||||
|
||||
/// Gets the window tab page
|
||||
///
|
||||
/// @param window The window handle
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return The tab page that contains the window
|
||||
Tabpage window_get_tabpage(Window window, Error *err);
|
||||
|
||||
/// Checks if a window is valid
|
||||
///
|
||||
/// @param window The window handle
|
||||
/// @return true if the window is valid, false otherwise
|
||||
bool window_is_valid(Window window);
|
||||
|
||||
#endif // NEOVIM_API_WINDOW_H
|
||||
|
Reference in New Issue
Block a user