feat(options)!: disallow setting hidden options #28400

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.
This commit is contained in:
Famiu Haque
2024-11-04 19:00:12 +06:00
committed by GitHub
parent 04d178053f
commit a27419f3fc
18 changed files with 102 additions and 99 deletions

View File

@ -408,7 +408,9 @@ fun! s:SetSaneOpts()
let dict.shellslash = &shellslash let dict.shellslash = &shellslash
let &report = 10 let &report = 10
let &shellslash = 0 if exists('+shellslash')
let &shellslash = 0
endif
return dict return dict
endfun endfun

View File

@ -113,6 +113,8 @@ OPTIONS
of just string |global-local| options. of just string |global-local| options.
• `:setlocal {option}<` copies the global value to the local value for number • `:setlocal {option}<` copies the global value to the local value for number
and boolean |global-local| options instead of removing the local value. and boolean |global-local| options instead of removing the local value.
• Setting |hidden-options| now gives an error. In particular, setting
'noshellslash' is now only allowed on Windows.
PLUGINS PLUGINS

View File

@ -642,11 +642,12 @@ Hidden options *hidden-options*
Not all options are supported in all versions. This depends on the supported Not all options are supported in all versions. This depends on the supported
features and sometimes on the system. A remark about this is in curly braces features and sometimes on the system. A remark about this is in curly braces
below. When an option is not supported it may still be set without getting an below. When an option is not supported, it is called a hidden option. Trying
error, this is called a hidden option. You can't get the value of a hidden to get the value of a hidden option will not give an error, it will return the
option though, it is not stored. default value for that option instead. You can't change the value of a hidden
option.
To test if option "foo" can be used with ":set" use something like this: > To test if "foo" is a valid option name, use something like this: >
if exists('&foo') if exists('&foo')
This also returns true for a hidden option. To test if option "foo" is really This also returns true for a hidden option. To test if option "foo" is really
supported use something like this: > supported use something like this: >
@ -1573,7 +1574,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'completeslash'* *'csl'* *'completeslash'* *'csl'*
'completeslash' 'csl' string (default "") 'completeslash' 'csl' string (default "")
local to buffer local to buffer
only for MS-Windows only modifiable in MS-Windows
When this option is set it overrules 'shellslash' for completion: When this option is set it overrules 'shellslash' for completion:
- When this option is set to "slash", a forward slash is used for path - When this option is set to "slash", a forward slash is used for path
completion in insert mode. This is useful when editing HTML tag, or completion in insert mode. This is useful when editing HTML tag, or
@ -5277,9 +5278,9 @@ A jump table for the options with a short description can be found at |Q_op|.
security reasons. security reasons.
*'shellslash'* *'ssl'* *'noshellslash'* *'nossl'* *'shellslash'* *'ssl'* *'noshellslash'* *'nossl'*
'shellslash' 'ssl' boolean (default off) 'shellslash' 'ssl' boolean (default on, Windows: off)
global global
only for MS-Windows only modifiable in MS-Windows
When set, a forward slash is used when expanding file names. This is When set, a forward slash is used when expanding file names. This is
useful when a Unix-like shell is used instead of cmd.exe. Backward useful when a Unix-like shell is used instead of cmd.exe. Backward
slashes can still be typed, but they are changed to forward slashes by slashes can still be typed, but they are changed to forward slashes by

View File

@ -1106,7 +1106,7 @@ vim.bo.cot = vim.bo.completeopt
vim.go.completeopt = vim.o.completeopt vim.go.completeopt = vim.o.completeopt
vim.go.cot = vim.go.completeopt vim.go.cot = vim.go.completeopt
--- only for MS-Windows --- only modifiable in MS-Windows
--- When this option is set it overrules 'shellslash' for completion: --- When this option is set it overrules 'shellslash' for completion:
--- - When this option is set to "slash", a forward slash is used for path --- - When this option is set to "slash", a forward slash is used for path
--- completion in insert mode. This is useful when editing HTML tag, or --- completion in insert mode. This is useful when editing HTML tag, or
@ -5547,7 +5547,7 @@ vim.o.srr = vim.o.shellredir
vim.go.shellredir = vim.o.shellredir vim.go.shellredir = vim.o.shellredir
vim.go.srr = vim.go.shellredir vim.go.srr = vim.go.shellredir
--- only for MS-Windows --- only modifiable in MS-Windows
--- When set, a forward slash is used when expanding file names. This is --- When set, a forward slash is used when expanding file names. This is
--- useful when a Unix-like shell is used instead of cmd.exe. Backward --- useful when a Unix-like shell is used instead of cmd.exe. Backward
--- slashes can still be typed, but they are changed to forward slashes by --- slashes can still be typed, but they are changed to forward slashes by
@ -5564,7 +5564,7 @@ vim.go.srr = vim.go.shellredir
--- Also see 'completeslash'. --- Also see 'completeslash'.
--- ---
--- @type boolean --- @type boolean
vim.o.shellslash = false vim.o.shellslash = true
vim.o.ssl = vim.o.shellslash vim.o.ssl = vim.o.shellslash
vim.go.shellslash = vim.o.shellslash vim.go.shellslash = vim.o.shellslash
vim.go.ssl = vim.go.shellslash vim.go.ssl = vim.go.shellslash

