diff options
author | zeertzjq <zeertzjq@outlook.com> | 2022-08-19 19:20:39 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-19 19:20:39 +0800 |
commit | 2af9be3db59b2e26268dc62cb65e673e2f7d4783 (patch) | |
tree | 37d12b5e0ff7b9847c2504c544e691733015a577 | |
parent | 5dc43265b1f966a9653cc552fa5301ee487a8116 (diff) | |
download | rneovim-2af9be3db59b2e26268dc62cb65e673e2f7d4783.tar.gz rneovim-2af9be3db59b2e26268dc62cb65e673e2f7d4783.tar.bz2 rneovim-2af9be3db59b2e26268dc62cb65e673e2f7d4783.zip |
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)
https://github.com/vim/vim/commit/e677df8d93772a705f40a94f3c871aee78fe4d99
-rw-r--r-- | src/nvim/eval.c | 23 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 111 | ||||
-rw-r--r-- | src/nvim/hardcopy.c | 1 | ||||
-rw-r--r-- | src/nvim/indent.c | 308 | ||||
-rw-r--r-- | src/nvim/mbyte.c | 2 | ||||
-rw-r--r-- | src/nvim/message.c | 1 | ||||
-rw-r--r-- | src/nvim/option.c | 783 | ||||
-rw-r--r-- | src/nvim/quickfix.c | 138 | ||||
-rw-r--r-- | src/nvim/screen.c | 321 | ||||
-rw-r--r-- | src/nvim/spell.c | 68 | ||||
-rw-r--r-- | src/nvim/window.c | 82 | ||||
-rw-r--r-- | test/unit/indent_spec.lua | 30 | ||||
-rw-r--r-- | test/unit/option_spec.lua | 24 |
13 files changed, 951 insertions, 941 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 4014f2dd22..21068e9101 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -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) diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 8eaa9c1f72..456fd9fdf6 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -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 diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index d811a78ca8..6a1bbcb089 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -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" diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 684c00b7e7..f18a6d7b32 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -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) { diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 2b2baeacbc..af9e214d92 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -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" diff --git a/src/nvim/message.c b/src/nvim/message.c index 95460e1aaf..6cc68edb82 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -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" diff --git a/src/nvim/option.c b/src/nvim/option.c index 49c8bd3cd4..09793cbdcf 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -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) diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 504fb2464e..1c416a872b 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -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); +} diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 59ef3a76b1..2419a42a2a 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -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) diff --git a/src/nvim/spell.c b/src/nvim/spell.c index c8da6af9c9..1e44d328b3 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -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; +} diff --git a/src/nvim/window.c b/src/nvim/window.c index 496bc13a37..564b5c4c51 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -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) { diff --git a/test/unit/indent_spec.lua b/test/unit/indent_spec.lua new file mode 100644 index 0000000000..ec86822b55 --- /dev/null +++ b/test/unit/indent_spec.lua @@ -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) diff --git a/test/unit/option_spec.lua b/test/unit/option_spec.lua index b8b8a435bc..b3c3718035 100644 --- a/test/unit/option_spec.lua +++ b/test/unit/option_spec.lua @@ -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) |