refactor(options): remove .indir, redesign option scopes #31066

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
This commit is contained in:
Famiu Haque 2024-11-17 02:56:16 +06:00 committed by GitHub
parent be8648f345
commit 29ded88957
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 800 additions and 892 deletions

View File

@ -617,8 +617,8 @@ local function render_option_meta(_f, opt, write)
end
for _, s in pairs {
{ 'wo', 'window' },
{ 'bo', 'buffer' },
{ 'wo', 'win' },
{ 'bo', 'buf' },
{ 'go', 'global' },
} do
local id, scope = s[1], s[2]
@ -661,8 +661,8 @@ end
local function scope_to_doc(s)
local m = {
global = 'global',
buffer = 'local to buffer',
window = 'local to window',
buf = 'local to buffer',
win = 'local to window',
tab = 'local to tab page',
}
@ -717,7 +717,7 @@ local function get_option_meta()
local optinfo = vim.api.nvim_get_all_options_info()
local ret = {} --- @type table<string,vim.option_meta>
for _, o in ipairs(opts) do
local is_window_option = #o.scope == 1 and o.scope[1] == 'window'
local is_window_option = #o.scope == 1 and o.scope[1] == 'win'
local is_option_hidden = o.immutable and not o.varname and not is_window_option
if not is_option_hidden and o.desc then
if o.full_name == 'cmdheight' then

View File

@ -308,7 +308,6 @@ set(GENERATOR_C_GRAMMAR ${GENERATOR_DIR}/c_grammar.lua)
set(GENERATOR_HASHY ${GENERATOR_DIR}/hashy.lua)
set(GENERATOR_PRELOAD ${GENERATOR_DIR}/preload.lua)
set(HEADER_GENERATOR ${GENERATOR_DIR}/gen_declarations.lua)
set(OPTIONS_ENUM_GENERATOR ${GENERATOR_DIR}/gen_options_enum.lua)
set(OPTIONS_GENERATOR ${GENERATOR_DIR}/gen_options.lua)
# GENERATED_DIR and GENERATED_INCLUDES_DIR
@ -687,16 +686,11 @@ add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP}
DEPENDS ${LUA_GEN_DEPS} ${EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/auevents.lua
)
add_custom_command(OUTPUT ${GENERATED_OPTIONS}
COMMAND ${LUA_GEN} ${OPTIONS_GENERATOR} ${GENERATED_OPTIONS}
add_custom_command(OUTPUT ${GENERATED_OPTIONS} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP}
COMMAND ${LUA_GEN} ${OPTIONS_GENERATOR} ${GENERATED_OPTIONS} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP}
DEPENDS ${LUA_GEN_DEPS} ${OPTIONS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/options.lua
)
add_custom_command(OUTPUT ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP}
COMMAND ${LUA_GEN} ${OPTIONS_ENUM_GENERATOR} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP}
DEPENDS ${LUA_GEN_DEPS} ${OPTIONS_ENUM_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/options.lua
)
# NVIM_GENERATED_FOR_SOURCES and NVIM_GENERATED_FOR_HEADERS must be mutually exclusive.
foreach(hfile ${NVIM_GENERATED_FOR_HEADERS})
list(FIND NVIM_GENERATED_FOR_SOURCES ${hfile} hfile_idx)

View File

