Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(option): DRY up get/set option value #19038

Merged
merged 1 commit into from
Jun 22, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 72 additions & 131 deletions src/nvim/api/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,75 +21,87 @@
# include "api/options.c.generated.h"
#endif

/// Gets the value of an option. The behavior of this function matches that of
/// |:set|: the local value of an option is returned if it exists; otherwise,
/// the global value is returned. Local values always correspond to the current
/// buffer or window, unless "buf" or "win" is set in {opts}.
///
/// @param name Option name
/// @param opts Optional parameters
/// - scope: One of "global" or "local". Analogous to
/// |:setglobal| and |:setlocal|, respectively.
/// - win: |window-ID|. Used for getting window local options.
/// - buf: Buffer number. Used for getting buffer local options.
/// Implies {scope} is "local".
/// @param[out] err Error details, if any
/// @return Option value
Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
FUNC_API_SINCE(9)
static int validate_option_value_args(Dict(option) *opts, int *scope, int *opt_type, void **from,
Error *err)
{
Object rv = OBJECT_INIT;

int scope = 0;
if (opts->scope.type == kObjectTypeString) {
if (!strcmp(opts->scope.data.string.data, "local")) {
scope = OPT_LOCAL;
*scope = OPT_LOCAL;
} else if (!strcmp(opts->scope.data.string.data, "global")) {
scope = OPT_GLOBAL;
*scope = OPT_GLOBAL;
} else {
api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'");
goto end;
return FAIL;
}
} else if (HAS_KEY(opts->scope)) {
api_set_error(err, kErrorTypeValidation, "invalid value for key: scope");
goto end;
return FAIL;
}

int opt_type = SREQ_GLOBAL;
void *from = NULL;
*opt_type = SREQ_GLOBAL;

if (opts->win.type == kObjectTypeInteger) {
opt_type = SREQ_WIN;
from = find_window_by_handle((int)opts->win.data.integer, err);
*opt_type = SREQ_WIN;
*from = find_window_by_handle((int)opts->win.data.integer, err);
} else if (HAS_KEY(opts->win)) {
api_set_error(err, kErrorTypeValidation, "invalid value for key: win");
goto end;
return FAIL;
}

if (opts->buf.type == kObjectTypeInteger) {
scope = OPT_LOCAL;
opt_type = SREQ_BUF;
from = find_buffer_by_handle((int)opts->buf.data.integer, err);
*scope = OPT_LOCAL;
*opt_type = SREQ_BUF;
*from = find_buffer_by_handle((int)opts->buf.data.integer, err);
} else if (HAS_KEY(opts->buf)) {
api_set_error(err, kErrorTypeValidation, "invalid value for key: buf");
goto end;
return FAIL;
}

if (HAS_KEY(opts->scope) && HAS_KEY(opts->buf)) {
api_set_error(err, kErrorTypeValidation, "scope and buf cannot be used together");
goto end;
return FAIL;
}

if (HAS_KEY(opts->win) && HAS_KEY(opts->buf)) {
api_set_error(err, kErrorTypeValidation, "buf and win cannot be used together");
goto end;
return FAIL;
}

return OK;
}

/// Gets the value of an option. The behavior of this function matches that of
/// |:set|: the local value of an option is returned if it exists; otherwise,
/// the global value is returned. Local values always correspond to the current
/// buffer or window, unless "buf" or "win" is set in {opts}.
///
/// @param name Option name
/// @param opts Optional parameters
/// - scope: One of "global" or "local". Analogous to
/// |:setglobal| and |:setlocal|, respectively.
/// - win: |window-ID|. Used for getting window local options.
/// - buf: Buffer number. Used for getting buffer local options.
/// Implies {scope} is "local".
/// @param[out] err Error details, if any
/// @return Option value
Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
FUNC_API_SINCE(9)
{
Object rv = OBJECT_INIT;

int scope = 0;
int opt_type = SREQ_GLOBAL;
void *from = NULL;
if (!validate_option_value_args(opts, &scope, &opt_type, &from, err)) {
return rv;
}

long numval = 0;
char *stringval = NULL;
int result = get_option_value_for(name.data, &numval, &stringval, scope, opt_type, from, err);
int result = access_option_value_for(name.data, &numval, &stringval, scope, opt_type, from,
true, err);
if (ERROR_SET(err)) {
goto end;
return rv;
}

switch (result) {
Expand All @@ -114,10 +126,9 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
break;
default:
api_set_error(err, kErrorTypeValidation, "unknown option '%s'", name.data);
goto end;
return rv;
}

end:
return rv;
}

