vim-patch:8.1.1966: some code in options.c fits better elsewhere (#19840)

Problem:    Some code in options.c fits better elsewhere.
Solution:   Move functions from options.c to other files. (Yegappan
            Lakshmanan, closes vim/vim#4889)
e677df8d93
This commit is contained in:
zeertzjq 2022-08-19 19:20:39 +08:00 committed by GitHub
parent 5dc43265b1
commit 2af9be3db5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 951 additions and 941 deletions

View File

@ -5195,29 +5195,6 @@ linenr_T tv_get_lnum_buf(const typval_T *const tv, const buf_T *const buf)
return (linenr_T)tv_get_number_chk(tv, NULL);
}
void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv)
{
if (what_arg->v_type == VAR_UNKNOWN) {
tv_list_alloc_ret(rettv, kListLenMayKnow);
if (is_qf || wp != NULL) {
(void)get_errorlist(NULL, wp, -1, 0, rettv->vval.v_list);
}
} else {
tv_dict_alloc_ret(rettv);
if (is_qf || wp != NULL) {
if (what_arg->v_type == VAR_DICT) {
dict_T *d = what_arg->vval.v_dict;
if (d != NULL) {
qf_get_properties(wp, d, rettv->vval.v_dict);
}
} else {
emsg(_(e_dictreq));
}
}
}
}
/// @return information (variables, options, etc.) about a tab page
/// as a dictionary.
dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx)

View File

@ -3097,13 +3097,6 @@ static void f_getline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
get_buffer_lines(curbuf, lnum, end, retlist, rettv);
}
/// "getloclist()" function
static void f_getloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
win_T *wp = find_win_by_nr_or_id(&argvars[0]);
get_qf_loc_list(false, wp, &argvars[1], rettv);
}
/// "getmarklist()" function
static void f_getmarklist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@ -3186,12 +3179,6 @@ static void f_getpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
getpos_both(argvars, rettv, false, false);
}
/// "getqflist()" functions
static void f_getqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
get_qf_loc_list(true, NULL, &argvars[0], rettv);
}
/// Common between getreg(), getreginfo() and getregtype(): get the register
/// name from the first argument.
/// Returns zero on error.
@ -7761,110 +7748,12 @@ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
set_buffer_lines(curbuf, lnum, false, &argvars[1], rettv);
}
/// Create quickfix/location list from VimL values
///
/// Used by `setqflist()` and `setloclist()` functions. Accepts invalid
/// args argument in which case errors out, including VAR_UNKNOWN parameters.
///
/// @param[in,out] wp Window to create location list for. May be NULL in
/// which case quickfix list will be created.
/// @param[in] args [list, action, what]
/// @param[in] args[0] Quickfix list contents.
/// @param[in] args[1] Optional. Action to perform:
/// append to an existing list, replace its content,
/// or create a new one.
/// @param[in] args[2] Optional. Quickfix list properties or title.
/// Defaults to caller function name.
/// @param[out] rettv Return value: 0 in case of success, -1 otherwise.
static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
FUNC_ATTR_NONNULL_ARG(2, 3)
{
static char *e_invact = N_("E927: Invalid action: '%s'");
const char *title = NULL;
char action = ' ';
static int recursive = 0;
rettv->vval.v_number = -1;
dict_T *what = NULL;
typval_T *list_arg = &args[0];
if (list_arg->v_type != VAR_LIST) {
emsg(_(e_listreq));
return;
} else if (recursive != 0) {
emsg(_(e_au_recursive));
return;
}
typval_T *action_arg = &args[1];
if (action_arg->v_type == VAR_UNKNOWN) {
// Option argument was not given.
goto skip_args;
} else if (action_arg->v_type != VAR_STRING) {
emsg(_(e_stringreq));
return;
}
const char *const act = tv_get_string_chk(action_arg);
if ((*act == 'a' || *act == 'r' || *act == ' ' || *act == 'f')
&& act[1] == NUL) {
action = *act;
} else {
semsg(_(e_invact), act);
return;
}
typval_T *const what_arg = &args[2];
if (what_arg->v_type == VAR_UNKNOWN) {
// Option argument was not given.
goto skip_args;
} else if (what_arg->v_type == VAR_STRING) {
title = tv_get_string_chk(what_arg);
if (!title) {
// Type error. Error already printed by tv_get_string_chk().
return;
}
} else if (what_arg->v_type == VAR_DICT && what_arg->vval.v_dict != NULL) {
what = what_arg->vval.v_dict;
} else {
emsg(_(e_dictreq));
return;
}
skip_args:
if (!title) {
title = (wp ? ":setloclist()" : ":setqflist()");
}
recursive++;
list_T *const l = list_arg->vval.v_list;
if (set_errorlist(wp, l, action, (char *)title, what) == OK) {
rettv->vval.v_number = 0;
}
recursive--;
}
/// "setloclist()" function
static void f_setloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = -1;
win_T *win = find_win_by_nr_or_id(&argvars[0]);
if (win != NULL) {
set_qf_ll_list(win, &argvars[1], rettv);
}
}
/// "setpos()" function
static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
set_position(argvars, rettv, false);
}
/// "setqflist()" function
static void f_setqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
set_qf_ll_list(NULL, argvars, rettv);
}
/// Translate a register type string to the yank type and block length
static int get_yank_type(char **const pp, MotionType *const yank_type, long *const block_len)
FUNC_ATTR_NONNULL_ALL

View File

@ -23,6 +23,7 @@
#include "nvim/grid.h"
#include "nvim/hardcopy.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"

View File

