mirror of
https://github.com/neovim/neovim.git
synced 2025-09-26 21:18:34 +00:00
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:
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user