diff options
-rw-r--r-- | src/api/helpers.c | 189 | ||||
-rw-r--r-- | src/api/helpers.h | 19 | ||||
-rw-r--r-- | src/api/vim.c | 13 | ||||
-rw-r--r-- | src/api/vim.h | 10 | ||||
-rw-r--r-- | src/option.c | 177 | ||||
-rw-r--r-- | src/option.h | 9 | ||||
-rw-r--r-- | src/option_defs.h | 18 | ||||
-rw-r--r-- | src/vim.h | 5 |
8 files changed, 415 insertions, 25 deletions
diff --git a/src/api/helpers.c b/src/api/helpers.c index 584249fcc6..40888f351d 100644 --- a/src/api/helpers.c +++ b/src/api/helpers.c @@ -5,8 +5,11 @@ #include "api/helpers.h" #include "api/defs.h" #include "../vim.h" +#include "../window.h" #include "memory.h" #include "eval.h" +#include "option.h" +#include "option_defs.h" #include "lib/khash.h" @@ -22,6 +25,20 @@ static Object vim_to_object_rec(typval_T *obj, khash_t(Lookup) *lookup); static bool object_to_vim(Object obj, typval_T *tv, Error *err); +static void set_option_value_for(char *key, + int numval, + char *stringval, + int opt_flags, + int opt_type, + void *from, + Error *err); + +static void set_option_value_err(char *key, + int numval, + char *stringval, + int opt_flags, + Error *err); + void try_start() { ++trylevel; @@ -134,6 +151,110 @@ Object dict_set_value(dict_T *dict, String key, Object value, Error *err) return rv; } +Object get_option_from(void *from, int type, String name, Error *err) +{ + Object rv = {.type = kObjectTypeNil}; + + if (name.size == 0) { + set_api_error("Empty option name", err); + return rv; + } + + // Return values + long numval; + char *stringval = NULL; + // + char key[name.size + 1]; + memcpy(key, name.data, name.size); + key[name.size] = NUL; + int flags = get_option_value_strict(key, &numval, &stringval, type, from); + + if (!flags) { + set_api_error("invalid option name", err); + return rv; + } + + if (flags & SOPT_BOOL) { + rv.type = kObjectTypeBool; + rv.data.boolean = numval ? true : false; + } else if (flags & SOPT_NUM) { + rv.type = kObjectTypeInt; + rv.data.integer = numval; + } else if (flags & SOPT_STRING) { + if (stringval) { + rv.type = kObjectTypeString; + rv.data.string.data = stringval; + rv.data.string.size = strlen(stringval); + } else { + set_api_error(N_("Unable to get option value"), err); + } + } else { + set_api_error(N_("internal error: unknown option type"), err); + } + + return rv; +} + +void set_option_to(void *to, int type, String name, Object value, Error *err) +{ + if (name.size == 0) { + set_api_error("Empty option name", err); + return; + } + + char key[name.size + 1]; + memcpy(key, name.data, name.size); + key[name.size] = NUL; + int flags = get_option_value_strict(key, NULL, NULL, type, to); + + if (flags == 0) { + set_api_error("invalid option name", err); + return; + } + + if (value.type == kObjectTypeNil) { + if (type == SREQ_GLOBAL) { + set_api_error("unable to unset option", err); + return; + } else if (!(flags & SOPT_GLOBAL)) { + set_api_error("cannot unset option that doesn't have a global value", + err); + return; + } else { + unset_global_local_option(key, to); + return; + } + } + + int opt_flags = (type ? OPT_LOCAL : OPT_GLOBAL); + + if (flags & SOPT_BOOL) { + if (value.type != kObjectTypeBool) { + set_api_error("option requires a boolean value", err); + return; + } + bool val = value.data.boolean; + set_option_value_for(key, val, NULL, opt_flags, type, to, err); + + } else if (flags & SOPT_NUM) { + if (value.type != kObjectTypeInt) { + set_api_error("option requires an integer value", err); + return; + } + + int val = value.data.integer; + set_option_value_for(key, val, NULL, opt_flags, type, to, err); + } else { + if (value.type != kObjectTypeString) { + set_api_error("option requires a string value", err); + return; + } + + char *val = xstrndup(value.data.string.data, value.data.string.size); + set_option_value_for(key, 0, val, opt_flags, type, to, err); + } +} + Object vim_to_object(typval_T *obj) { Object rv; @@ -336,3 +457,71 @@ static Object vim_to_object_rec(typval_T *obj, khash_t(Lookup) *lookup) return rv; } + + +static void set_option_value_for(char *key, + int numval, + char *stringval, + int opt_flags, + int opt_type, + void *from, + Error *err) +{ + win_T *save_curwin = NULL; + tabpage_T *save_curtab = NULL; + buf_T *save_curbuf = NULL; + + try_start(); + switch (opt_type) + { + case SREQ_WIN: + if (switch_win(&save_curwin, &save_curtab, (win_T *)from, + win_find_tabpage((win_T *)from), FALSE) == FAIL) + { + if (try_end(err)) { + return; + } + set_api_error("problem while switching windows", err); + return; + } + set_option_value_err(key, numval, stringval, opt_flags, err); + restore_win(save_curwin, save_curtab, TRUE); + break; + case SREQ_BUF: + switch_buffer(&save_curbuf, (buf_T *)from); + set_option_value_err(key, numval, stringval, opt_flags, err); + restore_buffer(save_curbuf); + break; + case SREQ_GLOBAL: + set_option_value_err(key, numval, stringval, opt_flags, err); + break; + } + + if (err->set) { + return; + } + + try_end(err); +} + + +static void set_option_value_err(char *key, + int numval, + char *stringval, + int opt_flags, + Error *err) +{ + char *errmsg; + + if ((errmsg = (char *)set_option_value((uint8_t *)key, + numval, + (uint8_t *)stringval, + opt_flags))) + { + if (try_end(err)) { + return; + } + + set_api_error(errmsg, err); + } +} diff --git a/src/api/helpers.h b/src/api/helpers.h index d839fe30e7..d98ed3b22b 100644 --- a/src/api/helpers.h +++ b/src/api/helpers.h @@ -40,6 +40,25 @@ Object dict_get_value(dict_T *dict, String key, bool pop, Error *err); /// @return the old value, if any Object dict_set_value(dict_T *dict, String key, Object value, Error *err); +/// Gets the value of a global or local(buffer, window) option. +/// +/// @param from If `type` is `SREQ_WIN` or `SREQ_BUF`, this must be a pointer +/// to the window or buffer. +/// @param type One of `SREQ_GLOBAL`, `SREQ_WIN` or `SREQ_BUF` +/// @param name The option name +/// @param[out] err Details of an error that may have occurred +/// @return the option value +Object get_option_from(void *from, int type, String name, Error *err); + +/// Sets the value of a global or local(buffer, window) option. +/// +/// @param to If `type` is `SREQ_WIN` or `SREQ_BUF`, this must be a pointer +/// to the window or buffer. +/// @param type One of `SREQ_GLOBAL`, `SREQ_WIN` or `SREQ_BUF` +/// @param name The option name +/// @param[out] err Details of an error that may have occurred +void set_option_to(void *to, int type, String name, Object value, Error *err); + /// Convert a vim object to an `Object` instance, recursively expanding /// Arrays/Dictionaries. /// diff --git a/src/api/vim.c b/src/api/vim.c index 93654da91f..7eaa27d580 100644 --- a/src/api/vim.c +++ b/src/api/vim.c @@ -139,19 +139,14 @@ Object vim_set_var(String name, Object value, Error *err) return dict_set_value(&globvardict, name, value, err); } -String vim_get_option(String name, Error *err) +Object vim_get_option(String name, Error *err) { - abort(); -} - -void vim_set_option(String name, String value, Error *err) -{ - abort(); + return get_option_from(NULL, SREQ_GLOBAL, name, err); } -void vim_del_option(String name, Error *err) +void vim_set_option(String name, Object value, Error *err) { - abort(); + set_option_to(NULL, SREQ_GLOBAL, name, value, err); } void vim_out_write(String str) diff --git a/src/api/vim.h b/src/api/vim.h index a18f9c69e9..81aadb2014 100644 --- a/src/api/vim.h +++ b/src/api/vim.h @@ -77,20 +77,14 @@ Object vim_set_var(String name, Object value, Error *err); /// @param name The option name /// @param[out] err Details of an error that may have occurred /// @return The option value -String vim_get_option(String name, Error *err); +Object vim_get_option(String name, Error *err); /// Sets an option value /// /// @param name The option name /// @param value The new option value /// @param[out] err Details of an error that may have occurred -void vim_set_option(String name, String value, Error *err); - -/// Deletes an option, falling back to the default value -/// -/// @param name The option name -/// @param[out] err Details of an error that may have occurred -void vim_del_option(String name, Error *err); +void vim_set_option(String name, Object value, Error *err); /// Write a message to vim output buffer /// diff --git a/src/option.c b/src/option.c index 56f86e34fd..89069d96ef 100644 --- a/src/option.c +++ b/src/option.c @@ -32,6 +32,7 @@ #define IN_OPTION_C #include <string.h> +#include <stdint.h> #include <stdlib.h> #include "vim.h" @@ -5946,6 +5947,124 @@ get_option_value ( return 1; } +// Returns the option attributes and its value. Unlike the above function it +// will return either global value or local value of the option depending on +// what was requested, but it will never return global value if it was +// requested to return local one and vice versa. Neither it will return +// buffer-local value if it was requested to return window-local one. +// +// Pretends that option is absent if it is not present in the requested scope +// (i.e. has no global, window-local or buffer-local value depending on +// opt_type). Uses +// +// Returned flags: +// 0 hidden or unknown option, also option that does not have requested +// type (see SREQ_* in vim.h) +// see SOPT_* in vim.h for other flags +// +// Possible opt_type values: see SREQ_* in vim.h +int get_option_value_strict(char *name, + int64_t *numval, + char **stringval, + int opt_type, + void *from) +{ + char_u *varp = NULL; + struct vimoption *p; + int rv = 0; + int opt_idx = findoption((uint8_t *)name); + if (opt_idx < 0) { + return 0; + } + + p = &(options[opt_idx]); + + // Hidden option + if (p->var == NULL) { + return 0; + } + + if (p->flags & P_BOOL) { + rv |= SOPT_BOOL; + } else if (p->flags & P_NUM) { + rv |= SOPT_NUM; + } else if (p->flags & P_STRING) { + rv |= SOPT_STRING; + } + + if (p->indir == PV_NONE) { + if (opt_type == SREQ_GLOBAL) + rv |= SOPT_GLOBAL; + else + return 0; // Did not request global-only option + } else { + if (p->indir & PV_BOTH) { + rv |= SOPT_GLOBAL; + } else if (opt_type == SREQ_GLOBAL) { + return 0; // Requested global option + } + + if (p->indir & PV_WIN) { + if (opt_type == SREQ_BUF) { + return 0; // Did not request window-local option + } else { + rv |= SOPT_WIN; + } + } else if (p->indir & PV_BUF) { + if (opt_type == SREQ_WIN) { + return 0; // Did not request buffer-local option + } else { + rv |= SOPT_BUF; + } + } + } + + if (stringval == NULL) { + return rv; + } + + if (opt_type == SREQ_GLOBAL) { + varp = p->var; + } else { + if (opt_type == SREQ_BUF) { + // Special case: 'modified' is b_changed, but we also want to + // consider it set when 'ff' or 'fenc' changed. + if (p->indir == PV_MOD) { + *numval = bufIsChanged((buf_T *) from); + varp = NULL; + } else { + aco_save_T aco; + aucmd_prepbuf(&aco, (buf_T *) from); + varp = get_varp(p); + aucmd_restbuf(&aco); + } + } else if (opt_type == SREQ_WIN) { + win_T *save_curwin; + save_curwin = curwin; + curwin = (win_T *) from; + curbuf = curwin->w_buffer; + varp = get_varp(p); + curwin = save_curwin; + curbuf = curwin->w_buffer; + } + + if (varp == p->var) { + return (rv | SOPT_UNSET); + } + } + + if (varp != NULL) { + if (p->flags & P_STRING) { + *stringval = xstrdup(*(char **)(varp)); + } else if (p->flags & P_NUM) { + *numval = *(long *) varp; + } else { + *numval = *(int *)varp; + } + } + + return rv; +} /* * Set the value of option "name". @@ -6554,6 +6673,64 @@ void comp_col(void) ru_col = 1; } +// Unset local option value, similar to ":set opt<". +void unset_global_local_option(char *name, void *from) +{ + struct vimoption *p; + int opt_idx; + buf_T *buf = (buf_T *)from; + + opt_idx = findoption((uint8_t *)name); + p = &(options[opt_idx]); + + switch ((int)p->indir) + { + // global option with local value: use local value if it's been set + case PV_EP: + clear_string_option(&buf->b_p_ep); + break; + case PV_KP: + clear_string_option(&buf->b_p_kp); + break; + case PV_PATH: + clear_string_option(&buf->b_p_path); + break; + case PV_AR: + buf->b_p_ar = -1; + break; + case PV_TAGS: + clear_string_option(&buf->b_p_tags); + break; + case PV_DEF: + clear_string_option(&buf->b_p_def); + break; + case PV_INC: + clear_string_option(&buf->b_p_inc); + break; + case PV_DICT: + clear_string_option(&buf->b_p_dict); + break; + case PV_TSR: + clear_string_option(&buf->b_p_tsr); + break; + case PV_EFM: + clear_string_option(&buf->b_p_efm); + break; + case PV_GP: + clear_string_option(&buf->b_p_gp); + break; + case PV_MP: + clear_string_option(&buf->b_p_mp); + break; + case PV_STL: + clear_string_option(&((win_T *)from)->w_p_stl); + break; + case PV_UL: + buf->b_p_ul = NO_LOCAL_UNDOLEVEL; + break; + } +} + /* * Get pointer to option variable, depending on local or global scope. */ diff --git a/src/option.h b/src/option.h index 31fd1e2025..ec27e1b493 100644 --- a/src/option.h +++ b/src/option.h @@ -1,5 +1,8 @@ #ifndef NEOVIM_OPTION_H #define NEOVIM_OPTION_H + +#include <stdint.h> + /* option.c */ void set_init_1(void); void set_string_default(char *name, char_u *val); @@ -26,6 +29,11 @@ char_u *check_colorcolumn(win_T *wp); char_u *check_stl_option(char_u *s); int get_option_value(char_u *name, long *numval, char_u **stringval, int opt_flags); +int get_option_value_strict(char *name, + int64_t *numval, + char **stringval, + int opt_type, + void *from); char_u *option_iter_next(void **option, int opt_type); char_u *set_option_value(char_u *name, long number, char_u *string, int opt_flags); @@ -39,6 +47,7 @@ void free_termoptions(void); void free_one_termoption(char_u *var); void set_term_defaults(void); void comp_col(void); +void unset_global_local_option(char *name, void *from); char_u *get_equalprg(void); void win_copy_options(win_T *wp_from, win_T *wp_to); void copy_winopt(winopt_T *from, winopt_T *to); diff --git a/src/option_defs.h b/src/option_defs.h index 72a5836b2d..cfd6a6d31f 100644 --- a/src/option_defs.h +++ b/src/option_defs.h @@ -3,9 +3,21 @@ #include "types.h" -/* - * option_defs.h: definition of global variables for settable options - */ +// option_defs.h: definition of global variables for settable options + +// Return value from get_option_value_strict */ +#define SOPT_BOOL 0x01 // Boolean option +#define SOPT_NUM 0x02 // Number option +#define SOPT_STRING 0x04 // String option +#define SOPT_GLOBAL 0x08 // Option has global value +#define SOPT_WIN 0x10 // Option has window-local value +#define SOPT_BUF 0x20 // Option has buffer-local value +#define SOPT_UNSET 0x40 // Option does not have local value set + +// Option types for various functions in option.c +#define SREQ_GLOBAL 0 // Request global option +#define SREQ_WIN 1 // Request window-local option +#define SREQ_BUF 2 // Request buffer-local option /* * Default values for 'errorformat'. @@ -1382,11 +1382,6 @@ typedef int VimClipboard; /* This is required for the prototypes. */ #define FILEINFO_READ_FAIL 2 /* CreateFile() failed */ #define FILEINFO_INFO_FAIL 3 /* GetFileInformationByHandle() failed */ -/* Option types for various functions in option.c */ -#define SREQ_GLOBAL 0 /* Request global option */ -#define SREQ_WIN 1 /* Request window-local option */ -#define SREQ_BUF 2 /* Request buffer-local option */ - /* Character used as separated in autoload function/variable names. */ #define AUTOLOAD_CHAR '#' |