@ -533,7 +533,7 @@ void nvim_set_option(uint64_t channel_id, String name, Object value, Error *err)
FUNC_API_SINCE(1)
FUNC_API_DEPRECATED_SINCE(11)
{
set_option_to(channel_id, NULL, kOptReqGlobal, name, value, err);
set_option_to(channel_id, NULL, kOptScopeGlobal, name, value, err);
}
/// Gets the global value of an option.
@ -546,7 +546,7 @@ Object nvim_get_option(String name, Error *err)
FUNC_API_SINCE(1)
FUNC_API_DEPRECATED_SINCE(11)
{
return get_option_from(NULL, kOptReqGlobal, name, err);
return get_option_from(NULL, kOptScopeGlobal, name, err);
}
/// Gets a buffer option value
@ -566,7 +566,7 @@ Object nvim_buf_get_option(Buffer buffer, String name, Error *err)
return (Object)OBJECT_INIT;
}
return get_option_from(buf, kOptReqBuf, name, err);
return get_option_from(buf, kOptScopeBuf, name, err);
}
/// Sets a buffer option value. Passing `nil` as value deletes the option (only
@ -588,7 +588,7 @@ void nvim_buf_set_option(uint64_t channel_id, Buffer buffer, String name, Object
return;
}
set_option_to(channel_id, buf, kOptReqBuf, name, value, err);
set_option_to(channel_id, buf, kOptScopeBuf, name, value, err);
}
/// Gets a window option value
@ -608,7 +608,7 @@ Object nvim_win_get_option(Window window, String name, Error *err)
return (Object)OBJECT_INIT;
}
return get_option_from(win, kOptReqWin, name, err);
return get_option_from(win, kOptScopeWin, name, err);
}
/// Sets a window option value. Passing `nil` as value deletes the option (only
@ -630,48 +630,18 @@ void nvim_win_set_option(uint64_t channel_id, Window window, String name, Object
return;
}
set_option_to(channel_id, win, kOptReqWin, name, value, err);
}
/// Check if option has a value in the requested scope.
///
/// @param opt_idx Option index in options[] table.
/// @param req_scope Requested option scope. See OptReqScope in option.h.
///
/// @return true if option has a value in the requested scope, false otherwise.
static bool option_has_scope(OptIndex opt_idx, OptReqScope req_scope)
{
if (opt_idx == kOptInvalid) {
return false;
}
vimoption_T *opt = get_option(opt_idx);
// TTY option.
if (is_tty_option(opt->fullname)) {
return req_scope == kOptReqGlobal;
}
switch (req_scope) {
case kOptReqGlobal:
return opt->var != VAR_WIN;
case kOptReqBuf:
return opt->indir & PV_BUF;
case kOptReqWin:
return opt->indir & PV_WIN;
}
UNREACHABLE;
set_option_to(channel_id, win, kOptScopeWin, name, value, err);
}
/// Gets the value of a global or local (buffer, window) option.
///
/// @param[in] from Pointer to buffer or window for local option value.
/// @param req_scope Requested option scope. See OptReqScope in option.h.
/// @param req_scope Requested option scope. See OptScope in option.h.
/// @param name The option name.
/// @param[out] err Details of an error that may have occurred.
///
/// @return the option value.
static Object get_option_from(void *from, OptReqScope req_scope, String name, Error *err)
static Object get_option_from(void *from, OptScope req_scope, String name, Error *err)
{
VALIDATE_S(name.size > 0, "option name", "<empty>", {
return (Object)OBJECT_INIT;
@ -681,7 +651,7 @@ static Object get_option_from(void *from, OptReqScope req_scope, String name, Er
OptVal value = NIL_OPTVAL;
if (option_has_scope(opt_idx, req_scope)) {
value = get_option_value_for(opt_idx, req_scope == kOptReqGlobal ? OPT_GLOBAL : OPT_LOCAL,
value = get_option_value_for(opt_idx, req_scope == kOptScopeGlobal ? OPT_GLOBAL : OPT_LOCAL,
req_scope, from, err);
if (ERROR_SET(err)) {
return (Object)OBJECT_INIT;
@ -698,11 +668,11 @@ static Object get_option_from(void *from, OptReqScope req_scope, String name, Er
/// Sets the value of a global or local (buffer, window) option.
///
/// @param[in] to Pointer to buffer or window for local option value.
/// @param req_scope Requested option scope. See OptReqScope in option.h.
/// @param req_scope Requested option scope. See OptScope in option.h.
/// @param name The option name.
/// @param value New option value.
/// @param[out] err Details of an error that may have occurred.
static void set_option_to(uint64_t channel_id, void *to, OptReqScope req_scope, String name,
static void set_option_to(uint64_t channel_id, void *to, OptScope req_scope, String name,
Object value, Error *err)
{
VALIDATE_S(name.size > 0, "option name", "<empty>", {
@ -725,12 +695,12 @@ static void set_option_to(uint64_t channel_id, void *to, OptReqScope req_scope,
return;
});
int attrs = get_option_attrs(opt_idx);
// For global-win-local options -> setlocal
// For win-local options -> setglobal and setlocal (opt_flags == 0)
const int opt_flags = (req_scope == kOptReqWin && !(attrs & SOPT_GLOBAL))
? 0
: (req_scope == kOptReqGlobal) ? OPT_GLOBAL : OPT_LOCAL;
const int opt_flags
= (req_scope == kOptScopeWin && !option_has_scope(opt_idx, kOptScopeGlobal))
? 0
: ((req_scope == kOptScopeGlobal) ? OPT_GLOBAL : OPT_LOCAL);
WITH_SCRIPT_CONTEXT(channel_id, {
set_option_value_for(name.data, opt_idx, optval, opt_flags, req_scope, to, err);

View File

@ -23,8 +23,8 @@
#endif
static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *opt_idxp,
int *scope, OptReqScope *req_scope, void **from,
char **filetype, Error *err)
int *scope, OptScope *req_scope, void **from, char **filetype,
Error *err)
{
#define HAS_KEY_X(d, v) HAS_KEY(d, option, v)
if (HAS_KEY_X(opts, scope)) {
@ -39,14 +39,14 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
}
}
*req_scope = kOptReqGlobal;
*req_scope = kOptScopeGlobal;
if (filetype != NULL && HAS_KEY_X(opts, filetype)) {
*filetype = opts->filetype.data;
}
if (HAS_KEY_X(opts, win)) {
*req_scope = kOptReqWin;
*req_scope = kOptScopeWin;
*from = find_window_by_handle(opts->win, err);
if (ERROR_SET(err)) {
return FAIL;
@ -59,7 +59,7 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
return FAIL;
});
*scope = OPT_LOCAL;
*req_scope = kOptReqBuf;
*req_scope = kOptScopeBuf;
*from = find_buffer_by_handle(opts->buf, err);
if (ERROR_SET(err)) {
return FAIL;
@ -78,18 +78,17 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
});
*opt_idxp = find_option(name);
int flags = get_option_attrs(*opt_idxp);
if (flags == 0) {
if (*opt_idxp == kOptInvalid) {
// unknown option
api_set_error(err, kErrorTypeValidation, "Unknown option '%s'", name);
} else if (*req_scope == kOptReqBuf || *req_scope == kOptReqWin) {
} else if (*req_scope == kOptScopeBuf || *req_scope == kOptScopeWin) {
// if 'buf' or 'win' is passed, make sure the option supports it
int req_flags = *req_scope == kOptReqBuf ? SOPT_BUF : SOPT_WIN;
if (!(flags & req_flags)) {
char *tgt = *req_scope & kOptReqBuf ? "buf" : "win";
char *global = flags & SOPT_GLOBAL ? "global " : "";
char *req = flags & SOPT_BUF ? "buffer-local "
: flags & SOPT_WIN ? "window-local " : "";
if (!option_has_scope(*opt_idxp, *req_scope)) {
char *tgt = *req_scope == kOptScopeBuf ? "buf" : "win";
char *global = option_has_scope(*opt_idxp, kOptScopeGlobal) ? "global " : "";
char *req = option_has_scope(*opt_idxp, kOptScopeBuf)
? "buffer-local "
: (option_has_scope(*opt_idxp, kOptScopeWin) ? "window-local " : "");
api_set_error(err, kErrorTypeValidation, "'%s' cannot be passed for %s%soption '%s'",
tgt, global, req, name);
@ -153,7 +152,7 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
{
OptIndex opt_idx = 0;
int scope = 0;
OptReqScope req_scope = kOptReqGlobal;
OptScope req_scope = kOptScopeGlobal;
void *from = NULL;
char *filetype = NULL;
@ -218,7 +217,7 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict(
{
OptIndex opt_idx = 0;
int scope = 0;
OptReqScope req_scope = kOptReqGlobal;
OptScope req_scope = kOptScopeGlobal;
void *to = NULL;
if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &to, NULL, err)) {
return;
@ -230,9 +229,8 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict(
// - option is global or local to window (global-local)
//
// Then force scope to local since we don't want to change the global option
if (req_scope == kOptReqWin && scope == 0) {
int flags = get_option_attrs(opt_idx);
if (flags & SOPT_GLOBAL) {
if (req_scope == kOptScopeWin && scope == 0) {
if (option_has_scope(opt_idx, kOptScopeGlobal)) {
scope = OPT_LOCAL;
}
}
@ -305,15 +303,15 @@ Dict nvim_get_option_info2(String name, Dict(option) *opts, Arena *arena, Error
{
OptIndex opt_idx = 0;
int scope = 0;
OptReqScope req_scope = kOptReqGlobal;
OptScope req_scope = kOptScopeGlobal;
void *from = NULL;
if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &from, NULL,
err)) {
return (Dict)ARRAY_DICT_INIT;
}
buf_T *buf = (req_scope == kOptReqBuf) ? (buf_T *)from : curbuf;
win_T *win = (req_scope == kOptReqWin) ? (win_T *)from : curwin;
buf_T *buf = (req_scope == kOptScopeBuf) ? (buf_T *)from : curbuf;
win_T *win = (req_scope == kOptScopeWin) ? (win_T *)from : curwin;
return get_vimoption(name, scope, buf, win, arena, err);
}

View File

@ -1004,10 +1004,10 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
buf_copy_options(buf, BCO_ENTER | BCO_NOHELP);
if (scratch) {
set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL, 0, kOptReqBuf,
buf);
set_option_direct_for(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL, 0, kOptReqBuf,
buf);
set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL, 0,
kOptScopeBuf, buf);
set_option_direct_for(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL, 0,
kOptScopeBuf, buf);
assert(buf->b_ml.ml_mfp->mf_fd < 0); // ml_open() should not have opened swapfile already
buf->b_p_swf = false;
buf->b_p_ml = false;

View File

@ -206,7 +206,7 @@ typedef struct {
OptInt wo_winbl;
#define w_p_winbl w_onebuf_opt.wo_winbl // 'winblend'
LastSet wo_script_ctx[WV_COUNT]; // SCTXs for window-local options
LastSet wo_script_ctx[kWinOptCount]; // SCTXs for window-local options
#define w_p_script_ctx w_onebuf_opt.wo_script_ctx
} winopt_T;
@ -512,7 +512,7 @@ struct file_buffer {
// or contents of the file being edited.
bool b_p_initialized; // set when options initialized
LastSet b_p_script_ctx[BV_COUNT]; // SCTXs for buffer-local options
LastSet b_p_script_ctx[kBufOptCount]; // SCTXs for buffer-local options
int b_p_ai; ///< 'autoindent'
int b_p_ai_nopaste; ///< b_p_ai saved for paste mode

View File

@ -1390,8 +1390,8 @@ void diff_win_options(win_T *wp, bool addbuf)
}
wp->w_p_fdm_save = xstrdup(wp->w_p_fdm);
}
set_option_direct_for(kOptFoldmethod, STATIC_CSTR_AS_OPTVAL("diff"), OPT_LOCAL, 0, kOptReqWin,
wp);
set_option_direct_for(kOptFoldmethod, STATIC_CSTR_AS_OPTVAL("diff"), OPT_LOCAL, 0,
kOptScopeWin, wp);
if (!wp->w_p_diff) {
wp->w_p_fen_save = wp->w_p_fen;

View File

@ -1368,7 +1368,7 @@ int eval_foldexpr(win_T *wp, int *cp)
const bool use_sandbox = was_set_insecurely(wp, kOptFoldexpr, OPT_LOCAL);
char *arg = skipwhite(wp->w_p_fde);
current_sctx = wp->w_p_script_ctx[WV_FDE].script_ctx;
current_sctx = wp->w_p_script_ctx[kWinOptFoldexpr].script_ctx;
emsg_off++;
if (use_sandbox) {

View File

@ -1648,7 +1648,7 @@ static char *eval_includeexpr(const char *const ptr, const size_t len)
{
const sctx_T save_sctx = current_sctx;
set_vim_var_string(VV_FNAME, ptr, (ptrdiff_t)len);
current_sctx = curbuf->b_p_script_ctx[BV_INEX].script_ctx;
current_sctx = curbuf->b_p_script_ctx[kBufOptIncludeexpr].script_ctx;
char *res = eval_to_string_safe(curbuf->b_p_inex,
was_set_insecurely(curwin, kOptIncludeexpr, OPT_LOCAL),

View File

@ -1731,7 +1731,7 @@ char *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldinfo
curwin = wp;
curbuf = wp->w_buffer;
current_sctx = wp->w_p_script_ctx[WV_FDT].script_ctx;
current_sctx = wp->w_p_script_ctx[kWinOptFoldtext].script_ctx;
emsg_off++; // handle exceptions, but don't display errors

View File

@ -1,6 +1,10 @@
local options_file = arg[1]
local options_enum_file = arg[2]
local options_map_file = arg[3]
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 w = function(s)
if s:match('^ %.') then
@ -10,10 +14,129 @@ local w = function(s)
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
--- @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] = {}
end
--- @param s string
--- @return string
local lowercase_to_titlecase = function(s)
return s:sub(1, 1):upper() .. s:sub(2)
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)
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()
local redraw_flags = {
ui_option = 'kOptFlagUIOption',
@ -35,12 +158,6 @@ local list_flags = {
flagscomma = 'kOptFlagComma|kOptFlagFlagList',
}
--- @param s string
--- @return string
local lowercase_to_titlecase = function(s)
return s:sub(1, 1):upper() .. s:sub(2)
end
--- @param o vim.option_meta
--- @return string
local function get_flags(o)
@ -95,6 +212,12 @@ local function opt_type_enum(opt_type)
return ('kOptValType%s'):format(lowercase_to_titlecase(opt_type))
end
--- @param scope vim.option_scope
--- @return string
local function opt_scope_enum(scope)
return ('kOptScope%s'):format(lowercase_to_titlecase(scope))
end
--- @param o vim.option_meta
--- @return string
local function get_type_flags(o)
@ -110,6 +233,35 @@ local function get_type_flags(o)
return type_flags
end
--- @param o vim.option_meta
--- @return string
local function get_scope_flags(o)
local scope_flags = '0'
for _, scope in ipairs(o.scope) do
scope_flags = ('%s | (1 << %s)'):format(scope_flags, opt_scope_enum(scope))
end
return scope_flags
end
--- @param o vim.option_meta
--- @return string
local function get_scope_idx(o)
--- @type string[]
local strs = {}
for _, scope in pairs(valid_scopes) do
local has_scope = vim.tbl_contains(o.scope, scope)
strs[#strs + 1] = (' [%s] = %s'):format(
opt_scope_enum(scope),
get_scope_option(scope, has_scope and o.full_name or 'Invalid')
)
end
return ('{\n%s\n }'):format(table.concat(strs, ',\n'))
end
--- @param c string|string[]
--- @param base_string? string
--- @return string
@ -166,7 +318,6 @@ end
--- @param d vim.option_value|function
--- @param n string
--- @return string
local get_defaults = function(d, n)
if d == nil then
error("option '" .. n .. "' should have a default value")
@ -174,9 +325,6 @@ local get_defaults = function(d, n)
return get_opt_val(d)
end
--- @type [string,string][]
local defines = {}
--- @param i integer
--- @param o vim.option_meta
local function dump_option(i, o)
@ -187,42 +335,28 @@ local function dump_option(i, o)
end
w(' .flags=' .. get_flags(o))
w(' .type_flags=' .. get_type_flags(o))
w(' .scope_flags=' .. get_scope_flags(o))
w(' .scope_idx=' .. get_scope_idx(o))
if o.enable_if then
w(get_cond(o.enable_if))
end
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))
elseif #o.scope == 1 and o.scope[1] == 'window' then
w(' .var=VAR_WIN')
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
else
-- Option must be immutable or have a variable.
assert(false)
w(' .var=NULL')
end
w(' .immutable=' .. (o.immutable and 'true' or 'false'))
if #o.scope == 1 and o.scope[1] == 'global' then
w(' .indir=PV_NONE')
else
assert(#o.scope == 1 or #o.scope == 2)
assert(#o.scope == 1 or o.scope[1] == 'global')
local min_scope = o.scope[#o.scope]
local varname = o.pv_name or o.varname or ('p_' .. (o.abbreviation or o.full_name))
local pv_name = (
'OPT_'
.. min_scope:sub(1, 3):upper()
.. '('
.. (min_scope:sub(1, 1):upper() .. 'V_' .. varname:sub(3):upper())
.. ')'
)
if #o.scope == 2 then
pv_name = 'OPT_BOTH(' .. pv_name .. ')'
end
table.insert(defines, { 'PV_' .. varname:sub(3):upper(), pv_name })
w(' .indir=' .. pv_name)
end
if o.cb then
w(' .opt_did_set_cb=' .. o.cb)
end
@ -235,7 +369,6 @@ local function dump_option(i, o)
w((' .var=&options[%u].def_val.data'):format(i - 1))
-- Option is always immutable on the false branch of `enable_if`.
w(' .immutable=true')
w(' .indir=PV_NONE')
w('#endif')
end
if o.defaults then
@ -256,6 +389,7 @@ local function dump_option(i, o)
w(' },')
end
-- Generate options[] array.
w([[
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
@ -274,8 +408,3 @@ for i, o in ipairs(options.options) do
dump_option(i, o)
end
w('};')
w('')
for _, v in ipairs(defines) do
w('#define ' .. v[1] .. ' ' .. v[2])
end

View File

@ -1,129 +0,0 @@
-- Generates option index enum and map of option name to option index.
-- Handles option full name, short name and aliases.
-- Also generates BV_ and WV_ enum constants.
local options_enum_file = arg[1]
local options_map_file = arg[2]
local options_enum_fd = assert(io.open(options_enum_file, 'w'))
local options_map_fd = assert(io.open(options_map_file, 'w'))
--- @param s string
local function enum_w(s)
options_enum_fd:write(s .. '\n')
end
--- @param s string
local function map_w(s)
options_map_fd:write(s .. '\n')
end
enum_w('// IWYU pragma: private, include "nvim/option_defs.h"')
enum_w('')
--- @param s string
--- @return string
local lowercase_to_titlecase = function(s)
return s:sub(1, 1):upper() .. s:sub(2)
end
--- @type vim.option_meta[]
local options = require('options').options
-- Generate BV_ enum constants.
enum_w('/// "indir" values for buffer-local options.')
enum_w('/// These need to be defined globally, so that the BV_COUNT can be used with')
enum_w('/// b_p_script_stx[].')
enum_w('enum {')
local bv_val = 0
for _, o in ipairs(options) do
assert(#o.scope == 1 or #o.scope == 2)
assert(#o.scope == 1 or o.scope[1] == 'global')
local min_scope = o.scope[#o.scope]
if min_scope == 'buffer' then
local varname = o.pv_name or o.varname or ('p_' .. (o.abbreviation or o.full_name))
local bv_name = 'BV_' .. varname:sub(3):upper()
enum_w((' %s = %u,'):format(bv_name, bv_val))
bv_val = bv_val + 1
end
end
enum_w((' BV_COUNT = %u, ///< must be the last one'):format(bv_val))
enum_w('};')
enum_w('')
-- Generate WV_ enum constants.
enum_w('/// "indir" values for window-local options.')
enum_w('/// These need to be defined globally, so that the WV_COUNT can be used in the')
enum_w('/// window structure.')
enum_w('enum {')
local wv_val = 0
for _, o in ipairs(options) do
assert(#o.scope == 1 or #o.scope == 2)
assert(#o.scope == 1 or o.scope[1] == 'global')
local min_scope = o.scope[#o.scope]
if min_scope == 'window' then
local varname = o.pv_name or o.varname or ('p_' .. (o.abbreviation or o.full_name))
local wv_name = 'WV_' .. varname:sub(3):upper()
enum_w((' %s = %u,'):format(wv_name, wv_val))
wv_val = wv_val + 1
end
end
enum_w((' WV_COUNT = %u, ///< must be the last one'):format(wv_val))
enum_w('};')
enum_w('')
--- @type { [string]: string }
local option_index = {}
-- Generate option index enum and populate the `option_index` dict.
enum_w('typedef enum {')
enum_w(' kOptInvalid = -1,')
for i, o in ipairs(options) 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
end
enum_w(' // Option count, used when iterating through options')
enum_w('#define kOptIndexCount ' .. tostring(#options))
enum_w('} OptIndex;')
enum_w('')
options_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)
options_map_fd:close()

View File

@ -1182,7 +1182,7 @@ int get_expr_indent(void)
sandbox++;
}
textlock++;
current_sctx = curbuf->b_p_script_ctx[BV_INDE].script_ctx;
current_sctx = curbuf->b_p_script_ctx[kBufOptIndentexpr].script_ctx;
// Need to make a copy, the 'indentexpr' option could be changed while
// evaluating it.

File diff suppressed because it is too large Load Diff

View File

@ -13,56 +13,6 @@
#include "nvim/option_defs.h" // IWYU pragma: keep
#include "nvim/types_defs.h" // IWYU pragma: keep
/// The options that are local to a window or buffer have "indir" set to one of
/// these values. Special values:
/// PV_NONE: global option.
/// PV_WIN is added: window-local option
/// PV_BUF is added: buffer-local option
/// PV_BOTH is added: global option which also has a local value.
enum {
PV_BOTH = 0x1000,
PV_WIN = 0x2000,
PV_BUF = 0x4000,
PV_MASK = 0x0fff,
};
#define OPT_WIN(x) (idopt_T)(PV_WIN + (int)(x))
#define OPT_BUF(x) (idopt_T)(PV_BUF + (int)(x))
#define OPT_BOTH(x) (idopt_T)(PV_BOTH + (int)(x))
/// WV_ and BV_ values get typecasted to this for the "indir" field
typedef enum {
PV_NONE = 0,
PV_MAXVAL = 0xffff, ///< to avoid warnings for value out of range
} idopt_T;
// Options local to a window have a value local to a buffer and global to all
// buffers. Indicate this by setting "var" to VAR_WIN.
#define VAR_WIN ((char *)-1)
typedef struct {
char *fullname; ///< full option name
char *shortname; ///< permissible abbreviation
uint32_t flags; ///< see above
OptTypeFlags type_flags; ///< option type flags, see OptValType
void *var; ///< global option: pointer to variable;
///< window-local option: VAR_WIN;
///< buffer-local option: global value
idopt_T indir; ///< global option: PV_NONE;
///< local option: indirect option index
bool immutable; ///< option is immutable, trying to set its value will give an error.
/// 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;
/// callback function to invoke when expanding possible values on the
/// cmdline. Only useful for string options.
opt_expand_cb_T opt_expand_cb;
OptVal def_val; ///< default value
LastSet last_set; ///< script in which the option was last set
} vimoption_T;
/// flags for buf_copy_options()
enum {
BCO_ENTER = 1, ///< going to enter the buffer
@ -85,13 +35,6 @@ typedef enum {
OPT_SKIPRTP = 0x80, ///< "skiprtp" in 'sessionoptions'
} OptionSetFlags;
/// Return value from get_option_attrs().
enum {
SOPT_GLOBAL = 0x01, ///< Option has global value
SOPT_WIN = 0x02, ///< Option has window-local value
SOPT_BUF = 0x04, ///< Option has buffer-local value
};
/// Get name of OptValType as a string.
static inline const char *optval_type_get_name(const OptValType type)
{

View File

@ -54,12 +54,20 @@ typedef enum {
kOptValTypeNumber,
kOptValTypeString,
} OptValType;
/// Always update this whenever a new option type is added.
#define kOptValTypeSize (kOptValTypeString + 1)
typedef uint32_t OptTypeFlags;
/// Scopes that an option can support.
typedef enum {
kOptScopeGlobal = 0, ///< Request global option value
kOptScopeWin, ///< Request window-local option value
kOptScopeBuf, ///< Request buffer-local option value
} OptScope;
/// Always update this whenever a new option scope is added.
#define kOptScopeSize (kOptScopeBuf + 1)
typedef uint8_t OptScopeFlags;
typedef union {
// boolean options are actually tri-states because they have a third "None" value.
TriState boolean;
@ -161,9 +169,26 @@ typedef struct {
/// caller.
typedef int (*opt_expand_cb_T)(optexpand_T *args, int *numMatches, char ***matches);
/// Requested option scopes for various functions in option.c
typedef enum {
kOptReqGlobal = 0, ///< Request global option value
kOptReqWin = 1, ///< Request window-local option value
kOptReqBuf = 2, ///< Request buffer-local option value
} OptReqScope;
typedef struct {
char *fullname; ///< full option name
char *shortname; ///< permissible abbreviation
uint32_t flags; ///< see above
OptTypeFlags type_flags; ///< option type flags, see OptValType
OptScopeFlags scope_flags; ///< option scope flags, see OptScope
void *var; ///< global option: pointer to variable;
///< window-local option: NULL;
///< buffer-local option: global value
ssize_t scope_idx[kOptScopeSize]; ///< index of option at every scope.
bool immutable; ///< option is immutable, trying to set it will give an error.
/// 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;
/// callback function to invoke when expanding possible values on the
/// cmdline. Only useful for string options.
opt_expand_cb_T opt_expand_cb;
OptVal def_val; ///< default value
LastSet last_set; ///< script in which the option was last set
} vimoption_T;

File diff suppressed because it is too large Load Diff

View File

@ -863,7 +863,7 @@ int fex_format(linenr_T lnum, long count, int c)
// Make a copy, the option could be changed while calling it.
char *fex = xstrdup(curbuf->b_p_fex);
current_sctx = curbuf->b_p_script_ctx[BV_FEX].script_ctx;
current_sctx = curbuf->b_p_script_ctx[kBufOptFormatexpr].script_ctx;
// Evaluate the function.
if (use_sandbox) {

View File

@ -6755,8 +6755,8 @@ void win_comp_scroll(win_T *wp)
if (wp->w_p_scr != old_w_p_scr) {
// Used by "verbose set scroll".
wp->w_p_script_ctx[WV_SCROLL].script_ctx.sc_sid = SID_WINLAYOUT;
wp->w_p_script_ctx[WV_SCROLL].script_ctx.sc_lnum = 0;
wp->w_p_script_ctx[kWinOptScroll].script_ctx.sc_sid = SID_WINLAYOUT;
wp->w_p_script_ctx[kWinOptScroll].script_ctx.sc_lnum = 0;
}
}

View File

@ -412,8 +412,8 @@ win_T *win_float_create(bool enter, bool new_buf)
return handle_error_and_cleanup(wp, &err);
}
buf->b_p_bl = false; // unlist
set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("wipe"), OPT_LOCAL, 0, kOptReqBuf,
buf);
set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("wipe"), OPT_LOCAL, 0,
kOptScopeBuf, buf);
win_set_buf(wp, buf, &err);
if (ERROR_SET(&err)) {
return handle_error_and_cleanup(wp, &err);