aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/api/options.c
Commit message (Collapse)AuthorAge
* fix(options): extend dry-run validationLewis Russell4 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Problem: nvim_set_option_value() dry runs skip checks in did_set_* callbacks. This logic also prepares values needed to apply the option, such as tab widths or a compiled regexp. Moving the checks into validation callbacks needs a way to save that work for did_set_*. Solution: Store prepared values in optset_T so did_set_* can consume them after the option is stored. Dry runs perform the same validation, then free the prepared values without changing live state. This extends dry-run validation for: - 'cedit': reject invalid command-line window keys. - 'completeitemalign': require each completion column exactly once. - 'cursorlineopt': reject empty values and conflicting line modes. - 'encoding': accept only UTF-8 and its aliases. - 'fileencoding': reject lists and local changes with 'nomodifiable'. - 'messagesopt': require mandatory fields and enforce numeric limits. - 'spellcapcheck': return regexp errors, including under :silent!. - 'spellsuggest': reject conflicting suggestion modes. - 'varsofttabstop' and 'vartabstop': reject invalid tab widths. - 'wildmode': reject more than four completion stages. 'makeencoding' also uses the shared encoding preparation. Separating value preparation exposed a bug in 'vartabstop' and 'varsofttabstop': failed assignments could discard local tab widths, and global assignments could overwrite them. Fix this by preparing new widths separately and applying them only after a valid local assignment. AI-assisted
* fix(api): nvim_set_option_value() "remove" ignores non-adjacent items #42351Justin M. Keyes5 days
| | | | | | | | | | | | | Problem: `nvim_set_option_value('fo', {'c','r','o'}, {operation='remove'})` does nothing on "jcqrol": the items are joined into one `:set-=` value, which only matches a contiguous substring. Removing map keys, e.g. {'eol','space'} from 'listchars', never matches. `vim.opt:remove{…}` inherits this. (Regression from f34ee3da80e8; 429338e74bf4 fixed the case where the flags are adjacent and in order.) Solution: Remove each item separately.
* refactor(options): simplify option callback argumentsLewis Russell2026-09-24
| | | | | | | | | | | | | Problem: Option callbacks carry untyped buffer and window pointers, requiring casts. Helper arguments also pass unused flags and an error value that is always NULL when OptionSet is triggered. Solution: Use typed pointers in optset_T and remove the redundant arguments and checks. Clarify that sandbox assignments also affect option security. AI-assisted
* fix(api): preserve option trust during mergesLewis Russell2026-09-22
| | | | | | | | | | | | | Problem: API append, prepend, and remove operations reach the option setter as full replacements. This can clear an expression's untrusted flag and allow it to write files, unlike equivalent :set operations. Solution: Pass replacement status through the option setters so merges preserve the existing value's trust restrictions. AI-assisted
* refactor(option): simplify error buffer handlingLewis Russell2026-09-21
| | | | | | | | | | | | | | | | | | | | | | | | | | Problem: Option validators and setters repeatedly pass error buffers and their sizes separately. Some setters return messages in static storage. Solution: - Add CharBuf for borrowed writable storage, with its length including space for a terminating NUL. - Move option error buffers to the callers that report errors. Preserve existing buffer sizes, truncation, and calls that do not need a message. - Consolidate option error formatting and the duplicated code that lists valid option values. - Return error messages directly from do_one_set_option(). - Remove unused arguments from get_option_newval() and set_option_value_for(). - Handle legacy TTY assignments in the existing callers and remove the redundant compatibility wrapper. - Reuse CharBuf for Tree-sitter query errors. - Pass CharBuf through the statusline and statuscolumn builders, retaining the buffer sizes provided by their callers, including title formatting. AI-assisted
* fix(options): validate nvim_set_option_value() dry runsLewis Russell2026-09-18
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | Problem: nvim_set_option_value() with dry_run=true accepts invalid values such as foldmethod='nonsense'. Validation is mixed with side effects in the did_set_* callbacks, so dry runs skip it. Solution: Split value validation out of the did_set_* callbacks into separate validation_cb callbacks. Use these checks for both normal assignments and nvim_set_option_value() dry runs. Keep side effects in did_set_*. Replace direct foldenable/foldlevel assignments in C folding commands with setters. This gives C commands the same OptionSet and synchronization behavior as Lua assignments. Use normal setters for 'tabstop' and 'vartabstop' in :retab so C code indentation and folds based on indentation use the new tab widths. Direct assignments could leave them using the old widths. :retab temporarily turns off 'list' to calculate tab widths. Previously, :retab -1 reported an error and returned without restoring 'list'. Check the argument before turning 'list' off, so an invalid argument leaves the option unchanged. AI-assisted
* refactor(options): simplify "unset" logicJustin M. Keyes2026-07-31
|
* refactor(options): memory managementJustin M. Keyes2026-07-31
| | | | | | | | | | | | | | | | Problem: Numerous callers have to manually check optval ownership (i.e. whether, and how, to release) via `is_callback_option`, `option_is_global_local`, etc. This is fragile, hard to use correctly; and if we introduce another optval variant in the future, we'll have to redo all of these careful checks and boilerplate again. Solution: Provide a unified system and use it everywhere: optval_free_owned optval_is_owned optval_own
* feat(options): lua closure/function optionsJustin M. Keyes2026-07-31
| | | | | | | | | | | | | | | Problem: Cannot assign Lua functions/closures to "func" ('completefunc', 'tagfun', …) or "expr" ('foldexpr', 'indentexpr', …) options. Solution: - Store "func"/"expr" options as `Callback` instead of string. - Delete oceans of copy-pasted code. - BREAKING: LuaRef returned via RPC/Vimscript is now represented as `"<Lua N: file:line>"` (like what `:map` shows) instead of `nil`. - Note: `man.vim` still uses `v:lua` string, bc it's a vimscript ftplugin. Helped-by: Lewis Russell <lewis6991@gmail.com>
* refactor(options): naming #41046Justin M. Keyes2026-07-30
| | | | | - "obj" refers to structured (non-scalar `Object`) option values. - "optval" generally refers to the internal scalar (legacy ":set"-style) option value.
* refactor(options): drop OptVal, use ObjectJustin M. Keyes2026-07-29
| | | | | | | | | | | | | Problem: The object subsystem has an intermediate representation for no real reason. Besides the code cost, this also adds an extra (api <=> OptVal) conversion step, which is a (small) perf cost. Solution: We already have `Object`, so use it instead. - drop `OptVal`, `OptValData`, `OptValType`, and related boilerplate. - add `kObjectTypeUnset`.
* refactor(options): store dict options in :set-string formJustin M. Keyes2026-07-24
| | | | | | | | | | The parent commit made a valiant effort to store "dict options" in their reified form, but this is more trouble than it's worth: - inconsistent model for developers to understand. - string lifetime issues (the "varp" convention is to pass around an aliased, long-lived option value). - lots of extra plumbing to deal with the 2 different option-storage paradigms.
* feat(options): schema, "dict" options, messagesJustin M. Keyes2026-07-24
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Problem: Options parsing is still painful for dict-style options. Solution: schema-maxxing => better `opt:get()` (will be the basis for `vim.o()`), unified (and more-detailed) err msgs. - Drop bespoke structure-builder in `_core/options.lua`. - Define `schema` for all non-primitive options (except 'guicursor' and statusline-style options); generate reified keysets `OptKeyDict`). - Generate 'fillchars' => `fcs_tab`, 'listchars' => `lcs_tab`. - `nvim_set_option_value`: - Return the improved structures. Also from `vim.opt.x:get()`. - Eliminate api <=> lua roundtrip, centralize option structure handling. - Improve/unify errors. - Bump ERR_BUFLEN 80 → 256 so the "one of" list isn't truncated. - Eliminate old 'diffopt' order-dependence (`iwhiteall` before `iwhite`) Error samples: Typed-key path (opt_strings_check → diffopt/mousescroll/breakindentopt): E474: Unknown item 'foo' E474: 'context' requires a number E474: 'ver' number is out of range E474: 'algorithm' must be one of: myers, minimal, patience, histogram E474: 'filler' does not take a value Related: - #31084 - #34661 - #31820 - #14739 - #20107 - fix #18875 - :get() returns `{ sbr = true, shift = '3' }` (reified-keyset) instead of `{'sbr', 'shift:3'}` - Setting via table now works too. `object_as_optval_for` `is_map` now recognizes struct options. - fix #30296 - instead of `E474: Invalid argument`, errors now look like: ``` E474: Invalid value 'x', expected one of: single, double: ambiwidth=x E474: Unknown item 'foo': diffopt=foo E474: 'context' requires a number: diffopt=context:x ``` simplify `win_float_parse_option` from #26799.
* refactor(ctx): drop aucmd_prepbuf/aucmd_restbuf #40692Justin M. Keyes2026-07-11
|
* perf(options): avoid Lua<=>API roundtrips #40477Justin M. Keyes2026-06-29
| | | | | | | | | | | | | | | | Problem: - Lua<=>API roundtrips - Although we prefer Lua for most business-logic code, doing this conversion in C makes sense in this case because: 1. setting options is a hot path 2. most of the options logic lives in C 3. the current arrangement is MORE verbose and requires MORE code Solution: Move conversion to a C util. - nvim_set_option_value passes the raw Object (scalar, Array, or Dict) to `object_as_optval_for()` which flattens it to the canonical `:set` string and validates the type. - drop `convert_value_to_vim`, eliminate its roundtrip.
* feat(api): nvim_set_option_value(operation=...) #39849Kyle2026-06-28
| | | | | | | | | | | | | Problem: `nvim_set_option_value` cannot "update" options similar to `:set opt=`, `:set opt+=`, etc. The Lua impls of "vim.opt" / "vim.o" have incomplete, bespoke reimplementations of those operations. ref #38420 Solution: - Add `operation` param to `nvim_set_option_value`, which may be "set", "append", "prepend", or "remove". - Use this feature to implement `vim.opt` / `vim.o`.
* refactor: simplify aucmd_restbuf usage #39879Justin M. Keyes2026-05-19
| | | | | | | | Problem: `aucmd_restbuf` must be guarded in case `aucmd_prepbuf` wasn't called. Solution: Update `aucmd_restbuf` to be a no-op if `aucmd_prepbuf` wasn't called. This requires `aco` to be zero-initialized.
* feat(api): tab-local option scope #39811Justin M. Keyes2026-05-17
| | | | | | | | | | | | | | | | | | Problem: 2d795face6e4 added support for tab-local options ('cmdheight') to `nvim_get_option_value`, but not to: nvim_get_option_info2() nvim_set_option_value(…, { tab = … }) gettabwinvar() Solution: - Update `options.lua` to model tab-local options. Introduce `kOptScopeTab`. - Handle tab scope in the options layer so it works for all options APIs. Note: - No change to `gettabvar()`. Not sure if needed/wanted. fix https://github.com/neovim/neovim/issues/31140
* refactor(api): tab-local nvim_get_option_value #39810Justin M. Keyes2026-05-16
|
* fix(api): nvim_get_option_value tab-local 'cmdheight' #39259EliWiegman2026-05-16
| | | | | | | Problem: API clients cannot query the tab-local value of 'cmdheight'. Solution: Allow nvim_get_option_value() to accept { tab = <tab-ID> } for 'cmdheight'.
* docs: sort quasi-keysetsJustin M. Keyes2026-04-26
|
* fix(api): nvim_get_option_value FileType autocmd handling #37414Sean Dewar2026-03-18
| | | | | | | | | | | | Problem: nvim_get_option_value with "filetype" set silently returns incorrect defaults if autocommands are blocked, like when they're already running. Solution: Allow its FileType autocommands to nest: `do_filetype_autocmd(force=true)`. Also error if executing them fails, rather than silently return wrong defaults. Endless nesting from misbehaving scripts should be prevented by the recursion limit in apply_autocmds_group, which is 10.
* fix(api): nvim_get_option_value dummy buffer crashesSean Dewar2026-01-15
| | | | | | | | Problem: nvim_get_option_value with "filetype" set can crash if autocommands open the dummy buffer in more windows, or if &bufhidden == "wipe". Solution: Attempt to close all dummy buffer windows before wiping. Promote the dummy buffer to a normal buffer if that fails.
* fix(api): autocmds mess up nvim_get_option_value's dummy bufferSean Dewar2026-01-15
| | | | | | | | Problem: When the "filetype" key is set for nvim_get_option_value, autocommands can crash Nvim by prematurely wiping the dummy buffer, or cause options intended for it to instead be set for unrelated buffers if switched during OptionSet. Solution: Don't crash. Also quash side-effects from setting the buffer options.
* refactor(build): remove INCLUDE_GENERATED_DECLARATIONS guardsbfredl2025-08-14
| | | | | | | | | These are not needed after #35129 but making uncrustify still play nice with them was a bit tricky. Unfortunately `uncrustify --update-config-with-doc` breaks strings with backslashes. This issue has been reported upstream, and in the meanwhile auto-update on every single run has been disabled.
* docs(api): document types using LuaCATS typesLewis Russell2025-06-26
| | | | | | | | | | | | | | | | | | | | - Render Lua types in api.txt. - Added `DictAs(name)` API type which acts the same as `Dict` (no parens) when generating the dispatchers, but acts the same as `Dict(name)` when generating docs. - Added `Tuple(...)` API type which is the treated the as `Array` for generating the dispatchers, but is used to document richer types. - Added `Enum(...)` API type to better document enums - Improve typing of some API functions. - Improve c_grammar to properly parse API types and replace string pattern logic in the parsers. - Removed all the hardcoded type overrides in gen_eval_files.lua
* Revert "refactor(options): set option value for non-current context ↵zeertzjq2025-01-09
| | | | | directly" (#31924) Reverts #31112
* refactor(options): set option value for non-current context directlyFamiu Haque2024-12-26
| | | | | | Problem: Currently, we use `switch_option_context` to temporarily switch the current option context before setting an option for a different buffer / window. This is not ideal because we already support getting and setting option values for non-current contexts in the underlying implementation. Solution: Set option value for non-current context by passing the context directly to the lower level functions. Also introduce a new `OptCtx` struct to store option context information, this will scale much better if we add more option scopes and other context information in the future.
* refactor: iwyu #31637Justin M. Keyes2024-12-23
| | | Result of `make iwyu` (after some "fixups").
* refactor(options): fix confusing naming of `scope` and `req_scope` (#31317)Famiu Haque2024-11-25
| | | | | | | | | | Problem: The name `scope` is often used to refer to option flags because `OPT_LOCAL` and `OPT_GLOBAL` are often used to determine the option scope. This leads to the name `req_scope` being used for actual option scopes instead. Solution: Since the end-goal is to remove `OPT_LOCAL` and `OPT_GLOBAL` entirely and replace them with `OptScope`, rename `OptScope` variables to `scope` and the old scope flag variables to `opt_flags`.
* fix(api): don't try to get/set option for invalid option name (#31302)Famiu Haque2024-11-22
| | | | | | | | | | | | | | | | Problem: `validate_option_value_args()` returns `OK` even if option name is invalid or if option doesn't have the supported scope, which leads to Neovim still trying to erroneously get/set the option in those cases, which can lead to an assertion failure when `option_has_scope()` is invoked. This issue miraculously doesn't exist in release builds since the assertion is skipped and `(get/set)_option_value_for` returns if there is an error set, but that is not the intended location for that error to be caught. Solution: Make `validate_option_value_args()` return `FAIL` if there is an error set, which causes the API option functions to return early instead of trying to get/set an invalid option.
* fix(api): nvim_get_option_value does not clean up on FileType error #31219altermo2024-11-18
| | | | | | | | Problem: If there's an error in `FileType` autocmd, the filetype get-opt buffer doesn't get cleaned up. Solution: Call `aucmd_restbuf`.
* refactor(options): remove `.indir`, redesign option scopes #31066Famiu Haque2024-11-16
| | | | | | | | | | | | | | | | | | | | | | Problem: The way option scopes currently work is inflexible and does not allow for nested option scopes or easily finding the value of an option at any arbitrary scope without having to do long handwritten switch-case statements like in `get_varp()`. `.indir` is also confusing and redundant since option indices for each scope can be autogenerated. Solution: Expand option scopes in such a way that an option can support any amount of scopes using a set of scope flags, similarly to how it's already done for option types. Also make options contain information about its index at each scope it supports. This allows for massively simplifying `get_varp()` and `get_varp_scope()` in the future by just using a struct for options at each scope. This would be done by creating a table that stores the offset of an option's variable at a scope by using the option's index at that scope as a key. This PR also autogenerates enums for option indices at each scope to remove the need for `.indir` entirely, and also to allow easily iterating over options all options that support any scope. Ref: #29314
* feat(options)!: disallow setting hidden options #28400Famiu Haque2024-11-04
| | | | | | | | | | | | | | | | | Problem: There are three different ways of marking an option as hidden, `enable_if = false`, `hidden = true` and `immutable = true`. These also have different behaviors. Options hidden with `enable_if = false` can't have their value fetched using Vim script or the API, but options hidden with `hidden = true` or `immutable = true` can. On the other hand, options with `hidden = true` do not error when trying to set their value, but options with `immutable = true` do. Solution: Remove `enable_if = false`, remove the `hidden` property for options, and use `immutable = true` to mark an option as hidden instead. Also make hidden option variable pointers always point to the default value, which allows fetching the value of every hidden option using Vim script and the API. This does also mean that trying to set a hidden option will now give an error instead of just being ignored.
* refactor(api)!: rename Dictionary => DictJustin M. Keyes2024-09-23
| | | | | | | | | | | | | | In the api_info() output: :new|put =map(filter(api_info().functions, '!has_key(v:val,''deprecated_since'')'), 'v:val') ... {'return_type': 'ArrayOf(Integer, 2)', 'name': 'nvim_win_get_position', 'method': v:true, 'parameters': [['Window', 'window']], 'since': 1} The `ArrayOf(Integer, 2)` return type didn't break clients when we added it, which is evidence that clients don't use the `return_type` field, thus renaming Dictionary => Dict in api_info() is not (in practice) a breaking change.
* fix(api): allow `scope = 'local'` with `buf` when using `nvim_get_option_value`dundargoc2024-06-10
| | | | | | | | `nvim_get_option_value` throws a warning if both `scope` and `buf` options are used at the same time. This is because using `buf` always implies `scope` is local, and is therefore not needed. There's however no need to error if `scope` is already set "local" as it's the correct value.
* feat(docs): replace lua2dox.luaLewis Russell2024-02-27
| | | | | | | | | | | | | | | | | | | | | | | | | | Problem: The documentation flow (`gen_vimdoc.py`) has several issues: - it's not very versatile - depends on doxygen - doesn't work well with Lua code as it requires an awkward filter script to convert it into pseudo-C. - The intermediate XML files and filters makes it too much like a rube goldberg machine. Solution: Re-implement the flow using Lua, LPEG and treesitter. - `gen_vimdoc.py` is now replaced with `gen_vimdoc.lua` and replicates a portion of the logic. - `lua2dox.lua` is gone! - No more XML files. - Doxygen is now longer used and instead we now use: - LPEG for comment parsing (see `scripts/luacats_grammar.lua` and `scripts/cdoc_grammar.lua`). - LPEG for C parsing (see `scripts/cdoc_parser.lua`) - Lua patterns for Lua parsing (see `scripts/luacats_parser.lua`). - Treesitter for Markdown parsing (see `scripts/text_utils.lua`). - The generated `runtime/doc/*.mpack` files have been removed. - `scripts/gen_eval_files.lua` now instead uses `scripts/cdoc_parser.lua` directly. - Text wrapping is implemented in `scripts/text_utils.lua` and appears to produce more consistent results (the main contributer to the diff of this change).
* refactor(api): make freeing of return-value opt-in instead of opt outbfredl2024-02-21
| | | | | | | | | As only a few API functions make use of explicit freeing of the return value, make it opt-in instead. The arena is always present under the hood, so `Arena *arena` arg now doesn't mean anything other than getting access to this arena. Also it is in principle possible to return an allocated value while still using the arena as scratch space for other stuff (unlikely, but there no reason to not allow it).
* refactor(api): use arena for nvim_get_option_info()bfredl2024-02-09
|
* refactor(IWYU): fix headersdundargoc2024-01-11
| | | | | | Remove `export` pramgas from defs headers as it causes IWYU to believe that the definitions from the defs headers comes from main header, which is not what we really want.
* refactor(options): move some functions from options.c to option.cFamiu Haque2023-12-23
|
* refactor: eliminate cyclic includesdundargoc2023-12-20
|
* refactor: move non-symbols to defs.h headersdundargoc2023-12-17
|
* refactor(options): use hashy for finding options (#26573)Famiu Haque2023-12-17
| | | | | | | | | | | Problem: `findoption()` searches through the options[] table linearly for option names, even though hashy can be used to generate a compile-time hash table for it. Solution: Use hashy to generate a compile time hash table for finding options. This also allows handling option aliases, so we don't need separate options[] table entries for things like 'viminfo'.
* refactor(options): convert `opt_idx` variables to `OptIndex`Famiu Haque2023-12-09
|
* refactor(options): reduce `findoption()` usageFamiu Haque2023-12-09
| | | | | | Problem: Many places in the code use `findoption()` to access an option using its name, even if the option index is available. This is very slow because it requires looping through the options array over and over. Solution: Use option index instead of name wherever possible. Also introduce an `OptIndex` enum which contains the index for every option as enum constants, this eliminates the need to pass static option names as strings.
* refactor(options): split `get_option_value()` into smaller functionsFamiu Haque2023-12-07
| | | | | | Problem: Currently, `get_option_value()` returns 3 separate things: The actual value of the option, whether the option is hidden, and the option flags. This makes the function difficult to refactor, modify or otherwise reason about. Solution: Split `get_option_value()` into 3 functions, each with a single purpose. This also affects `get_option_value_for()`.
* refactor(options): remove SOPT type enums (#26417)Famiu Haque2023-12-06
| | | | | Problem: SOPT type enums (`SOPT_NUM`, `SOPT_BOOL`, `SOPT_STRING`) are no longer used anywhere. Solution: Remove them.
* build: don't define FUNC_ATTR_* as empty in headers (#26317)zeertzjq2023-11-30
| | | | | | FUNC_ATTR_* should only be used in .c files with generated headers. Defining FUNC_ATTR_* as empty in headers causes misuses of them to be silently ignored. Instead don't define them by default, and only define them as empty after a .c file has included its generated header.
* refactor: fix headers with IWYUdundargoc2023-11-28
|