Merge pull request #18936 from bfredl/apioption

refactor(api): reorganize code
This commit is contained in:
bfredl 2022-06-12 21:01:16 +02:00 committed by GitHub
commit feba56af7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 2164 additions and 2096 deletions

View File

@ -708,67 +708,6 @@ nvim_create_buf({listed}, {scratch}) *nvim_create_buf()*
See also: ~
buf_open_scratch
*nvim_create_user_command()*
nvim_create_user_command({name}, {command}, {*opts})
Create a new user command |user-commands|
{name} is the name of the new command. The name must begin
with an uppercase letter.
{command} is the replacement text or Lua function to execute.
Example: >
:call nvim_create_user_command('SayHello', 'echo "Hello world!"', {})
:SayHello
Hello world!
<
Parameters: ~
{name} Name of the new user command. Must begin with
an uppercase letter.
{command} Replacement command to execute when this user
command is executed. When called from Lua, the
command can also be a Lua function. The
function is called with a single table argument
that contains the following keys:
• args: (string) The args passed to the
command, if any |<args>|
• fargs: (table) The args split by unescaped
whitespace (when more than one argument is
allowed), if any |<f-args>|
• bang: (boolean) "true" if the command was
executed with a ! modifier |<bang>|
• line1: (number) The starting line of the
command range |<line1>|
• line2: (number) The final line of the command
range |<line2>|
• range: (number) The number of items in the
command range: 0, 1, or 2 |<range>|
• count: (number) Any count supplied |<count>|
• reg: (string) The optional register, if
specified |<reg>|
• mods: (string) Command modifiers, if any
|<mods>|
• smods: (table) Command modifiers in a
structured format. Has the same structure as
the "mods" key of |nvim_parse_cmd()|.
{opts} Optional command attributes. See
|command-attributes| for more details. To use
boolean attributes (such as |:command-bang| or
|:command-bar|) set the value to "true". In
addition to the string options listed in
|:command-complete|, the "complete" key also
accepts a Lua function which works like the
"customlist" completion mode
|:command-completion-customlist|. Additional
parameters:
• desc: (string) Used for listing the command
when a Lua function is used for {command}.
• force: (boolean, default true) Override any
previous definition.
• preview: (function) Preview callback for
'inccommand' |:command-preview|
nvim_del_current_line() *nvim_del_current_line()*
Deletes the current line.
@ -800,12 +739,6 @@ nvim_del_mark({name}) *nvim_del_mark()*
|nvim_buf_del_mark()|
|nvim_get_mark()|
nvim_del_user_command({name}) *nvim_del_user_command()*
Delete a user-defined command.
Parameters: ~
{name} Name of the command to delete.
nvim_del_var({name}) *nvim_del_var()*
Removes a global (g:) variable.
@ -923,15 +856,6 @@ nvim_feedkeys({keys}, {mode}, {escape_ks}) *nvim_feedkeys()*
feedkeys()
vim_strsave_escape_ks
nvim_get_all_options_info() *nvim_get_all_options_info()*
Gets the option information for all options.
The dictionary has the full option names as keys and option
metadata dictionaries as detailed at |nvim_get_option_info|.
Return: ~
dictionary of all options
nvim_get_api_info() *nvim_get_api_info()*
Returns a 2-tuple (Array), where item 0 is the current channel
id and item 1 is the |api-metadata| map (Dictionary).
@ -996,19 +920,6 @@ nvim_get_color_map() *nvim_get_color_map()*
Return: ~
Map of color names and RGB values.
nvim_get_commands({*opts}) *nvim_get_commands()*
Gets a map of global (non-buffer-local) Ex commands.
Currently only |user-commands| are supported, not builtin Ex
commands.
Parameters: ~
{opts} Optional parameters. Currently only supports
{"builtin":false}
Return: ~
Map of maps describing commands.
nvim_get_context({*opts}) *nvim_get_context()*
Gets a map of the current editor state.
@ -1119,56 +1030,6 @@ nvim_get_mode() *nvim_get_mode()*
Attributes: ~
|api-fast|
nvim_get_option({name}) *nvim_get_option()*
Gets the global value of an option.
Parameters: ~
{name} Option name
Return: ~
Option value (global)
nvim_get_option_info({name}) *nvim_get_option_info()*
Gets the option information for one option
Resulting dictionary has keys:
• name: Name of the option (like 'filetype')
• shortname: Shortened name of the option (like 'ft')
• type: type of option ("string", "number" or "boolean")
• default: The default value for the option
• was_set: Whether the option was set.
• last_set_sid: Last set script id (if any)
• last_set_linenr: line number where option was set
• last_set_chan: Channel where option was set (0 for local)
• scope: one of "global", "win", or "buf"
• global_local: whether win or buf option has a global value
• commalist: List of comma separated values
• flaglist: List of single char flags
Parameters: ~
{name} Option name
Return: ~
Option Information
nvim_get_option_value({name}, {*opts}) *nvim_get_option_value()*
Gets the value of an option. The behavior of this function
matches that of |:set|: the local value of an option is
returned if it exists; otherwise, the global value is
returned. Local values always correspond to the current buffer
or window. To get a buffer-local or window-local option for a
specific buffer or window, use |nvim_buf_get_option()| or
|nvim_win_get_option()|.
Parameters: ~
{name} Option name
{opts} Optional parameters
• scope: One of 'global' or 'local'. Analogous to
|:setglobal| and |:setlocal|, respectively.
Return: ~
Option value
nvim_get_proc({pid}) *nvim_get_proc()*
Gets info describing process `pid`.
@ -1670,33 +1531,6 @@ nvim_set_keymap({mode}, {lhs}, {rhs}, {*opts}) *nvim_set_keymap()*
that takes a Lua function to call when the mapping
is executed.
nvim_set_option({name}, {value}) *nvim_set_option()*
Sets the global value of an option.
Parameters: ~
{name} Option name
{value} New option value
*nvim_set_option_value()*
nvim_set_option_value({name}, {value}, {*opts})
Sets the value of an option. The behavior of this function
matches that of |:set|: for global-local options, both the
global and local value are set unless otherwise specified with
{scope}.
Note the options {win} and {buf} cannot be used together.
Parameters: ~
{name} Option name
{value} New option value
{opts} Optional parameters
• scope: One of 'global' or 'local'. Analogous to
|:setglobal| and |:setlocal|, respectively.
• win: |window-ID|. Used for setting window local
option.
• buf: Buffer number. Used for setting buffer
local option.
nvim_set_var({name}, {value}) *nvim_set_var()*
Sets a global (g:) variable.
@ -1770,36 +1604,6 @@ nvim_call_function({fn}, {args}) *nvim_call_function()*
Return: ~
Result of the function call
nvim_cmd({*cmd}, {*opts}) *nvim_cmd()*
Executes an Ex command.
Unlike |nvim_command()| this command takes a structured
Dictionary instead of a String. This allows for easier
construction and manipulation of an Ex command. This also
allows for things such as having spaces inside a command
argument, expanding filenames in a command that otherwise
doesn't expand filenames, etc.
On execution error: fails with VimL error, updates v:errmsg.
Parameters: ~
{cmd} Command to execute. Must be a Dictionary that can
contain the same values as the return value of
|nvim_parse_cmd()| except "addr", "nargs" and
"nextcmd" which are ignored if provided. All
values except for "cmd" are optional.
{opts} Optional parameters.
• output: (boolean, default false) Whether to
return command output.
Return: ~
Command output (non-error, non-shell |:!|) if `output` is
true, else empty string.
See also: ~
|nvim_exec()|
|nvim_command()|
nvim_command({command}) *nvim_command()*
Executes an Ex command.
@ -1850,76 +1654,6 @@ nvim_exec({src}, {output}) *nvim_exec()*
|nvim_command()|
|nvim_cmd()|
nvim_parse_cmd({str}, {opts}) *nvim_parse_cmd()*
Parse command line.
Doesn't check the validity of command arguments.
Attributes: ~
|api-fast|
Parameters: ~
{str} Command line string to parse. Cannot contain "\n".
{opts} Optional parameters. Reserved for future use.
Return: ~
Dictionary containing command information, with these
keys:
• cmd: (string) Command name.
• range: (array) Command <range>. Can have 0-2 elements
depending on how many items the range contains. Has no
elements if command doesn't accept a range or if no
range was specified, one element if only a single range
item was specified and two elements if both range items
were specified.
• count: (number) Any |<count>| that was supplied to the
command. -1 if command cannot take a count.
• reg: (number) The optional command |<register>|, if
specified. Empty string if not specified or if command
cannot take a register.
• bang: (boolean) Whether command contains a |<bang>| (!)
modifier.
• args: (array) Command arguments.
• addr: (string) Value of |:command-addr|. Uses short
name.
• nargs: (string) Value of |:command-nargs|.
• nextcmd: (string) Next command if there are multiple
commands separated by a |:bar|. Empty if there isn't a
next command.
• magic: (dictionary) Which characters have special
meaning in the command arguments.
• file: (boolean) The command expands filenames. Which
means characters such as "%", "#" and wildcards are
expanded.
• bar: (boolean) The "|" character is treated as a
command separator and the double quote character (")
is treated as the start of a comment.
• mods: (dictionary) |:command-modifiers|.
• silent: (boolean) |:silent|.
• emsg_silent: (boolean) |:silent!|.
• sandbox: (boolean) |:sandbox|.
• noautocmd: (boolean) |:noautocmd|.
• browse: (boolean) |:browse|.
• confirm: (boolean) |:confirm|.
• hide: (boolean) |:hide|.
• keepalt: (boolean) |:keepalt|.
• keepjumps: (boolean) |:keepjumps|.
• keepmarks: (boolean) |:keepmarks|.
• keeppatterns: (boolean) |:keeppatterns|.
• lockmarks: (boolean) |:lockmarks|.
• noswapfile: (boolean) |:noswapfile|.
• tab: (integer) |:tab|.
• verbose: (integer) |:verbose|. -1 when omitted.
• vertical: (boolean) |:vertical|.
• split: (string) Split modifier string, is an empty
string when there's no split modifier. If there is a
split modifier it can be one of:
• "aboveleft": |:aboveleft|.
• "belowright": |:belowright|.
• "topleft": |:topleft|.
• "botright": |:botright|.
*nvim_parse_expression()*
nvim_parse_expression({expr}, {flags}, {highlight})
Parse a VimL expression.
@ -2019,6 +1753,350 @@ nvim_parse_expression({expr}, {flags}, {highlight})
"DoubleQuotedString" nodes.
==============================================================================
Command Functions *api-command*
*nvim_buf_create_user_command()*
nvim_buf_create_user_command({buffer}, {name}, {command}, {*opts})
Create a new user command |user-commands| in the given buffer.
Parameters: ~
{buffer} Buffer handle, or 0 for current buffer.
See also: ~
nvim_create_user_command
*nvim_buf_del_user_command()*
nvim_buf_del_user_command({buffer}, {name})
Delete a buffer-local user-defined command.
Only commands created with |:command-buffer| or
|nvim_buf_create_user_command()| can be deleted with this
function.
Parameters: ~
{buffer} Buffer handle, or 0 for current buffer.
{name} Name of the command to delete.
nvim_buf_get_commands({buffer}, {*opts}) *nvim_buf_get_commands()*
Gets a map of buffer-local |user-commands|.
Parameters: ~
{buffer} Buffer handle, or 0 for current buffer
{opts} Optional parameters. Currently not used.
Return: ~
Map of maps describing commands.
nvim_cmd({*cmd}, {*opts}) *nvim_cmd()*
Executes an Ex command.
Unlike |nvim_command()| this command takes a structured
Dictionary instead of a String. This allows for easier
construction and manipulation of an Ex command. This also
allows for things such as having spaces inside a command
argument, expanding filenames in a command that otherwise
doesn't expand filenames, etc.
On execution error: fails with VimL error, updates v:errmsg.
Parameters: ~
{cmd} Command to execute. Must be a Dictionary that can
contain the same values as the return value of
|nvim_parse_cmd()| except "addr", "nargs" and
"nextcmd" which are ignored if provided. All
values except for "cmd" are optional.
{opts} Optional parameters.
• output: (boolean, default false) Whether to
return command output.
Return: ~
Command output (non-error, non-shell |:!|) if `output` is
true, else empty string.
See also: ~
|nvim_exec()|
|nvim_command()|
*nvim_create_user_command()*
nvim_create_user_command({name}, {command}, {*opts})
Create a new user command |user-commands|
{name} is the name of the new command. The name must begin
with an uppercase letter.
{command} is the replacement text or Lua function to execute.
Example: >
:call nvim_create_user_command('SayHello', 'echo "Hello world!"', {})
:SayHello
Hello world!
<
Parameters: ~
{name} Name of the new user command. Must begin with
an uppercase letter.
{command} Replacement command to execute when this user
command is executed. When called from Lua, the
command can also be a Lua function. The
function is called with a single table argument
that contains the following keys:
• args: (string) The args passed to the
command, if any |<args>|
• fargs: (table) The args split by unescaped
whitespace (when more than one argument is
allowed), if any |<f-args>|
• bang: (boolean) "true" if the command was
executed with a ! modifier |<bang>|
• line1: (number) The starting line of the
command range |<line1>|
• line2: (number) The final line of the command
range |<line2>|
• range: (number) The number of items in the
command range: 0, 1, or 2 |<range>|
• count: (number) Any count supplied |<count>|
• reg: (string) The optional register, if
specified |<reg>|
• mods: (string) Command modifiers, if any
|<mods>|
• smods: (table) Command modifiers in a
structured format. Has the same structure as
the "mods" key of |nvim_parse_cmd()|.
{opts} Optional command attributes. See
|command-attributes| for more details. To use
boolean attributes (such as |:command-bang| or
|:command-bar|) set the value to "true". In
addition to the string options listed in
|:command-complete|, the "complete" key also
accepts a Lua function which works like the
"customlist" completion mode
|:command-completion-customlist|. Additional
parameters:
• desc: (string) Used for listing the command
when a Lua function is used for {command}.
• force: (boolean, default true) Override any
previous definition.
• preview: (function) Preview callback for
'inccommand' |:command-preview|
nvim_del_user_command({name}) *nvim_del_user_command()*
Delete a user-defined command.
Parameters: ~
{name} Name of the command to delete.
nvim_get_commands({*opts}) *nvim_get_commands()*
Gets a map of global (non-buffer-local) Ex commands.
Currently only |user-commands| are supported, not builtin Ex
commands.
Parameters: ~
{opts} Optional parameters. Currently only supports
{"builtin":false}
Return: ~
Map of maps describing commands.
nvim_parse_cmd({str}, {opts}) *nvim_parse_cmd()*
Parse command line.
Doesn't check the validity of command arguments.
Attributes: ~
|api-fast|
Parameters: ~
{str} Command line string to parse. Cannot contain "\n".
{opts} Optional parameters. Reserved for future use.
Return: ~
Dictionary containing command information, with these
keys:
• cmd: (string) Command name.
• range: (array) Command <range>. Can have 0-2 elements
depending on how many items the range contains. Has no
elements if command doesn't accept a range or if no
range was specified, one element if only a single range
item was specified and two elements if both range items
were specified.
• count: (number) Any |<count>| that was supplied to the
command. -1 if command cannot take a count.
• reg: (number) The optional command |<register>|, if
specified. Empty string if not specified or if command
cannot take a register.
• bang: (boolean) Whether command contains a |<bang>| (!)
modifier.
• args: (array) Command arguments.
• addr: (string) Value of |:command-addr|. Uses short
name.
• nargs: (string) Value of |:command-nargs|.
• nextcmd: (string) Next command if there are multiple
commands separated by a |:bar|. Empty if there isn't a
next command.
• magic: (dictionary) Which characters have special
meaning in the command arguments.
• file: (boolean) The command expands filenames. Which
means characters such as "%", "#" and wildcards are
expanded.
• bar: (boolean) The "|" character is treated as a
command separator and the double quote character (")
is treated as the start of a comment.
• mods: (dictionary) |:command-modifiers|.
• silent: (boolean) |:silent|.
• emsg_silent: (boolean) |:silent!|.
• sandbox: (boolean) |:sandbox|.
• noautocmd: (boolean) |:noautocmd|.
• browse: (boolean) |:browse|.
• confirm: (boolean) |:confirm|.
• hide: (boolean) |:hide|.
• keepalt: (boolean) |:keepalt|.
• keepjumps: (boolean) |:keepjumps|.
• keepmarks: (boolean) |:keepmarks|.
• keeppatterns: (boolean) |:keeppatterns|.
• lockmarks: (boolean) |:lockmarks|.
• noswapfile: (boolean) |:noswapfile|.
• tab: (integer) |:tab|.
• verbose: (integer) |:verbose|. -1 when omitted.
• vertical: (boolean) |:vertical|.
• split: (string) Split modifier string, is an empty
string when there's no split modifier. If there is a
split modifier it can be one of:
• "aboveleft": |:aboveleft|.
• "belowright": |:belowright|.
• "topleft": |:topleft|.
• "botright": |:botright|.
==============================================================================
Options Functions *api-options*
nvim_buf_get_option({buffer}, {name}) *nvim_buf_get_option()*
Gets a buffer option value
Parameters: ~
{buffer} Buffer handle, or 0 for current buffer
{name} Option name
Return: ~
Option value
nvim_buf_set_option({buffer}, {name}, {value}) *nvim_buf_set_option()*
Sets a buffer option value. Passing 'nil' as value deletes the
option (only works if there's a global fallback)
Parameters: ~
{buffer} Buffer handle, or 0 for current buffer
{name} Option name
{value} Option value
nvim_get_all_options_info() *nvim_get_all_options_info()*
Gets the option information for all options.
The dictionary has the full option names as keys and option
metadata dictionaries as detailed at |nvim_get_option_info|.
Return: ~
dictionary of all options
nvim_get_option({name}) *nvim_get_option()*
Gets the global value of an option.
Parameters: ~
{name} Option name
Return: ~
Option value (global)
nvim_get_option_info({name}) *nvim_get_option_info()*
Gets the option information for one option
Resulting dictionary has keys:
• name: Name of the option (like 'filetype')
• shortname: Shortened name of the option (like 'ft')
• type: type of option ("string", "number" or "boolean")
• default: The default value for the option
• was_set: Whether the option was set.
• last_set_sid: Last set script id (if any)
• last_set_linenr: line number where option was set
• last_set_chan: Channel where option was set (0 for local)
• scope: one of "global", "win", or "buf"
• global_local: whether win or buf option has a global value
• commalist: List of comma separated values
• flaglist: List of single char flags
Parameters: ~
{name} Option name
Return: ~
Option Information
nvim_get_option_value({name}, {*opts}) *nvim_get_option_value()*
Gets the value of an option. The behavior of this function
matches that of |:set|: the local value of an option is
returned if it exists; otherwise, the global value is
returned. Local values always correspond to the current buffer
or window. To get a buffer-local or window-local option for a
specific buffer or window, use |nvim_buf_get_option()| or
|nvim_win_get_option()|.
Parameters: ~
{name} Option name
{opts} Optional parameters
• scope: One of 'global' or 'local'. Analogous to
|:setglobal| and |:setlocal|, respectively.
Return: ~
Option value
nvim_set_option({name}, {value}) *nvim_set_option()*
Sets the global value of an option.
Parameters: ~
{name} Option name
{value} New option value
*nvim_set_option_value()*
nvim_set_option_value({name}, {value}, {*opts})
Sets the value of an option. The behavior of this function
matches that of |:set|: for global-local options, both the
global and local value are set unless otherwise specified with
{scope}.
Note the options {win} and {buf} cannot be used together.
Parameters: ~
{name} Option name
{value} New option value
{opts} Optional parameters
• scope: One of 'global' or 'local'. Analogous to
|:setglobal| and |:setlocal|, respectively.
• win: |window-ID|. Used for setting window local
option.
• buf: Buffer number. Used for setting buffer
local option.
nvim_win_get_option({window}, {name}) *nvim_win_get_option()*
Gets a window option value
Parameters: ~
{window} Window handle, or 0 for current window
{name} Option name
Return: ~
Option value
nvim_win_set_option({window}, {name}, {value}) *nvim_win_set_option()*
Sets a window option value. Passing 'nil' as value deletes the
option(only works if there's a global fallback)
Parameters: ~
{window} Window handle, or 0 for current window
{name} Option name
{value} Option value
==============================================================================
Buffer Functions *api-buffer*
@ -2157,16 +2235,6 @@ nvim_buf_call({buffer}, {fun}) *nvim_buf_call()*
Return value of function. NB: will deepcopy lua values
currently, use upvalues to send lua references in and out.
*nvim_buf_create_user_command()*
nvim_buf_create_user_command({buffer}, {name}, {command}, {*opts})
Create a new user command |user-commands| in the given buffer.
Parameters: ~
{buffer} Buffer handle, or 0 for current buffer.
See also: ~
nvim_create_user_command
nvim_buf_del_keymap({buffer}, {mode}, {lhs}) *nvim_buf_del_keymap()*
Unmaps a buffer-local |mapping| for the given mode.
@ -2194,18 +2262,6 @@ nvim_buf_del_mark({buffer}, {name}) *nvim_buf_del_mark()*
|nvim_buf_set_mark()|
|nvim_del_mark()|
*nvim_buf_del_user_command()*
nvim_buf_del_user_command({buffer}, {name})
Delete a buffer-local user-defined command.
Only commands created with |:command-buffer| or
|nvim_buf_create_user_command()| can be deleted with this
function.
Parameters: ~
{buffer} Buffer handle, or 0 for current buffer.
{name} Name of the command to delete.
nvim_buf_del_var({buffer}, {name}) *nvim_buf_del_var()*
Removes a buffer-scoped (b:) variable
@ -2253,16 +2309,6 @@ nvim_buf_get_changedtick({buffer}) *nvim_buf_get_changedtick()*
Return: ~
`b:changedtick` value.
nvim_buf_get_commands({buffer}, {*opts}) *nvim_buf_get_commands()*
Gets a map of buffer-local |user-commands|.
Parameters: ~
{buffer} Buffer handle, or 0 for current buffer
{opts} Optional parameters. Currently not used.
Return: ~
Map of maps describing commands.
nvim_buf_get_keymap({buffer}, {mode}) *nvim_buf_get_keymap()*
Gets a list of buffer-local |mapping| definitions.
@ -2341,16 +2387,6 @@ nvim_buf_get_offset({buffer}, {index}) *nvim_buf_get_offset()*
Return: ~
Integer byte offset, or -1 for unloaded buffer.
nvim_buf_get_option({buffer}, {name}) *nvim_buf_get_option()*
Gets a buffer option value
Parameters: ~
{buffer} Buffer handle, or 0 for current buffer
{name} Option name
Return: ~
Option value
*nvim_buf_get_text()*
nvim_buf_get_text({buffer}, {start_row}, {start_col}, {end_row}, {end_col},
{opts})
@ -2486,15 +2522,6 @@ nvim_buf_set_name({buffer}, {name}) *nvim_buf_set_name()*
{buffer} Buffer handle, or 0 for current buffer
{name} Buffer name
nvim_buf_set_option({buffer}, {name}, {value}) *nvim_buf_set_option()*
Sets a buffer option value. Passing 'nil' as value deletes the
option (only works if there's a global fallback)
Parameters: ~
{buffer} Buffer handle, or 0 for current buffer
{name} Option name
{value} Option value
*nvim_buf_set_text()*
nvim_buf_set_text({buffer}, {start_row}, {start_col}, {end_row}, {end_col},
{replacement})
@ -2968,16 +2995,6 @@ nvim_win_get_number({window}) *nvim_win_get_number()*
Return: ~
Window number
nvim_win_get_option({window}, {name}) *nvim_win_get_option()*
Gets a window option value
Parameters: ~
{window} Window handle, or 0 for current window
{name} Option name
Return: ~
Option value
nvim_win_get_position({window}) *nvim_win_get_position()*
Gets the window position in display cells. First position is
zero.
@ -3066,15 +3083,6 @@ nvim_win_set_height({window}, {height}) *nvim_win_set_height()*
{window} Window handle, or 0 for current window
{height} Height as a count of rows
nvim_win_set_option({window}, {name}, {value}) *nvim_win_set_option()*
Sets a window option value. Passing 'nil' as value deletes the
option(only works if there's a global fallback)
Parameters: ~
{window} Window handle, or 0 for current window
{name} Option name
{value} Option value
nvim_win_set_var({window}, {name}, {value}) *nvim_win_set_var()*
Sets a window-scoped (w:) variable

View File

@ -95,6 +95,8 @@ CONFIG = {
'section_order': [
'vim.c',
'vimscript.c',
'command.c',
'options.c',
'buffer.c',
'extmark.c',
'window.c',

View File

@ -969,37 +969,6 @@ void nvim_buf_del_keymap(uint64_t channel_id, Buffer buffer, String mode, String
modify_keymap(channel_id, buffer, true, mode, lhs, rhs, NULL, err);
}
/// Gets a map of buffer-local |user-commands|.
///
/// @param buffer Buffer handle, or 0 for current buffer
/// @param opts Optional parameters. Currently not used.
/// @param[out] err Error details, if any.
///
/// @returns Map of maps describing commands.
Dictionary nvim_buf_get_commands(Buffer buffer, Dict(get_commands) *opts, Error *err)
FUNC_API_SINCE(4)
{
bool global = (buffer == -1);
bool builtin = api_object_to_bool(opts->builtin, "builtin", false, err);
if (ERROR_SET(err)) {
return (Dictionary)ARRAY_DICT_INIT;
}
if (global) {
if (builtin) {
api_set_error(err, kErrorTypeValidation, "builtin=true not implemented");
return (Dictionary)ARRAY_DICT_INIT;
}
return commands_array(NULL);
}
buf_T *buf = find_buffer_by_handle(buffer, err);
if (builtin || !buf) {
return (Dictionary)ARRAY_DICT_INIT;
}
return commands_array(buf);
}
/// Sets a buffer-scoped (b:) variable
///
/// @param buffer Buffer handle, or 0 for current buffer
@ -1035,44 +1004,6 @@ void nvim_buf_del_var(Buffer buffer, String name, Error *err)
dict_set_var(buf->b_vars, name, NIL, true, false, err);
}
/// Gets a buffer option value
///
/// @param buffer Buffer handle, or 0 for current buffer
/// @param name Option name
/// @param[out] err Error details, if any
/// @return Option value
Object nvim_buf_get_option(Buffer buffer, String name, Error *err)
FUNC_API_SINCE(1)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
if (!buf) {
return (Object)OBJECT_INIT;
}
return get_option_from(buf, SREQ_BUF, name, err);
}
/// Sets a buffer option value. Passing 'nil' as value deletes the option (only
/// works if there's a global fallback)
///
/// @param channel_id
/// @param buffer Buffer handle, or 0 for current buffer
/// @param name Option name
/// @param value Option value
/// @param[out] err Error details, if any
void nvim_buf_set_option(uint64_t channel_id, Buffer buffer, String name, Object value, Error *err)
FUNC_API_SINCE(1)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
if (!buf) {
return;
}
set_option_to(channel_id, buf, SREQ_BUF, name, value, err);
}
/// Gets the full file name for the buffer
///
/// @param buffer Buffer handle, or 0 for current buffer
@ -1369,63 +1300,6 @@ Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err)
return res;
}
/// Create a new user command |user-commands| in the given buffer.
///
/// @param buffer Buffer handle, or 0 for current buffer.
/// @param[out] err Error details, if any.
/// @see nvim_create_user_command
void nvim_buf_create_user_command(Buffer buffer, String name, Object command,
Dict(user_command) *opts, Error *err)
FUNC_API_SINCE(9)
{
buf_T *target_buf = find_buffer_by_handle(buffer, err);
if (ERROR_SET(err)) {
return;
}
buf_T *save_curbuf = curbuf;
curbuf = target_buf;
create_user_command(name, command, opts, UC_BUFFER, err);
curbuf = save_curbuf;
}
/// Delete a buffer-local user-defined command.
///
/// Only commands created with |:command-buffer| or
/// |nvim_buf_create_user_command()| can be deleted with this function.
///
/// @param buffer Buffer handle, or 0 for current buffer.
/// @param name Name of the command to delete.
/// @param[out] err Error details, if any.
void nvim_buf_del_user_command(Buffer buffer, String name, Error *err)
FUNC_API_SINCE(9)
{
garray_T *gap;
if (buffer == -1) {
gap = &ucmds;
} else {
buf_T *buf = find_buffer_by_handle(buffer, err);
gap = &buf->b_ucmds;
}
for (int i = 0; i < gap->ga_len; i++) {
ucmd_T *cmd = USER_CMD_GA(gap, i);
if (!STRCMP(name.data, cmd->uc_name)) {
free_ucmd(cmd);
gap->ga_len -= 1;
if (i < gap->ga_len) {
memmove(cmd, cmd + 1, (size_t)(gap->ga_len - i) * sizeof(ucmd_T));
}
return;
}
}
api_set_error(err, kErrorTypeException, "No such user-defined command: %s", name.data);
}
Dictionary nvim__buf_stats(Buffer buffer, Error *err)
{
Dictionary rv = ARRAY_DICT_INIT;

1131
src/nvim/api/command.c Normal file

File diff suppressed because it is too large Load Diff

11
src/nvim/api/command.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef NVIM_API_COMMAND_H
#define NVIM_API_COMMAND_H
#include "nvim/api/private/defs.h"
#include "nvim/decoration.h"
#include "nvim/ex_cmds.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/command.h.generated.h"
#endif
#endif // NVIM_API_COMMAND_H

View File

@ -8,6 +8,7 @@
#include "nvim/api/extmark.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/charset.h"
#include "nvim/decoration_provider.h"
#include "nvim/extmark.h"
#include "nvim/highlight_group.h"
@ -1033,3 +1034,154 @@ void nvim_set_decoration_provider(Integer ns_id, DictionaryOf(LuaRef) opts, Erro
error:
decor_provider_clear(p);
}
/// Gets the line and column of an extmark.
///
/// Extmarks may be queried by position, name or even special names
/// in the future such as "cursor".
///
/// @param[out] lnum extmark line
/// @param[out] colnr extmark column
///
/// @return true if the extmark was found, else false
static bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, int *row,
colnr_T *col, Error *err)
{
// Check if it is mark id
if (obj.type == kObjectTypeInteger) {
Integer id = obj.data.integer;
if (id == 0) {
*row = 0;
*col = 0;
return true;
} else if (id == -1) {
*row = MAXLNUM;
*col = MAXCOL;
return true;
} else if (id < 0) {
api_set_error(err, kErrorTypeValidation, "Mark id must be positive");
return false;
}
ExtmarkInfo extmark = extmark_from_id(buf, (uint32_t)ns_id, (uint32_t)id);
if (extmark.row >= 0) {
*row = extmark.row;
*col = extmark.col;
return true;
} else {
api_set_error(err, kErrorTypeValidation, "No mark with requested id");
return false;
}
// Check if it is a position
} else if (obj.type == kObjectTypeArray) {
Array pos = obj.data.array;
if (pos.size != 2
|| pos.items[0].type != kObjectTypeInteger
|| pos.items[1].type != kObjectTypeInteger) {
api_set_error(err, kErrorTypeValidation,
"Position must have 2 integer elements");
return false;
}
Integer pos_row = pos.items[0].data.integer;
Integer pos_col = pos.items[1].data.integer;
*row = (int)(pos_row >= 0 ? pos_row : MAXLNUM);
*col = (colnr_T)(pos_col >= 0 ? pos_col : MAXCOL);
return true;
} else {
api_set_error(err, kErrorTypeValidation,
"Position must be a mark id Integer or position Array");
return false;
}
}
// adapted from sign.c:sign_define_init_text.
// TODO(lewis6991): Consider merging
static int init_sign_text(char **sign_text, char *text)
{
char *s;
char *endp = text + (int)STRLEN(text);
// Count cells and check for non-printable chars
int cells = 0;
for (s = text; s < endp; s += utfc_ptr2len(s)) {
if (!vim_isprintc(utf_ptr2char(s))) {
break;
}
cells += utf_ptr2cells(s);
}
// Currently must be empty, one or two display cells
if (s != endp || cells > 2) {
return FAIL;
}
if (cells < 1) {
return OK;
}
// Allocate one byte more if we need to pad up
// with a space.
size_t len = (size_t)(endp - text + ((cells == 1) ? 1 : 0));
*sign_text = xstrnsave(text, len);
if (cells == 1) {
STRCPY(*sign_text + len - 1, " ");
}
return OK;
}
VirtText parse_virt_text(Array chunks, Error *err, int *width)
{
VirtText virt_text = KV_INITIAL_VALUE;
int w = 0;
for (size_t i = 0; i < chunks.size; i++) {
if (chunks.items[i].type != kObjectTypeArray) {
api_set_error(err, kErrorTypeValidation, "Chunk is not an array");
goto free_exit;
}
Array chunk = chunks.items[i].data.array;
if (chunk.size == 0 || chunk.size > 2
|| chunk.items[0].type != kObjectTypeString) {
api_set_error(err, kErrorTypeValidation,
"Chunk is not an array with one or two strings");
goto free_exit;
}
String str = chunk.items[0].data.string;
int hl_id = 0;
if (chunk.size == 2) {
Object hl = chunk.items[1];
if (hl.type == kObjectTypeArray) {
Array arr = hl.data.array;
for (size_t j = 0; j < arr.size; j++) {
hl_id = object_to_hl_id(arr.items[j], "virt_text highlight", err);
if (ERROR_SET(err)) {
goto free_exit;
}
if (j < arr.size - 1) {
kv_push(virt_text, ((VirtTextChunk){ .text = NULL,
.hl_id = hl_id }));
}
}
} else {
hl_id = object_to_hl_id(hl, "virt_text highlight", err);
if (ERROR_SET(err)) {
goto free_exit;
}
}
}
char *text = transstr(str.size > 0 ? str.data : "", false); // allocates
w += (int)mb_string2cells(text);
kv_push(virt_text, ((VirtTextChunk){ .text = text, .hl_id = hl_id }));
}
*width = w;
return virt_text;
free_exit:
clear_virttext(&virt_text);
return virt_text;
}

View File

@ -2,6 +2,7 @@
#define NVIM_API_EXTMARK_H
#include "nvim/api/private/defs.h"
#include "nvim/decoration.h"
#include "nvim/map.h"
EXTERN Map(String, handle_T) namespace_ids INIT(= MAP_INIT);

509
src/nvim/api/options.c Normal file
View File

@ -0,0 +1,509 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "nvim/api/options.h"
#include "nvim/api/private/converter.h"
#include "nvim/api/private/helpers.h"
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/options.c.generated.h"
#endif
/// Gets the value of an option. The behavior of this function matches that of
/// |:set|: the local value of an option is returned if it exists; otherwise,
/// the global value is returned. Local values always correspond to the current
/// buffer or window. To get a buffer-local or window-local option for a
/// specific buffer or window, use |nvim_buf_get_option()| or
/// |nvim_win_get_option()|.
///
/// @param name Option name
/// @param opts Optional parameters
/// - scope: One of 'global' or 'local'. Analogous to
/// |:setglobal| and |:setlocal|, respectively.
/// @param[out] err Error details, if any
/// @return Option value
Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
FUNC_API_SINCE(9)
{
Object rv = OBJECT_INIT;
int scope = 0;
if (opts->scope.type == kObjectTypeString) {
if (!strcmp(opts->scope.data.string.data, "local")) {
scope = OPT_LOCAL;
} else if (!strcmp(opts->scope.data.string.data, "global")) {
scope = OPT_GLOBAL;
} else {
api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'");
goto end;
}
} else if (HAS_KEY(opts->scope)) {
api_set_error(err, kErrorTypeValidation, "invalid value for key: scope");
goto end;
}
long numval = 0;
char *stringval = NULL;
switch (get_option_value(name.data, &numval, &stringval, scope)) {
case 0:
rv = STRING_OBJ(cstr_as_string(stringval));
break;
case 1:
rv = INTEGER_OBJ(numval);
break;
case 2:
switch (numval) {
case 0:
case 1:
rv = BOOLEAN_OBJ(numval);
break;
default:
// Boolean options that return something other than 0 or 1 should return nil. Currently this
// only applies to 'autoread' which uses -1 as a local value to indicate "unset"
rv = NIL;
break;
}
break;
default:
api_set_error(err, kErrorTypeValidation, "unknown option '%s'", name.data);
goto end;
}
end:
return rv;
}
/// Sets the value of an option. The behavior of this function matches that of
/// |:set|: for global-local options, both the global and local value are set
/// unless otherwise specified with {scope}.
///
/// Note the options {win} and {buf} cannot be used together.
///
/// @param name Option name
/// @param value New option value
/// @param opts Optional parameters
/// - scope: One of 'global' or 'local'. Analogous to
/// |:setglobal| and |:setlocal|, respectively.
/// - win: |window-ID|. Used for setting window local option.
/// - buf: Buffer number. Used for setting buffer local option.
/// @param[out] err Error details, if any
void nvim_set_option_value(String name, Object value, Dict(option) *opts, Error *err)
FUNC_API_SINCE(9)
{
int scope = 0;
if (opts->scope.type == kObjectTypeString) {
if (!strcmp(opts->scope.data.string.data, "local")) {
scope = OPT_LOCAL;
} else if (!strcmp(opts->scope.data.string.data, "global")) {
scope = OPT_GLOBAL;
} else {
api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'");
return;
}
} else if (HAS_KEY(opts->scope)) {
api_set_error(err, kErrorTypeValidation, "invalid value for key: scope");
return;
}
int opt_type = SREQ_GLOBAL;
void *to = NULL;
if (opts->win.type == kObjectTypeInteger) {
opt_type = SREQ_WIN;
to = find_window_by_handle((int)opts->win.data.integer, err);
} else if (HAS_KEY(opts->win)) {
api_set_error(err, kErrorTypeValidation, "invalid value for key: win");
return;
}
if (opts->buf.type == kObjectTypeInteger) {
scope = OPT_LOCAL;
opt_type = SREQ_BUF;
to = find_buffer_by_handle((int)opts->buf.data.integer, err);
} else if (HAS_KEY(opts->buf)) {
api_set_error(err, kErrorTypeValidation, "invalid value for key: buf");
return;
}
if (HAS_KEY(opts->scope) && HAS_KEY(opts->buf)) {
api_set_error(err, kErrorTypeValidation, "scope and buf cannot be used together");
return;
}
if (HAS_KEY(opts->win) && HAS_KEY(opts->buf)) {
api_set_error(err, kErrorTypeValidation, "buf and win cannot be used together");
return;
}
long numval = 0;
char *stringval = NULL;
switch (value.type) {
case kObjectTypeInteger:
numval = value.data.integer;
break;
case kObjectTypeBoolean:
numval = value.data.boolean ? 1 : 0;
break;
case kObjectTypeString:
stringval = value.data.string.data;
break;
case kObjectTypeNil:
scope |= OPT_CLEAR;
break;
default:
api_set_error(err, kErrorTypeValidation, "invalid value for option");
return;
}
set_option_value_for(name.data, numval, stringval, scope, opt_type, to, err);
}
/// Gets the option information for all options.
///
/// The dictionary has the full option names as keys and option metadata
/// dictionaries as detailed at |nvim_get_option_info|.
///
/// @return dictionary of all options
Dictionary nvim_get_all_options_info(Error *err)
FUNC_API_SINCE(7)
{
return get_all_vimoptions();
}
/// Gets the option information for one option
///
/// Resulting dictionary has keys:
/// - name: Name of the option (like 'filetype')
/// - shortname: Shortened name of the option (like 'ft')
/// - type: type of option ("string", "number" or "boolean")
/// - default: The default value for the option
/// - was_set: Whether the option was set.
///
/// - last_set_sid: Last set script id (if any)
/// - last_set_linenr: line number where option was set
/// - last_set_chan: Channel where option was set (0 for local)
///
/// - scope: one of "global", "win", or "buf"
/// - global_local: whether win or buf option has a global value
///
/// - commalist: List of comma separated values
/// - flaglist: List of single char flags
///
///
/// @param name Option name
/// @param[out] err Error details, if any
/// @return Option Information
Dictionary nvim_get_option_info(String name, Error *err)
FUNC_API_SINCE(7)
{
return get_vimoption(name, err);
}
/// Sets the global value of an option.
///
/// @param channel_id
/// @param name Option name
/// @param value New option value
/// @param[out] err Error details, if any
void nvim_set_option(uint64_t channel_id, String name, Object value, Error *err)
FUNC_API_SINCE(1)
{
set_option_to(channel_id, NULL, SREQ_GLOBAL, name, value, err);
}
/// Gets the global value of an option.
///
/// @param name Option name
/// @param[out] err Error details, if any
/// @return Option value (global)
Object nvim_get_option(String name, Error *err)
FUNC_API_SINCE(1)
{
return get_option_from(NULL, SREQ_GLOBAL, name, err);
}
/// Gets a buffer option value
///
/// @param buffer Buffer handle, or 0 for current buffer
/// @param name Option name
/// @param[out] err Error details, if any
/// @return Option value
Object nvim_buf_get_option(Buffer buffer, String name, Error *err)
FUNC_API_SINCE(1)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
if (!buf) {
return (Object)OBJECT_INIT;
}
return get_option_from(buf, SREQ_BUF, name, err);
}
/// Sets a buffer option value. Passing 'nil' as value deletes the option (only
/// works if there's a global fallback)
///
/// @param channel_id
/// @param buffer Buffer handle, or 0 for current buffer
/// @param name Option name
/// @param value Option value
/// @param[out] err Error details, if any
void nvim_buf_set_option(uint64_t channel_id, Buffer buffer, String name, Object value, Error *err)
FUNC_API_SINCE(1)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
if (!buf) {
return;
}
set_option_to(channel_id, buf, SREQ_BUF, name, value, err);
}
/// Gets a window option value
///
/// @param window Window handle, or 0 for current window
/// @param name Option name
/// @param[out] err Error details, if any
/// @return Option value
Object nvim_win_get_option(Window window, String name, Error *err)
FUNC_API_SINCE(1)
{
win_T *win = find_window_by_handle(window, err);
if (!win) {
return (Object)OBJECT_INIT;
}
return get_option_from(win, SREQ_WIN, name, err);
}
/// Sets a window option value. Passing 'nil' as value deletes the option(only
/// works if there's a global fallback)
///
/// @param channel_id
/// @param window Window handle, or 0 for current window
/// @param name Option name
/// @param value Option value
/// @param[out] err Error details, if any
void nvim_win_set_option(uint64_t channel_id, Window window, String name, Object value, Error *err)
FUNC_API_SINCE(1)
{
win_T *win = find_window_by_handle(window, err);
if (!win) {
return;
}
set_option_to(channel_id, win, SREQ_WIN, name, value, 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)
{
Object rv = OBJECT_INIT;
if (name.size == 0) {
api_set_error(err, kErrorTypeValidation, "Empty option name");
return rv;
}
// Return values
int64_t numval;
char *stringval = NULL;
int flags = get_option_value_strict(name.data, &numval, &stringval,
type, from);
if (!flags) {
api_set_error(err, kErrorTypeValidation, "Invalid option name: '%s'",
name.data);
return rv;
}
if (flags & SOPT_BOOL) {
rv.type = kObjectTypeBoolean;
rv.data.boolean = numval ? true : false;
} else if (flags & SOPT_NUM) {
rv.type = kObjectTypeInteger;
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 {
api_set_error(err, kErrorTypeException,
"Failed to get value for option '%s'",
name.data);
}
} else {
api_set_error(err,
kErrorTypeException,
"Unknown type for option '%s'",
name.data);
}
return rv;
}
/// 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(uint64_t channel_id, void *to, int type, String name, Object value, Error *err)
{
if (name.size == 0) {
api_set_error(err, kErrorTypeValidation, "Empty option name");
return;
}
int flags = get_option_value_strict(name.data, NULL, NULL, type, to);
if (flags == 0) {
api_set_error(err, kErrorTypeValidation, "Invalid option name '%s'",
name.data);
return;
}
if (value.type == kObjectTypeNil) {
if (type == SREQ_GLOBAL) {
api_set_error(err, kErrorTypeException, "Cannot unset option '%s'",
name.data);
return;
} else if (!(flags & SOPT_GLOBAL)) {
api_set_error(err,
kErrorTypeException,
"Cannot unset option '%s' "
"because it doesn't have a global value",
name.data);
return;
} else {
unset_global_local_option(name.data, to);
return;
}
}
int numval = 0;
char *stringval = NULL;
if (flags & SOPT_BOOL) {
if (value.type != kObjectTypeBoolean) {
api_set_error(err,
kErrorTypeValidation,
"Option '%s' requires a Boolean value",
name.data);
return;
}
numval = value.data.boolean;
} else if (flags & SOPT_NUM) {
if (value.type != kObjectTypeInteger) {
api_set_error(err, kErrorTypeValidation,
"Option '%s' requires an integer value",
name.data);
return;
}
if (value.data.integer > INT_MAX || value.data.integer < INT_MIN) {
api_set_error(err, kErrorTypeValidation,
"Value for option '%s' is out of range",
name.data);
return;
}
numval = (int)value.data.integer;
} else {
if (value.type != kObjectTypeString) {
api_set_error(err, kErrorTypeValidation,
"Option '%s' requires a string value",
name.data);
return;
}
stringval = value.data.string.data;
}
WITH_SCRIPT_CONTEXT(channel_id, {
const int opt_flags = (type == SREQ_WIN && !(flags & SOPT_GLOBAL))
? 0 : (type == SREQ_GLOBAL)
? OPT_GLOBAL : OPT_LOCAL;
set_option_value_for(name.data, numval, stringval,
opt_flags, type, to, err);
});
}
void set_option_value_for(char *key, long numval, char *stringval, int opt_flags, int opt_type,
void *from, Error *err)
{
switchwin_T switchwin;
aco_save_T aco;
try_start();
switch (opt_type) {
case SREQ_WIN:
if (switch_win_noblock(&switchwin, (win_T *)from, win_find_tabpage((win_T *)from), true)
== FAIL) {
restore_win_noblock(&switchwin, true);
if (try_end(err)) {
return;
}
api_set_error(err,
kErrorTypeException,
"Problem while switching windows");
return;
}
set_option_value_err(key, numval, stringval, opt_flags, err);
restore_win_noblock(&switchwin, true);
break;
case SREQ_BUF:
aucmd_prepbuf(&aco, (buf_T *)from);
set_option_value_err(key, numval, stringval, opt_flags, err);
aucmd_restbuf(&aco);
break;
case SREQ_GLOBAL:
set_option_value_err(key, numval, stringval, opt_flags, err);
break;
}
if (ERROR_SET(err)) {
return;
}
try_end(err);
}
static void set_option_value_err(char *key, long numval, char *stringval, int opt_flags, Error *err)
{
char *errmsg;
if ((errmsg = set_option_value(key, numval, stringval, opt_flags))) {
if (try_end(err)) {
return;
}
api_set_error(err, kErrorTypeException, "%s", errmsg);
}
}

9
src/nvim/api/options.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef NVIM_API_OPTIONS_H
#define NVIM_API_OPTIONS_H
#include "nvim/api/private/defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/options.h.generated.h"
#endif
#endif // NVIM_API_OPTIONS_H

View File

@ -23,7 +23,9 @@
// ===========================================================================
#include "nvim/api/autocmd.h"
#include "nvim/api/buffer.h"
#include "nvim/api/command.h"
#include "nvim/api/extmark.h"
#include "nvim/api/options.h"
#include "nvim/api/tabpage.h"
#include "nvim/api/ui.h"
#include "nvim/api/vim.h"

View File

@ -16,7 +16,6 @@
#include "nvim/assert.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/decoration.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
#include "nvim/ex_cmds_defs.h"
@ -32,8 +31,6 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/msgpack_rpc/helpers.h"
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/ui.h"
#include "nvim/version.h"
#include "nvim/vim.h"
@ -262,151 +259,6 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retva
return rv;
}
/// 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)
{
Object rv = OBJECT_INIT;
if (name.size == 0) {
api_set_error(err, kErrorTypeValidation, "Empty option name");
return rv;
}
// Return values
int64_t numval;
char *stringval = NULL;
int flags = get_option_value_strict(name.data, &numval, &stringval,
type, from);
if (!flags) {
api_set_error(err, kErrorTypeValidation, "Invalid option name: '%s'",
name.data);
return rv;
}
if (flags & SOPT_BOOL) {
rv.type = kObjectTypeBoolean;
rv.data.boolean = numval ? true : false;
} else if (flags & SOPT_NUM) {
rv.type = kObjectTypeInteger;
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 {
api_set_error(err, kErrorTypeException,
"Failed to get value for option '%s'",
name.data);
}
} else {
api_set_error(err,
kErrorTypeException,
"Unknown type for option '%s'",
name.data);
}
return rv;
}
/// 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(uint64_t channel_id, void *to, int type, String name, Object value, Error *err)
{
if (name.size == 0) {
api_set_error(err, kErrorTypeValidation, "Empty option name");
return;
}
int flags = get_option_value_strict(name.data, NULL, NULL, type, to);
if (flags == 0) {
api_set_error(err, kErrorTypeValidation, "Invalid option name '%s'",
name.data);
return;
}
if (value.type == kObjectTypeNil) {
if (type == SREQ_GLOBAL) {
api_set_error(err, kErrorTypeException, "Cannot unset option '%s'",
name.data);
return;
} else if (!(flags & SOPT_GLOBAL)) {
api_set_error(err,
kErrorTypeException,
"Cannot unset option '%s' "
"because it doesn't have a global value",
name.data);
return;
} else {
unset_global_local_option(name.data, to);
return;
}
}
int numval = 0;
char *stringval = NULL;
if (flags & SOPT_BOOL) {
if (value.type != kObjectTypeBoolean) {
api_set_error(err,
kErrorTypeValidation,
"Option '%s' requires a Boolean value",
name.data);
return;
}
numval = value.data.boolean;
} else if (flags & SOPT_NUM) {
if (value.type != kObjectTypeInteger) {
api_set_error(err, kErrorTypeValidation,
"Option '%s' requires an integer value",
name.data);
return;
}
if (value.data.integer > INT_MAX || value.data.integer < INT_MIN) {
api_set_error(err, kErrorTypeValidation,
"Value for option '%s' is out of range",
name.data);
return;
}
numval = (int)value.data.integer;
} else {
if (value.type != kObjectTypeString) {
api_set_error(err, kErrorTypeValidation,
"Option '%s' requires a string value",
name.data);
return;
}
stringval = value.data.string.data;
}
WITH_SCRIPT_CONTEXT(channel_id, {
const int opt_flags = (type == SREQ_WIN && !(flags & SOPT_GLOBAL))
? 0 : (type == SREQ_GLOBAL)
? OPT_GLOBAL : OPT_LOCAL;
set_option_value_for(name.data, numval, stringval,
opt_flags, type, to, err);
});
}
buf_T *find_buffer_by_handle(Buffer buffer, Error *err)
{
if (buffer == 0) {
@ -1035,59 +887,6 @@ Object copy_object(Object obj)
}
}
void set_option_value_for(char *key, long numval, char *stringval, int opt_flags, int opt_type,
void *from, Error *err)
{
switchwin_T switchwin;
aco_save_T aco;
try_start();
switch (opt_type) {
case SREQ_WIN:
if (switch_win_noblock(&switchwin, (win_T *)from, win_find_tabpage((win_T *)from), true)
== FAIL) {
restore_win_noblock(&switchwin, true);
if (try_end(err)) {
return;
}
api_set_error(err,
kErrorTypeException,
"Problem while switching windows");
return;
}
set_option_value_err(key, numval, stringval, opt_flags, err);
restore_win_noblock(&switchwin, true);
break;
case SREQ_BUF:
aucmd_prepbuf(&aco, (buf_T *)from);
set_option_value_err(key, numval, stringval, opt_flags, err);
aucmd_restbuf(&aco);
break;
case SREQ_GLOBAL:
set_option_value_err(key, numval, stringval, opt_flags, err);
break;
}
if (ERROR_SET(err)) {
return;
}
try_end(err);
}
static void set_option_value_err(char *key, long numval, char *stringval, int opt_flags, Error *err)
{
char *errmsg;
if ((errmsg = set_option_value(key, numval, stringval, opt_flags))) {
if (try_end(err)) {
return;
}
api_set_error(err, kErrorTypeException, "%s", errmsg);
}
}
void api_set_error(Error *err, ErrorType errType, const char *format, ...)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PRINTF(3, 4)
{
@ -1160,122 +959,6 @@ ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf, bool from_lua)
return mappings;
}
/// Gets the line and column of an extmark.
///
/// Extmarks may be queried by position, name or even special names
/// in the future such as "cursor".
///
/// @param[out] lnum extmark line
/// @param[out] colnr extmark column
///
/// @return true if the extmark was found, else false
bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, int
*row, colnr_T *col, Error *err)
{
// Check if it is mark id
if (obj.type == kObjectTypeInteger) {
Integer id = obj.data.integer;
if (id == 0) {
*row = 0;
*col = 0;
return true;
} else if (id == -1) {
*row = MAXLNUM;
*col = MAXCOL;
return true;
} else if (id < 0) {
api_set_error(err, kErrorTypeValidation, "Mark id must be positive");
return false;
}
ExtmarkInfo extmark = extmark_from_id(buf, (uint32_t)ns_id, (uint32_t)id);
if (extmark.row >= 0) {
*row = extmark.row;
*col = extmark.col;
return true;
} else {
api_set_error(err, kErrorTypeValidation, "No mark with requested id");
return false;
}
// Check if it is a position
} else if (obj.type == kObjectTypeArray) {
Array pos = obj.data.array;
if (pos.size != 2
|| pos.items[0].type != kObjectTypeInteger
|| pos.items[1].type != kObjectTypeInteger) {
api_set_error(err, kErrorTypeValidation,
"Position must have 2 integer elements");
return false;
}
Integer pos_row = pos.items[0].data.integer;
Integer pos_col = pos.items[1].data.integer;
*row = (int)(pos_row >= 0 ? pos_row : MAXLNUM);
*col = (colnr_T)(pos_col >= 0 ? pos_col : MAXCOL);
return true;
} else {
api_set_error(err, kErrorTypeValidation,
"Position must be a mark id Integer or position Array");
return false;
}
}
VirtText parse_virt_text(Array chunks, Error *err, int *width)
{
VirtText virt_text = KV_INITIAL_VALUE;
int w = 0;
for (size_t i = 0; i < chunks.size; i++) {
if (chunks.items[i].type != kObjectTypeArray) {
api_set_error(err, kErrorTypeValidation, "Chunk is not an array");
goto free_exit;
}
Array chunk = chunks.items[i].data.array;
if (chunk.size == 0 || chunk.size > 2
|| chunk.items[0].type != kObjectTypeString) {
api_set_error(err, kErrorTypeValidation,
"Chunk is not an array with one or two strings");
goto free_exit;
}
String str = chunk.items[0].data.string;
int hl_id = 0;
if (chunk.size == 2) {
Object hl = chunk.items[1];
if (hl.type == kObjectTypeArray) {
Array arr = hl.data.array;
for (size_t j = 0; j < arr.size; j++) {
hl_id = object_to_hl_id(arr.items[j], "virt_text highlight", err);
if (ERROR_SET(err)) {
goto free_exit;
}
if (j < arr.size - 1) {
kv_push(virt_text, ((VirtTextChunk){ .text = NULL,
.hl_id = hl_id }));
}
}
} else {
hl_id = object_to_hl_id(hl, "virt_text highlight", err);
if (ERROR_SET(err)) {
goto free_exit;
}
}
}
char *text = transstr(str.size > 0 ? str.data : "", false); // allocates
w += (int)mb_string2cells(text);
kv_push(virt_text, ((VirtTextChunk){ .text = text, .hl_id = hl_id }));
}
*width = w;
return virt_text;
free_exit:
clear_virttext(&virt_text);
return virt_text;
}
/// Force obj to bool.
/// If it fails, returns false and sets err
/// @param obj The object to coerce to a boolean
@ -1424,212 +1107,6 @@ const char *get_default_stl_hl(win_T *wp, bool use_winbar)
}
}
void create_user_command(String name, Object command, Dict(user_command) *opts, int flags,
Error *err)
{
uint32_t argt = 0;
long def = -1;
cmd_addr_T addr_type_arg = ADDR_NONE;
int compl = EXPAND_NOTHING;
char *compl_arg = NULL;
char *rep = NULL;
LuaRef luaref = LUA_NOREF;
LuaRef compl_luaref = LUA_NOREF;
LuaRef preview_luaref = LUA_NOREF;
if (!uc_validate_name(name.data)) {
api_set_error(err, kErrorTypeValidation, "Invalid command name");
goto err;
}
if (mb_islower(name.data[0])) {
api_set_error(err, kErrorTypeValidation, "'name' must begin with an uppercase letter");
goto err;
}
if (HAS_KEY(opts->range) && HAS_KEY(opts->count)) {
api_set_error(err, kErrorTypeValidation, "'range' and 'count' are mutually exclusive");
goto err;
}
if (opts->nargs.type == kObjectTypeInteger) {
switch (opts->nargs.data.integer) {
case 0:
// Default value, nothing to do
break;
case 1:
argt |= EX_EXTRA | EX_NOSPC | EX_NEEDARG;
break;
default:
api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
goto err;
}
} else if (opts->nargs.type == kObjectTypeString) {
if (opts->nargs.data.string.size > 1) {
api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
goto err;
}
switch (opts->nargs.data.string.data[0]) {
case '*':
argt |= EX_EXTRA;
break;
case '?':
argt |= EX_EXTRA | EX_NOSPC;
break;
case '+':
argt |= EX_EXTRA | EX_NEEDARG;
break;
default:
api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
goto err;
}
} else if (HAS_KEY(opts->nargs)) {
api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
goto err;
}
if (HAS_KEY(opts->complete) && !argt) {
api_set_error(err, kErrorTypeValidation, "'complete' used without 'nargs'");
goto err;
}
if (opts->range.type == kObjectTypeBoolean) {
if (opts->range.data.boolean) {
argt |= EX_RANGE;
addr_type_arg = ADDR_LINES;
}
} else if (opts->range.type == kObjectTypeString) {
if (opts->range.data.string.data[0] == '%' && opts->range.data.string.size == 1) {
argt |= EX_RANGE | EX_DFLALL;
addr_type_arg = ADDR_LINES;
} else {
api_set_error(err, kErrorTypeValidation, "Invalid value for 'range'");
goto err;
}
} else if (opts->range.type == kObjectTypeInteger) {
argt |= EX_RANGE | EX_ZEROR;
def = opts->range.data.integer;
addr_type_arg = ADDR_LINES;
} else if (HAS_KEY(opts->range)) {
api_set_error(err, kErrorTypeValidation, "Invalid value for 'range'");
goto err;
}
if (opts->count.type == kObjectTypeBoolean) {
if (opts->count.data.boolean) {
argt |= EX_COUNT | EX_ZEROR | EX_RANGE;
addr_type_arg = ADDR_OTHER;
def = 0;
}
} else if (opts->count.type == kObjectTypeInteger) {
argt |= EX_COUNT | EX_ZEROR | EX_RANGE;
addr_type_arg = ADDR_OTHER;
def = opts->count.data.integer;
} else if (HAS_KEY(opts->count)) {
api_set_error(err, kErrorTypeValidation, "Invalid value for 'count'");
goto err;
}
if (opts->addr.type == kObjectTypeString) {
if (parse_addr_type_arg(opts->addr.data.string.data, (int)opts->addr.data.string.size,
&addr_type_arg) != OK) {
api_set_error(err, kErrorTypeValidation, "Invalid value for 'addr'");
goto err;
}
if (addr_type_arg != ADDR_LINES) {
argt |= EX_ZEROR;
}
} else if (HAS_KEY(opts->addr)) {
api_set_error(err, kErrorTypeValidation, "Invalid value for 'addr'");
goto err;
}
if (api_object_to_bool(opts->bang, "bang", false, err)) {
argt |= EX_BANG;
} else if (ERROR_SET(err)) {
goto err;
}
if (api_object_to_bool(opts->bar, "bar", false, err)) {
argt |= EX_TRLBAR;
} else if (ERROR_SET(err)) {
goto err;
}
if (api_object_to_bool(opts->register_, "register", false, err)) {
argt |= EX_REGSTR;
} else if (ERROR_SET(err)) {
goto err;
}
if (api_object_to_bool(opts->keepscript, "keepscript", false, err)) {
argt |= EX_KEEPSCRIPT;
} else if (ERROR_SET(err)) {
goto err;
}
bool force = api_object_to_bool(opts->force, "force", true, err);
if (ERROR_SET(err)) {
goto err;
}
if (opts->complete.type == kObjectTypeLuaRef) {
compl = EXPAND_USER_LUA;
compl_luaref = api_new_luaref(opts->complete.data.luaref);
} else if (opts->complete.type == kObjectTypeString) {
if (parse_compl_arg(opts->complete.data.string.data,
(int)opts->complete.data.string.size, &compl, &argt,
&compl_arg) != OK) {
api_set_error(err, kErrorTypeValidation, "Invalid value for 'complete'");
goto err;
}
} else if (HAS_KEY(opts->complete)) {
api_set_error(err, kErrorTypeValidation, "Invalid value for 'complete'");
goto err;
}
if (opts->preview.type == kObjectTypeLuaRef) {
argt |= EX_PREVIEW;
preview_luaref = api_new_luaref(opts->preview.data.luaref);
} else if (HAS_KEY(opts->preview)) {
api_set_error(err, kErrorTypeValidation, "Invalid value for 'preview'");
goto err;
}
switch (command.type) {
case kObjectTypeLuaRef:
luaref = api_new_luaref(command.data.luaref);
if (opts->desc.type == kObjectTypeString) {
rep = opts->desc.data.string.data;
} else {
snprintf((char *)IObuff, IOSIZE, "<Lua function %d>", luaref);
rep = (char *)IObuff;
}
break;
case kObjectTypeString:
rep = command.data.string.data;
break;
default:
api_set_error(err, kErrorTypeValidation, "'command' must be a string or Lua function");
goto err;
}
if (uc_add_command(name.data, name.size, rep, argt, def, flags, compl, compl_arg, compl_luaref,
preview_luaref, addr_type_arg, luaref, force) != OK) {
api_set_error(err, kErrorTypeException, "Failed to create user command");
// Do not goto err, since uc_add_command now owns luaref, compl_luaref, and compl_arg
}
return;
err:
NLUA_CLEAR_REF(luaref);
NLUA_CLEAR_REF(compl_luaref);
xfree(compl_arg);
}
int find_sid(uint64_t channel_id)
{
switch (channel_id) {
@ -1659,172 +1136,3 @@ sctx_T api_set_sctx(uint64_t channel_id)
}
return old_current_sctx;
}
// adapted from sign.c:sign_define_init_text.
// TODO(lewis6991): Consider merging
int init_sign_text(char **sign_text, char *text)
{
char *s;
char *endp = text + (int)STRLEN(text);
// Count cells and check for non-printable chars
int cells = 0;
for (s = text; s < endp; s += utfc_ptr2len(s)) {
if (!vim_isprintc(utf_ptr2char(s))) {
break;
}
cells += utf_ptr2cells(s);
}
// Currently must be empty, one or two display cells
if (s != endp || cells > 2) {
return FAIL;
}
if (cells < 1) {
return OK;
}
// Allocate one byte more if we need to pad up
// with a space.
size_t len = (size_t)(endp - text + ((cells == 1) ? 1 : 0));
*sign_text = xstrnsave(text, len);
if (cells == 1) {
STRCPY(*sign_text + len - 1, " ");
}
return OK;
}
/// Check if a string contains only whitespace characters.
bool string_iswhite(String str)
{
for (size_t i = 0; i < str.size; i++) {
if (!ascii_iswhite(str.data[i])) {
// Found a non-whitespace character
return false;
} else if (str.data[i] == NUL) {
// Terminate at first occurence of a NUL character
break;
}
}
return true;
}
/// Build cmdline string for command, used by `nvim_cmd()`.
///
/// @return OK or FAIL.
void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdinfo, char **args,
size_t argc)
{
StringBuilder cmdline = KV_INITIAL_VALUE;
// Add command modifiers
if (cmdinfo->cmdmod.tab != 0) {
kv_printf(cmdline, "%dtab ", cmdinfo->cmdmod.tab - 1);
}
if (cmdinfo->verbose != -1) {
kv_printf(cmdline, "%ldverbose ", cmdinfo->verbose);
}
if (cmdinfo->emsg_silent) {
kv_concat(cmdline, "silent! ");
} else if (cmdinfo->silent) {
kv_concat(cmdline, "silent ");
}
switch (cmdinfo->cmdmod.split & (WSP_ABOVE | WSP_BELOW | WSP_TOP | WSP_BOT)) {
case WSP_ABOVE:
kv_concat(cmdline, "aboveleft ");
break;
case WSP_BELOW:
kv_concat(cmdline, "belowright ");
break;
case WSP_TOP:
kv_concat(cmdline, "topleft ");
break;
case WSP_BOT:
kv_concat(cmdline, "botright ");
break;
default:
break;
}
#define CMDLINE_APPEND_IF(cond, str) \
do { \
if (cond) { \
kv_concat(cmdline, str); \
} \
} while (0)
CMDLINE_APPEND_IF(cmdinfo->cmdmod.split & WSP_VERT, "vertical ");
CMDLINE_APPEND_IF(cmdinfo->sandbox, "sandbox ");
CMDLINE_APPEND_IF(cmdinfo->noautocmd, "noautocmd ");
CMDLINE_APPEND_IF(cmdinfo->cmdmod.browse, "browse ");
CMDLINE_APPEND_IF(cmdinfo->cmdmod.confirm, "confirm ");
CMDLINE_APPEND_IF(cmdinfo->cmdmod.hide, "hide ");
CMDLINE_APPEND_IF(cmdinfo->cmdmod.keepalt, "keepalt ");
CMDLINE_APPEND_IF(cmdinfo->cmdmod.keepjumps, "keepjumps ");
CMDLINE_APPEND_IF(cmdinfo->cmdmod.keepmarks, "keepmarks ");
CMDLINE_APPEND_IF(cmdinfo->cmdmod.keeppatterns, "keeppatterns ");
CMDLINE_APPEND_IF(cmdinfo->cmdmod.lockmarks, "lockmarks ");
CMDLINE_APPEND_IF(cmdinfo->cmdmod.noswapfile, "noswapfile ");
#undef CMDLINE_APPEND_IF
// Command range / count.
if (eap->argt & EX_RANGE) {
if (eap->addr_count == 1) {
kv_printf(cmdline, "%" PRIdLINENR, eap->line2);
} else if (eap->addr_count > 1) {
kv_printf(cmdline, "%" PRIdLINENR ",%" PRIdLINENR, eap->line1, eap->line2);
eap->addr_count = 2; // Make sure address count is not greater than 2
}
}
// Keep the index of the position where command name starts, so eap->cmd can point to it.
size_t cmdname_idx = cmdline.size;
kv_printf(cmdline, "%s", eap->cmd);
// Command bang.
if (eap->argt & EX_BANG && eap->forceit) {
kv_printf(cmdline, "!");
}
// Command register.
if (eap->argt & EX_REGSTR && eap->regname) {
kv_printf(cmdline, " %c", eap->regname);
}
// Iterate through each argument and store the starting index and length of each argument
size_t *argidx = xcalloc(argc, sizeof(size_t));
eap->argc = argc;
eap->arglens = xcalloc(argc, sizeof(size_t));
for (size_t i = 0; i < argc; i++) {
argidx[i] = cmdline.size + 1; // add 1 to account for the space.
eap->arglens[i] = STRLEN(args[i]);
kv_printf(cmdline, " %s", args[i]);
}
// Now that all the arguments are appended, use the command index and argument indices to set the
// values of eap->cmd, eap->arg and eap->args.
eap->cmd = cmdline.items + cmdname_idx;
eap->args = xcalloc(argc, sizeof(char *));
for (size_t i = 0; i < argc; i++) {
eap->args[i] = cmdline.items + argidx[i];
}
// If there isn't an argument, make eap->arg point to end of cmdline.
eap->arg = argc > 0 ? eap->args[0] : cmdline.items + cmdline.size;
// Finally, make cmdlinep point to the cmdline string.
*cmdlinep = cmdline.items;
xfree(argidx);
// Replace, :make and :grep with 'makeprg' and 'grepprg'.
char *p = replace_makeprg(eap, eap->arg, cmdlinep);
if (p != eap->arg) {
// If replace_makeprg modified the cmdline string, correct the argument pointers.
assert(argc == 1);
eap->arg = p;
eap->args[0] = p;
}
}

