diff options
Diffstat (limited to 'src/nvim/api/vim.c')
-rw-r--r-- | src/nvim/api/vim.c | 515 |
1 files changed, 338 insertions, 177 deletions
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 3be45d0cf7..b5cc02e761 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -17,6 +17,7 @@ #include "nvim/api/window.h" #include "nvim/ascii.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/context.h" #include "nvim/decoration.h" #include "nvim/edit.h" @@ -59,7 +60,6 @@ #endif void api_vim_free_all_mem(void) - FUNC_API_NOEXPORT { String name; handle_T id; @@ -196,7 +196,7 @@ Dictionary nvim_get_hl_by_id(Integer hl_id, Boolean rgb, Error *err) Integer nvim_get_hl_id_by_name(String name) FUNC_API_SINCE(7) { - return syn_check_group((const char_u *)name.data, (int)name.size); + return syn_check_group(name.data, (int)name.size); } Dictionary nvim__get_hl_defs(Integer ns_id, Error *err) @@ -228,7 +228,7 @@ Dictionary nvim__get_hl_defs(Integer ns_id, Error *err) void nvim_set_hl(Integer ns_id, String name, Dictionary val, Error *err) FUNC_API_SINCE(7) { - int hl_id = syn_check_group( (char_u *)(name.data), (int)name.size); + int hl_id = syn_check_group(name.data, (int)name.size); int link_id = -1; HlAttrs attrs = dict2hlattrs(val, true, &link_id, err); @@ -264,7 +264,6 @@ void nvim__set_hl_ns(Integer ns_id, Error *err) } static void on_redraw_event(void **argv) - FUNC_API_NOEXPORT { redraw_all_later(NOT_VALID); } @@ -714,7 +713,7 @@ Object nvim_call_dict_function(Object dict, String fn, Array args, Error *err) } fn = (String) { .data = (char *)di->di_tv.vval.v_string, - .size = strlen((char *)di->di_tv.vval.v_string), + .size = STRLEN(di->di_tv.vval.v_string), }; } @@ -758,6 +757,11 @@ ArrayOf(String) nvim_list_runtime_paths(Error *err) return nvim_get_runtime_file(NULL_STRING, true, err); } +Array nvim__runtime_inspect(void) +{ + return runtime_inspect(); +} + /// Find files in runtime directories /// /// 'name' can contain wildcards. For example @@ -796,6 +800,25 @@ String nvim__get_lib_dir(void) return cstr_as_string(get_lib_dir()); } +/// Find files in runtime directories +/// +/// @param pat pattern of files to search for +/// @param all whether to return all matches or only the first +/// @param options +/// is_lua: only search lua subdirs +/// @return list of absolute paths to the found files +ArrayOf(String) nvim__get_runtime(Array pat, Boolean all, Dict(runtime) *opts, Error *err) + FUNC_API_SINCE(8) + FUNC_API_FAST +{ + bool is_lua = api_object_to_bool(opts->is_lua, "is_lua", false, err); + if (ERROR_SET(err)) { + return (Array)ARRAY_DICT_INIT; + } + return runtime_get_named(is_lua, pat, all); +} + + /// Changes the global working directory. /// /// @param dir Directory path @@ -1221,14 +1244,20 @@ fail: /// buffer in a configured window before calling this. For instance, for a /// floating display, first create an empty buffer using |nvim_create_buf()|, /// then display it using |nvim_open_win()|, and then call this function. -/// Then |nvim_chan_send()| cal be called immediately to process sequences +/// Then |nvim_chan_send()| can be called immediately to process sequences /// in a virtual terminal having the intended size. /// /// @param buffer the buffer to use (expected to be empty) -/// @param opts Optional parameters. Reserved for future use. +/// @param opts Optional parameters. +/// - on_input: lua callback for input sent, i e keypresses in terminal +/// mode. Note: keypresses are sent raw as they would be to the pty +/// master end. For instance, a carriage return is sent +/// as a "\r", not as a "\n". |textlock| applies. It is possible +/// to call |nvim_chan_send| directly in the callback however. +/// ["input", term, bufnr, data] /// @param[out] err Error details, if any /// @return Channel id, or 0 on error -Integer nvim_open_term(Buffer buffer, Dictionary opts, Error *err) +Integer nvim_open_term(Buffer buffer, DictionaryOf(LuaRef) opts, Error *err) FUNC_API_SINCE(7) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -1236,13 +1265,27 @@ Integer nvim_open_term(Buffer buffer, Dictionary opts, Error *err) return 0; } - if (opts.size > 0) { - api_set_error(err, kErrorTypeValidation, "opts dict isn't empty"); - return 0; + LuaRef cb = LUA_NOREF; + for (size_t i = 0; i < opts.size; i++) { + String k = opts.items[i].key; + Object *v = &opts.items[i].value; + if (strequal("on_input", k.data)) { + if (v->type != kObjectTypeLuaRef) { + api_set_error(err, kErrorTypeValidation, + "%s is not a function", "on_input"); + return 0; + } + cb = v->data.luaref; + v->data.luaref = LUA_NOREF; + break; + } else { + api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); + } } TerminalOptions topts; Channel *chan = channel_alloc(kChannelStreamInternal); + chan->stream.internal.cb = cb; topts.data = chan; // NB: overridden in terminal_check_size if a window is already // displaying the buffer @@ -1260,7 +1303,18 @@ Integer nvim_open_term(Buffer buffer, Dictionary opts, Error *err) static void term_write(char *buf, size_t size, void *data) { - // TODO(bfredl): lua callback + Channel *chan = data; + LuaRef cb = chan->stream.internal.cb; + if (cb == LUA_NOREF) { + return; + } + FIXED_TEMP_ARRAY(args, 3); + args.items[0] = INTEGER_OBJ((Integer)chan->id); + args.items[1] = BUFFER_OBJ(terminal_buf(chan->term)); + args.items[2] = STRING_OBJ(((String){ .data = buf, .size = size })); + textlock++; + nlua_call_ref(cb, "input", args, false, NULL); + textlock--; } static void term_resize(uint16_t width, uint16_t height, void *data) @@ -1305,153 +1359,6 @@ void nvim_chan_send(Integer chan, String data, Error *err) } } -/// Open a new window. -/// -/// Currently this is used to open floating and external windows. -/// Floats are windows that are drawn above the split layout, at some anchor -/// position in some other window. Floats can be drawn internally or by external -/// GUI with the |ui-multigrid| extension. External windows are only supported -/// with multigrid GUIs, and are displayed as separate top-level windows. -/// -/// For a general overview of floats, see |api-floatwin|. -/// -/// Exactly one of `external` and `relative` must be specified. The `width` and -/// `height` of the new window must be specified. -/// -/// With relative=editor (row=0,col=0) refers to the top-left corner of the -/// screen-grid and (row=Lines-1,col=Columns-1) refers to the bottom-right -/// corner. Fractional values are allowed, but the builtin implementation -/// (used by non-multigrid UIs) will always round down to nearest integer. -/// -/// Out-of-bounds values, and configurations that make the float not fit inside -/// the main editor, are allowed. The builtin implementation truncates values -/// so floats are fully within the main screen grid. External GUIs -/// could let floats hover outside of the main window like a tooltip, but -/// this should not be used to specify arbitrary WM screen positions. -/// -/// Example (Lua): window-relative float -/// <pre> -/// vim.api.nvim_open_win(0, false, -/// {relative='win', row=3, col=3, width=12, height=3}) -/// </pre> -/// -/// Example (Lua): buffer-relative float (travels as buffer is scrolled) -/// <pre> -/// vim.api.nvim_open_win(0, false, -/// {relative='win', width=12, height=3, bufpos={100,10}}) -/// </pre> -/// -/// @param buffer Buffer to display, or 0 for current buffer -/// @param enter Enter the window (make it the current window) -/// @param config Map defining the window configuration. Keys: -/// - `relative`: Sets the window layout to "floating", placed at (row,col) -/// coordinates relative to: -/// - "editor" The global editor grid -/// - "win" Window given by the `win` field, or current window. -/// - "cursor" Cursor position in current window. -/// - `win`: |window-ID| for relative="win". -/// - `anchor`: Decides which corner of the float to place at (row,col): -/// - "NW" northwest (default) -/// - "NE" northeast -/// - "SW" southwest -/// - "SE" southeast -/// - `width`: Window width (in character cells). Minimum of 1. -/// - `height`: Window height (in character cells). Minimum of 1. -/// - `bufpos`: Places float relative to buffer text (only when -/// relative="win"). Takes a tuple of zero-indexed [line, column]. -/// `row` and `col` if given are applied relative to this -/// position, else they default to `row=1` and `col=0` -/// (thus like a tooltip near the buffer text). -/// - `row`: Row position in units of "screen cell height", may be fractional. -/// - `col`: Column position in units of "screen cell width", may be -/// fractional. -/// - `focusable`: Enable focus by user actions (wincmds, mouse events). -/// Defaults to true. Non-focusable windows can be entered by -/// |nvim_set_current_win()|. -/// - `external`: GUI should display the window as an external -/// top-level window. Currently accepts no other positioning -/// configuration together with this. -/// - `zindex`: Stacking order. floats with higher `zindex` go on top on -/// floats with lower indices. Must be larger than zero. The -/// following screen elements have hard-coded z-indices: -/// - 100: insert completion popupmenu -/// - 200: message scrollback -/// - 250: cmdline completion popupmenu (when wildoptions+=pum) -/// The default value for floats are 50. In general, values below 100 are -/// recommended, unless there is a good reason to overshadow builtin -/// elements. -/// - `style`: Configure the appearance of the window. Currently only takes -/// one non-empty value: -/// - "minimal" Nvim will display the window with many UI options -/// disabled. This is useful when displaying a temporary -/// float where the text should not be edited. Disables -/// 'number', 'relativenumber', 'cursorline', 'cursorcolumn', -/// 'foldcolumn', 'spell' and 'list' options. 'signcolumn' -/// is changed to `auto` and 'colorcolumn' is cleared. The -/// end-of-buffer region is hidden by setting `eob` flag of -/// 'fillchars' to a space char, and clearing the -/// |EndOfBuffer| region in 'winhighlight'. -/// - `border`: Style of (optional) window border. This can either be a string -/// or an array. The string values are -/// - "none": No border (default). -/// - "single": A single line box. -/// - "double": A double line box. -/// - "rounded": Like "single", but with rounded corners ("╭" etc.). -/// - "solid": Adds padding by a single whitespace cell. -/// - "shadow": A drop shadow effect by blending with the background. -/// - If it is an array, it should have a length of eight or any divisor of -/// eight. The array will specifify the eight chars building up the border -/// in a clockwise fashion starting with the top-left corner. As an -/// example, the double box style could be specified as -/// [ "╔", "═" ,"╗", "║", "╝", "═", "╚", "║" ]. -/// If the number of chars are less than eight, they will be repeated. Thus -/// an ASCII border could be specified as -/// [ "/", "-", "\\", "|" ], -/// or all chars the same as -/// [ "x" ]. -/// An empty string can be used to turn off a specific border, for instance, -/// [ "", "", "", ">", "", "", "", "<" ] -/// will only make vertical borders but not horizontal ones. -/// By default, `FloatBorder` highlight is used, which links to `VertSplit` -/// when not defined. It could also be specified by character: -/// [ {"+", "MyCorner"}, {"x", "MyBorder"} ]. -/// - `noautocmd`: If true then no buffer-related autocommand events such as -/// |BufEnter|, |BufLeave| or |BufWinEnter| may fire from -/// calling this function. -/// -/// @param[out] err Error details, if any -/// -/// @return Window handle, or 0 on error -Window nvim_open_win(Buffer buffer, Boolean enter, Dictionary config, Error *err) - FUNC_API_SINCE(6) - FUNC_API_CHECK_TEXTLOCK -{ - FloatConfig fconfig = FLOAT_CONFIG_INIT; - if (!parse_float_config(config, &fconfig, false, true, err)) { - return 0; - } - win_T *wp = win_new_float(NULL, fconfig, err); - if (!wp) { - return 0; - } - if (enter) { - win_enter(wp, false); - } - if (!win_valid(wp)) { - api_set_error(err, kErrorTypeException, "Window was closed immediately"); - return 0; - } - if (buffer > 0) { - win_set_buf(wp->handle, buffer, fconfig.noautocmd, err); - } - - if (fconfig.style == kWinStyleMinimal) { - win_set_minimal_style(wp); - didset_window_options(wp); - } - return wp->handle; -} - /// Gets the current list of tabpage handles. /// /// @return List of tabpage handles @@ -1507,7 +1414,7 @@ void nvim_set_current_tabpage(Tabpage tabpage, Error *err) } } -/// Creates a new *namespace*, or gets an existing one. +/// Creates a new \*namespace\* or gets an existing one. /// /// Namespaces are used for buffer highlights and virtual text, see /// |nvim_buf_add_highlight()| and |nvim_buf_set_extmark()|. @@ -1758,24 +1665,15 @@ Dictionary nvim_get_color_map(void) /// @param[out] err Error details, if any /// /// @return map of global |context|. -Dictionary nvim_get_context(Dictionary opts, Error *err) +Dictionary nvim_get_context(Dict(context) *opts, Error *err) FUNC_API_SINCE(6) { Array types = ARRAY_DICT_INIT; - for (size_t i = 0; i < opts.size; i++) { - String k = opts.items[i].key; - Object v = opts.items[i].value; - if (strequal("types", k.data)) { - if (v.type != kObjectTypeArray) { - api_set_error(err, kErrorTypeValidation, "invalid value for key: %s", - k.data); - return (Dictionary)ARRAY_DICT_INIT; - } - types = v.data.array; - } else { - api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); - return (Dictionary)ARRAY_DICT_INIT; - } + if (opts->types.type == kObjectTypeArray) { + types = opts->types.data.array; + } else if (opts->types.type != kObjectTypeNil) { + api_set_error(err, kErrorTypeValidation, "invalid value for key: types"); + return (Dictionary)ARRAY_DICT_INIT; } int int_types = types.size > 0 ? 0 : kCtxAll; @@ -1885,7 +1783,7 @@ ArrayOf(Dictionary) nvim_get_keymap(String mode) /// as keys excluding |<buffer>| but including |noremap|. /// Values are Booleans. Unknown key is an error. /// @param[out] err Error details, if any. -void nvim_set_keymap(String mode, String lhs, String rhs, Dictionary opts, Error *err) +void nvim_set_keymap(String mode, String lhs, String rhs, Dict(keymap) *opts, Error *err) FUNC_API_SINCE(6) { modify_keymap(-1, false, mode, lhs, rhs, opts, err); @@ -1911,7 +1809,7 @@ void nvim_del_keymap(String mode, String lhs, Error *err) /// @param[out] err Error details, if any. /// /// @returns Map of maps describing commands. -Dictionary nvim_get_commands(Dictionary opts, Error *err) +Dictionary nvim_get_commands(Dict(get_commands) *opts, Error *err) FUNC_API_SINCE(4) { return nvim_buf_get_commands(-1, opts, err); @@ -2937,3 +2835,266 @@ 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; +} + +/// Evaluates statusline string. +/// +/// @param str Statusline string (see 'statusline'). +/// @param opts Optional parameters. +/// - winid: (number) |window-ID| of the window to use as context for statusline. +/// - maxwidth: (number) Maximum width of statusline. +/// - fillchar: (string) Character to fill blank spaces in the statusline (see +/// 'fillchars'). +/// - highlights: (boolean) Return highlight information. +/// - use_tabline: (boolean) Evaluate tabline instead of statusline. When |TRUE|, {winid} +/// is ignored. +/// +/// @param[out] err Error details, if any. +/// @return Dictionary containing statusline information, with these keys: +/// - str: (string) Characters that will be displayed on the statusline. +/// - width: (number) Display width of the statusline. +/// - highlights: Array containing highlight information of the statusline. Only included when +/// the "highlights" key in {opts} is |TRUE|. Each element of the array is a +/// |Dictionary| with these keys: +/// - start: (number) Byte index (0-based) of first character that uses the highlight. +/// - group: (string) Name of highlight group. +Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *err) + FUNC_API_SINCE(8) FUNC_API_FAST +{ + Dictionary result = ARRAY_DICT_INIT; + + int maxwidth; + char fillchar = 0; + Window window = 0; + bool use_tabline = false; + bool highlights = false; + + if (HAS_KEY(opts->winid)) { + if (opts->winid.type != kObjectTypeInteger) { + api_set_error(err, kErrorTypeValidation, "winid must be an integer"); + return result; + } + + window = (Window)opts->winid.data.integer; + } + + if (HAS_KEY(opts->fillchar)) { + if (opts->fillchar.type != kObjectTypeString || opts->fillchar.data.string.size > 1) { + api_set_error(err, kErrorTypeValidation, "fillchar must be an ASCII character"); + return result; + } + + fillchar = opts->fillchar.data.string.data[0]; + } + + if (HAS_KEY(opts->highlights)) { + highlights = api_object_to_bool(opts->highlights, "highlights", false, err); + + if (ERROR_SET(err)) { + return result; + } + } + + if (HAS_KEY(opts->use_tabline)) { + use_tabline = api_object_to_bool(opts->use_tabline, "use_tabline", false, err); + + if (ERROR_SET(err)) { + return result; + } + } + + win_T *wp, *ewp; + + if (use_tabline) { + wp = NULL; + ewp = curwin; + fillchar = ' '; + } else { + wp = find_window_by_handle(window, err); + ewp = wp; + + if (fillchar == 0) { + int attr; + fillchar = (char)fillchar_status(&attr, wp); + } + } + + if (HAS_KEY(opts->maxwidth)) { + if (opts->maxwidth.type != kObjectTypeInteger) { + api_set_error(err, kErrorTypeValidation, "maxwidth must be an integer"); + return result; + } + + maxwidth = (int)opts->maxwidth.data.integer; + } else { + maxwidth = use_tabline ? Columns : wp->w_width; + } + + char buf[MAXPATHL]; + stl_hlrec_t *hltab; + stl_hlrec_t **hltab_ptr = highlights ? &hltab : NULL; + + // Temporarily reset 'cursorbind' to prevent side effects from moving the cursor away and back. + int p_crb_save = ewp->w_p_crb; + ewp->w_p_crb = false; + + int width = build_stl_str_hl( + ewp, + (char_u *)buf, + sizeof(buf), + (char_u *)str.data, + false, + (char_u)fillchar, + maxwidth, + hltab_ptr, + NULL); + + PUT(result, "width", INTEGER_OBJ(width)); + + // Restore original value of 'cursorbind' + ewp->w_p_crb = p_crb_save; + + if (highlights) { + Array hl_values = ARRAY_DICT_INIT; + const char *grpname; + char user_group[6]; + + // If first character doesn't have a defined highlight, + // add the default highlight at the beginning of the highlight list + if (hltab->start == NULL || ((char *)hltab->start - buf) != 0) { + Dictionary hl_info = ARRAY_DICT_INIT; + grpname = get_default_stl_hl(wp); + + PUT(hl_info, "start", INTEGER_OBJ(0)); + PUT(hl_info, "group", CSTR_TO_OBJ(grpname)); + + ADD(hl_values, DICTIONARY_OBJ(hl_info)); + } + + for (stl_hlrec_t *sp = hltab; sp->start != NULL; sp++) { + Dictionary hl_info = ARRAY_DICT_INIT; + + PUT(hl_info, "start", INTEGER_OBJ((char *)sp->start - buf)); + + if (sp->userhl == 0) { + grpname = get_default_stl_hl(wp); + } else if (sp->userhl < 0) { + grpname = (char *)syn_id2name(-sp->userhl); + } else { + snprintf(user_group, sizeof(user_group), "User%d", sp->userhl); + grpname = user_group; + } + + PUT(hl_info, "group", CSTR_TO_OBJ(grpname)); + + ADD(hl_values, DICTIONARY_OBJ(hl_info)); + } + + PUT(result, "highlights", ARRAY_OBJ(hl_values)); + } + + PUT(result, "str", CSTR_TO_OBJ((char *)buf)); + + return result; +} |