@ -11,6 +11,7 @@
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/extmark.h"
#include "nvim/indent.h"
@ -30,6 +31,313 @@
# include "indent.c.generated.h"
#endif
/// Set the integer values corresponding to the string setting of 'vartabstop'.
/// "array" will be set, caller must free it if needed.
/// Return false for an error.
bool tabstop_set(char_u *var, long **array)
{
long valcount = 1;
int t;
char_u *cp;
if (var[0] == NUL || (var[0] == '0' && var[1] == NUL)) {
*array = NULL;
return true;
}
for (cp = var; *cp != NUL; cp++) {
if (cp == var || cp[-1] == ',') {
char *end;
if (strtol((char *)cp, &end, 10) <= 0) {
if (cp != (char_u *)end) {
emsg(_(e_positive));
} else {
semsg(_(e_invarg2), cp);
}
return false;
}
}
if (ascii_isdigit(*cp)) {
continue;
}
if (cp[0] == ',' && cp > var && cp[-1] != ',' && cp[1] != NUL) {
valcount++;
continue;
}
semsg(_(e_invarg2), var);
return false;
}
*array = (long *)xmalloc((unsigned)(valcount + 1) * sizeof(long));
(*array)[0] = valcount;
t = 1;
for (cp = var; *cp != NUL;) {
int n = atoi((char *)cp);
// Catch negative values, overflow and ridiculous big values.
if (n <= 0 || n > TABSTOP_MAX) {
semsg(_(e_invarg2), cp);
XFREE_CLEAR(*array);
return false;
}
(*array)[t++] = n;
while (*cp != NUL && *cp != ',') {
cp++;
}
if (*cp != NUL) {
cp++;
}
}
return true;
}
/// Calculate the number of screen spaces a tab will occupy.
/// If "vts" is set then the tab widths are taken from that array,
/// otherwise the value of ts is used.
int tabstop_padding(colnr_T col, long ts_arg, long *vts)
{
long ts = ts_arg == 0 ? 8 : ts_arg;
colnr_T tabcol = 0;
int t;
long padding = 0;
if (vts == NULL || vts[0] == 0) {
return (int)(ts - (col % ts));
}
const long tabcount = vts[0];
for (t = 1; t <= tabcount; t++) {
tabcol += (colnr_T)vts[t];
if (tabcol > col) {
padding = tabcol - col;
break;
}
}
if (t > tabcount) {
padding = vts[tabcount] - ((col - tabcol) % vts[tabcount]);
}
return (int)padding;
}
/// Find the size of the tab that covers a particular column.
int tabstop_at(colnr_T col, long ts, long *vts)
{
colnr_T tabcol = 0;
int t;
long tab_size = 0;
if (vts == NULL || vts[0] == 0) {
return (int)ts;
}
const long tabcount = vts[0];
for (t = 1; t <= tabcount; t++) {
tabcol += (colnr_T)vts[t];
if (tabcol > col) {
tab_size = vts[t];
break;
}
}
if (t > tabcount) {
tab_size = vts[tabcount];
}
return (int)tab_size;
}
/// Find the column on which a tab starts.
colnr_T tabstop_start(colnr_T col, long ts, long *vts)
{
colnr_T tabcol = 0;
int t;
if (vts == NULL || vts[0] == 0) {
return (int)((col / ts) * ts);
}
const long tabcount = vts[0];
for (t = 1; t <= tabcount; t++) {
tabcol += (colnr_T)vts[t];
if (tabcol > col) {
return (int)(tabcol - vts[t]);
}
}
const int excess = (int)(tabcol % vts[tabcount]);
return (int)(excess + ((col - excess) / vts[tabcount]) * vts[tabcount]);
}
/// Find the number of tabs and spaces necessary to get from one column
/// to another.
void tabstop_fromto(colnr_T start_col, colnr_T end_col, long ts_arg, long *vts, int *ntabs,
int *nspcs)
{
int spaces = end_col - start_col;
colnr_T tabcol = 0;
long padding = 0;
int t;
long ts = ts_arg == 0 ? curbuf->b_p_ts : ts_arg;
if (vts == NULL || vts[0] == 0) {
int tabs = 0;
const int initspc = (int)(ts - (start_col % ts));
if (spaces >= initspc) {
spaces -= initspc;
tabs++;
}
tabs += (int)(spaces / ts);
spaces -= (int)((spaces / ts) * ts);
*ntabs = tabs;
*nspcs = spaces;
return;
}
// Find the padding needed to reach the next tabstop.
const long tabcount = vts[0];
for (t = 1; t <= tabcount; t++) {
tabcol += (colnr_T)vts[t];
if (tabcol > start_col) {
padding = tabcol - start_col;
break;
}
}
if (t > tabcount) {
padding = vts[tabcount] - ((start_col - tabcol) % vts[tabcount]);
}
// If the space needed is less than the padding no tabs can be used.
if (spaces < padding) {
*ntabs = 0;
*nspcs = spaces;
return;
}
*ntabs = 1;
spaces -= (int)padding;
// At least one tab has been used. See if any more will fit.
while (spaces != 0 && ++t <= tabcount) {
padding = vts[t];
if (spaces < padding) {
*nspcs = spaces;
return;
}
*ntabs += 1;
spaces -= (int)padding;
}
*ntabs += spaces / (int)vts[tabcount];
*nspcs = spaces % (int)vts[tabcount];
}
/// See if two tabstop arrays contain the same values.
bool tabstop_eq(long *ts1, long *ts2)
{
int t;
if ((ts1 == 0 && ts2) || (ts1 && ts2 == 0)) {
return false;
}
if (ts1 == ts2) {
return true;
}
if (ts1[0] != ts2[0]) {
return false;
}
for (t = 1; t <= ts1[0]; t++) {
if (ts1[t] != ts2[t]) {
return false;
}
}
return true;
}
/// Copy a tabstop array, allocating space for the new array.
int *tabstop_copy(long *oldts)
{
long *newts;
int t;
if (oldts == 0) {
return 0;
}
newts = xmalloc((unsigned)(oldts[0] + 1) * sizeof(long));
for (t = 0; t <= oldts[0]; t++) {
newts[t] = oldts[t];
}
return (int *)newts;
}
/// Return a count of the number of tabstops.
int tabstop_count(long *ts)
{
return ts != NULL ? (int)ts[0] : 0;
}
/// Return the first tabstop, or 8 if there are no tabstops defined.
int tabstop_first(long *ts)
{
return ts != NULL ? (int)ts[1] : 8;
}
/// Return the effective shiftwidth value for current buffer, using the
/// 'tabstop' value when 'shiftwidth' is zero.
int get_sw_value(buf_T *buf)
{
long result = get_sw_value_col(buf, 0);
assert(result >= 0 && result <= INT_MAX);
return (int)result;
}
/// Idem, using "pos".
long get_sw_value_pos(buf_T *buf, pos_T *pos)
{
pos_T save_cursor = curwin->w_cursor;
long sw_value;
curwin->w_cursor = *pos;
sw_value = get_sw_value_col(buf, get_nolist_virtcol());
curwin->w_cursor = save_cursor;
return sw_value;
}
/// Idem, using the first non-black in the current line.
long get_sw_value_indent(buf_T *buf)
{
pos_T pos = curwin->w_cursor;
pos.col = (colnr_T)getwhitecols_curline();
return get_sw_value_pos(buf, &pos);
}
/// Idem, using virtual column "col".
long get_sw_value_col(buf_T *buf, colnr_T col)
{
return buf->b_p_sw ? buf->b_p_sw
: tabstop_at(col, buf->b_p_ts, buf->b_p_vts_array);
}
/// Return the effective softtabstop value for the current buffer,
/// using the shiftwidth value when 'softtabstop' is negative.
int get_sts_value(void)
{
long result = curbuf->b_p_sts < 0 ? get_sw_value(curbuf) : curbuf->b_p_sts;
assert(result >= 0 && result <= INT_MAX);
return (int)result;
}
// Count the size (in window cells) of the indent in the current line.
int get_indent(void)
{

View File

@ -50,9 +50,9 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/os/os.h"
#include "nvim/path.h"
#include "nvim/screen.h"
#include "nvim/spell.h"
#include "nvim/strings.h"

View File

@ -26,6 +26,7 @@
#include "nvim/getchar.h"
#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/indent.h"
#include "nvim/input.h"
#include "nvim/keycodes.h"
#include "nvim/main.h"

View File

@ -52,6 +52,7 @@
#include "nvim/hardcopy.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
#include "nvim/insexpand.h"
#include "nvim/keycodes.h"
@ -72,6 +73,7 @@
#include "nvim/popupmenu.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
#include "nvim/screen.h"
#include "nvim/spell.h"
#include "nvim/spellfile.h"
#include "nvim/spellsuggest.h"
@ -344,9 +346,6 @@ static char_u SHM_ALL[] = {
static char e_unclosed_expression_sequence[] = N_("E540: Unclosed expression sequence");
static char e_unbalanced_groups[] = N_("E542: unbalanced groups");
static char e_conflicts_with_value_of_listchars[] = N_("E834: Conflicts with value of 'listchars'");
static char e_conflicts_with_value_of_fillchars[] = N_("E835: Conflicts with value of 'fillchars'");
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "option.c.generated.h"
#endif
@ -2317,7 +2316,7 @@ static char *set_string_option(const int opt_idx, const char *const value, const
/// Return true if "val" is a valid name: only consists of alphanumeric ASCII
/// characters or characters in "allowed".
static bool valid_name(const char_u *val, const char *allowed)
bool valid_name(const char_u *val, const char *allowed)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
for (const char_u *s = val; *s != NUL; s++) {
@ -2337,25 +2336,6 @@ static bool valid_filetype(const char_u *val)
return valid_name(val, ".-_");
}
/// Return true if "val" is a valid 'spelllang' value.
bool valid_spelllang(const char_u *val)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return valid_name(val, ".-_,@");
}
/// Return true if "val" is a valid 'spellfile' value.
static bool valid_spellfile(const char_u *val)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
for (const char_u *s = val; *s != NUL; s++) {
if (!vim_isfilec(*s) && *s != ',' && *s != ' ') {
return false;
}
}
return true;
}
/// Handle setting 'mousescroll'.
/// @return error message, NULL if it's OK.
static char *check_mousescroll(char *string)
@ -3432,12 +3412,6 @@ static char *did_set_string_option(int opt_idx, char_u **varp, char_u *oldval, c
return errmsg;
}
/// Simple int comparison function for use with qsort()
static int int_cmp(const void *a, const void *b)
{
return *(const int *)a - *(const int *)b;
}
/// Handle setting 'signcolumn' for value 'val'
///
/// @return OK when the value is valid, FAIL otherwise
@ -3468,366 +3442,12 @@ int check_signcolumn(char_u *val)
return FAIL;
}
/// Handle setting 'colorcolumn' or 'textwidth' in window "wp".
///
/// @return error message, NULL if it's OK.
char *check_colorcolumn(win_T *wp)
{
char *s;
int col;
unsigned int count = 0;
int color_cols[256];
int j = 0;
if (wp->w_buffer == NULL) {
return NULL; // buffer was closed
}
for (s = (char *)wp->w_p_cc; *s != NUL && count < 255;) {
if (*s == '-' || *s == '+') {
// -N and +N: add to 'textwidth'
col = (*s == '-') ? -1 : 1;
s++;
if (!ascii_isdigit(*s)) {
return e_invarg;
}
col = col * getdigits_int(&s, true, 0);
if (wp->w_buffer->b_p_tw == 0) {
goto skip; // 'textwidth' not set, skip this item
}
assert((col >= 0
&& wp->w_buffer->b_p_tw <= INT_MAX - col
&& wp->w_buffer->b_p_tw + col >= INT_MIN)
|| (col < 0
&& wp->w_buffer->b_p_tw >= INT_MIN - col
&& wp->w_buffer->b_p_tw + col <= INT_MAX));
col += (int)wp->w_buffer->b_p_tw;
if (col < 0) {
goto skip;
}
} else if (ascii_isdigit(*s)) {
col = getdigits_int(&s, true, 0);
} else {
return e_invarg;
}
color_cols[count++] = col - 1; // 1-based to 0-based
skip:
if (*s == NUL) {
break;
}
if (*s != ',') {
return e_invarg;
}
if (*++s == NUL) {
return e_invarg; // illegal trailing comma as in "set cc=80,"
}
}
xfree(wp->w_p_cc_cols);
if (count == 0) {
wp->w_p_cc_cols = NULL;
} else {
wp->w_p_cc_cols = xmalloc(sizeof(int) * (count + 1));
/* sort the columns for faster usage on screen redraw inside
* win_line() */
qsort(color_cols, count, sizeof(int), int_cmp);
for (unsigned int i = 0; i < count; i++) {
// skip duplicates
if (j == 0 || wp->w_p_cc_cols[j - 1] != color_cols[i]) {
wp->w_p_cc_cols[j++] = color_cols[i];
}
}
wp->w_p_cc_cols[j] = -1; // end marker
}
return NULL; // no error
}
void check_blending(win_T *wp)
{
wp->w_grid_alloc.blending =
wp->w_p_winbl > 0 || (wp->w_floating && wp->w_float_config.shadow);
}
/// Calls mb_cptr2char_adv(p) and returns the character.
/// If "p" starts with "\x", "\u" or "\U" the hex or unicode value is used.
/// Returns 0 for invalid hex or invalid UTF-8 byte.
static int get_encoded_char_adv(char_u **p)
{
char_u *s = *p;
if (s[0] == '\\' && (s[1] == 'x' || s[1] == 'u' || s[1] == 'U')) {
int64_t num = 0;
int bytes;
int n;
for (bytes = s[1] == 'x' ? 1 : s[1] == 'u' ? 2 : 4; bytes > 0; bytes--) {
*p += 2;
n = hexhex2nr(*p);
if (n < 0) {
return 0;
}
num = num * 256 + n;
}
*p += 2;
return (int)num;
}
// TODO(bfredl): use schar_T representation and utfc_ptr2len
int clen = utf_ptr2len((char *)s);
int c = mb_cptr2char_adv((const char_u **)p);
if (clen == 1 && c > 127) { // Invalid UTF-8 byte
return 0;
}
return c;
}
/// Handle setting 'listchars' or 'fillchars'.
/// Assume monocell characters
///
/// @param varp either &curwin->w_p_lcs or &curwin->w_p_fcs
/// @return error message, NULL if it's OK.
char *set_chars_option(win_T *wp, char_u **varp, bool set)
{
int round, i, len, len2, entries;
char_u *p, *s;
int c1;
int c2 = 0;
int c3 = 0;
char_u *last_multispace = NULL; // Last occurrence of "multispace:"
char_u *last_lmultispace = NULL; // Last occurrence of "leadmultispace:"
int multispace_len = 0; // Length of lcs-multispace string
int lead_multispace_len = 0; // Length of lcs-leadmultispace string
struct chars_tab {
int *cp; ///< char value
char *name; ///< char id
int def; ///< default value
};
struct chars_tab *tab;
// XXX: Characters taking 2 columns is forbidden (TUI limitation?). Set old defaults in this case.
struct chars_tab fcs_tab[] = {
{ &wp->w_p_fcs_chars.stl, "stl", ' ' },
{ &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' },
{ &wp->w_p_fcs_chars.wbr, "wbr", ' ' },
{ &wp->w_p_fcs_chars.horiz, "horiz", char2cells(0x2500) == 1 ? 0x2500 : '-' }, // ─
{ &wp->w_p_fcs_chars.horizup, "horizup", char2cells(0x2534) == 1 ? 0x2534 : '-' }, // ┴
{ &wp->w_p_fcs_chars.horizdown, "horizdown", char2cells(0x252c) == 1 ? 0x252c : '-' }, // ┬
{ &wp->w_p_fcs_chars.vert, "vert", char2cells(0x2502) == 1 ? 0x2502 : '|' }, // │
{ &wp->w_p_fcs_chars.vertleft, "vertleft", char2cells(0x2524) == 1 ? 0x2524 : '|' }, // ┤
{ &wp->w_p_fcs_chars.vertright, "vertright", char2cells(0x251c) == 1 ? 0x251c : '|' }, // ├
{ &wp->w_p_fcs_chars.verthoriz, "verthoriz", char2cells(0x253c) == 1 ? 0x253c : '+' }, // ┼
{ &wp->w_p_fcs_chars.fold, "fold", char2cells(0x00b7) == 1 ? 0x00b7 : '-' }, // ·
{ &wp->w_p_fcs_chars.foldopen, "foldopen", '-' },
{ &wp->w_p_fcs_chars.foldclosed, "foldclose", '+' },
{ &wp->w_p_fcs_chars.foldsep, "foldsep", char2cells(0x2502) == 1 ? 0x2502 : '|' }, // │
{ &wp->w_p_fcs_chars.diff, "diff", '-' },
{ &wp->w_p_fcs_chars.msgsep, "msgsep", ' ' },
{ &wp->w_p_fcs_chars.eob, "eob", '~' },
};
struct chars_tab lcs_tab[] = {
{ &wp->w_p_lcs_chars.eol, "eol", NUL },
{ &wp->w_p_lcs_chars.ext, "extends", NUL },
{ &wp->w_p_lcs_chars.nbsp, "nbsp", NUL },
{ &wp->w_p_lcs_chars.prec, "precedes", NUL },
{ &wp->w_p_lcs_chars.space, "space", NUL },
{ &wp->w_p_lcs_chars.tab2, "tab", NUL },
{ &wp->w_p_lcs_chars.lead, "lead", NUL },
{ &wp->w_p_lcs_chars.trail, "trail", NUL },
{ &wp->w_p_lcs_chars.conceal, "conceal", NUL },
};
if (varp == &p_lcs || varp == &wp->w_p_lcs) {
tab = lcs_tab;
entries = ARRAY_SIZE(lcs_tab);
if (varp == &wp->w_p_lcs && wp->w_p_lcs[0] == NUL) {
varp = &p_lcs;
}
} else {
tab = fcs_tab;
entries = ARRAY_SIZE(fcs_tab);
if (varp == &wp->w_p_fcs && wp->w_p_fcs[0] == NUL) {
varp = &p_fcs;
}
}
// first round: check for valid value, second round: assign values
for (round = 0; round <= (set ? 1 : 0); round++) {
if (round > 0) {
// After checking that the value is valid: set defaults
for (i = 0; i < entries; i++) {
if (tab[i].cp != NULL) {
*(tab[i].cp) = tab[i].def;
}
}
if (varp == &p_lcs || varp == &wp->w_p_lcs) {
wp->w_p_lcs_chars.tab1 = NUL;
wp->w_p_lcs_chars.tab3 = NUL;
xfree(wp->w_p_lcs_chars.multispace);
if (multispace_len > 0) {
wp->w_p_lcs_chars.multispace = xmalloc(((size_t)multispace_len + 1) * sizeof(int));
wp->w_p_lcs_chars.multispace[multispace_len] = NUL;
} else {
wp->w_p_lcs_chars.multispace = NULL;
}
xfree(wp->w_p_lcs_chars.leadmultispace);
if (lead_multispace_len > 0) {
wp->w_p_lcs_chars.leadmultispace
= xmalloc(((size_t)lead_multispace_len + 1) * sizeof(int));
wp->w_p_lcs_chars.leadmultispace[lead_multispace_len] = NUL;
} else {
wp->w_p_lcs_chars.leadmultispace = NULL;
}
}
}
p = *varp;
while (*p) {
for (i = 0; i < entries; i++) {
len = (int)STRLEN(tab[i].name);
if (STRNCMP(p, tab[i].name, len) == 0
&& p[len] == ':'
&& p[len + 1] != NUL) {
c2 = c3 = 0;
s = p + len + 1;
c1 = get_encoded_char_adv(&s);
if (c1 == 0 || char2cells(c1) > 1) {
return e_invarg;
}
if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
if (*s == NUL) {
return e_invarg;
}
c2 = get_encoded_char_adv(&s);
if (c2 == 0 || char2cells(c2) > 1) {
return e_invarg;
}
if (!(*s == ',' || *s == NUL)) {
c3 = get_encoded_char_adv(&s);
if (c3 == 0 || char2cells(c3) > 1) {
return e_invarg;
}
}
}
if (*s == ',' || *s == NUL) {
if (round > 0) {
if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
wp->w_p_lcs_chars.tab1 = c1;
wp->w_p_lcs_chars.tab2 = c2;
wp->w_p_lcs_chars.tab3 = c3;
} else if (tab[i].cp != NULL) {
*(tab[i].cp) = c1;
}
}
p = s;
break;
}
}
}
if (i == entries) {
len = (int)STRLEN("multispace");
len2 = (int)STRLEN("leadmultispace");
if ((varp == &p_lcs || varp == &wp->w_p_lcs)
&& STRNCMP(p, "multispace", len) == 0
&& p[len] == ':'
&& p[len + 1] != NUL) {
s = p + len + 1;
if (round == 0) {
// Get length of lcs-multispace string in the first round
last_multispace = p;
multispace_len = 0;
while (*s != NUL && *s != ',') {
c1 = get_encoded_char_adv(&s);
if (c1 == 0 || char2cells(c1) > 1) {
return e_invarg;
}
multispace_len++;
}
if (multispace_len == 0) {
// lcs-multispace cannot be an empty string
return e_invarg;
}
p = s;
} else {
int multispace_pos = 0;
while (*s != NUL && *s != ',') {
c1 = get_encoded_char_adv(&s);
if (p == last_multispace) {
wp->w_p_lcs_chars.multispace[multispace_pos++] = c1;
}
}
p = s;
}
} else if ((varp == &p_lcs || varp == &wp->w_p_lcs)
&& STRNCMP(p, "leadmultispace", len2) == 0
&& p[len2] == ':'
&& p[len2 + 1] != NUL) {
s = p + len2 + 1;
if (round == 0) {
// get length of lcs-leadmultispace string in first round
last_lmultispace = p;
lead_multispace_len = 0;
while (*s != NUL && *s != ',') {
c1 = get_encoded_char_adv(&s);
if (c1 == 0 || char2cells(c1) > 1) {
return e_invarg;
}
lead_multispace_len++;
}
if (lead_multispace_len == 0) {
// lcs-leadmultispace cannot be an empty string
return e_invarg;
}
p = s;
} else {
int multispace_pos = 0;
while (*s != NUL && *s != ',') {
c1 = get_encoded_char_adv(&s);
if (p == last_lmultispace) {
wp->w_p_lcs_chars.leadmultispace[multispace_pos++] = c1;
}
}
p = s;
}
} else {
return e_invarg;
}
}
if (*p == ',') {
p++;
}
}
}
return NULL; // no error
}
/// Check all global and local values of 'listchars' and 'fillchars'.
/// May set different defaults in case character widths change.
///
/// @return an untranslated error message if any of them is invalid, NULL otherwise.
char *check_chars_options(void)
{
if (set_chars_option(curwin, &p_lcs, false) != NULL) {
return e_conflicts_with_value_of_listchars;
}
if (set_chars_option(curwin, &p_fcs, false) != NULL) {
return e_conflicts_with_value_of_fillchars;
}
FOR_ALL_TAB_WINDOWS(tp, wp) {
if (set_chars_option(wp, &wp->w_p_lcs, true) != NULL) {
return e_conflicts_with_value_of_listchars;
}
if (set_chars_option(wp, &wp->w_p_fcs, true) != NULL) {
return e_conflicts_with_value_of_fillchars;
}
}
return NULL;
}
/// Check validity of options with the 'statusline' format.
/// Return an untranslated error message or NULL.
char *check_stl_option(char *s)
@ -3898,55 +3518,6 @@ char *check_stl_option(char *s)
return NULL;
}
static char *did_set_spell_option(bool is_spellfile)
{
char *errmsg = NULL;
if (is_spellfile) {
int l = (int)STRLEN(curwin->w_s->b_p_spf);
if (l > 0
&& (l < 4 || STRCMP(curwin->w_s->b_p_spf + l - 4, ".add") != 0)) {
errmsg = e_invarg;
}
}
if (errmsg == NULL) {
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_buffer == curbuf && wp->w_p_spell) {
errmsg = did_set_spelllang(wp);
break;
}
}
}
return errmsg;
}
/// Set curbuf->b_cap_prog to the regexp program for 'spellcapcheck'.
/// Return error message when failed, NULL when OK.
static char *compile_cap_prog(synblock_T *synblock)
FUNC_ATTR_NONNULL_ALL
{
regprog_T *rp = synblock->b_cap_prog;
char_u *re;
if (synblock->b_p_spc == NULL || *synblock->b_p_spc == NUL) {
synblock->b_cap_prog = NULL;
} else {
// Prepend a ^ so that we only match at one column
re = concat_str((char_u *)"^", synblock->b_p_spc);
synblock->b_cap_prog = vim_regcomp((char *)re, RE_MAGIC);
xfree(re);
if (synblock->b_cap_prog == NULL) {
synblock->b_cap_prog = rp; // restore the previous program
return e_invarg;
}
}
vim_regfree(rp);
return NULL;
}
/// Handle setting `winhighlight' in window "wp"
bool parse_winhl_opt(win_T *wp)
{
@ -5767,47 +5338,6 @@ static int put_setbool(FILE *fd, char *cmd, char *name, int value)
return OK;
}
/// Compute columns for ruler and shown command. 'sc_col' is also used to
/// decide what the maximum length of a message on the status line can be.
/// If there is a status line for the last window, 'sc_col' is independent
/// of 'ru_col'.
#define COL_RULER 17 // columns needed by standard ruler
void comp_col(void)
{
int last_has_status = (p_ls > 1 || (p_ls == 1 && !ONE_WINDOW));
sc_col = 0;
ru_col = 0;
if (p_ru) {
ru_col = (ru_wid ? ru_wid : COL_RULER) + 1;
// no last status line, adjust sc_col
if (!last_has_status) {
sc_col = ru_col;
}
}
if (p_sc) {
sc_col += SHOWCMD_COLS;
if (!p_ru || last_has_status) { // no need for separating space
sc_col++;
}
}
assert(sc_col >= 0
&& INT_MIN + sc_col <= Columns);
sc_col = Columns - sc_col;
assert(ru_col >= 0
&& INT_MIN + ru_col <= Columns);
ru_col = Columns - ru_col;
if (sc_col <= 0) { // screen too narrow, will become a mess
sc_col = 1;
}
if (ru_col <= 0) {
ru_col = 1;
}
set_vim_var_nr(VV_ECHOSPACE, sc_col - 1);
}
// Unset local option value, similar to ":set opt<".
void unset_global_local_option(char *name, void *from)
{
@ -7562,313 +7092,6 @@ int check_ff_value(char_u *p)
return check_opt_strings(p, p_ff_values, false);
}
// Set the integer values corresponding to the string setting of 'vartabstop'.
// "array" will be set, caller must free it if needed.
// Return false for an error.
bool tabstop_set(char_u *var, long **array)
{
long valcount = 1;
int t;
char_u *cp;
if (var[0] == NUL || (var[0] == '0' && var[1] == NUL)) {
*array = NULL;
return true;
}
for (cp = var; *cp != NUL; cp++) {
if (cp == var || cp[-1] == ',') {
char *end;
if (strtol((char *)cp, &end, 10) <= 0) {
if (cp != (char_u *)end) {
emsg(_(e_positive));
} else {
semsg(_(e_invarg2), cp);
}
return false;
}
}
if (ascii_isdigit(*cp)) {
continue;
}
if (cp[0] == ',' && cp > var && cp[-1] != ',' && cp[1] != NUL) {
valcount++;
continue;
}
semsg(_(e_invarg2), var);
return false;
}
*array = (long *)xmalloc((unsigned)(valcount + 1) * sizeof(long));
(*array)[0] = valcount;
t = 1;
for (cp = var; *cp != NUL;) {
int n = atoi((char *)cp);
// Catch negative values, overflow and ridiculous big values.
if (n <= 0 || n > TABSTOP_MAX) {
semsg(_(e_invarg2), cp);
XFREE_CLEAR(*array);
return false;
}
(*array)[t++] = n;
while (*cp != NUL && *cp != ',') {
cp++;
}
if (*cp != NUL) {
cp++;
}
}
return true;
}
// Calculate the number of screen spaces a tab will occupy.
// If "vts" is set then the tab widths are taken from that array,
// otherwise the value of ts is used.
int tabstop_padding(colnr_T col, long ts_arg, long *vts)
{
long ts = ts_arg == 0 ? 8 : ts_arg;
colnr_T tabcol = 0;
int t;
long padding = 0;
if (vts == NULL || vts[0] == 0) {
return (int)(ts - (col % ts));
}
const long tabcount = vts[0];
for (t = 1; t <= tabcount; t++) {
tabcol += (colnr_T)vts[t];
if (tabcol > col) {
padding = tabcol - col;
break;
}
}
if (t > tabcount) {
padding = vts[tabcount] - ((col - tabcol) % vts[tabcount]);
}
return (int)padding;
}
// Find the size of the tab that covers a particular column.
int tabstop_at(colnr_T col, long ts, long *vts)
{
colnr_T tabcol = 0;
int t;
long tab_size = 0;
if (vts == NULL || vts[0] == 0) {
return (int)ts;
}
const long tabcount = vts[0];
for (t = 1; t <= tabcount; t++) {
tabcol += (colnr_T)vts[t];
if (tabcol > col) {
tab_size = vts[t];
break;
}
}
if (t > tabcount) {
tab_size = vts[tabcount];
}
return (int)tab_size;
}
// Find the column on which a tab starts.
colnr_T tabstop_start(colnr_T col, long ts, long *vts)
{
colnr_T tabcol = 0;
int t;
if (vts == NULL || vts[0] == 0) {
return (int)((col / ts) * ts);
}
const long tabcount = vts[0];
for (t = 1; t <= tabcount; t++) {
tabcol += (colnr_T)vts[t];
if (tabcol > col) {
return (int)(tabcol - vts[t]);
}
}
const int excess = (int)(tabcol % vts[tabcount]);
return (int)(excess + ((col - excess) / vts[tabcount]) * vts[tabcount]);
}
// Find the number of tabs and spaces necessary to get from one column
// to another.
void tabstop_fromto(colnr_T start_col, colnr_T end_col, long ts_arg, long *vts, int *ntabs,
int *nspcs)
{
int spaces = end_col - start_col;
colnr_T tabcol = 0;
long padding = 0;
int t;
long ts = ts_arg == 0 ? curbuf->b_p_ts : ts_arg;
if (vts == NULL || vts[0] == 0) {
int tabs = 0;
const int initspc = (int)(ts - (start_col % ts));
if (spaces >= initspc) {
spaces -= initspc;
tabs++;
}
tabs += (int)(spaces / ts);
spaces -= (int)((spaces / ts) * ts);
*ntabs = tabs;
*nspcs = spaces;
return;
}
// Find the padding needed to reach the next tabstop.
const long tabcount = vts[0];
for (t = 1; t <= tabcount; t++) {
tabcol += (colnr_T)vts[t];
if (tabcol > start_col) {
padding = tabcol - start_col;
break;
}
}
if (t > tabcount) {
padding = vts[tabcount] - ((start_col - tabcol) % vts[tabcount]);
}
// If the space needed is less than the padding no tabs can be used.
if (spaces < padding) {
*ntabs = 0;
*nspcs = spaces;
return;
}
*ntabs = 1;
spaces -= (int)padding;
// At least one tab has been used. See if any more will fit.
while (spaces != 0 && ++t <= tabcount) {
padding = vts[t];
if (spaces < padding) {
*nspcs = spaces;
return;
}
*ntabs += 1;
spaces -= (int)padding;
}
*ntabs += spaces / (int)vts[tabcount];
*nspcs = spaces % (int)vts[tabcount];
}
// See if two tabstop arrays contain the same values.
bool tabstop_eq(long *ts1, long *ts2)
{
int t;
if ((ts1 == 0 && ts2) || (ts1 && ts2 == 0)) {
return false;
}
if (ts1 == ts2) {
return true;
}
if (ts1[0] != ts2[0]) {
return false;
}
for (t = 1; t <= ts1[0]; t++) {
if (ts1[t] != ts2[t]) {
return false;
}
}
return true;
}
// Copy a tabstop array, allocating space for the new array.
int *tabstop_copy(long *oldts)
{
long *newts;
int t;
if (oldts == 0) {
return 0;
}
newts = xmalloc((unsigned)(oldts[0] + 1) * sizeof(long));
for (t = 0; t <= oldts[0]; t++) {
newts[t] = oldts[t];
}
return (int *)newts;
}
// Return a count of the number of tabstops.
int tabstop_count(long *ts)
{
return ts != NULL ? (int)ts[0] : 0;
}
// Return the first tabstop, or 8 if there are no tabstops defined.
int tabstop_first(long *ts)
{
return ts != NULL ? (int)ts[1] : 8;
}
/// Return the effective shiftwidth value for current buffer, using the
/// 'tabstop' value when 'shiftwidth' is zero.
int get_sw_value(buf_T *buf)
{
long result = get_sw_value_col(buf, 0);
assert(result >= 0 && result <= INT_MAX);
return (int)result;
}
// Idem, using the first non-black in the current line.
long get_sw_value_indent(buf_T *buf)
{
pos_T pos = curwin->w_cursor;
pos.col = (colnr_T)getwhitecols_curline();
return get_sw_value_pos(buf, &pos);
}
// Idem, using "pos".
long get_sw_value_pos(buf_T *buf, pos_T *pos)
{
pos_T save_cursor = curwin->w_cursor;
long sw_value;
curwin->w_cursor = *pos;
sw_value = get_sw_value_col(buf, get_nolist_virtcol());
curwin->w_cursor = save_cursor;
return sw_value;
}
// Idem, using virtual column "col".
long get_sw_value_col(buf_T *buf, colnr_T col)
{
return buf->b_p_sw ? buf->b_p_sw
: tabstop_at(col, buf->b_p_ts, buf->b_p_vts_array);
}
/// Return the effective softtabstop value for the current buffer,
/// using the shiftwidth value when 'softtabstop' is negative.
int get_sts_value(void)
{
long result = curbuf->b_p_sts < 0 ? get_sw_value(curbuf) : curbuf->b_p_sts;
assert(result >= 0 && result <= INT_MAX);
return (int)result;
}
/// This is called when 'breakindentopt' is changed and when a window is
/// initialized
static bool briopt_check(win_T *wp)

