mirror of
https://github.com/neovim/neovim.git
synced 2026-08-03 22:28:43 +00:00
feat(options): schema, "dict" options, messages
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.
This commit is contained in:
@@ -95,46 +95,6 @@
|
||||
local M = {}
|
||||
local api = vim.api
|
||||
|
||||
-- TODO(tjdevries): Improve option metadata so that this doesn't have to be hardcoded.
|
||||
local key_value_options = {
|
||||
fillchars = true,
|
||||
fcs = true,
|
||||
listchars = true,
|
||||
lcs = true,
|
||||
winhighlight = true,
|
||||
winhl = true,
|
||||
}
|
||||
|
||||
--- @nodoc
|
||||
--- @class vim._option.Info : vim.api.keyset.get_option_info
|
||||
--- @field metatype 'boolean'|'string'|'number'|'map'|'array'|'set'
|
||||
|
||||
--- Convert a vimoption_T style dictionary to the correct OptionType associated with it.
|
||||
---@return string
|
||||
local function get_option_metatype(name, info)
|
||||
if info.type == 'string' then
|
||||
if info.flaglist then
|
||||
return 'set'
|
||||
elseif info.commalist then
|
||||
if key_value_options[name] then
|
||||
return 'map'
|
||||
end
|
||||
return 'array'
|
||||
end
|
||||
return 'string'
|
||||
end
|
||||
return info.type
|
||||
end
|
||||
|
||||
--- @param name string
|
||||
--- @return vim._option.Info
|
||||
local function get_options_info(name)
|
||||
local info = api.nvim_get_option_info2(name)
|
||||
--- @cast info vim._option.Info
|
||||
info.metatype = get_option_metatype(name, info)
|
||||
return info
|
||||
end
|
||||
|
||||
--- Gets or sets environment variables in the current editor process. See |expand-env| and
|
||||
--- |:let-environment| for the Vimscript behavior. Invalid or unset key returns `nil`.
|
||||
---
|
||||
@@ -305,113 +265,6 @@ vim.bo = new_buf_opt_accessor()
|
||||
--- ```
|
||||
vim.wo = new_win_opt_accessor()
|
||||
|
||||
local function passthrough(_, x)
|
||||
return x
|
||||
end
|
||||
|
||||
-- Map of OptionType to functions that take vimoption_T values and convert to Lua values.
|
||||
-- Each function takes (info, vim_value) -> lua_value
|
||||
local to_lua_value = {
|
||||
boolean = passthrough,
|
||||
number = passthrough,
|
||||
string = passthrough,
|
||||
|
||||
array = function(_, value)
|
||||
-- Empty strings mean that there is nothing there,
|
||||
-- so empty table should be returned.
|
||||
if value == '' then
|
||||
return {}
|
||||
end
|
||||
|
||||
-- Handles unescaped commas in a list.
|
||||
if value:find(',,,') then
|
||||
--- @type string, string
|
||||
local left, right = unpack(vim.split(value, ',,,'))
|
||||
|
||||
local result = {}
|
||||
vim.list_extend(result, vim.split(left, ','))
|
||||
table.insert(result, ',')
|
||||
vim.list_extend(result, vim.split(right, ','))
|
||||
|
||||
table.sort(result)
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
if value:find(',^,,', 1, true) then
|
||||
--- @type string, string
|
||||
local left, right = unpack(vim.split(value, ',^,,', { plain = true }))
|
||||
|
||||
local result = {}
|
||||
vim.list_extend(result, vim.split(left, ','))
|
||||
table.insert(result, '^,')
|
||||
vim.list_extend(result, vim.split(right, ','))
|
||||
|
||||
table.sort(result)
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
return vim.split(value, ',')
|
||||
end,
|
||||
|
||||
set = function(info, value)
|
||||
if type(value) == 'table' then
|
||||
return value
|
||||
end
|
||||
|
||||
-- Empty strings mean that there is nothing there,
|
||||
-- so empty table should be returned.
|
||||
if value == '' then
|
||||
return {}
|
||||
end
|
||||
|
||||
assert(info.flaglist, 'That is the only one I know how to handle')
|
||||
|
||||
local result = {} --- @type table<string,true>
|
||||
|
||||
if info.flaglist and info.commalist then
|
||||
local split_value = vim.split(value, ',')
|
||||
for _, v in ipairs(split_value) do
|
||||
result[v] = true
|
||||
end
|
||||
else
|
||||
for i = 1, #value do
|
||||
result[value:sub(i, i)] = true
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end,
|
||||
|
||||
map = function(info, raw_value)
|
||||
if type(raw_value) == 'table' then
|
||||
return raw_value
|
||||
end
|
||||
|
||||
assert(info.commalist, 'Only commas are supported currently')
|
||||
|
||||
local result = {} --- @type table<string,string>
|
||||
|
||||
local comma_split = vim.split(raw_value, ',')
|
||||
for _, key_value_str in ipairs(comma_split) do
|
||||
--- @type string, string
|
||||
local key, value = unpack(vim.split(key_value_str, ':'))
|
||||
key = vim.trim(key)
|
||||
|
||||
result[key] = value
|
||||
end
|
||||
|
||||
return result
|
||||
end,
|
||||
}
|
||||
|
||||
--- Converts a vimoption_T style value to a Lua value
|
||||
function M.convert_value_to_lua(name, option_value)
|
||||
local info = get_options_info(name) or error('Not a valid option name: ' .. name)
|
||||
return to_lua_value[info.metatype](info, option_value)
|
||||
end
|
||||
|
||||
local function create_option_accessor(scope)
|
||||
--- @diagnostic disable-next-line: no-unknown
|
||||
local option_mt
|
||||
@@ -433,7 +286,12 @@ local function create_option_accessor(scope)
|
||||
|
||||
option_mt = {
|
||||
get = function(self)
|
||||
return M.convert_value_to_lua(self._name, self._value)
|
||||
-- `nvim_set_option_value(dry_run)` returns the value in its structured form.
|
||||
return api.nvim_set_option_value(
|
||||
self._name,
|
||||
self._value,
|
||||
{ operation = 'set', scope = scope, dry_run = true }
|
||||
)
|
||||
end,
|
||||
|
||||
append = function(self, right)
|
||||
|
||||
Reference in New Issue
Block a user