API: Implement vim_{get,set}_option

Some functions from upstream VIM were reintegrated for this:
- get_option_value_strict
- set_option_value_err
- set_option_value_for
- unset_global_local_option
This commit is contained in:
Thiago de Arruda 2014-05-09 11:52:39 -03:00
parent 7c01d5ff92
commit d2b715bf1d
8 changed files with 415 additions and 25 deletions

View File

@ -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);
}
}

View File

@ -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.
///

View File

@ -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();
return get_option_from(NULL, SREQ_GLOBAL, name, err);
}
void vim_set_option(String name, String value, Error *err)
void vim_set_option(String name, Object value, Error *err)
{
abort();
}
void vim_del_option(String name, Error *err)
{
abort();
set_option_to(NULL, SREQ_GLOBAL, name, value, err);
}
void vim_out_write(String str)

View File

@ -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
///

View File

@ -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.
*/

View File

@ -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);

View File

@ -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'.

View File

@ -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 '#'