View File

@ -5781,7 +5781,7 @@ static int get_qfline_items(qfline_T *qfp, list_T *list)
/// If qf_idx is -1, use the current list. Otherwise, use the specified list.
/// If eidx is not 0, then return only the specified entry. Otherwise return
/// all the entries.
int get_errorlist(qf_info_T *qi_arg, win_T *wp, int qf_idx, int eidx, list_T *list)
static int get_errorlist(qf_info_T *qi_arg, win_T *wp, int qf_idx, int eidx, list_T *list)
{
qf_info_T *qi = qi_arg;
@ -6151,7 +6151,7 @@ static int qf_getprop_qftf(qf_list_T *qfl, dict_T *retdict)
/// Return quickfix/location list details (title) as a dictionary.
/// 'what' contains the details to return. If 'list_idx' is -1,
/// then current list is used. Otherwise the specified list is used.
int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
static int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
{
qf_info_T *qi = &ql_info;
dictitem_T *di = NULL;
@ -7159,3 +7159,137 @@ void ex_helpgrep(exarg_T *eap)
}
}
}
static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv)
{
if (what_arg->v_type == VAR_UNKNOWN) {
tv_list_alloc_ret(rettv, kListLenMayKnow);
if (is_qf || wp != NULL) {
(void)get_errorlist(NULL, wp, -1, 0, rettv->vval.v_list);
}
} else {
tv_dict_alloc_ret(rettv);
if (is_qf || wp != NULL) {
if (what_arg->v_type == VAR_DICT) {
dict_T *d = what_arg->vval.v_dict;
if (d != NULL) {
qf_get_properties(wp, d, rettv->vval.v_dict);
}
} else {
emsg(_(e_dictreq));
}
}
}
}
/// "getloclist()" function
void f_getloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
win_T *wp = find_win_by_nr_or_id(&argvars[0]);
get_qf_loc_list(false, wp, &argvars[1], rettv);
}
/// "getqflist()" functions
void f_getqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
get_qf_loc_list(true, NULL, &argvars[0], rettv);
}
/// Create quickfix/location list from VimL values
///
/// Used by `setqflist()` and `setloclist()` functions. Accepts invalid
/// args argument in which case errors out, including VAR_UNKNOWN parameters.
///
/// @param[in,out] wp Window to create location list for. May be NULL in
/// which case quickfix list will be created.
/// @param[in] args [list, action, what]
/// @param[in] args[0] Quickfix list contents.
/// @param[in] args[1] Optional. Action to perform:
/// append to an existing list, replace its content,
/// or create a new one.
/// @param[in] args[2] Optional. Quickfix list properties or title.
/// Defaults to caller function name.
/// @param[out] rettv Return value: 0 in case of success, -1 otherwise.
static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
FUNC_ATTR_NONNULL_ARG(2, 3)
{
static char *e_invact = N_("E927: Invalid action: '%s'");
const char *title = NULL;
char action = ' ';
static int recursive = 0;
rettv->vval.v_number = -1;
dict_T *what = NULL;
typval_T *list_arg = &args[0];
if (list_arg->v_type != VAR_LIST) {
emsg(_(e_listreq));
return;
} else if (recursive != 0) {
emsg(_(e_au_recursive));
return;
}
typval_T *action_arg = &args[1];
if (action_arg->v_type == VAR_UNKNOWN) {
// Option argument was not given.
goto skip_args;
} else if (action_arg->v_type != VAR_STRING) {
emsg(_(e_stringreq));
return;
}
const char *const act = tv_get_string_chk(action_arg);
if ((*act == 'a' || *act == 'r' || *act == ' ' || *act == 'f')
&& act[1] == NUL) {
action = *act;
} else {
semsg(_(e_invact), act);
return;
}
typval_T *const what_arg = &args[2];
if (what_arg->v_type == VAR_UNKNOWN) {
// Option argument was not given.
goto skip_args;
} else if (what_arg->v_type == VAR_STRING) {
title = tv_get_string_chk(what_arg);
if (!title) {
// Type error. Error already printed by tv_get_string_chk().
return;
}
} else if (what_arg->v_type == VAR_DICT && what_arg->vval.v_dict != NULL) {
what = what_arg->vval.v_dict;
} else {
emsg(_(e_dictreq));
return;
}
skip_args:
if (!title) {
title = (wp ? ":setloclist()" : ":setqflist()");
}
recursive++;
list_T *const l = list_arg->vval.v_list;
if (set_errorlist(wp, l, action, (char *)title, what) == OK) {
rettv->vval.v_number = 0;
}
recursive--;
}
/// "setloclist()" function
void f_setloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = -1;
win_T *win = find_win_by_nr_or_id(&argvars[0]);
if (win != NULL) {
set_qf_ll_list(win, &argvars[1], rettv);
}
}
/// "setqflist()" function
void f_setqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
set_qf_ll_list(NULL, argvars, rettv);
}

