mirror of
https://github.com/neovim/neovim.git
synced 2025-09-05 19:08:15 +00:00
refactor(options): generic expand and did_set callbacks (#32011)
* refactor(options): generic expand and did_set callbacks Problem: Many options have similar callbacks to check the values are valid. Solution: Generalize these callbacks into a single function that reads the option table. * refactor: gen_options.lua refactor: gen_options.lua - inline get_cond * refactor(options): use a simpler format for the common default
This commit is contained in:
2
runtime/lua/vim/_meta/options.lua
generated
2
runtime/lua/vim/_meta/options.lua
generated
@@ -4947,7 +4947,7 @@ vim.wo.rl = vim.wo.rightleft
|
||||
--- This is useful for languages such as Hebrew, Arabic and Farsi.
|
||||
--- The 'rightleft' option must be set for 'rightleftcmd' to take effect.
|
||||
---
|
||||
--- @type 'search'
|
||||
--- @type string
|
||||
vim.o.rightleftcmd = "search"
|
||||
vim.o.rlc = vim.o.rightleftcmd
|
||||
vim.wo.rightleftcmd = vim.o.rightleftcmd
|
||||
|
@@ -1,237 +1,30 @@
|
||||
local options_file = arg[1]
|
||||
local options_enum_file = arg[2]
|
||||
local options_map_file = arg[3]
|
||||
local option_vars_file = arg[4]
|
||||
|
||||
local opt_fd = assert(io.open(options_file, 'w'))
|
||||
local opt_enum_fd = assert(io.open(options_enum_file, 'w'))
|
||||
local opt_map_fd = assert(io.open(options_map_file, 'w'))
|
||||
local opt_vars_fd = assert(io.open(option_vars_file, 'w'))
|
||||
|
||||
local w = function(s)
|
||||
if s:match('^ %.') then
|
||||
opt_fd:write(s .. ',\n')
|
||||
else
|
||||
opt_fd:write(s .. '\n')
|
||||
end
|
||||
end
|
||||
|
||||
--- @param s string
|
||||
local function enum_w(s)
|
||||
opt_enum_fd:write(s .. '\n')
|
||||
end
|
||||
|
||||
--- @param s string
|
||||
local function map_w(s)
|
||||
opt_map_fd:write(s .. '\n')
|
||||
end
|
||||
|
||||
local function vars_w(s)
|
||||
opt_vars_fd:write(s .. '\n')
|
||||
end
|
||||
|
||||
--- @module 'nvim.options'
|
||||
local options = require('options')
|
||||
local options_meta = options.options
|
||||
|
||||
local cstr = options.cstr
|
||||
local valid_scopes = options.valid_scopes
|
||||
|
||||
--- Options for each scope.
|
||||
--- @type table<string, vim.option_meta[]>
|
||||
local scope_options = {}
|
||||
for _, scope in ipairs(valid_scopes) do
|
||||
scope_options[scope] = {}
|
||||
--- @param o vim.option_meta
|
||||
--- @return string
|
||||
local function get_values_var(o)
|
||||
return ('opt_%s_values'):format(o.abbreviation or o.full_name)
|
||||
end
|
||||
|
||||
--- @param s string
|
||||
--- @return string
|
||||
local lowercase_to_titlecase = function(s)
|
||||
local function lowercase_to_titlecase(s)
|
||||
return table.concat(vim.tbl_map(function(word) --- @param word string
|
||||
return word:sub(1, 1):upper() .. word:sub(2)
|
||||
end, vim.split(s, '[-_]')))
|
||||
end
|
||||
|
||||
-- Generate options enum file
|
||||
enum_w('// IWYU pragma: private, include "nvim/option_defs.h"')
|
||||
enum_w('')
|
||||
|
||||
--- Map of option name to option index
|
||||
--- @type table<string, string>
|
||||
local option_index = {}
|
||||
|
||||
-- Generate option index enum and populate the `option_index` and `scope_option` dicts.
|
||||
enum_w('typedef enum {')
|
||||
enum_w(' kOptInvalid = -1,')
|
||||
|
||||
for i, o in ipairs(options_meta) do
|
||||
local enum_val_name = 'kOpt' .. lowercase_to_titlecase(o.full_name)
|
||||
enum_w((' %s = %u,'):format(enum_val_name, i - 1))
|
||||
|
||||
option_index[o.full_name] = enum_val_name
|
||||
|
||||
if o.abbreviation then
|
||||
option_index[o.abbreviation] = enum_val_name
|
||||
end
|
||||
|
||||
if o.alias then
|
||||
o.alias = type(o.alias) == 'string' and { o.alias } or o.alias
|
||||
|
||||
for _, v in ipairs(o.alias) do
|
||||
option_index[v] = enum_val_name
|
||||
end
|
||||
end
|
||||
|
||||
for _, scope in ipairs(o.scope) do
|
||||
table.insert(scope_options[scope], o)
|
||||
end
|
||||
end
|
||||
|
||||
enum_w(' // Option count')
|
||||
enum_w('#define kOptCount ' .. tostring(#options_meta))
|
||||
enum_w('} OptIndex;')
|
||||
|
||||
--- @param scope string
|
||||
--- @param option_name string
|
||||
--- @return string
|
||||
local get_scope_option = function(scope, option_name)
|
||||
local function get_scope_option(scope, option_name)
|
||||
return ('k%sOpt%s'):format(lowercase_to_titlecase(scope), lowercase_to_titlecase(option_name))
|
||||
end
|
||||
|
||||
-- Generate option index enum for each scope
|
||||
for _, scope in ipairs(valid_scopes) do
|
||||
enum_w('')
|
||||
|
||||
local scope_name = lowercase_to_titlecase(scope)
|
||||
enum_w('typedef enum {')
|
||||
enum_w((' %s = -1,'):format(get_scope_option(scope, 'Invalid')))
|
||||
|
||||
for idx, option in ipairs(scope_options[scope]) do
|
||||
enum_w((' %s = %u,'):format(get_scope_option(scope, option.full_name), idx - 1))
|
||||
end
|
||||
|
||||
enum_w((' // %s option count'):format(scope_name))
|
||||
enum_w(('#define %s %d'):format(get_scope_option(scope, 'Count'), #scope_options[scope]))
|
||||
enum_w(('} %sOptIndex;'):format(scope_name))
|
||||
end
|
||||
|
||||
-- Generate reverse lookup from option scope index to option index for each scope.
|
||||
for _, scope in ipairs(valid_scopes) do
|
||||
enum_w('')
|
||||
enum_w(('EXTERN const OptIndex %s_opt_idx[] INIT( = {'):format(scope))
|
||||
for _, option in ipairs(scope_options[scope]) do
|
||||
local idx = option_index[option.full_name]
|
||||
enum_w((' [%s] = %s,'):format(get_scope_option(scope, option.full_name), idx))
|
||||
end
|
||||
enum_w('});')
|
||||
end
|
||||
|
||||
opt_enum_fd:close()
|
||||
|
||||
-- Generate option index map.
|
||||
local hashy = require('generators.hashy')
|
||||
local neworder, hashfun = hashy.hashy_hash('find_option', vim.tbl_keys(option_index), function(idx)
|
||||
return ('option_hash_elems[%s].name'):format(idx)
|
||||
end)
|
||||
|
||||
map_w('static const struct { const char *name; OptIndex opt_idx; } option_hash_elems[] = {')
|
||||
|
||||
for _, name in ipairs(neworder) do
|
||||
assert(option_index[name] ~= nil)
|
||||
map_w((' { .name = "%s", .opt_idx = %s },'):format(name, option_index[name]))
|
||||
end
|
||||
|
||||
map_w('};\n')
|
||||
map_w('static ' .. hashfun)
|
||||
|
||||
opt_map_fd:close()
|
||||
|
||||
vars_w('// IWYU pragma: private, include "nvim/option_vars.h"')
|
||||
|
||||
-- Generate enums for option flags.
|
||||
for _, option in ipairs(options_meta) do
|
||||
if option.flags and (type(option.flags) == 'table' or option.values) then
|
||||
vars_w('')
|
||||
vars_w('typedef enum {')
|
||||
|
||||
local opt_name = lowercase_to_titlecase(option.abbreviation or option.full_name)
|
||||
--- @type table<string,integer>
|
||||
local enum_values
|
||||
|
||||
if type(option.flags) == 'table' then
|
||||
enum_values = option.flags --[[ @as table<string,integer> ]]
|
||||
else
|
||||
enum_values = {}
|
||||
for i, flag_name in ipairs(option.values) do
|
||||
assert(type(flag_name) == 'string')
|
||||
enum_values[flag_name] = math.pow(2, i - 1)
|
||||
end
|
||||
end
|
||||
|
||||
-- Sort the keys by the flag value so that the enum can be generated in order.
|
||||
--- @type string[]
|
||||
local flag_names = vim.tbl_keys(enum_values)
|
||||
table.sort(flag_names, function(a, b)
|
||||
return enum_values[a] < enum_values[b]
|
||||
end)
|
||||
|
||||
for _, flag_name in pairs(flag_names) do
|
||||
vars_w(
|
||||
(' kOpt%sFlag%s = 0x%02x,'):format(
|
||||
opt_name,
|
||||
lowercase_to_titlecase(flag_name:gsub(':$', '')),
|
||||
enum_values[flag_name]
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
vars_w(('} Opt%sFlags;'):format(opt_name))
|
||||
end
|
||||
end
|
||||
|
||||
-- Generate valid values for each option.
|
||||
for _, option in ipairs(options_meta) do
|
||||
--- @type function
|
||||
local preorder_traversal
|
||||
--- @param prefix string
|
||||
--- @param values vim.option_valid_values
|
||||
preorder_traversal = function(prefix, values)
|
||||
vars_w('')
|
||||
vars_w(
|
||||
('EXTERN const char *(%s_values[%s]) INIT( = {'):format(prefix, #vim.tbl_keys(values) + 1)
|
||||
)
|
||||
|
||||
--- @type [string,vim.option_valid_values][]
|
||||
local children = {}
|
||||
|
||||
for _, value in ipairs(values) do
|
||||
if type(value) == 'string' then
|
||||
vars_w((' "%s",'):format(value))
|
||||
else
|
||||
assert(type(value) == 'table' and type(value[1]) == 'string' and type(value[2]) == 'table')
|
||||
|
||||
vars_w((' "%s",'):format(value[1]))
|
||||
table.insert(children, value)
|
||||
end
|
||||
end
|
||||
|
||||
vars_w(' NULL')
|
||||
vars_w('});')
|
||||
|
||||
for _, value in pairs(children) do
|
||||
-- Remove trailing colon from the added prefix to prevent syntax errors.
|
||||
preorder_traversal(prefix .. '_' .. value[1]:gsub(':$', ''), value[2])
|
||||
end
|
||||
end
|
||||
|
||||
-- Since option values can be nested, we need to do preorder traversal to generate the values.
|
||||
if option.values then
|
||||
preorder_traversal(('opt_%s'):format(option.abbreviation or option.full_name), option.values)
|
||||
end
|
||||
end
|
||||
|
||||
opt_vars_fd:close()
|
||||
|
||||
local redraw_flags = {
|
||||
ui_option = 'kOptFlagUIOption',
|
||||
tabline = 'kOptFlagRedrTabl',
|
||||
@@ -255,28 +48,29 @@ local list_flags = {
|
||||
--- @param o vim.option_meta
|
||||
--- @return string
|
||||
local function get_flags(o)
|
||||
--- @type string
|
||||
local flags = '0'
|
||||
--- @type string[]
|
||||
local flags = { '0' }
|
||||
|
||||
--- @param f string
|
||||
local add_flag = function(f)
|
||||
flags = flags .. '|' .. f
|
||||
local function add_flag(f)
|
||||
table.insert(flags, f)
|
||||
end
|
||||
|
||||
if o.list then
|
||||
add_flag(list_flags[o.list])
|
||||
end
|
||||
if o.redraw then
|
||||
for _, r_flag in ipairs(o.redraw) do
|
||||
add_flag(redraw_flags[r_flag])
|
||||
end
|
||||
|
||||
for _, r_flag in ipairs(o.redraw or {}) do
|
||||
add_flag(redraw_flags[r_flag])
|
||||
end
|
||||
|
||||
if o.expand then
|
||||
add_flag('kOptFlagExpand')
|
||||
if o.expand == 'nodefault' then
|
||||
add_flag('kOptFlagNoDefExp')
|
||||
end
|
||||
end
|
||||
|
||||
for _, flag_desc in ipairs({
|
||||
{ 'nodefault', 'NoDefault' },
|
||||
{ 'no_mkrc', 'NoMkrc' },
|
||||
@@ -291,13 +85,14 @@ local function get_flags(o)
|
||||
{ 'modelineexpr', 'MLE' },
|
||||
{ 'func' },
|
||||
}) do
|
||||
local key_name = flag_desc[1]
|
||||
local def_name = 'kOptFlag' .. (flag_desc[2] or lowercase_to_titlecase(key_name))
|
||||
local key_name, flag_suffix = flag_desc[1], flag_desc[2]
|
||||
if o[key_name] then
|
||||
local def_name = 'kOptFlag' .. (flag_suffix or lowercase_to_titlecase(key_name))
|
||||
add_flag(def_name)
|
||||
end
|
||||
end
|
||||
return flags
|
||||
|
||||
return table.concat(flags, '|')
|
||||
end
|
||||
|
||||
--- @param opt_type vim.option_type
|
||||
@@ -341,35 +136,15 @@ local function get_scope_idx(o)
|
||||
return ('{\n%s\n }'):format(table.concat(strs, ',\n'))
|
||||
end
|
||||
|
||||
--- @param c string|string[]
|
||||
--- @param base_string? string
|
||||
--- @return string
|
||||
local function get_cond(c, base_string)
|
||||
local cond_string = base_string or '#if '
|
||||
if type(c) == 'table' then
|
||||
cond_string = cond_string .. get_cond(c[1], '')
|
||||
for i, subc in ipairs(c) do
|
||||
if i > 1 then
|
||||
cond_string = cond_string .. ' && ' .. get_cond(subc, '')
|
||||
end
|
||||
end
|
||||
elseif c:sub(1, 1) == '!' then
|
||||
cond_string = cond_string .. '!defined(' .. c:sub(2) .. ')'
|
||||
else
|
||||
cond_string = cond_string .. 'defined(' .. c .. ')'
|
||||
end
|
||||
return cond_string
|
||||
end
|
||||
|
||||
--- @param s string
|
||||
--- @return string
|
||||
local static_cstr_as_string = function(s)
|
||||
local function static_cstr_as_string(s)
|
||||
return ('{ .data = %s, .size = sizeof(%s) - 1 }'):format(s, s)
|
||||
end
|
||||
|
||||
--- @param v vim.option_value|function
|
||||
--- @return string
|
||||
local get_opt_val = function(v)
|
||||
local function get_opt_val(v)
|
||||
--- @type vim.option_type
|
||||
local v_type
|
||||
|
||||
@@ -387,6 +162,7 @@ local get_opt_val = function(v)
|
||||
elseif v_type == 'number' then
|
||||
v = ('%iL'):format(v)
|
||||
elseif v_type == 'string' then
|
||||
--- @cast v string
|
||||
v = static_cstr_as_string(cstr(v))
|
||||
end
|
||||
end
|
||||
@@ -397,7 +173,7 @@ end
|
||||
--- @param d vim.option_value|function
|
||||
--- @param n string
|
||||
--- @return string
|
||||
local get_defaults = function(d, n)
|
||||
local function get_defaults(d, n)
|
||||
if d == nil then
|
||||
error("option '" .. n .. "' should have a default value")
|
||||
end
|
||||
@@ -406,84 +182,354 @@ end
|
||||
|
||||
--- @param i integer
|
||||
--- @param o vim.option_meta
|
||||
local function dump_option(i, o)
|
||||
w(' [' .. ('%u'):format(i - 1) .. ']={')
|
||||
w(' .fullname=' .. cstr(o.full_name))
|
||||
--- @param write fun(...: string)
|
||||
local function dump_option(i, o, write)
|
||||
write(' [', ('%u'):format(i - 1) .. ']={')
|
||||
write(' .fullname=', cstr(o.full_name))
|
||||
if o.abbreviation then
|
||||
w(' .shortname=' .. cstr(o.abbreviation))
|
||||
write(' .shortname=', cstr(o.abbreviation))
|
||||
end
|
||||
w(' .type=' .. opt_type_enum(o.type))
|
||||
w(' .flags=' .. get_flags(o))
|
||||
w(' .scope_flags=' .. get_scope_flags(o))
|
||||
w(' .scope_idx=' .. get_scope_idx(o))
|
||||
write(' .type=', opt_type_enum(o.type))
|
||||
write(' .flags=', get_flags(o))
|
||||
write(' .scope_flags=', get_scope_flags(o))
|
||||
write(' .scope_idx=', get_scope_idx(o))
|
||||
write(' .values=', (o.values and get_values_var(o) or 'NULL'))
|
||||
write(' .values_len=', (o.values and #o.values or '0'))
|
||||
write(' .flags_var=', (o.flags_varname and ('&%s'):format(o.flags_varname) or 'NULL'))
|
||||
if o.enable_if then
|
||||
w(get_cond(o.enable_if))
|
||||
write(('#if defined(%s)'):format(o.enable_if))
|
||||
end
|
||||
|
||||
local is_window_local = #o.scope == 1 and o.scope[1] == 'win'
|
||||
|
||||
if not is_window_local then
|
||||
if o.varname then
|
||||
w(' .var=&' .. o.varname)
|
||||
elseif o.immutable then
|
||||
-- Immutable options can directly point to the default value.
|
||||
w((' .var=&options[%u].def_val.data'):format(i - 1))
|
||||
else
|
||||
-- Option must be immutable or have a variable.
|
||||
assert(false)
|
||||
end
|
||||
if is_window_local then
|
||||
write(' .var=NULL')
|
||||
elseif o.varname then
|
||||
write(' .var=&', o.varname)
|
||||
elseif o.immutable then
|
||||
-- Immutable options can directly point to the default value.
|
||||
write((' .var=&options[%u].def_val.data'):format(i - 1))
|
||||
else
|
||||
w(' .var=NULL')
|
||||
end
|
||||
w(' .immutable=' .. (o.immutable and 'true' or 'false'))
|
||||
if o.cb then
|
||||
w(' .opt_did_set_cb=' .. o.cb)
|
||||
end
|
||||
if o.expand_cb then
|
||||
w(' .opt_expand_cb=' .. o.expand_cb)
|
||||
error('Option must be immutable or have a variable.')
|
||||
end
|
||||
|
||||
write(' .immutable=', (o.immutable and 'true' or 'false'))
|
||||
write(' .opt_did_set_cb=', o.cb or 'NULL')
|
||||
write(' .opt_expand_cb=', o.expand_cb or 'NULL')
|
||||
|
||||
if o.enable_if then
|
||||
w('#else')
|
||||
write('#else')
|
||||
-- Hidden option directly points to default value.
|
||||
w((' .var=&options[%u].def_val.data'):format(i - 1))
|
||||
write((' .var=&options[%u].def_val.data'):format(i - 1))
|
||||
-- Option is always immutable on the false branch of `enable_if`.
|
||||
w(' .immutable=true')
|
||||
w('#endif')
|
||||
write(' .immutable=true')
|
||||
write('#endif')
|
||||
end
|
||||
if o.defaults then
|
||||
if o.defaults.condition then
|
||||
w(get_cond(o.defaults.condition))
|
||||
end
|
||||
w(' .def_val=' .. get_defaults(o.defaults.if_true, o.full_name))
|
||||
if o.defaults.condition then
|
||||
if o.defaults.if_false then
|
||||
w('#else')
|
||||
w(' .def_val=' .. get_defaults(o.defaults.if_false, o.full_name))
|
||||
end
|
||||
w('#endif')
|
||||
|
||||
if not o.defaults then
|
||||
write(' .def_val=NIL_OPTVAL')
|
||||
elseif o.defaults.condition then
|
||||
write(('#if defined(%s)'):format(o.defaults.condition))
|
||||
write(' .def_val=', get_defaults(o.defaults.if_true, o.full_name))
|
||||
if o.defaults.if_false then
|
||||
write('#else')
|
||||
write(' .def_val=', get_defaults(o.defaults.if_false, o.full_name))
|
||||
end
|
||||
write('#endif')
|
||||
else
|
||||
w(' .def_val=NIL_OPTVAL')
|
||||
write(' .def_val=', get_defaults(o.defaults.if_true, o.full_name))
|
||||
end
|
||||
w(' },')
|
||||
|
||||
write(' },')
|
||||
end
|
||||
|
||||
-- Generate options[] array.
|
||||
w([[
|
||||
#include "nvim/ex_docmd.h"
|
||||
#include "nvim/ex_getln.h"
|
||||
#include "nvim/insexpand.h"
|
||||
#include "nvim/mapping.h"
|
||||
#include "nvim/ops.h"
|
||||
#include "nvim/option.h"
|
||||
#include "nvim/optionstr.h"
|
||||
#include "nvim/quickfix.h"
|
||||
#include "nvim/runtime.h"
|
||||
#include "nvim/tag.h"
|
||||
#include "nvim/window.h"
|
||||
--- @param prefix string
|
||||
--- @param values vim.option_valid_values
|
||||
local function preorder_traversal(prefix, values)
|
||||
local out = {} --- @type string[]
|
||||
|
||||
static vimoption_T options[] = {]])
|
||||
for i, o in ipairs(options.options) do
|
||||
dump_option(i, o)
|
||||
local function add(s)
|
||||
table.insert(out, s)
|
||||
end
|
||||
|
||||
add('')
|
||||
add(('EXTERN const char *(%s_values[%s]) INIT( = {'):format(prefix, #vim.tbl_keys(values) + 1))
|
||||
|
||||
--- @type [string,vim.option_valid_values][]
|
||||
local children = {}
|
||||
|
||||
for _, value in ipairs(values) do
|
||||
if type(value) == 'string' then
|
||||
add((' "%s",'):format(value))
|
||||
else
|
||||
assert(type(value) == 'table' and type(value[1]) == 'string' and type(value[2]) == 'table')
|
||||
add((' "%s",'):format(value[1]))
|
||||
table.insert(children, value)
|
||||
end
|
||||
end
|
||||
|
||||
add(' NULL')
|
||||
add('});')
|
||||
|
||||
for _, value in pairs(children) do
|
||||
-- Remove trailing colon from the added prefix to prevent syntax errors.
|
||||
add(preorder_traversal(prefix .. '_' .. value[1]:gsub(':$', ''), value[2]))
|
||||
end
|
||||
|
||||
return table.concat(out, '\n')
|
||||
end
|
||||
w('};')
|
||||
|
||||
--- @param o vim.option_meta
|
||||
--- @return string
|
||||
local function gen_opt_enum(o)
|
||||
local out = {} --- @type string[]
|
||||
|
||||
local function add(s)
|
||||
table.insert(out, s)
|
||||
end
|
||||
|
||||
add('')
|
||||
add('typedef enum {')
|
||||
|
||||
local opt_name = lowercase_to_titlecase(o.abbreviation or o.full_name)
|
||||
--- @type table<string,integer>
|
||||
local enum_values
|
||||
|
||||
if type(o.flags) == 'table' then
|
||||
enum_values = o.flags --[[ @as table<string,integer> ]]
|
||||
else
|
||||
enum_values = {}
|
||||
for i, flag_name in ipairs(o.values) do
|
||||
assert(type(flag_name) == 'string')
|
||||
enum_values[flag_name] = math.pow(2, i - 1)
|
||||
end
|
||||
end
|
||||
|
||||
-- Sort the keys by the flag value so that the enum can be generated in order.
|
||||
--- @type string[]
|
||||
local flag_names = vim.tbl_keys(enum_values)
|
||||
table.sort(flag_names, function(a, b)
|
||||
return enum_values[a] < enum_values[b]
|
||||
end)
|
||||
|
||||
for _, flag_name in pairs(flag_names) do
|
||||
add(
|
||||
(' kOpt%sFlag%s = 0x%02x,'):format(
|
||||
opt_name,
|
||||
lowercase_to_titlecase(flag_name:gsub(':$', '')),
|
||||
enum_values[flag_name]
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
add(('} Opt%sFlags;'):format(opt_name))
|
||||
|
||||
return table.concat(out, '\n')
|
||||
end
|
||||
|
||||
--- @param output_file string
|
||||
--- @return table<string,string> options_index Map of option name to option index
|
||||
local function gen_enums(output_file)
|
||||
--- Options for each scope.
|
||||
--- @type table<string, vim.option_meta[]>
|
||||
local scope_options = {}
|
||||
for _, scope in ipairs(valid_scopes) do
|
||||
scope_options[scope] = {}
|
||||
end
|
||||
|
||||
local fd = assert(io.open(output_file, 'w'))
|
||||
|
||||
--- @param s string
|
||||
local function write(s)
|
||||
fd:write(s)
|
||||
fd:write('\n')
|
||||
end
|
||||
|
||||
-- Generate options enum file
|
||||
write('// IWYU pragma: private, include "nvim/option_defs.h"')
|
||||
write('')
|
||||
|
||||
--- Map of option name to option index
|
||||
--- @type table<string, string>
|
||||
local option_index = {}
|
||||
|
||||
-- Generate option index enum and populate the `option_index` and `scope_option` dicts.
|
||||
write('typedef enum {')
|
||||
write(' kOptInvalid = -1,')
|
||||
|
||||
for i, o in ipairs(options_meta) do
|
||||
local enum_val_name = 'kOpt' .. lowercase_to_titlecase(o.full_name)
|
||||
write((' %s = %u,'):format(enum_val_name, i - 1))
|
||||
|
||||
option_index[o.full_name] = enum_val_name
|
||||
|
||||
if o.abbreviation then
|
||||
option_index[o.abbreviation] = enum_val_name
|
||||
end
|
||||
|
||||
local alias = o.alias or {} --[[@as string[] ]]
|
||||
for _, v in ipairs(alias) do
|
||||
option_index[v] = enum_val_name
|
||||
end
|
||||
|
||||
for _, scope in ipairs(o.scope) do
|
||||
table.insert(scope_options[scope], o)
|
||||
end
|
||||
end
|
||||
|
||||
write(' // Option count')
|
||||
write('#define kOptCount ' .. tostring(#options_meta))
|
||||
write('} OptIndex;')
|
||||
|
||||
-- Generate option index enum for each scope
|
||||
for _, scope in ipairs(valid_scopes) do
|
||||
write('')
|
||||
|
||||
local scope_name = lowercase_to_titlecase(scope)
|
||||
write('typedef enum {')
|
||||
write((' %s = -1,'):format(get_scope_option(scope, 'Invalid')))
|
||||
|
||||
for idx, option in ipairs(scope_options[scope]) do
|
||||
write((' %s = %u,'):format(get_scope_option(scope, option.full_name), idx - 1))
|
||||
end
|
||||
|
||||
write((' // %s option count'):format(scope_name))
|
||||
write(('#define %s %d'):format(get_scope_option(scope, 'Count'), #scope_options[scope]))
|
||||
write(('} %sOptIndex;'):format(scope_name))
|
||||
end
|
||||
|
||||
-- Generate reverse lookup from option scope index to option index for each scope.
|
||||
for _, scope in ipairs(valid_scopes) do
|
||||
write('')
|
||||
write(('EXTERN const OptIndex %s_opt_idx[] INIT( = {'):format(scope))
|
||||
for _, option in ipairs(scope_options[scope]) do
|
||||
local idx = option_index[option.full_name]
|
||||
write((' [%s] = %s,'):format(get_scope_option(scope, option.full_name), idx))
|
||||
end
|
||||
write('});')
|
||||
end
|
||||
|
||||
fd:close()
|
||||
|
||||
return option_index
|
||||
end
|
||||
|
||||
--- @param output_file string
|
||||
--- @param option_index table<string,string>
|
||||
local function gen_map(output_file, option_index)
|
||||
-- Generate option index map.
|
||||
local hashy = require('generators.hashy')
|
||||
|
||||
local neworder, hashfun = hashy.hashy_hash(
|
||||
'find_option',
|
||||
vim.tbl_keys(option_index),
|
||||
function(idx)
|
||||
return ('option_hash_elems[%s].name'):format(idx)
|
||||
end
|
||||
)
|
||||
|
||||
local fd = assert(io.open(output_file, 'w'))
|
||||
|
||||
--- @param s string
|
||||
local function write(s)
|
||||
fd:write(s)
|
||||
fd:write('\n')
|
||||
end
|
||||
|
||||
write('static const struct { const char *name; OptIndex opt_idx; } option_hash_elems[] = {')
|
||||
|
||||
for _, name in ipairs(neworder) do
|
||||
assert(option_index[name] ~= nil)
|
||||
write((' { .name = "%s", .opt_idx = %s },'):format(name, option_index[name]))
|
||||
end
|
||||
|
||||
write('};')
|
||||
write('')
|
||||
write('static ' .. hashfun)
|
||||
|
||||
fd:close()
|
||||
end
|
||||
|
||||
--- @param output_file string
|
||||
local function gen_vars(output_file)
|
||||
local fd = assert(io.open(output_file, 'w'))
|
||||
|
||||
--- @param s string
|
||||
local function write(s)
|
||||
fd:write(s)
|
||||
fd:write('\n')
|
||||
end
|
||||
|
||||
write('// IWYU pragma: private, include "nvim/option_vars.h"')
|
||||
|
||||
-- Generate enums for option flags.
|
||||
for _, o in ipairs(options_meta) do
|
||||
if o.flags and (type(o.flags) == 'table' or o.values) then
|
||||
write(gen_opt_enum(o))
|
||||
end
|
||||
end
|
||||
|
||||
-- Generate valid values for each option.
|
||||
for _, option in ipairs(options_meta) do
|
||||
-- Since option values can be nested, we need to do preorder traversal to generate the values.
|
||||
if option.values then
|
||||
local values_var = ('opt_%s'):format(option.abbreviation or option.full_name)
|
||||
write(preorder_traversal(values_var, option.values))
|
||||
end
|
||||
end
|
||||
|
||||
fd:close()
|
||||
end
|
||||
|
||||
--- @param output_file string
|
||||
local function gen_options(output_file)
|
||||
local fd = assert(io.open(output_file, 'w'))
|
||||
|
||||
--- @param ... string
|
||||
local function write(...)
|
||||
local s = table.concat({ ... }, '')
|
||||
fd:write(s)
|
||||
if s:match('^ %.') then
|
||||
fd:write(',')
|
||||
end
|
||||
fd:write('\n')
|
||||
end
|
||||
|
||||
-- Generate options[] array.
|
||||
write([[
|
||||
#include "nvim/ex_docmd.h"
|
||||
#include "nvim/ex_getln.h"
|
||||
#include "nvim/insexpand.h"
|
||||
#include "nvim/mapping.h"
|
||||
#include "nvim/ops.h"
|
||||
#include "nvim/option.h"
|
||||
#include "nvim/optionstr.h"
|
||||
#include "nvim/quickfix.h"
|
||||
#include "nvim/runtime.h"
|
||||
#include "nvim/tag.h"
|
||||
#include "nvim/window.h"
|
||||
|
||||
static vimoption_T options[] = {]])
|
||||
|
||||
for i, o in ipairs(options_meta) do
|
||||
dump_option(i, o, write)
|
||||
end
|
||||
|
||||
write('};')
|
||||
|
||||
fd:close()
|
||||
end
|
||||
|
||||
local function main()
|
||||
local options_file = arg[1]
|
||||
local options_enum_file = arg[2]
|
||||
local options_map_file = arg[3]
|
||||
local option_vars_file = arg[4]
|
||||
|
||||
local option_index = gen_enums(options_enum_file)
|
||||
gen_map(options_map_file, option_index)
|
||||
gen_vars(option_vars_file)
|
||||
gen_options(options_file)
|
||||
end
|
||||
|
||||
main()
|
||||
|
@@ -55,7 +55,7 @@ function M.build_pos_hash(strings)
|
||||
end
|
||||
|
||||
function M.switcher(put, tab, maxlen, worst_buck_size)
|
||||
local neworder = {}
|
||||
local neworder = {} --- @type string[]
|
||||
put ' switch (len) {\n'
|
||||
local bucky = worst_buck_size > 1
|
||||
for len = 1, maxlen do
|
||||
|
@@ -5715,6 +5715,7 @@ int ExpandStringSetting(expand_T *xp, regmatch_T *regmatch, int *numMatches, cha
|
||||
|
||||
optexpand_T args = {
|
||||
.oe_varp = get_varp_scope(&options[expand_option_idx], expand_option_flags),
|
||||
.oe_idx = expand_option_idx,
|
||||
.oe_append = expand_option_append,
|
||||
.oe_regmatch = regmatch,
|
||||
.oe_xp = xp,
|
||||
|
@@ -134,6 +134,7 @@ typedef const char *(*opt_did_set_cb_T)(optset_T *args);
|
||||
typedef struct {
|
||||
/// Pointer to the option variable. It's always a string.
|
||||
char *oe_varp;
|
||||
OptIndex oe_idx;
|
||||
/// The original option value, escaped.
|
||||
char *oe_opt_value;
|
||||
|
||||
@@ -174,9 +175,13 @@ typedef struct {
|
||||
void *var; ///< global option: pointer to variable;
|
||||
///< window-local option: NULL;
|
||||
///< buffer-local option: global value
|
||||
unsigned *flags_var;
|
||||
ssize_t scope_idx[kOptScopeSize]; ///< index of option at every scope.
|
||||
bool immutable; ///< option is immutable, trying to set it will give an error.
|
||||
|
||||
const char **values; ///< possible values for string options
|
||||
const size_t values_len; ///< length of values array
|
||||
|
||||
/// callback function to invoke after an option is modified to validate and
|
||||
/// apply the new value.
|
||||
opt_did_set_cb_T opt_did_set_cb;
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user