View File

@ -708,220 +708,6 @@ void nvim_set_vvar(String name, Object value, Error *err)
dict_set_var(&vimvardict, name, value, false, false, err);
}
/// Gets the global value of an option.
///
/// @param name Option name
/// @param[out] err Error details, if any
/// @return Option value (global)
Object nvim_get_option(String name, Error *err)
FUNC_API_SINCE(1)
{
return get_option_from(NULL, SREQ_GLOBAL, name, err);
}
/// Gets the value of an option. The behavior of this function matches that of
/// |:set|: the local value of an option is returned if it exists; otherwise,
/// the global value is returned. Local values always correspond to the current
/// buffer or window. To get a buffer-local or window-local option for a
/// specific buffer or window, use |nvim_buf_get_option()| or
/// |nvim_win_get_option()|.
///
/// @param name Option name
/// @param opts Optional parameters
/// - scope: One of 'global' or 'local'. Analogous to
/// |:setglobal| and |:setlocal|, respectively.
/// @param[out] err Error details, if any
/// @return Option value
Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
FUNC_API_SINCE(9)
{
Object rv = OBJECT_INIT;
int scope = 0;
if (opts->scope.type == kObjectTypeString) {
if (!strcmp(opts->scope.data.string.data, "local")) {
scope = OPT_LOCAL;
} else if (!strcmp(opts->scope.data.string.data, "global")) {
scope = OPT_GLOBAL;
} else {
api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'");
goto end;
}
} else if (HAS_KEY(opts->scope)) {
api_set_error(err, kErrorTypeValidation, "invalid value for key: scope");
goto end;
}
long numval = 0;
char *stringval = NULL;
switch (get_option_value(name.data, &numval, &stringval, scope)) {
case 0:
rv = STRING_OBJ(cstr_as_string(stringval));
break;
case 1:
rv = INTEGER_OBJ(numval);
break;
case 2:
switch (numval) {
case 0:
case 1:
rv = BOOLEAN_OBJ(numval);
break;
default:
// Boolean options that return something other than 0 or 1 should return nil. Currently this
// only applies to 'autoread' which uses -1 as a local value to indicate "unset"
rv = NIL;
break;
}
break;
default:
api_set_error(err, kErrorTypeValidation, "unknown option '%s'", name.data);
goto end;
}
end:
return rv;
}
/// Sets the value of an option. The behavior of this function matches that of
/// |:set|: for global-local options, both the global and local value are set
/// unless otherwise specified with {scope}.
///
/// Note the options {win} and {buf} cannot be used together.
///
/// @param name Option name
/// @param value New option value
/// @param opts Optional parameters
/// - scope: One of 'global' or 'local'. Analogous to
/// |:setglobal| and |:setlocal|, respectively.
/// - win: |window-ID|. Used for setting window local option.
/// - buf: Buffer number. Used for setting buffer local option.
/// @param[out] err Error details, if any
void nvim_set_option_value(String name, Object value, Dict(option) *opts, Error *err)
FUNC_API_SINCE(9)
{
int scope = 0;
if (opts->scope.type == kObjectTypeString) {
if (!strcmp(opts->scope.data.string.data, "local")) {
scope = OPT_LOCAL;
} else if (!strcmp(opts->scope.data.string.data, "global")) {
scope = OPT_GLOBAL;
} else {
api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'");
return;
}
} else if (HAS_KEY(opts->scope)) {
api_set_error(err, kErrorTypeValidation, "invalid value for key: scope");
return;
}
int opt_type = SREQ_GLOBAL;
void *to = NULL;
if (opts->win.type == kObjectTypeInteger) {
opt_type = SREQ_WIN;
to = find_window_by_handle((int)opts->win.data.integer, err);
} else if (HAS_KEY(opts->win)) {
api_set_error(err, kErrorTypeValidation, "invalid value for key: win");
return;
}
if (opts->buf.type == kObjectTypeInteger) {
scope = OPT_LOCAL;
opt_type = SREQ_BUF;
to = find_buffer_by_handle((int)opts->buf.data.integer, err);
} else if (HAS_KEY(opts->buf)) {
api_set_error(err, kErrorTypeValidation, "invalid value for key: buf");
return;
}
if (HAS_KEY(opts->scope) && HAS_KEY(opts->buf)) {
api_set_error(err, kErrorTypeValidation, "scope and buf cannot be used together");
return;
}
if (HAS_KEY(opts->win) && HAS_KEY(opts->buf)) {
api_set_error(err, kErrorTypeValidation, "buf and win cannot be used together");
return;
}
long numval = 0;
char *stringval = NULL;
switch (value.type) {
case kObjectTypeInteger:
numval = value.data.integer;
break;
case kObjectTypeBoolean:
numval = value.data.boolean ? 1 : 0;
break;
case kObjectTypeString:
stringval = value.data.string.data;
break;
case kObjectTypeNil:
scope |= OPT_CLEAR;
break;
default:
api_set_error(err, kErrorTypeValidation, "invalid value for option");
return;
}
set_option_value_for(name.data, numval, stringval, scope, opt_type, to, err);
}
/// Gets the option information for all options.
///
/// The dictionary has the full option names as keys and option metadata
/// dictionaries as detailed at |nvim_get_option_info|.
///
/// @return dictionary of all options
Dictionary nvim_get_all_options_info(Error *err)
FUNC_API_SINCE(7)
{
return get_all_vimoptions();
}
/// Gets the option information for one option
///
/// Resulting dictionary has keys:
/// - name: Name of the option (like 'filetype')
/// - shortname: Shortened name of the option (like 'ft')
/// - type: type of option ("string", "number" or "boolean")
/// - default: The default value for the option
/// - was_set: Whether the option was set.
///
/// - last_set_sid: Last set script id (if any)
/// - last_set_linenr: line number where option was set
/// - last_set_chan: Channel where option was set (0 for local)
///
/// - scope: one of "global", "win", or "buf"
/// - global_local: whether win or buf option has a global value
///
/// - commalist: List of comma separated values
/// - flaglist: List of single char flags
///
///
/// @param name Option name
/// @param[out] err Error details, if any
/// @return Option Information
Dictionary nvim_get_option_info(String name, Error *err)
FUNC_API_SINCE(7)
{
return get_vimoption(name, err);
}
/// Sets the global value of an option.
///
/// @param channel_id
/// @param name Option name
/// @param value New option value
/// @param[out] err Error details, if any
void nvim_set_option(uint64_t channel_id, String name, Object value, Error *err)
FUNC_API_SINCE(1)
{
set_option_to(channel_id, NULL, SREQ_GLOBAL, name, value, err);
}
/// Echo a message.
///
/// @param chunks A list of [text, hl_group] arrays, each representing a
@ -1679,21 +1465,6 @@ void nvim_del_keymap(uint64_t channel_id, String mode, String lhs, Error *err)
nvim_buf_del_keymap(channel_id, -1, mode, lhs, err);
}
/// Gets a map of global (non-buffer-local) Ex commands.
///
/// Currently only |user-commands| are supported, not builtin Ex commands.
///
/// @param opts Optional parameters. Currently only supports
/// {"builtin":false}
/// @param[out] err Error details, if any.
///
/// @returns Map of maps describing commands.
Dictionary nvim_get_commands(Dict(get_commands) *opts, Error *err)
FUNC_API_SINCE(4)
{
return nvim_buf_get_commands(-1, opts, err);
}
/// Returns a 2-tuple (Array), where item 0 is the current channel id and item
/// 1 is the |api-metadata| map (Dictionary).
///
@ -2483,58 +2254,3 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
return result;
}
/// Create a new user command |user-commands|
///
/// {name} is the name of the new command. The name must begin with an uppercase letter.
///
/// {command} is the replacement text or Lua function to execute.
///
/// Example:
/// <pre>
/// :call nvim_create_user_command('SayHello', 'echo "Hello world!"', {})
/// :SayHello
/// Hello world!
/// </pre>
///
/// @param name Name of the new user command. Must begin with an uppercase letter.
/// @param command Replacement command to execute when this user command is executed. When called
/// from Lua, the command can also be a Lua function. The function is called with a
/// single table argument that contains the following keys:
/// - args: (string) The args passed to the command, if any |<args>|
/// - fargs: (table) The args split by unescaped whitespace (when more than one
/// argument is allowed), if any |<f-args>|
/// - bang: (boolean) "true" if the command was executed with a ! modifier |<bang>|
/// - line1: (number) The starting line of the command range |<line1>|
/// - line2: (number) The final line of the command range |<line2>|
/// - range: (number) The number of items in the command range: 0, 1, or 2 |<range>|
/// - count: (number) Any count supplied |<count>|
/// - reg: (string) The optional register, if specified |<reg>|
/// - mods: (string) Command modifiers, if any |<mods>|
/// - smods: (table) Command modifiers in a structured format. Has the same
/// structure as the "mods" key of |nvim_parse_cmd()|.
/// @param opts Optional command attributes. See |command-attributes| for more details. To use
/// boolean attributes (such as |:command-bang| or |:command-bar|) set the value to
/// "true". In addition to the string options listed in |:command-complete|, the
/// "complete" key also accepts a Lua function which works like the "customlist"
/// completion mode |:command-completion-customlist|. Additional parameters:
/// - desc: (string) Used for listing the command when a Lua function is used for
/// {command}.
/// - force: (boolean, default true) Override any previous definition.
/// - preview: (function) Preview callback for 'inccommand' |:command-preview|
/// @param[out] err Error details, if any.
void nvim_create_user_command(String name, Object command, Dict(user_command) *opts, Error *err)
FUNC_API_SINCE(9)
{
create_user_command(name, command, opts, 0, err);
}
/// Delete a user-defined command.
///
/// @param name Name of the command to delete.
/// @param[out] err Error details, if any.
void nvim_del_user_command(String name, Error *err)
FUNC_API_SINCE(9)
{
nvim_buf_del_user_command(-1, name, err);
}