View File

@ -42,6 +42,9 @@
# include "screen.c.generated.h"
#endif
static char e_conflicts_with_value_of_listchars[] = N_("E834: Conflicts with value of 'listchars'");
static char e_conflicts_with_value_of_fillchars[] = N_("E835: Conflicts with value of 'fillchars'");
/// Return true if the cursor line in window "wp" may be concealed, according
/// to the 'concealcursor' option.
bool conceal_cursor_line(const win_T *wp)
@ -1599,6 +1602,46 @@ void win_redr_ruler(win_T *wp, bool always)
}
}
#define COL_RULER 17 // columns needed by standard ruler
/// Compute columns for ruler and shown command. 'sc_col' is also used to
/// decide what the maximum length of a message on the status line can be.
/// If there is a status line for the last window, 'sc_col' is independent
/// of 'ru_col'.
void comp_col(void)
{
int last_has_status = (p_ls > 1 || (p_ls == 1 && !ONE_WINDOW));
sc_col = 0;
ru_col = 0;
if (p_ru) {
ru_col = (ru_wid ? ru_wid : COL_RULER) + 1;
// no last status line, adjust sc_col
if (!last_has_status) {
sc_col = ru_col;
}
}
if (p_sc) {
sc_col += SHOWCMD_COLS;
if (!p_ru || last_has_status) { // no need for separating space
sc_col++;
}
}
assert(sc_col >= 0
&& INT_MIN + sc_col <= Columns);
sc_col = Columns - sc_col;
assert(ru_col >= 0
&& INT_MIN + ru_col <= Columns);
ru_col = Columns - ru_col;
if (sc_col <= 0) { // screen too narrow, will become a mess
sc_col = 1;
}
if (ru_col <= 0) {
ru_col = 1;
}
set_vim_var_nr(VV_ECHOSPACE, sc_col - 1);
}
/// Return the width of the 'number' and 'relativenumber' column.
/// Caller may need to check if 'number' or 'relativenumber' is set.
/// Otherwise it depends on 'numberwidth' and the line count.
@ -1642,6 +1685,284 @@ int number_width(win_T *wp)
return n;
}
/// Calls mb_cptr2char_adv(p) and returns the character.
/// If "p" starts with "\x", "\u" or "\U" the hex or unicode value is used.
/// Returns 0 for invalid hex or invalid UTF-8 byte.
static int get_encoded_char_adv(char_u **p)
{
char_u *s = *p;
if (s[0] == '\\' && (s[1] == 'x' || s[1] == 'u' || s[1] == 'U')) {
int64_t num = 0;
int bytes;
int n;
for (bytes = s[1] == 'x' ? 1 : s[1] == 'u' ? 2 : 4; bytes > 0; bytes--) {
*p += 2;
n = hexhex2nr(*p);
if (n < 0) {
return 0;
}
num = num * 256 + n;
}
*p += 2;
return (int)num;
}
// TODO(bfredl): use schar_T representation and utfc_ptr2len
int clen = utf_ptr2len((char *)s);
int c = mb_cptr2char_adv((const char_u **)p);
if (clen == 1 && c > 127) { // Invalid UTF-8 byte
return 0;
}
return c;
}
/// Handle setting 'listchars' or 'fillchars'.
/// Assume monocell characters
///
/// @param varp either &curwin->w_p_lcs or &curwin->w_p_fcs
/// @return error message, NULL if it's OK.
char *set_chars_option(win_T *wp, char_u **varp, bool set)
{
int round, i, len, len2, entries;
char_u *p, *s;
int c1;
int c2 = 0;
int c3 = 0;
char_u *last_multispace = NULL; // Last occurrence of "multispace:"
char_u *last_lmultispace = NULL; // Last occurrence of "leadmultispace:"
int multispace_len = 0; // Length of lcs-multispace string
int lead_multispace_len = 0; // Length of lcs-leadmultispace string
struct chars_tab {
int *cp; ///< char value
char *name; ///< char id
int def; ///< default value
};
struct chars_tab *tab;
// XXX: Characters taking 2 columns is forbidden (TUI limitation?). Set old defaults in this case.
struct chars_tab fcs_tab[] = {
{ &wp->w_p_fcs_chars.stl, "stl", ' ' },
{ &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' },
{ &wp->w_p_fcs_chars.wbr, "wbr", ' ' },
{ &wp->w_p_fcs_chars.horiz, "horiz", char2cells(0x2500) == 1 ? 0x2500 : '-' }, // ─
{ &wp->w_p_fcs_chars.horizup, "horizup", char2cells(0x2534) == 1 ? 0x2534 : '-' }, // ┴
{ &wp->w_p_fcs_chars.horizdown, "horizdown", char2cells(0x252c) == 1 ? 0x252c : '-' }, // ┬
{ &wp->w_p_fcs_chars.vert, "vert", char2cells(0x2502) == 1 ? 0x2502 : '|' }, // │
{ &wp->w_p_fcs_chars.vertleft, "vertleft", char2cells(0x2524) == 1 ? 0x2524 : '|' }, // ┤
{ &wp->w_p_fcs_chars.vertright, "vertright", char2cells(0x251c) == 1 ? 0x251c : '|' }, // ├
{ &wp->w_p_fcs_chars.verthoriz, "verthoriz", char2cells(0x253c) == 1 ? 0x253c : '+' }, // ┼
{ &wp->w_p_fcs_chars.fold, "fold", char2cells(0x00b7) == 1 ? 0x00b7 : '-' }, // ·
{ &wp->w_p_fcs_chars.foldopen, "foldopen", '-' },
{ &wp->w_p_fcs_chars.foldclosed, "foldclose", '+' },
{ &wp->w_p_fcs_chars.foldsep, "foldsep", char2cells(0x2502) == 1 ? 0x2502 : '|' }, // │
{ &wp->w_p_fcs_chars.diff, "diff", '-' },
{ &wp->w_p_fcs_chars.msgsep, "msgsep", ' ' },
{ &wp->w_p_fcs_chars.eob, "eob", '~' },
};
struct chars_tab lcs_tab[] = {
{ &wp->w_p_lcs_chars.eol, "eol", NUL },
{ &wp->w_p_lcs_chars.ext, "extends", NUL },
{ &wp->w_p_lcs_chars.nbsp, "nbsp", NUL },
{ &wp->w_p_lcs_chars.prec, "precedes", NUL },
{ &wp->w_p_lcs_chars.space, "space", NUL },
{ &wp->w_p_lcs_chars.tab2, "tab", NUL },
{ &wp->w_p_lcs_chars.lead, "lead", NUL },
{ &wp->w_p_lcs_chars.trail, "trail", NUL },
{ &wp->w_p_lcs_chars.conceal, "conceal", NUL },
};
if (varp == &p_lcs || varp == &wp->w_p_lcs) {
tab = lcs_tab;
entries = ARRAY_SIZE(lcs_tab);
if (varp == &wp->w_p_lcs && wp->w_p_lcs[0] == NUL) {
varp = &p_lcs;
}
} else {
tab = fcs_tab;
entries = ARRAY_SIZE(fcs_tab);
if (varp == &wp->w_p_fcs && wp->w_p_fcs[0] == NUL) {
varp = &p_fcs;
}
}
// first round: check for valid value, second round: assign values
for (round = 0; round <= (set ? 1 : 0); round++) {
if (round > 0) {
// After checking that the value is valid: set defaults
for (i = 0; i < entries; i++) {
if (tab[i].cp != NULL) {
*(tab[i].cp) = tab[i].def;
}
}
if (varp == &p_lcs || varp == &wp->w_p_lcs) {
wp->w_p_lcs_chars.tab1 = NUL;
wp->w_p_lcs_chars.tab3 = NUL;
xfree(wp->w_p_lcs_chars.multispace);
if (multispace_len > 0) {
wp->w_p_lcs_chars.multispace = xmalloc(((size_t)multispace_len + 1) * sizeof(int));
wp->w_p_lcs_chars.multispace[multispace_len] = NUL;
} else {
wp->w_p_lcs_chars.multispace = NULL;
}
xfree(wp->w_p_lcs_chars.leadmultispace);
if (lead_multispace_len > 0) {
wp->w_p_lcs_chars.leadmultispace
= xmalloc(((size_t)lead_multispace_len + 1) * sizeof(int));
wp->w_p_lcs_chars.leadmultispace[lead_multispace_len] = NUL;
} else {
wp->w_p_lcs_chars.leadmultispace = NULL;
}
}
}
p = *varp;
while (*p) {
for (i = 0; i < entries; i++) {
len = (int)STRLEN(tab[i].name);
if (STRNCMP(p, tab[i].name, len) == 0
&& p[len] == ':'
&& p[len + 1] != NUL) {
c2 = c3 = 0;
s = p + len + 1;
c1 = get_encoded_char_adv(&s);
if (c1 == 0 || char2cells(c1) > 1) {
return e_invarg;
}
if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
if (*s == NUL) {
return e_invarg;
}
c2 = get_encoded_char_adv(&s);
if (c2 == 0 || char2cells(c2) > 1) {
return e_invarg;
}
if (!(*s == ',' || *s == NUL)) {
c3 = get_encoded_char_adv(&s);
if (c3 == 0 || char2cells(c3) > 1) {
return e_invarg;
}
}
}
if (*s == ',' || *s == NUL) {
if (round > 0) {
if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
wp->w_p_lcs_chars.tab1 = c1;
wp->w_p_lcs_chars.tab2 = c2;
wp->w_p_lcs_chars.tab3 = c3;
} else if (tab[i].cp != NULL) {
*(tab[i].cp) = c1;
}
}
p = s;
break;
}
}
}
if (i == entries) {
len = (int)STRLEN("multispace");
len2 = (int)STRLEN("leadmultispace");
if ((varp == &p_lcs || varp == &wp->w_p_lcs)
&& STRNCMP(p, "multispace", len) == 0
&& p[len] == ':'
&& p[len + 1] != NUL) {
s = p + len + 1;
if (round == 0) {
// Get length of lcs-multispace string in the first round
last_multispace = p;
multispace_len = 0;
while (*s != NUL && *s != ',') {
c1 = get_encoded_char_adv(&s);
if (c1 == 0 || char2cells(c1) > 1) {
return e_invarg;
}
multispace_len++;
}
if (multispace_len == 0) {
// lcs-multispace cannot be an empty string
return e_invarg;
}
p = s;
} else {
int multispace_pos = 0;
while (*s != NUL && *s != ',') {
c1 = get_encoded_char_adv(&s);
if (p == last_multispace) {
wp->w_p_lcs_chars.multispace[multispace_pos++] = c1;
}
}
p = s;
}
} else if ((varp == &p_lcs || varp == &wp->w_p_lcs)
&& STRNCMP(p, "leadmultispace", len2) == 0
&& p[len2] == ':'
&& p[len2 + 1] != NUL) {
s = p + len2 + 1;
if (round == 0) {
// get length of lcs-leadmultispace string in first round
last_lmultispace = p;
lead_multispace_len = 0;
while (*s != NUL && *s != ',') {
c1 = get_encoded_char_adv(&s);
if (c1 == 0 || char2cells(c1) > 1) {
return e_invarg;
}
lead_multispace_len++;
}
if (lead_multispace_len == 0) {
// lcs-leadmultispace cannot be an empty string
return e_invarg;
}
p = s;
} else {
int multispace_pos = 0;
while (*s != NUL && *s != ',') {
c1 = get_encoded_char_adv(&s);
if (p == last_lmultispace) {
wp->w_p_lcs_chars.leadmultispace[multispace_pos++] = c1;
}
}
p = s;
}
} else {
return e_invarg;
}
}
if (*p == ',') {
p++;
}
}
}
return NULL; // no error
}
/// Check all global and local values of 'listchars' and 'fillchars'.
/// May set different defaults in case character widths change.
///
/// @return an untranslated error message if any of them is invalid, NULL otherwise.
char *check_chars_options(void)
{
if (set_chars_option(curwin, &p_lcs, false) != NULL) {
return e_conflicts_with_value_of_listchars;
}
if (set_chars_option(curwin, &p_fcs, false) != NULL) {
return e_conflicts_with_value_of_fillchars;
}
FOR_ALL_TAB_WINDOWS(tp, wp) {
if (set_chars_option(wp, &wp->w_p_lcs, true) != NULL) {
return e_conflicts_with_value_of_listchars;
}
if (set_chars_option(wp, &wp->w_p_fcs, true) != NULL) {
return e_conflicts_with_value_of_fillchars;
}
}
return NULL;
}
/// Check if the new Nvim application "screen" dimensions are valid.
/// Correct it if it's too small or way too big.
void check_screensize(void)