Expand All @@ -139,47 +150,9 @@ void nvim_set_option_value(String name, Object value, Dict(option) *opts, Error
FUNC_API_SINCE(9)
{
int scope = 0;
if (opts->scope.type == kObjectTypeString) {
if (!strcmp(opts->scope.data.string.data, "local")) {
scope = OPT_LOCAL;
} else if (!strcmp(opts->scope.data.string.data, "global")) {
scope = OPT_GLOBAL;
} else {
api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'");
return;
}
} else if (HAS_KEY(opts->scope)) {
api_set_error(err, kErrorTypeValidation, "invalid value for key: scope");
return;
}

int opt_type = SREQ_GLOBAL;
void *to = NULL;

if (opts->win.type == kObjectTypeInteger) {
opt_type = SREQ_WIN;
to = find_window_by_handle((int)opts->win.data.integer, err);
} else if (HAS_KEY(opts->win)) {
api_set_error(err, kErrorTypeValidation, "invalid value for key: win");
return;
}

if (opts->buf.type == kObjectTypeInteger) {
scope = OPT_LOCAL;
opt_type = SREQ_BUF;
to = find_buffer_by_handle((int)opts->buf.data.integer, err);
} else if (HAS_KEY(opts->buf)) {
api_set_error(err, kErrorTypeValidation, "invalid value for key: buf");
return;
}

if (HAS_KEY(opts->scope) && HAS_KEY(opts->buf)) {
api_set_error(err, kErrorTypeValidation, "scope and buf cannot be used together");
return;
}

if (HAS_KEY(opts->win) && HAS_KEY(opts->buf)) {
api_set_error(err, kErrorTypeValidation, "buf and win cannot be used together");
if (!validate_option_value_args(opts, &scope, &opt_type, &to, err)) {
return;
}

Expand All @@ -204,7 +177,7 @@ void nvim_set_option_value(String name, Object value, Dict(option) *opts, Error
return;
}

set_option_value_for(name.data, numval, stringval, scope, opt_type, to, err);
access_option_value_for(name.data, &numval, &stringval, scope, opt_type, to, false, err);
}

/// Gets the option information for all options.
Expand Down Expand Up @@ -441,7 +414,7 @@ void set_option_to(uint64_t channel_id, void *to, int type, String name, Object
}
}

int numval = 0;
long numval = 0;
char *stringval = NULL;

if (flags & SOPT_BOOL) {
Expand Down Expand Up @@ -486,66 +459,30 @@ void set_option_to(uint64_t channel_id, void *to, int type, String name, Object
? 0 : (type == SREQ_GLOBAL)
? OPT_GLOBAL : OPT_LOCAL;

set_option_value_for(name.data, numval, stringval,
opt_flags, type, to, err);
access_option_value_for(name.data, &numval, &stringval, opt_flags, type, to, false, err);
});
}

void set_option_value_for(char *key, long numval, char *stringval, int opt_flags, int opt_type,
void *from, Error *err)
static int access_option_value(char *key, long *numval, char **stringval, int opt_flags, bool get,
Error *err)
{
switchwin_T switchwin;
aco_save_T aco;

try_start();
switch (opt_type) {
case SREQ_WIN:
if (switch_win_noblock(&switchwin, (win_T *)from, win_find_tabpage((win_T *)from), true)
== FAIL) {
restore_win_noblock(&switchwin, true);
if (get) {
return get_option_value(key, numval, stringval, opt_flags);
} else {
char *errmsg;
if ((errmsg = set_option_value(key, *numval, *stringval, opt_flags))) {
if (try_end(err)) {
return;
return 0;
}
api_set_error(err,
kErrorTypeException,
"Problem while switching windows");
return;
}
set_option_value_err(key, numval, stringval, opt_flags, err);
restore_win_noblock(&switchwin, true);
break;
case SREQ_BUF:
aucmd_prepbuf(&aco, (buf_T *)from);
set_option_value_err(key, numval, stringval, opt_flags, err);
aucmd_restbuf(&aco);
break;
case SREQ_GLOBAL:
set_option_value_err(key, numval, stringval, opt_flags, err);
break;
}

if (ERROR_SET(err)) {
return;
}

try_end(err);
}

static void set_option_value_err(char *key, long numval, char *stringval, int opt_flags, Error *err)
{
char *errmsg;

if ((errmsg = set_option_value(key, numval, stringval, opt_flags))) {
if (try_end(err)) {
return;
api_set_error(err, kErrorTypeException, "%s", errmsg);
}

api_set_error(err, kErrorTypeException, "%s", errmsg);
return 0;
}
}

int get_option_value_for(char *key, long *numval, char **stringval, int opt_flags, int opt_type,
void *from, Error *err)
static int access_option_value_for(char *key, long *numval, char **stringval, int opt_flags,
int opt_type, void *from, bool get, Error *err)
{
switchwin_T switchwin;
aco_save_T aco;
Expand All @@ -565,19 +502,23 @@ int get_option_value_for(char *key, long *numval, char **stringval, int opt_flag
"Problem while switching windows");
return result;
}
result = get_option_value(key, numval, stringval, opt_flags);
result = access_option_value(key, numval, stringval, opt_flags, get, err);
restore_win_noblock(&switchwin, true);
break;
case SREQ_BUF:
aucmd_prepbuf(&aco, (buf_T *)from);
result = get_option_value(key, numval, stringval, opt_flags);
result = access_option_value(key, numval, stringval, opt_flags, get, err);
aucmd_restbuf(&aco);
break;
case SREQ_GLOBAL:
result = get_option_value(key, numval, stringval, opt_flags);
result = access_option_value(key, numval, stringval, opt_flags, get, err);
break;
}

if (ERROR_SET(err)) {
return result;
}

try_end(err);

return result;
Expand Down