View File

@ -717,7 +717,9 @@ local function get_option_meta()
local optinfo = vim.api.nvim_get_all_options_info() local optinfo = vim.api.nvim_get_all_options_info()
local ret = {} --- @type table<string,vim.option_meta> local ret = {} --- @type table<string,vim.option_meta>
for _, o in ipairs(opts) do for _, o in ipairs(opts) do
if not o.immutable and not o.hidden and o.enable_if ~= false and o.desc then local is_window_option = #o.scope == 1 and o.scope[1] == 'window'
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 if o.full_name == 'cmdheight' then
table.insert(o.scope, 'tab') table.insert(o.scope, 'tab')
end end

View File

@ -647,10 +647,6 @@ static bool option_has_scope(OptIndex opt_idx, OptReqScope req_scope)
vimoption_T *opt = get_option(opt_idx); vimoption_T *opt = get_option(opt_idx);
// Hidden option.
if (opt->var == NULL) {
return false;
}
// TTY option. // TTY option.
if (is_tty_option(opt->fullname)) { if (is_tty_option(opt->fullname)) {
return req_scope == kOptReqGlobal; return req_scope == kOptReqGlobal;

View File

@ -80,7 +80,7 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
*opt_idxp = find_option(name); *opt_idxp = find_option(name);
int flags = get_option_attrs(*opt_idxp); int flags = get_option_attrs(*opt_idxp);
if (flags == 0) { if (flags == 0) {
// hidden or unknown option // unknown option
api_set_error(err, kErrorTypeValidation, "Unknown option '%s'", name); api_set_error(err, kErrorTypeValidation, "Unknown option '%s'", name);
} else if (*req_scope == kOptReqBuf || *req_scope == kOptReqWin) { } else if (*req_scope == kOptReqBuf || *req_scope == kOptReqWin) {
// if 'buf' or 'win' is passed, make sure the option supports it // if 'buf' or 'win' is passed, make sure the option supports it
@ -175,7 +175,6 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
} }
OptVal value = get_option_value_for(opt_idx, scope, req_scope, from, err); OptVal value = get_option_value_for(opt_idx, scope, req_scope, from, err);
bool hidden = is_option_hidden(opt_idx);
if (ftbuf != NULL) { if (ftbuf != NULL) {
// restore curwin/curbuf and a few other things // restore curwin/curbuf and a few other things
@ -189,7 +188,7 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
goto err; goto err;
} }
VALIDATE_S(!hidden && value.type != kOptValTypeNil, "option", name.data, { VALIDATE_S(value.type != kOptValTypeNil, "option", name.data, {
goto err; goto err;
}); });

View File

@ -191,23 +191,17 @@ local function dump_option(i, o)
w(get_cond(o.enable_if)) w(get_cond(o.enable_if))
end end
-- An option cannot be both hidden and immutable.
assert(not o.hidden or not o.immutable)
local has_var = true
if o.varname then if o.varname then
w(' .var=&' .. o.varname) w(' .var=&' .. o.varname)
elseif o.hidden or o.immutable then elseif o.immutable then
-- Hidden and immutable options can directly point to the default value. -- Immutable options can directly point to the default value.
w((' .var=&options[%u].def_val.data'):format(i - 1)) w((' .var=&options[%u].def_val.data'):format(i - 1))
elseif #o.scope == 1 and o.scope[1] == 'window' then elseif #o.scope == 1 and o.scope[1] == 'window' then
w(' .var=VAR_WIN') w(' .var=VAR_WIN')
else else
has_var = false -- Option must be immutable or have a variable.
assert(false)
end end
-- `enable_if = false` should be present iff there is no variable.
assert((o.enable_if == false) == not has_var)
w(' .hidden=' .. (o.hidden and 'true' or 'false'))
w(' .immutable=' .. (o.immutable and 'true' or 'false')) w(' .immutable=' .. (o.immutable and 'true' or 'false'))
if #o.scope == 1 and o.scope[1] == 'global' then if #o.scope == 1 and o.scope[1] == 'global' then
w(' .indir=PV_NONE') w(' .indir=PV_NONE')
@ -237,7 +231,10 @@ local function dump_option(i, o)
end end
if o.enable_if then if o.enable_if then
w('#else') w('#else')
w(' .var=NULL') -- Hidden option directly points to default value.
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(' .indir=PV_NONE')
w('#endif') w('#endif')
end end

View File

@ -565,13 +565,16 @@ static char *find_dup_item(char *origval, const char *newval, const size_t newva
void free_all_options(void) void free_all_options(void)
{ {
for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) {
if (options[opt_idx].indir == PV_NONE) { bool hidden = is_option_hidden(opt_idx);
if (options[opt_idx].indir == PV_NONE || hidden) {
// global option: free value and default value. // global option: free value and default value.
if (options[opt_idx].var != NULL) { // hidden option: free default value only.
if (!hidden) {
optval_free(optval_from_varp(opt_idx, options[opt_idx].var)); optval_free(optval_from_varp(opt_idx, options[opt_idx].var));
} }
} else if (options[opt_idx].var != VAR_WIN) { } else if (options[opt_idx].var != VAR_WIN) {
// buffer-local option: free global value // buffer-local option: free global value.
optval_free(optval_from_varp(opt_idx, options[opt_idx].var)); optval_free(optval_from_varp(opt_idx, options[opt_idx].var));
} }
optval_free(options[opt_idx].def_val); optval_free(options[opt_idx].def_val);
@ -1244,16 +1247,6 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char *
uint32_t flags = 0; // flags for current option uint32_t flags = 0; // flags for current option
void *varp = NULL; // pointer to variable for current option void *varp = NULL; // pointer to variable for current option
if (options[opt_idx].var == NULL) { // hidden option: skip
// Only give an error message when requesting the value of
// a hidden option, ignore setting it.
if (vim_strchr("=:!&<", nextchar) == NULL
&& (!option_has_type(opt_idx, kOptValTypeBoolean) || nextchar == '?')) {
*errmsg = e_unsupportedoption;
}
return;
}
flags = options[opt_idx].flags; flags = options[opt_idx].flags;
varp = get_varp_scope(&(options[opt_idx]), opt_flags); varp = get_varp_scope(&(options[opt_idx]), opt_flags);
@ -1325,11 +1318,6 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char *
} }
} }
// Don't try to change hidden option.
if (varp == NULL) {
return;
}
OptVal newval = get_option_newval(opt_idx, opt_flags, prefix, argp, nextchar, op, flags, varp, OptVal newval = get_option_newval(opt_idx, opt_flags, prefix, argp, nextchar, op, flags, varp,
errbuf, errbuflen, errmsg); errbuf, errbuflen, errmsg);
@ -1587,7 +1575,7 @@ char *find_shada_parameter(int type)
static char *option_expand(OptIndex opt_idx, char *val) static char *option_expand(OptIndex opt_idx, char *val)
{ {
// if option doesn't need expansion nothing to do // if option doesn't need expansion nothing to do
if (!(options[opt_idx].flags & kOptFlagExpand) || options[opt_idx].var == NULL) { if (!(options[opt_idx].flags & kOptFlagExpand) || is_option_hidden(opt_idx)) {
return NULL; return NULL;
} }
@ -2863,6 +2851,8 @@ static const char *validate_num_option(OptIndex opt_idx, void *varp, OptInt *new
} else if (value > p_wiw) { } else if (value > p_wiw) {
return e_winwidth; return e_winwidth;
} }
} else if (varp == &p_mco) {
*newval = MAX_MCO;
} else if (varp == &p_titlelen) { } else if (varp == &p_titlelen) {
if (value < 0) { if (value < 0) {
return e_positive; return e_positive;
@ -3161,6 +3151,7 @@ static OptValType option_get_type(const OptIndex opt_idx)
/// ///
/// @return Option value stored in varp. /// @return Option value stored in varp.
OptVal optval_from_varp(OptIndex opt_idx, void *varp) OptVal optval_from_varp(OptIndex opt_idx, void *varp)
FUNC_ATTR_NONNULL_ARG(2)
{ {
// Special case: 'modified' is b_changed, but we also want to consider it set when 'ff' or 'fenc' // Special case: 'modified' is b_changed, but we also want to consider it set when 'ff' or 'fenc'
// changed. // changed.
@ -3170,7 +3161,7 @@ OptVal optval_from_varp(OptIndex opt_idx, void *varp)
if (option_is_multitype(opt_idx)) { if (option_is_multitype(opt_idx)) {
// Multitype options are stored as OptVal. // Multitype options are stored as OptVal.
return varp == NULL ? NIL_OPTVAL : *(OptVal *)varp; return *(OptVal *)varp;
} }
OptValType type = option_get_type(opt_idx); OptValType type = option_get_type(opt_idx);
@ -3179,11 +3170,11 @@ OptVal optval_from_varp(OptIndex opt_idx, void *varp)
case kOptValTypeNil: case kOptValTypeNil:
return NIL_OPTVAL; return NIL_OPTVAL;
case kOptValTypeBoolean: case kOptValTypeBoolean:
return BOOLEAN_OPTVAL(varp == NULL ? false : TRISTATE_FROM_INT(*(int *)varp)); return BOOLEAN_OPTVAL(TRISTATE_FROM_INT(*(int *)varp));
case kOptValTypeNumber: case kOptValTypeNumber:
return NUMBER_OPTVAL(varp == NULL ? 0 : *(OptInt *)varp); return NUMBER_OPTVAL(*(OptInt *)varp);
case kOptValTypeString: case kOptValTypeString:
return STRING_OPTVAL(varp == NULL ? (String)STRING_INIT : cstr_as_string(*(char **)varp)); return STRING_OPTVAL(cstr_as_string(*(char **)varp));
} }
UNREACHABLE; UNREACHABLE;
} }
@ -3319,7 +3310,10 @@ static char *option_get_valid_types(OptIndex opt_idx)
/// @return True if option is hidden, false otherwise. Returns false if option name is invalid. /// @return True if option is hidden, false otherwise. Returns false if option name is invalid.
bool is_option_hidden(OptIndex opt_idx) bool is_option_hidden(OptIndex opt_idx)
{ {
return opt_idx == kOptInvalid ? false : get_varp(&options[opt_idx]) == NULL; // Hidden options are always immutable and point to their default value
return opt_idx == kOptInvalid
? false
: (options[opt_idx].immutable && options[opt_idx].var == &options[opt_idx].def_val.data);
} }
static inline bool option_is_global_local(OptIndex opt_idx) static inline bool option_is_global_local(OptIndex opt_idx)
@ -3459,8 +3453,8 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
.os_win = curwin .os_win = curwin
}; };
if (direct || opt->hidden) { if (direct) {
// Don't do any extra processing if setting directly or if option is hidden. // Don't do any extra processing if setting directly.
} }
// Disallow changing immutable options. // Disallow changing immutable options.
else if (opt->immutable && !optval_equal(old_value, new_value)) { else if (opt->immutable && !optval_equal(old_value, new_value)) {
@ -3489,7 +3483,7 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
// If option is hidden or if an error is detected, restore the previous value and don't do any // If option is hidden or if an error is detected, restore the previous value and don't do any
// further processing. // further processing.
if (opt->hidden || errmsg != NULL) { if (errmsg != NULL) {
set_option_varp(opt_idx, varp, old_value, true); set_option_varp(opt_idx, varp, old_value, true);
// When resetting some values, need to act on it. // When resetting some values, need to act on it.
if (restore_chartab) { if (restore_chartab) {
@ -3749,7 +3743,7 @@ void set_option_direct(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set
vimoption_T *opt = get_option(opt_idx); vimoption_T *opt = get_option(opt_idx);
if (opt->var == NULL) { if (is_option_hidden(opt_idx)) {
return; return;
} }
@ -3960,11 +3954,6 @@ int get_option_attrs(OptIndex opt_idx)
vimoption_T *opt = get_option(opt_idx); vimoption_T *opt = get_option(opt_idx);
// Hidden option
if (opt->var == NULL) {
return 0;
}
int attrs = 0; int attrs = 0;
if (opt->indir == PV_NONE || (opt->indir & PV_BOTH)) { if (opt->indir == PV_NONE || (opt->indir & PV_BOTH)) {
@ -3976,6 +3965,7 @@ int get_option_attrs(OptIndex opt_idx)
attrs |= SOPT_BUF; attrs |= SOPT_BUF;
} }
assert(attrs != 0);
return attrs; return attrs;
} }
@ -4143,8 +4133,8 @@ static int optval_default(OptIndex opt_idx, void *varp)
{ {
vimoption_T *opt = &options[opt_idx]; vimoption_T *opt = &options[opt_idx];
// Hidden or immutable options always use their default value. // Hidden options always use their default value.
if (varp == NULL || opt->hidden || opt->immutable) { if (is_option_hidden(opt_idx)) {
return true; return true;
} }
@ -4554,9 +4544,9 @@ void *get_option_varp_scope_from(OptIndex opt_idx, int scope, buf_T *buf, win_T
void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win) void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win)
{ {
// hidden option, always return NULL // hidden options always use the same var pointer
if (p->var == NULL) { if (is_option_hidden(get_opt_idx(p))) {
return NULL; return p->var;
} }
switch ((int)p->indir) { switch ((int)p->indir) {
@ -5456,7 +5446,7 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
} }
nextchar = *p; nextchar = *p;
opt_idx = find_option_len(arg, (size_t)(p - arg)); opt_idx = find_option_len(arg, (size_t)(p - arg));
if (opt_idx == kOptInvalid || options[opt_idx].var == NULL) { if (opt_idx == kOptInvalid || is_option_hidden(opt_idx)) {
xp->xp_context = EXPAND_NOTHING; xp->xp_context = EXPAND_NOTHING;
return; return;
} }
@ -5680,7 +5670,7 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, char *fuzzystr, int *numM
char *str; char *str;
for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) {
str = options[opt_idx].fullname; str = options[opt_idx].fullname;
if (options[opt_idx].var == NULL) { if (is_option_hidden(opt_idx)) {
continue; continue;
} }
if (xp->xp_context == EXPAND_BOOL_SETTINGS if (xp->xp_context == EXPAND_BOOL_SETTINGS
@ -5928,6 +5918,7 @@ int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, c
static void option_value2string(vimoption_T *opt, int scope) static void option_value2string(vimoption_T *opt, int scope)
{ {
void *varp = get_varp_scope(opt, scope); void *varp = get_varp_scope(opt, scope);
assert(varp != NULL);
if (option_has_type(get_opt_idx(opt), kOptValTypeNumber)) { if (option_has_type(get_opt_idx(opt), kOptValTypeNumber)) {
OptInt wc = 0; OptInt wc = 0;
@ -5944,9 +5935,8 @@ static void option_value2string(vimoption_T *opt, int scope)
} }
} else { // string } else { // string
varp = *(char **)(varp); varp = *(char **)(varp);
if (varp == NULL) { // Just in case.
NameBuff[0] = NUL; if (opt->flags & kOptFlagExpand) {
} else if (opt->flags & kOptFlagExpand) {
home_replace(NULL, varp, NameBuff, MAXPATHL, false); home_replace(NULL, varp, NameBuff, MAXPATHL, false);
} else { } else {
xstrlcpy(NameBuff, varp, MAXPATHL); xstrlcpy(NameBuff, varp, MAXPATHL);

View File

@ -49,7 +49,6 @@ typedef struct {
///< buffer-local option: global value ///< buffer-local option: global value
idopt_T indir; ///< global option: PV_NONE; idopt_T indir; ///< global option: PV_NONE;
///< local option: indirect option index ///< local option: indirect option index
bool hidden; ///< option is hidden, any attempt to set its value will be ignored.
bool immutable; ///< option is immutable, trying to set its value will give an error. 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 /// callback function to invoke after an option is modified to validate and

View File

@ -530,6 +530,7 @@ EXTERN char *p_mef; ///< 'makeef'
EXTERN char *p_mp; ///< 'makeprg' EXTERN char *p_mp; ///< 'makeprg'
EXTERN char *p_mps; ///< 'matchpairs' EXTERN char *p_mps; ///< 'matchpairs'
EXTERN OptInt p_mat; ///< 'matchtime' EXTERN OptInt p_mat; ///< 'matchtime'
EXTERN OptInt p_mco; ///< 'maxcombine'
EXTERN OptInt p_mfd; ///< 'maxfuncdepth' EXTERN OptInt p_mfd; ///< 'maxfuncdepth'
EXTERN OptInt p_mmd; ///< 'maxmapdepth' EXTERN OptInt p_mmd; ///< 'maxmapdepth'
EXTERN OptInt p_mmp; ///< 'maxmempattern' EXTERN OptInt p_mmp; ///< 'maxmempattern'

View File

@ -13,7 +13,7 @@
--- @field list? 'comma'|'onecomma'|'commacolon'|'onecommacolon'|'flags'|'flagscomma' --- @field list? 'comma'|'onecomma'|'commacolon'|'onecommacolon'|'flags'|'flagscomma'
--- @field scope vim.option_scope[] --- @field scope vim.option_scope[]
--- @field deny_duplicates? boolean --- @field deny_duplicates? boolean
--- @field enable_if? string|false --- @field enable_if? string
--- @field defaults? vim.option_defaults --- @field defaults? vim.option_defaults
--- @field secure? true --- @field secure? true
--- @field noglob? true --- @field noglob? true
@ -87,11 +87,11 @@ return {
{ {
abbreviation = 'al', abbreviation = 'al',
defaults = { if_true = 224 }, defaults = { if_true = 224 },
enable_if = false,
full_name = 'aleph', full_name = 'aleph',
scope = { 'global' }, scope = { 'global' },
short_desc = N_('ASCII code of the letter Aleph (Hebrew)'), short_desc = N_('ASCII code of the letter Aleph (Hebrew)'),
type = 'number', type = 'number',
immutable = true,
}, },
{ {
abbreviation = 'ari', abbreviation = 'ari',
@ -789,11 +789,11 @@ return {
current Use the current directory. current Use the current directory.
{path} Use the specified directory {path} Use the specified directory
]=], ]=],
enable_if = false,
full_name = 'browsedir', full_name = 'browsedir',
scope = { 'global' }, scope = { 'global' },
short_desc = N_('which directory to start browsing in'), short_desc = N_('which directory to start browsing in'),
type = 'string', type = 'string',
immutable = true,
}, },
{ {
abbreviation = 'bh', abbreviation = 'bh',
@ -1493,7 +1493,7 @@ return {
cb = 'did_set_completeslash', cb = 'did_set_completeslash',
defaults = { if_true = '' }, defaults = { if_true = '' },
desc = [=[ desc = [=[
only for MS-Windows only modifiable in MS-Windows
When this option is set it overrules 'shellslash' for completion: When this option is set it overrules 'shellslash' for completion:
- When this option is set to "slash", a forward slash is used for path - When this option is set to "slash", a forward slash is used for path
completion in insert mode. This is useful when editing HTML tag, or completion in insert mode. This is useful when editing HTML tag, or
@ -3791,12 +3791,12 @@ return {
try to keep 'lines' and 'columns' the same when adding and try to keep 'lines' and 'columns' the same when adding and
removing GUI components. removing GUI components.
]=], ]=],
enable_if = false,
full_name = 'guioptions', full_name = 'guioptions',
list = 'flags', list = 'flags',
scope = { 'global' }, scope = { 'global' },
short_desc = N_('GUI: Which components and options are used'), short_desc = N_('GUI: Which components and options are used'),
type = 'string', type = 'string',
immutable = true,
}, },
{ {
abbreviation = 'gtl', abbreviation = 'gtl',
@ -3816,13 +3816,13 @@ return {
present in 'guioptions'. For the non-GUI tab pages line 'tabline' is present in 'guioptions'. For the non-GUI tab pages line 'tabline' is
used. used.
]=], ]=],
enable_if = false,
full_name = 'guitablabel', full_name = 'guitablabel',
modelineexpr = true, modelineexpr = true,
redraw = { 'current_window' }, redraw = { 'current_window' },
scope = { 'global' }, scope = { 'global' },
short_desc = N_('GUI: custom label for a tab page'), short_desc = N_('GUI: custom label for a tab page'),
type = 'string', type = 'string',
immutable = true,
}, },
{ {
abbreviation = 'gtt', abbreviation = 'gtt',
@ -3835,12 +3835,12 @@ return {
let &guitabtooltip = "line one\nline two" let &guitabtooltip = "line one\nline two"
< <
]=], ]=],
enable_if = false,
full_name = 'guitabtooltip', full_name = 'guitabtooltip',
redraw = { 'current_window' }, redraw = { 'current_window' },
scope = { 'global' }, scope = { 'global' },
short_desc = N_('GUI: custom tooltip for a tab page'), short_desc = N_('GUI: custom tooltip for a tab page'),
type = 'string', type = 'string',
immutable = true,
}, },
{ {
abbreviation = 'hf', abbreviation = 'hf',
@ -4082,11 +4082,11 @@ return {
English characters directly, e.g., when it's used to type accented English characters directly, e.g., when it's used to type accented
characters with dead keys. characters with dead keys.
]=], ]=],
enable_if = false,
full_name = 'imcmdline', full_name = 'imcmdline',
scope = { 'global' }, scope = { 'global' },
short_desc = N_('use IM when starting to edit a command line'), short_desc = N_('use IM when starting to edit a command line'),
type = 'boolean', type = 'boolean',
immutable = true,
}, },
{ {
abbreviation = 'imd', abbreviation = 'imd',
@ -4100,11 +4100,11 @@ return {
Currently this option is on by default for SGI/IRIX machines. This Currently this option is on by default for SGI/IRIX machines. This
may change in later releases. may change in later releases.
]=], ]=],
enable_if = false,
full_name = 'imdisable', full_name = 'imdisable',
scope = { 'global' }, scope = { 'global' },
short_desc = N_('do not use the IM in any mode'), short_desc = N_('do not use the IM in any mode'),
type = 'boolean', type = 'boolean',
immutable = true,
}, },
{ {
abbreviation = 'imi', abbreviation = 'imi',
@ -5236,7 +5236,7 @@ return {
scope = { 'global' }, scope = { 'global' },
short_desc = N_('maximum nr of combining characters displayed'), short_desc = N_('maximum nr of combining characters displayed'),
type = 'number', type = 'number',
hidden = true, varname = 'p_mco',
}, },
{ {
abbreviation = 'mfd', abbreviation = 'mfd',
@ -5732,13 +5732,13 @@ return {
indicate no input when the hit-enter prompt is displayed (since indicate no input when the hit-enter prompt is displayed (since
clicking the mouse has no effect in this state.) clicking the mouse has no effect in this state.)
]=], ]=],
enable_if = false,
full_name = 'mouseshape', full_name = 'mouseshape',
list = 'onecomma', list = 'onecomma',
scope = { 'global' }, scope = { 'global' },
short_desc = N_('shape of the mouse pointer in different modes'), short_desc = N_('shape of the mouse pointer in different modes'),
tags = { 'E547' }, tags = { 'E547' },
type = 'string', type = 'string',
immutable = true,
}, },
{ {
abbreviation = 'mouset', abbreviation = 'mouset',
@ -5898,11 +5898,11 @@ return {
Note that on Windows editing "aux.h", "lpt1.txt" and the like also Note that on Windows editing "aux.h", "lpt1.txt" and the like also
result in editing a device. result in editing a device.
]=], ]=],
enable_if = false,
full_name = 'opendevice', full_name = 'opendevice',
scope = { 'global' }, scope = { 'global' },
short_desc = N_('allow reading/writing devices on MS-Windows'), short_desc = N_('allow reading/writing devices on MS-Windows'),
type = 'boolean', type = 'boolean',
immutable = true,
}, },
{ {
abbreviation = 'opfunc', abbreviation = 'opfunc',
@ -5975,11 +5975,11 @@ return {
{ {
abbreviation = 'pt', abbreviation = 'pt',
defaults = { if_true = '' }, defaults = { if_true = '' },
enable_if = false,
full_name = 'pastetoggle', full_name = 'pastetoggle',
scope = { 'global' }, scope = { 'global' },
short_desc = N_('No description'), short_desc = N_('No description'),
type = 'string', type = 'string',
immutable = true,
}, },
{ {
abbreviation = 'pex', abbreviation = 'pex',
@ -7270,9 +7270,14 @@ return {
{ {
abbreviation = 'ssl', abbreviation = 'ssl',
cb = 'did_set_shellslash', cb = 'did_set_shellslash',
defaults = { if_true = false }, defaults = {
condition = 'MSWIN',
if_true = false,
if_false = true,
doc = 'on, Windows: off',
},
desc = [=[ desc = [=[
only for MS-Windows only modifiable in MS-Windows
When set, a forward slash is used when expanding file names. This is When set, a forward slash is used when expanding file names. This is
useful when a Unix-like shell is used instead of cmd.exe. Backward useful when a Unix-like shell is used instead of cmd.exe. Backward
slashes can still be typed, but they are changed to forward slashes by slashes can still be typed, but they are changed to forward slashes by
@ -8885,11 +8890,11 @@ return {
{ {
abbreviation = 'tenc', abbreviation = 'tenc',
defaults = { if_true = '' }, defaults = { if_true = '' },
enable_if = false,
full_name = 'termencoding', full_name = 'termencoding',
scope = { 'global' }, scope = { 'global' },
short_desc = N_('Terminal encoding'), short_desc = N_('Terminal encoding'),
type = 'string', type = 'string',
immutable = true,
}, },
{ {
abbreviation = 'tgc', abbreviation = 'tgc',

View File

@ -21,7 +21,9 @@ describe('runtime:', function()
exec('set rtp+=' .. plug_dir) exec('set rtp+=' .. plug_dir)
exec([[ exec([[
set shell=doesnotexist set shell=doesnotexist
set completeslash=slash if exists('+completeslash')
set completeslash=slash
endif
set isfname+=(,) set isfname+=(,)
]]) ]])
end) end)

View File

@ -392,7 +392,7 @@ for option in options
let fullname = option.full_name let fullname = option.full_name
let shortname = get(option, 'abbreviation', fullname) let shortname = get(option, 'abbreviation', fullname)
if get(option, 'immutable', v:false) if !exists('+' .. fullname)
continue continue
endif endif

View File

@ -2003,7 +2003,10 @@ func Test_Cmdline()
au! CmdlineLeave au! CmdlineLeave
let save_shellslash = &shellslash let save_shellslash = &shellslash
set noshellslash " Nvim doesn't allow setting value of a hidden option to non-default value
if exists('+shellslash')
set noshellslash
endif
au! CmdlineEnter / let g:entered = expand('<afile>') au! CmdlineEnter / let g:entered = expand('<afile>')
au! CmdlineLeave / let g:left = expand('<afile>') au! CmdlineLeave / let g:left = expand('<afile>')
let g:entered = 0 let g:entered = 0

View File

@ -2319,6 +2319,7 @@ endfunc
" Test for 'imcmdline' and 'imsearch' " Test for 'imcmdline' and 'imsearch'
" This test doesn't actually test the input method functionality. " This test doesn't actually test the input method functionality.
func Test_cmdline_inputmethod() func Test_cmdline_inputmethod()
throw 'Skipped: Nvim does not allow setting the value of a hidden option'
new new
call setline(1, ['', 'abc', '']) call setline(1, ['', 'abc', ''])
set imcmdline set imcmdline

View File

@ -10,9 +10,12 @@ func Test_compiler()
let save_LC_ALL = $LC_ALL let save_LC_ALL = $LC_ALL
let $LC_ALL= "C" let $LC_ALL= "C"
" %:S does not work properly with 'shellslash' set
let save_shellslash = &shellslash let save_shellslash = &shellslash
set noshellslash " Nvim doesn't allow setting value of a hidden option to non-default value
if exists('+shellslash')
" %:S does not work properly with 'shellslash' set
set noshellslash
endif
e Xfoo.pl e Xfoo.pl
compiler perl compiler perl

View File

@ -799,10 +799,10 @@ func Test_expr_completion()
call assert_equal('"echo 1 || g:tvar1 g:tvar2', @:) call assert_equal('"echo 1 || g:tvar1 g:tvar2', @:)
" completion for options " completion for options
call feedkeys(":echo &compat\<C-A>\<C-B>\"\<CR>", 'xt') "call feedkeys(":echo &compat\<C-A>\<C-B>\"\<CR>", 'xt')
call assert_equal('"echo &compatible', @:) "call assert_equal('"echo &compatible', @:)
call feedkeys(":echo 1 && &compat\<C-A>\<C-B>\"\<CR>", 'xt') "call feedkeys(":echo 1 && &compat\<C-A>\<C-B>\"\<CR>", 'xt')
call assert_equal('"echo 1 && &compatible', @:) "call assert_equal('"echo 1 && &compatible', @:)
call feedkeys(":echo &g:equala\<C-A>\<C-B>\"\<CR>", 'xt') call feedkeys(":echo &g:equala\<C-A>\<C-B>\"\<CR>", 'xt')
call assert_equal('"echo &g:equalalways', @:) call assert_equal('"echo &g:equalalways', @:)