View File

@ -3607,3 +3607,71 @@ int expand_spelling(linenr_T lnum, char_u *pat, char ***matchp)
*matchp = ga.ga_data;
return ga.ga_len;
}
/// Return true if "val" is a valid 'spelllang' value.
bool valid_spelllang(const char_u *val)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return valid_name(val, ".-_,@");
}
/// Return true if "val" is a valid 'spellfile' value.
bool valid_spellfile(const char_u *val)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
for (const char_u *s = val; *s != NUL; s++) {
if (!vim_isfilec(*s) && *s != ',' && *s != ' ') {
return false;
}
}
return true;
}
char *did_set_spell_option(bool is_spellfile)
{
char *errmsg = NULL;
if (is_spellfile) {
int l = (int)STRLEN(curwin->w_s->b_p_spf);
if (l > 0
&& (l < 4 || STRCMP(curwin->w_s->b_p_spf + l - 4, ".add") != 0)) {
errmsg = e_invarg;
}
}
if (errmsg == NULL) {
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_buffer == curbuf && wp->w_p_spell) {
errmsg = did_set_spelllang(wp);
break;
}
}
}
return errmsg;
}
/// Set curbuf->b_cap_prog to the regexp program for 'spellcapcheck'.
/// Return error message when failed, NULL when OK.
char *compile_cap_prog(synblock_T *synblock)
FUNC_ATTR_NONNULL_ALL
{
regprog_T *rp = synblock->b_cap_prog;
char_u *re;
if (synblock->b_p_spc == NULL || *synblock->b_p_spc == NUL) {
synblock->b_cap_prog = NULL;
} else {
// Prepend a ^ so that we only match at one column
re = concat_str((char_u *)"^", synblock->b_p_spc);
synblock->b_cap_prog = vim_regcomp((char *)re, RE_MAGIC);
xfree(re);
if (synblock->b_cap_prog == NULL) {
synblock->b_cap_prog = rp; // restore the previous program
return e_invarg;
}
}
vim_regfree(rp);
return NULL;
}