View File

@ -26,8 +26,6 @@
# include "api/vimscript.c.generated.h"
#endif
#define IS_USER_CMDIDX(idx) ((int)(idx) < 0)
/// Executes Vimscript (multiline block of Ex commands), like anonymous
/// |:source|.
///
@ -747,618 +745,3 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E
viml_parser_destroy(&pstate);
return ret;
}
/// Parse command line.
///
/// Doesn't check the validity of command arguments.
///
/// @param str Command line string to parse. Cannot contain "\n".
/// @param opts Optional parameters. Reserved for future use.
/// @param[out] err Error details, if any.
/// @return Dictionary containing command information, with these keys:
/// - cmd: (string) Command name.
/// - range: (array) Command <range>. Can have 0-2 elements depending on how many items the
/// range contains. Has no elements if command doesn't accept a range or if
/// no range was specified, one element if only a single range item was
/// specified and two elements if both range items were specified.
/// - count: (number) Any |<count>| that was supplied to the command. -1 if command cannot
/// take a count.
/// - reg: (number) The optional command |<register>|, if specified. Empty string if not
/// specified or if command cannot take a register.
/// - bang: (boolean) Whether command contains a |<bang>| (!) modifier.
/// - args: (array) Command arguments.
/// - addr: (string) Value of |:command-addr|. Uses short name.
/// - nargs: (string) Value of |:command-nargs|.
/// - nextcmd: (string) Next command if there are multiple commands separated by a |:bar|.
/// Empty if there isn't a next command.
/// - magic: (dictionary) Which characters have special meaning in the command arguments.
/// - file: (boolean) The command expands filenames. Which means characters such as "%",
/// "#" and wildcards are expanded.
/// - bar: (boolean) The "|" character is treated as a command separator and the double
/// quote character (\") is treated as the start of a comment.
/// - mods: (dictionary) |:command-modifiers|.
/// - silent: (boolean) |:silent|.
/// - emsg_silent: (boolean) |:silent!|.
/// - sandbox: (boolean) |:sandbox|.
/// - noautocmd: (boolean) |:noautocmd|.
/// - browse: (boolean) |:browse|.
/// - confirm: (boolean) |:confirm|.
/// - hide: (boolean) |:hide|.
/// - keepalt: (boolean) |:keepalt|.
/// - keepjumps: (boolean) |:keepjumps|.
/// - keepmarks: (boolean) |:keepmarks|.
/// - keeppatterns: (boolean) |:keeppatterns|.
/// - lockmarks: (boolean) |:lockmarks|.
/// - noswapfile: (boolean) |:noswapfile|.
/// - tab: (integer) |:tab|.
/// - verbose: (integer) |:verbose|. -1 when omitted.
/// - vertical: (boolean) |:vertical|.
/// - split: (string) Split modifier string, is an empty string when there's no split
/// modifier. If there is a split modifier it can be one of:
/// - "aboveleft": |:aboveleft|.
/// - "belowright": |:belowright|.
/// - "topleft": |:topleft|.
/// - "botright": |:botright|.
Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
FUNC_API_SINCE(10) FUNC_API_FAST
{
Dictionary result = ARRAY_DICT_INIT;
if (opts.size > 0) {
api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
return result;
}
// Parse command line
exarg_T ea;
CmdParseInfo cmdinfo;
char *cmdline = string_to_cstr(str);
char *errormsg = NULL;
if (!parse_cmdline(cmdline, &ea, &cmdinfo, &errormsg)) {
if (errormsg != NULL) {
api_set_error(err, kErrorTypeException, "Error while parsing command line: %s", errormsg);
} else {
api_set_error(err, kErrorTypeException, "Error while parsing command line");
}
goto end;
}
// Parse arguments
Array args = ARRAY_DICT_INIT;
size_t length = STRLEN(ea.arg);
// For nargs = 1 or '?', pass the entire argument list as a single argument,
// otherwise split arguments by whitespace.
if (ea.argt & EX_NOSPC) {
if (*ea.arg != NUL) {
ADD(args, STRING_OBJ(cstrn_to_string((char *)ea.arg, length)));
}
} else {
size_t end = 0;
size_t len = 0;
char *buf = xcalloc(length, sizeof(char));
bool done = false;
while (!done) {
done = uc_split_args_iter(ea.arg, length, &end, buf, &len);
if (len > 0) {
ADD(args, STRING_OBJ(cstrn_to_string(buf, len)));
}
}
xfree(buf);
}
ucmd_T *cmd = NULL;
if (ea.cmdidx == CMD_USER) {
cmd = USER_CMD(ea.useridx);
} else if (ea.cmdidx == CMD_USER_BUF) {
cmd = USER_CMD_GA(&curbuf->b_ucmds, ea.useridx);
}
if (cmd != NULL) {
PUT(result, "cmd", CSTR_TO_OBJ((char *)cmd->uc_name));
} else {
PUT(result, "cmd", CSTR_TO_OBJ((char *)get_command_name(NULL, ea.cmdidx)));
}
if ((ea.argt & EX_RANGE) && ea.addr_count > 0) {
Array range = ARRAY_DICT_INIT;
if (ea.addr_count > 1) {
ADD(range, INTEGER_OBJ(ea.line1));
}
ADD(range, INTEGER_OBJ(ea.line2));
PUT(result, "range", ARRAY_OBJ(range));
} else {
PUT(result, "range", ARRAY_OBJ(ARRAY_DICT_INIT));
}
if (ea.argt & EX_COUNT) {
if (ea.addr_count > 0) {
PUT(result, "count", INTEGER_OBJ(ea.line2));
} else if (cmd != NULL) {
PUT(result, "count", INTEGER_OBJ(cmd->uc_def));
} else {
PUT(result, "count", INTEGER_OBJ(0));
}
} else {
PUT(result, "count", INTEGER_OBJ(-1));
}
char reg[2];
reg[0] = (char)ea.regname;
reg[1] = '\0';
PUT(result, "reg", CSTR_TO_OBJ(reg));
PUT(result, "bang", BOOLEAN_OBJ(ea.forceit));
PUT(result, "args", ARRAY_OBJ(args));
char nargs[2];
if (ea.argt & EX_EXTRA) {
if (ea.argt & EX_NOSPC) {
if (ea.argt & EX_NEEDARG) {
nargs[0] = '1';
} else {
nargs[0] = '?';
}
} else if (ea.argt & EX_NEEDARG) {
nargs[0] = '+';
} else {
nargs[0] = '*';
}
} else {
nargs[0] = '0';
}
nargs[1] = '\0';
PUT(result, "nargs", CSTR_TO_OBJ(nargs));
const char *addr;
switch (ea.addr_type) {
case ADDR_LINES:
addr = "line";
break;
case ADDR_ARGUMENTS:
addr = "arg";
break;
case ADDR_BUFFERS:
addr = "buf";
break;
case ADDR_LOADED_BUFFERS:
addr = "load";
break;
case ADDR_WINDOWS:
addr = "win";
break;
case ADDR_TABS:
addr = "tab";
break;
case ADDR_QUICKFIX:
addr = "qf";
break;
case ADDR_NONE:
addr = "none";
break;
default:
addr = "?";
break;
}
PUT(result, "addr", CSTR_TO_OBJ(addr));
PUT(result, "nextcmd", CSTR_TO_OBJ((char *)ea.nextcmd));
Dictionary mods = ARRAY_DICT_INIT;
PUT(mods, "silent", BOOLEAN_OBJ(cmdinfo.silent));
PUT(mods, "emsg_silent", BOOLEAN_OBJ(cmdinfo.emsg_silent));
PUT(mods, "sandbox", BOOLEAN_OBJ(cmdinfo.sandbox));
PUT(mods, "noautocmd", BOOLEAN_OBJ(cmdinfo.noautocmd));
PUT(mods, "tab", INTEGER_OBJ(cmdinfo.cmdmod.tab));
PUT(mods, "verbose", INTEGER_OBJ(cmdinfo.verbose));
PUT(mods, "browse", BOOLEAN_OBJ(cmdinfo.cmdmod.browse));
PUT(mods, "confirm", BOOLEAN_OBJ(cmdinfo.cmdmod.confirm));
PUT(mods, "hide", BOOLEAN_OBJ(cmdinfo.cmdmod.hide));
PUT(mods, "keepalt", BOOLEAN_OBJ(cmdinfo.cmdmod.keepalt));
PUT(mods, "keepjumps", BOOLEAN_OBJ(cmdinfo.cmdmod.keepjumps));
PUT(mods, "keepmarks", BOOLEAN_OBJ(cmdinfo.cmdmod.keepmarks));
PUT(mods, "keeppatterns", BOOLEAN_OBJ(cmdinfo.cmdmod.keeppatterns));
PUT(mods, "lockmarks", BOOLEAN_OBJ(cmdinfo.cmdmod.lockmarks));
PUT(mods, "noswapfile", BOOLEAN_OBJ(cmdinfo.cmdmod.noswapfile));
PUT(mods, "vertical", BOOLEAN_OBJ(cmdinfo.cmdmod.split & WSP_VERT));
const char *split;
if (cmdinfo.cmdmod.split & WSP_BOT) {
split = "botright";
} else if (cmdinfo.cmdmod.split & WSP_TOP) {
split = "topleft";
} else if (cmdinfo.cmdmod.split & WSP_BELOW) {
split = "belowright";
} else if (cmdinfo.cmdmod.split & WSP_ABOVE) {
split = "aboveleft";
} else {
split = "";
}
PUT(mods, "split", CSTR_TO_OBJ(split));
PUT(result, "mods", DICTIONARY_OBJ(mods));
Dictionary magic = ARRAY_DICT_INIT;
PUT(magic, "file", BOOLEAN_OBJ(cmdinfo.magic.file));
PUT(magic, "bar", BOOLEAN_OBJ(cmdinfo.magic.bar));
PUT(result, "magic", DICTIONARY_OBJ(magic));
end:
xfree(cmdline);
return result;
}
/// Executes an Ex command.
///
/// Unlike |nvim_command()| this command takes a structured Dictionary instead of a String. This
/// allows for easier construction and manipulation of an Ex command. This also allows for things
/// such as having spaces inside a command argument, expanding filenames in a command that otherwise
/// doesn't expand filenames, etc.
///
/// On execution error: fails with VimL error, updates v:errmsg.
///
/// @see |nvim_exec()|
/// @see |nvim_command()|
///
/// @param cmd Command to execute. Must be a Dictionary that can contain the same values as
/// the return value of |nvim_parse_cmd()| except "addr", "nargs" and "nextcmd"
/// which are ignored if provided. All values except for "cmd" are optional.
/// @param opts Optional parameters.
/// - output: (boolean, default false) Whether to return command output.
/// @param[out] err Error details, if any.
/// @return Command output (non-error, non-shell |:!|) if `output` is true, else empty string.
String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error *err)
FUNC_API_SINCE(10)
{
exarg_T ea;
memset(&ea, 0, sizeof(ea));
ea.verbose_save = -1;
ea.save_msg_silent = -1;
CmdParseInfo cmdinfo;
memset(&cmdinfo, 0, sizeof(cmdinfo));
cmdinfo.verbose = -1;
char *cmdline = NULL;
char *cmdname = NULL;
char **args = NULL;
size_t argc = 0;
String retv = (String)STRING_INIT;
#define OBJ_TO_BOOL(var, value, default, varname) \
do { \
var = api_object_to_bool(value, varname, default, err); \
if (ERROR_SET(err)) { \
goto end; \
} \
} while (0)
#define VALIDATION_ERROR(...) \
do { \
api_set_error(err, kErrorTypeValidation, __VA_ARGS__); \
goto end; \
} while (0)
bool output;
OBJ_TO_BOOL(output, opts->output, false, "'output'");
// First, parse the command name and check if it exists and is valid.
if (!HAS_KEY(cmd->cmd) || cmd->cmd.type != kObjectTypeString
|| cmd->cmd.data.string.data[0] == NUL) {
VALIDATION_ERROR("'cmd' must be a non-empty String");
}
cmdname = string_to_cstr(cmd->cmd.data.string);
ea.cmd = cmdname;
char *p = find_ex_command(&ea, NULL);
// If this looks like an undefined user command and there are CmdUndefined
// autocommands defined, trigger the matching autocommands.
if (p != NULL && ea.cmdidx == CMD_SIZE && ASCII_ISUPPER(*ea.cmd)
&& has_event(EVENT_CMDUNDEFINED)) {
p = xstrdup(cmdname);
int ret = apply_autocmds(EVENT_CMDUNDEFINED, p, p, true, NULL);
xfree(p);
// If the autocommands did something and didn't cause an error, try
// finding the command again.
p = (ret && !aborting()) ? find_ex_command(&ea, NULL) : ea.cmd;
}
if (p == NULL || ea.cmdidx == CMD_SIZE) {
VALIDATION_ERROR("Command not found: %s", cmdname);
}
if (is_cmd_ni(ea.cmdidx)) {
VALIDATION_ERROR("Command not implemented: %s", cmdname);
}
// Get the command flags so that we can know what type of arguments the command uses.
// Not required for a user command since `find_ex_command` already deals with it in that case.
if (!IS_USER_CMDIDX(ea.cmdidx)) {
ea.argt = get_cmd_argt(ea.cmdidx);
}
// Parse command arguments since it's needed to get the command address type.
if (HAS_KEY(cmd->args)) {
if (cmd->args.type != kObjectTypeArray) {
VALIDATION_ERROR("'args' must be an Array");
}
// Check if every argument is valid
for (size_t i = 0; i < cmd->args.data.array.size; i++) {
Object elem = cmd->args.data.array.items[i];
if (elem.type != kObjectTypeString) {
VALIDATION_ERROR("Command argument must be a String");
} else if (string_iswhite(elem.data.string)) {
VALIDATION_ERROR("Command argument must have non-whitespace characters");
}
}
argc = cmd->args.data.array.size;
bool argc_valid;
// Check if correct number of arguments is used.
switch (ea.argt & (EX_EXTRA | EX_NOSPC | EX_NEEDARG)) {
case EX_EXTRA | EX_NOSPC | EX_NEEDARG:
argc_valid = argc == 1;
break;
case EX_EXTRA | EX_NOSPC:
argc_valid = argc <= 1;
break;
case EX_EXTRA | EX_NEEDARG:
argc_valid = argc >= 1;
break;
case EX_EXTRA:
argc_valid = true;
break;
default:
argc_valid = argc == 0;
break;
}
if (!argc_valid) {
argc = 0; // Ensure that args array isn't erroneously freed at the end.
VALIDATION_ERROR("Incorrect number of arguments supplied");
}
if (argc != 0) {
args = xcalloc(argc, sizeof(char *));
for (size_t i = 0; i < argc; i++) {
args[i] = string_to_cstr(cmd->args.data.array.items[i].data.string);
}
}
}
// Simply pass the first argument (if it exists) as the arg pointer to `set_cmd_addr_type()`
// since it only ever checks the first argument.
set_cmd_addr_type(&ea, argc > 0 ? (char_u *)args[0] : NULL);
if (HAS_KEY(cmd->range)) {
if (!(ea.argt & EX_RANGE)) {
VALIDATION_ERROR("Command cannot accept a range");
} else if (cmd->range.type != kObjectTypeArray) {
VALIDATION_ERROR("'range' must be an Array");
} else if (cmd->range.data.array.size > 2) {
VALIDATION_ERROR("'range' cannot contain more than two elements");
}
Array range = cmd->range.data.array;
ea.addr_count = (int)range.size;
for (size_t i = 0; i < range.size; i++) {
Object elem = range.items[i];
if (elem.type != kObjectTypeInteger || elem.data.integer < 0) {
VALIDATION_ERROR("'range' element must be a non-negative Integer");
}
}
if (range.size > 0) {
ea.line1 = (linenr_T)range.items[0].data.integer;
ea.line2 = (linenr_T)range.items[range.size - 1].data.integer;
}
if (invalid_range(&ea) != NULL) {
VALIDATION_ERROR("Invalid range provided");
}
}
if (ea.addr_count == 0) {
if (ea.argt & EX_DFLALL) {
set_cmd_dflall_range(&ea); // Default range for range=%
} else {
ea.line1 = ea.line2 = get_cmd_default_range(&ea); // Default range.
if (ea.addr_type == ADDR_OTHER) {
// Default is 1, not cursor.
ea.line2 = 1;
}
}
}
if (HAS_KEY(cmd->count)) {
if (!(ea.argt & EX_COUNT)) {
VALIDATION_ERROR("Command cannot accept a count");
} else if (cmd->count.type != kObjectTypeInteger || cmd->count.data.integer < 0) {
VALIDATION_ERROR("'count' must be a non-negative Integer");
}
set_cmd_count(&ea, cmd->count.data.integer, true);
}
if (HAS_KEY(cmd->reg)) {
if (!(ea.argt & EX_REGSTR)) {
VALIDATION_ERROR("Command cannot accept a register");
} else if (cmd->reg.type != kObjectTypeString || cmd->reg.data.string.size != 1) {
VALIDATION_ERROR("'reg' must be a single character");
}
char regname = cmd->reg.data.string.data[0];
if (regname == '=') {
VALIDATION_ERROR("Cannot use register \"=");
} else if (!valid_yank_reg(regname, ea.cmdidx != CMD_put && !IS_USER_CMDIDX(ea.cmdidx))) {
VALIDATION_ERROR("Invalid register: \"%c", regname);
}
ea.regname = (uint8_t)regname;
}
OBJ_TO_BOOL(ea.forceit, cmd->bang, false, "'bang'");
if (ea.forceit && !(ea.argt & EX_BANG)) {
VALIDATION_ERROR("Command cannot accept a bang");
}
if (HAS_KEY(cmd->magic)) {
if (cmd->magic.type != kObjectTypeDictionary) {
VALIDATION_ERROR("'magic' must be a Dictionary");
}
Dict(cmd_magic) magic = { 0 };
if (!api_dict_to_keydict(&magic, KeyDict_cmd_magic_get_field,
cmd->magic.data.dictionary, err)) {
goto end;
}
OBJ_TO_BOOL(cmdinfo.magic.file, magic.file, ea.argt & EX_XFILE, "'magic.file'");
OBJ_TO_BOOL(cmdinfo.magic.bar, magic.bar, ea.argt & EX_TRLBAR, "'magic.bar'");
} else {
cmdinfo.magic.file = ea.argt & EX_XFILE;
cmdinfo.magic.bar = ea.argt & EX_TRLBAR;
}
if (HAS_KEY(cmd->mods)) {
if (cmd->mods.type != kObjectTypeDictionary) {
VALIDATION_ERROR("'mods' must be a Dictionary");
}
Dict(cmd_mods) mods = { 0 };
if (!api_dict_to_keydict(&mods, KeyDict_cmd_mods_get_field, cmd->mods.data.dictionary, err)) {
goto end;
}
if (HAS_KEY(mods.tab)) {
if (mods.tab.type != kObjectTypeInteger || mods.tab.data.integer < 0) {
VALIDATION_ERROR("'mods.tab' must be a non-negative Integer");
}
cmdinfo.cmdmod.tab = (int)mods.tab.data.integer + 1;
}
if (HAS_KEY(mods.verbose)) {
if (mods.verbose.type != kObjectTypeInteger) {
VALIDATION_ERROR("'mods.verbose' must be a Integer");
} else if (mods.verbose.data.integer >= 0) {
// Silently ignore negative integers to allow mods.verbose to be set to -1.
cmdinfo.verbose = mods.verbose.data.integer;
}
}
bool vertical;
OBJ_TO_BOOL(vertical, mods.vertical, false, "'mods.vertical'");
cmdinfo.cmdmod.split |= (vertical ? WSP_VERT : 0);
if (HAS_KEY(mods.split)) {
if (mods.split.type != kObjectTypeString) {
VALIDATION_ERROR("'mods.split' must be a String");
}
if (*mods.split.data.string.data == NUL) {
// Empty string, do nothing.
} else if (STRCMP(mods.split.data.string.data, "aboveleft") == 0
|| STRCMP(mods.split.data.string.data, "leftabove") == 0) {
cmdinfo.cmdmod.split |= WSP_ABOVE;
} else if (STRCMP(mods.split.data.string.data, "belowright") == 0
|| STRCMP(mods.split.data.string.data, "rightbelow") == 0) {
cmdinfo.cmdmod.split |= WSP_BELOW;
} else if (STRCMP(mods.split.data.string.data, "topleft") == 0) {
cmdinfo.cmdmod.split |= WSP_TOP;
} else if (STRCMP(mods.split.data.string.data, "botright") == 0) {
cmdinfo.cmdmod.split |= WSP_BOT;
} else {
VALIDATION_ERROR("Invalid value for 'mods.split'");
}
}
OBJ_TO_BOOL(cmdinfo.silent, mods.silent, false, "'mods.silent'");
OBJ_TO_BOOL(cmdinfo.emsg_silent, mods.emsg_silent, false, "'mods.emsg_silent'");
OBJ_TO_BOOL(cmdinfo.sandbox, mods.sandbox, false, "'mods.sandbox'");
OBJ_TO_BOOL(cmdinfo.noautocmd, mods.noautocmd, false, "'mods.noautocmd'");
OBJ_TO_BOOL(cmdinfo.cmdmod.browse, mods.browse, false, "'mods.browse'");
OBJ_TO_BOOL(cmdinfo.cmdmod.confirm, mods.confirm, false, "'mods.confirm'");
OBJ_TO_BOOL(cmdinfo.cmdmod.hide, mods.hide, false, "'mods.hide'");
OBJ_TO_BOOL(cmdinfo.cmdmod.keepalt, mods.keepalt, false, "'mods.keepalt'");
OBJ_TO_BOOL(cmdinfo.cmdmod.keepjumps, mods.keepjumps, false, "'mods.keepjumps'");
OBJ_TO_BOOL(cmdinfo.cmdmod.keepmarks, mods.keepmarks, false, "'mods.keepmarks'");
OBJ_TO_BOOL(cmdinfo.cmdmod.keeppatterns, mods.keeppatterns, false, "'mods.keeppatterns'");
OBJ_TO_BOOL(cmdinfo.cmdmod.lockmarks, mods.lockmarks, false, "'mods.lockmarks'");
OBJ_TO_BOOL(cmdinfo.cmdmod.noswapfile, mods.noswapfile, false, "'mods.noswapfile'");
if (cmdinfo.sandbox && !(ea.argt & EX_SBOXOK)) {
VALIDATION_ERROR("Command cannot be run in sandbox");
}
}
// Finally, build the command line string that will be stored inside ea.cmdlinep.
// This also sets the values of ea.cmd, ea.arg, ea.args and ea.arglens.
build_cmdline_str(&cmdline, &ea, &cmdinfo, args, argc);
ea.cmdlinep = &cmdline;
garray_T capture_local;
const int save_msg_silent = msg_silent;
garray_T * const save_capture_ga = capture_ga;
if (output) {
ga_init(&capture_local, 1, 80);
capture_ga = &capture_local;
}
TRY_WRAP({
try_start();
if (output) {
msg_silent++;
}
WITH_SCRIPT_CONTEXT(channel_id, {
execute_cmd(&ea, &cmdinfo, false);
});
if (output) {
capture_ga = save_capture_ga;
msg_silent = save_msg_silent;
}
try_end(err);
});
if (ERROR_SET(err)) {
goto clear_ga;
}
if (output && capture_local.ga_len > 1) {
retv = (String){
.data = capture_local.ga_data,
.size = (size_t)capture_local.ga_len,
};
// redir usually (except :echon) prepends a newline.
if (retv.data[0] == '\n') {
memmove(retv.data, retv.data + 1, retv.size - 1);
retv.data[retv.size - 1] = '\0';
retv.size = retv.size - 1;
}
goto end;
}
clear_ga:
if (output) {
ga_clear(&capture_local);
}
end:
xfree(cmdline);
xfree(cmdname);
xfree(ea.args);
xfree(ea.arglens);
for (size_t i = 0; i < argc; i++) {
xfree(args[i]);
}
xfree(args);
return retv;
#undef OBJ_TO_BOOL
#undef VALIDATION_ERROR
}