View File

@ -7269,6 +7269,88 @@ static bool frame_check_width(const frame_T *topfrp, int width)
return true;
}
/// Simple int comparison function for use with qsort()
static int int_cmp(const void *a, const void *b)
{
return *(const int *)a - *(const int *)b;
}
/// Handle setting 'colorcolumn' or 'textwidth' in window "wp".
///
/// @return error message, NULL if it's OK.
char *check_colorcolumn(win_T *wp)
{
char *s;
int col;
unsigned int count = 0;
int color_cols[256];
int j = 0;
if (wp->w_buffer == NULL) {
return NULL; // buffer was closed
}
for (s = (char *)wp->w_p_cc; *s != NUL && count < 255;) {
if (*s == '-' || *s == '+') {
// -N and +N: add to 'textwidth'
col = (*s == '-') ? -1 : 1;
s++;
if (!ascii_isdigit(*s)) {
return e_invarg;
}
col = col * getdigits_int(&s, true, 0);
if (wp->w_buffer->b_p_tw == 0) {
goto skip; // 'textwidth' not set, skip this item
}
assert((col >= 0
&& wp->w_buffer->b_p_tw <= INT_MAX - col
&& wp->w_buffer->b_p_tw + col >= INT_MIN)
|| (col < 0
&& wp->w_buffer->b_p_tw >= INT_MIN - col
&& wp->w_buffer->b_p_tw + col <= INT_MAX));
col += (int)wp->w_buffer->b_p_tw;
if (col < 0) {
goto skip;
}
} else if (ascii_isdigit(*s)) {
col = getdigits_int(&s, true, 0);
} else {
return e_invarg;
}
color_cols[count++] = col - 1; // 1-based to 0-based
skip:
if (*s == NUL) {
break;
}
if (*s != ',') {
return e_invarg;
}
if (*++s == NUL) {
return e_invarg; // illegal trailing comma as in "set cc=80,"
}
}
xfree(wp->w_p_cc_cols);
if (count == 0) {
wp->w_p_cc_cols = NULL;
} else {
wp->w_p_cc_cols = xmalloc(sizeof(int) * (count + 1));
// sort the columns for faster usage on screen redraw inside
// win_line()
qsort(color_cols, count, sizeof(int), int_cmp);
for (unsigned int i = 0; i < count; i++) {
// skip duplicates
if (j == 0 || wp->w_p_cc_cols[j - 1] != color_cols[i]) {
wp->w_p_cc_cols[j++] = color_cols[i];
}
}
wp->w_p_cc_cols[j] = -1; // end marker
}
return NULL; // no error
}
int win_getid(typval_T *argvars)
{
if (argvars[0].v_type == VAR_UNKNOWN) {

30
test/unit/indent_spec.lua Normal file
View File

@ -0,0 +1,30 @@
local helpers = require("test.unit.helpers")(after_each)
local itp = helpers.gen_itp(it)
local eq = helpers.eq
local indent = helpers.cimport("./src/nvim/indent.h")
local globals = helpers.cimport("./src/nvim/globals.h")
describe('get_sts_value', function()
itp([[returns 'softtabstop' when it is non-negative]], function()
globals.curbuf.b_p_sts = 5
eq(5, indent.get_sts_value())
globals.curbuf.b_p_sts = 0
eq(0, indent.get_sts_value())
end)
itp([[returns "effective shiftwidth" when 'softtabstop' is negative]], function()
local shiftwidth = 2
globals.curbuf.b_p_sw = shiftwidth
local tabstop = 5
globals.curbuf.b_p_ts = tabstop
globals.curbuf.b_p_sts = -2
eq(shiftwidth, indent.get_sts_value())
shiftwidth = 0
globals.curbuf.b_p_sw = shiftwidth
eq(tabstop, indent.get_sts_value())
end)
end)

View File

@ -5,7 +5,6 @@ local to_cstr = helpers.to_cstr
local eq = helpers.eq
local option = helpers.cimport("./src/nvim/option.h")
local globals = helpers.cimport("./src/nvim/globals.h")
local check_ff_value = function(ff)
return option.check_ff_value(to_cstr(ff))
@ -27,26 +26,3 @@ describe('check_ff_value', function()
eq(0, check_ff_value("foo"))
end)
end)
describe('get_sts_value', function()
itp([[returns 'softtabstop' when it is non-negative]], function()
globals.curbuf.b_p_sts = 5
eq(5, option.get_sts_value())
globals.curbuf.b_p_sts = 0
eq(0, option.get_sts_value())
end)
itp([[returns "effective shiftwidth" when 'softtabstop' is negative]], function()
local shiftwidth = 2
globals.curbuf.b_p_sw = shiftwidth
local tabstop = 5
globals.curbuf.b_p_ts = tabstop
globals.curbuf.b_p_sts = -2
eq(shiftwidth, option.get_sts_value())
shiftwidth = 0
globals.curbuf.b_p_sw = shiftwidth
eq(tabstop, option.get_sts_value())
end)
end)