View File

@ -264,44 +264,6 @@ void nvim_win_del_var(Window window, String name, Error *err)
dict_set_var(win->w_vars, name, NIL, true, false, err);
}
/// Gets a window option value
///
/// @param window Window handle, or 0 for current window
/// @param name Option name
/// @param[out] err Error details, if any
/// @return Option value
Object nvim_win_get_option(Window window, String name, Error *err)
FUNC_API_SINCE(1)
{
win_T *win = find_window_by_handle(window, err);
if (!win) {
return (Object)OBJECT_INIT;
}
return get_option_from(win, SREQ_WIN, name, err);
}
/// Sets a window option value. Passing 'nil' as value deletes the option(only
/// works if there's a global fallback)
///
/// @param channel_id
/// @param window Window handle, or 0 for current window
/// @param name Option name
/// @param value Option value
/// @param[out] err Error details, if any
void nvim_win_set_option(uint64_t channel_id, Window window, String name, Object value, Error *err)
FUNC_API_SINCE(1)
{
win_T *win = find_window_by_handle(window, err);
if (!win) {
return;
}
set_option_to(channel_id, win, SREQ_WIN, name, value, err);
}
/// Gets the window position in display cells. First position is zero.
///
/// @param window Window handle, or 0 for current window

View File

@ -90,9 +90,6 @@ static bool ex_pressedreturn = false;
garray_T ucmds = { 0, 0, sizeof(ucmd_T), 4, NULL };
// Whether a command index indicates a user command.
#define IS_USER_CMDIDX(idx) ((int)(idx) < 0)
// Struct for storing a line inside a while/for loop
typedef struct {
char *line; // command line

View File

@ -17,6 +17,9 @@
#define VALID_PATH 1
#define VALID_HEAD 2
// Whether a command index indicates a user command.
#define IS_USER_CMDIDX(idx) ((int)(idx) < 0)
// Structure used to save the current state. Used when executing Normal mode
// commands while in any other mode.
typedef struct {