optionstr.c (73405B)
1 #include <assert.h> 2 #include <stdbool.h> 3 #include <stdint.h> 4 #include <string.h> 5 6 #include "nvim/api/private/defs.h" 7 #include "nvim/api/win_config.h" 8 #include "nvim/ascii_defs.h" 9 #include "nvim/autocmd.h" 10 #include "nvim/buffer_defs.h" 11 #include "nvim/charset.h" 12 #include "nvim/cmdexpand.h" 13 #include "nvim/cmdexpand_defs.h" 14 #include "nvim/cursor.h" 15 #include "nvim/cursor_shape.h" 16 #include "nvim/decoration.h" 17 #include "nvim/diff.h" 18 #include "nvim/digraph.h" 19 #include "nvim/drawscreen.h" 20 #include "nvim/errors.h" 21 #include "nvim/eval/userfunc.h" 22 #include "nvim/eval/vars.h" 23 #include "nvim/ex_getln.h" 24 #include "nvim/fold.h" 25 #include "nvim/gettext_defs.h" 26 #include "nvim/globals.h" 27 #include "nvim/grid.h" 28 #include "nvim/highlight_group.h" 29 #include "nvim/indent.h" 30 #include "nvim/indent_c.h" 31 #include "nvim/insexpand.h" 32 #include "nvim/macros_defs.h" 33 #include "nvim/mark.h" 34 #include "nvim/mbyte.h" 35 #include "nvim/memline.h" 36 #include "nvim/memory.h" 37 #include "nvim/message.h" 38 #include "nvim/move.h" 39 #include "nvim/option.h" 40 #include "nvim/option_defs.h" 41 #include "nvim/option_vars.h" 42 #include "nvim/optionstr.h" 43 #include "nvim/os/os.h" 44 #include "nvim/pos_defs.h" 45 #include "nvim/regexp.h" 46 #include "nvim/regexp_defs.h" 47 #include "nvim/shada.h" 48 #include "nvim/spell.h" 49 #include "nvim/spellfile.h" 50 #include "nvim/spellsuggest.h" 51 #include "nvim/strings.h" 52 #include "nvim/tag.h" 53 #include "nvim/terminal.h" 54 #include "nvim/types_defs.h" 55 #include "nvim/vim_defs.h" 56 #include "nvim/window.h" 57 #include "nvim/winfloat.h" 58 59 #include "optionstr.c.generated.h" 60 61 static const char e_illegal_character_after_chr[] 62 = N_("E535: Illegal character after <%c>"); 63 static const char e_comma_required[] 64 = N_("E536: Comma required"); 65 static const char e_unclosed_expression_sequence[] 66 = N_("E540: Unclosed expression sequence"); 67 static const char e_unbalanced_groups[] 68 = N_("E542: Unbalanced groups"); 69 static const char e_backupext_and_patchmode_are_equal[] 70 = N_("E589: 'backupext' and 'patchmode' are equal"); 71 static const char e_showbreak_contains_unprintable_or_wide_character[] 72 = N_("E595: 'showbreak' contains unprintable or wide character"); 73 static const char e_wrong_number_of_characters_for_field_str[] 74 = N_("E1511: Wrong number of characters for field \"%s\""); 75 static const char e_wrong_character_width_for_field_str[] 76 = N_("E1512: Wrong character width for field \"%s\""); 77 78 /// All possible flags for 'shm'. 79 /// the literal chars before 0 are removed flags. these are safely ignored 80 static char SHM_ALL[] = { SHM_RO, SHM_MOD, SHM_LINES, 81 SHM_WRI, SHM_ABBREVIATIONS, SHM_WRITE, SHM_TRUNC, SHM_TRUNCALL, 82 SHM_OVER, SHM_OVERALL, SHM_SEARCH, SHM_ATTENTION, SHM_INTRO, 83 SHM_COMPLETIONMENU, SHM_COMPLETIONSCAN, SHM_RECORDING, SHM_FILEINFO, 84 SHM_SEARCHCOUNT, 'n', 'f', 'x', 'i', 0, }; 85 86 /// After setting various option values: recompute variables that depend on 87 /// option values. 88 void didset_string_options(void) 89 { 90 check_str_opt(kOptCasemap, NULL); 91 check_str_opt(kOptBackupcopy, NULL); 92 check_str_opt(kOptBelloff, NULL); 93 check_str_opt(kOptCompleteopt, NULL); 94 check_str_opt(kOptSessionoptions, NULL); 95 check_str_opt(kOptViewoptions, NULL); 96 check_str_opt(kOptFoldopen, NULL); 97 check_str_opt(kOptDisplay, NULL); 98 check_str_opt(kOptJumpoptions, NULL); 99 check_str_opt(kOptRedrawdebug, NULL); 100 check_str_opt(kOptTagcase, NULL); 101 check_str_opt(kOptTermpastefilter, NULL); 102 check_str_opt(kOptVirtualedit, NULL); 103 check_str_opt(kOptSwitchbuf, NULL); 104 check_str_opt(kOptTabclose, NULL); 105 check_str_opt(kOptWildoptions, NULL); 106 check_str_opt(kOptClipboard, NULL); 107 } 108 109 char *illegal_char(char *errbuf, size_t errbuflen, int c) 110 { 111 if (errbuf == NULL) { 112 return ""; 113 } 114 vim_snprintf(errbuf, errbuflen, _("E539: Illegal character <%s>"), 115 transchar(c)); 116 return errbuf; 117 } 118 119 static char *illegal_char_after_chr(char *errbuf, size_t errbuflen, int c) 120 { 121 if (errbuf == NULL) { 122 return ""; 123 } 124 vim_snprintf(errbuf, errbuflen, _(e_illegal_character_after_chr), c); 125 return errbuf; 126 } 127 128 /// Check string options in a buffer for NULL value. 129 void check_buf_options(buf_T *buf) 130 { 131 check_string_option(&buf->b_p_bh); 132 check_string_option(&buf->b_p_bt); 133 check_string_option(&buf->b_p_fenc); 134 check_string_option(&buf->b_p_ff); 135 check_string_option(&buf->b_p_def); 136 check_string_option(&buf->b_p_inc); 137 check_string_option(&buf->b_p_inex); 138 check_string_option(&buf->b_p_inde); 139 check_string_option(&buf->b_p_indk); 140 check_string_option(&buf->b_p_fp); 141 check_string_option(&buf->b_p_fex); 142 check_string_option(&buf->b_p_kp); 143 check_string_option(&buf->b_p_mps); 144 check_string_option(&buf->b_p_fo); 145 check_string_option(&buf->b_p_flp); 146 check_string_option(&buf->b_p_isk); 147 check_string_option(&buf->b_p_com); 148 check_string_option(&buf->b_p_cms); 149 check_string_option(&buf->b_p_nf); 150 check_string_option(&buf->b_p_qe); 151 check_string_option(&buf->b_p_syn); 152 check_string_option(&buf->b_s.b_syn_isk); 153 check_string_option(&buf->b_s.b_p_spc); 154 check_string_option(&buf->b_s.b_p_spf); 155 check_string_option(&buf->b_s.b_p_spl); 156 check_string_option(&buf->b_s.b_p_spo); 157 check_string_option(&buf->b_p_sua); 158 check_string_option(&buf->b_p_cink); 159 check_string_option(&buf->b_p_cino); 160 parse_cino(buf); 161 check_string_option(&buf->b_p_lop); 162 check_string_option(&buf->b_p_ft); 163 check_string_option(&buf->b_p_cinw); 164 check_string_option(&buf->b_p_cinsd); 165 check_string_option(&buf->b_p_cot); 166 check_string_option(&buf->b_p_cpt); 167 check_string_option(&buf->b_p_cfu); 168 check_string_option(&buf->b_p_ofu); 169 check_string_option(&buf->b_p_keymap); 170 check_string_option(&buf->b_p_gefm); 171 check_string_option(&buf->b_p_gp); 172 check_string_option(&buf->b_p_mp); 173 check_string_option(&buf->b_p_efm); 174 check_string_option(&buf->b_p_ep); 175 check_string_option(&buf->b_p_path); 176 check_string_option(&buf->b_p_tags); 177 check_string_option(&buf->b_p_ffu); 178 check_string_option(&buf->b_p_tfu); 179 check_string_option(&buf->b_p_tc); 180 check_string_option(&buf->b_p_dict); 181 check_string_option(&buf->b_p_dia); 182 check_string_option(&buf->b_p_tsr); 183 check_string_option(&buf->b_p_tsrfu); 184 check_string_option(&buf->b_p_lw); 185 check_string_option(&buf->b_p_bkc); 186 check_string_option(&buf->b_p_menc); 187 check_string_option(&buf->b_p_vsts); 188 check_string_option(&buf->b_p_vts); 189 } 190 191 /// Free the string allocated for an option. 192 /// Checks for the string being empty_string_option. This may happen if we're out of memory, 193 /// xstrdup() returned NULL, which was replaced by empty_string_option by check_options(). 194 void free_string_option(char *p) 195 { 196 if (p != empty_string_option) { 197 xfree(p); 198 } 199 } 200 201 void clear_string_option(char **pp) 202 { 203 if (*pp != empty_string_option) { 204 xfree(*pp); 205 } 206 *pp = empty_string_option; 207 } 208 209 void check_string_option(char **pp) 210 { 211 if (*pp == NULL) { 212 *pp = empty_string_option; 213 } 214 } 215 216 /// Return true if "val" is a valid 'filetype' name. 217 /// Also used for 'syntax' and 'keymap'. 218 static bool valid_filetype(const char *val) 219 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT 220 { 221 return valid_name(val, ".-_"); 222 } 223 224 /// Handle setting 'signcolumn' for value 'val'. Store minimum and maximum width. 225 /// 226 /// @param wcl when NULL: use "wp->w_p_scl" 227 /// @param wp when NULL: only parse "scl" 228 /// 229 /// @return OK when the value is valid, FAIL otherwise 230 int check_signcolumn(char *scl, win_T *wp) 231 { 232 char *val = empty_string_option; 233 if (scl != NULL) { 234 val = scl; 235 } else if (wp != NULL) { 236 val = wp->w_p_scl; 237 } 238 239 if (*val == NUL) { 240 return FAIL; 241 } 242 243 if (opt_strings_flags(val, opt_scl_values, NULL, false) == OK) { 244 if (wp == NULL) { 245 return OK; 246 } 247 if (!strncmp(val, "no", 2)) { // no 248 wp->w_minscwidth = wp->w_maxscwidth = SCL_NO; 249 } else if (!strncmp(val, "nu", 2) && (wp->w_p_nu || wp->w_p_rnu)) { // number 250 wp->w_minscwidth = wp->w_maxscwidth = SCL_NUM; 251 } else if (!strncmp(val, "yes:", 4)) { // yes:<NUM> 252 wp->w_minscwidth = wp->w_maxscwidth = val[4] - '0'; 253 } else if (*val == 'y') { // yes 254 wp->w_minscwidth = wp->w_maxscwidth = 1; 255 } else if (!strncmp(val, "auto:", 5)) { // auto:<NUM> 256 wp->w_minscwidth = 0; 257 wp->w_maxscwidth = val[5] - '0'; 258 } else { // auto 259 wp->w_minscwidth = 0; 260 wp->w_maxscwidth = 1; 261 } 262 } else { 263 if (strncmp(val, "auto:", 5) != 0 264 || strlen(val) != 8 265 || !ascii_isdigit(val[5]) 266 || val[6] != '-' 267 || !ascii_isdigit(val[7])) { 268 return FAIL; 269 } 270 // auto:<NUM>-<NUM> 271 int min = val[5] - '0'; 272 int max = val[7] - '0'; 273 if (min < 1 || max < 2 || min > 8 || min >= max) { 274 return FAIL; 275 } 276 if (wp == NULL) { 277 return OK; 278 } 279 wp->w_minscwidth = min; 280 wp->w_maxscwidth = max; 281 } 282 283 int scwidth = wp->w_minscwidth <= 0 ? 0 : MIN(wp->w_maxscwidth, wp->w_scwidth); 284 wp->w_scwidth = MAX(wp->w_minscwidth, scwidth); 285 return OK; 286 } 287 288 /// Check validity of options with the 'statusline' format. 289 /// Return an untranslated error message or NULL. 290 const char *check_stl_option(char *s) 291 { 292 int groupdepth = 0; 293 static char errbuf[ERR_BUFLEN]; 294 295 while (*s) { 296 // Check for valid keys after % sequences 297 while (*s && *s != '%') { 298 s++; 299 } 300 if (!*s) { 301 break; 302 } 303 s++; 304 if (*s == '%' || *s == STL_TRUNCMARK || *s == STL_SEPARATE) { 305 s++; 306 continue; 307 } 308 if (*s == ')') { 309 s++; 310 if (--groupdepth < 0) { 311 break; 312 } 313 continue; 314 } 315 if (*s == '-') { 316 s++; 317 } 318 while (ascii_isdigit(*s)) { 319 s++; 320 } 321 if (*s == STL_USER_HL) { 322 continue; 323 } 324 if (*s == '.') { 325 s++; 326 while (*s && ascii_isdigit(*s)) { 327 s++; 328 } 329 } 330 if (*s == '(') { 331 groupdepth++; 332 continue; 333 } 334 if (vim_strchr(STL_ALL, (uint8_t)(*s)) == NULL) { 335 return illegal_char(errbuf, sizeof(errbuf), (uint8_t)(*s)); 336 } 337 if (*s == '{') { 338 bool reevaluate = (*++s == '%'); 339 340 if (reevaluate && *++s == '}') { 341 // "}" is not allowed immediately after "%{%" 342 return illegal_char(errbuf, sizeof(errbuf), '}'); 343 } 344 while ((*s != '}' || (reevaluate && s[-1] != '%')) && *s) { 345 s++; 346 } 347 if (*s != '}') { 348 return e_unclosed_expression_sequence; 349 } 350 } 351 } 352 if (groupdepth != 0) { 353 return e_unbalanced_groups; 354 } 355 return NULL; 356 } 357 358 /// Check for a "normal" directory or file name in some options. Disallow a 359 /// path separator (slash and/or backslash), wildcards and characters that are 360 /// often illegal in a file name. Be more permissive if "secure" is off. 361 bool check_illegal_path_names(char *val, uint32_t flags) 362 { 363 return (((flags & kOptFlagNFname) 364 && strpbrk(val, (secure ? "/\\*?[|;&<>\r\n" : "/\\*?[<>\r\n")) != NULL) 365 || ((flags & kOptFlagNDname) 366 && strpbrk(val, "*?[|;&<>\r\n") != NULL)); 367 } 368 369 /// An option that accepts a list of flags is changed. 370 /// e.g. 'viewoptions', 'switchbuf', 'casemap', etc. 371 static const char *did_set_opt_flags(char *val, const char **values, unsigned *flagp, bool list) 372 { 373 if (opt_strings_flags(val, values, flagp, list) != OK) { 374 return e_invarg; 375 } 376 return NULL; 377 } 378 379 static const char **opt_values(OptIndex idx, size_t *values_len) 380 { 381 OptIndex idx1 = idx == kOptViewoptions ? kOptSessionoptions 382 : idx == kOptFileformats ? kOptFileformat 383 : idx; 384 385 vimoption_T *opt = get_option(idx1); 386 if (values_len != NULL) { 387 *values_len = opt->values_len; 388 } 389 return opt->values; 390 } 391 392 static int check_str_opt(OptIndex idx, char **varp) 393 { 394 vimoption_T *opt = get_option(idx); 395 if (varp == NULL) { 396 varp = opt->var; 397 } 398 bool list = opt->flags & (kOptFlagComma | kOptFlagOneComma); 399 const char **values = opt_values(idx, NULL); 400 return opt_strings_flags(*varp, values, opt->flags_var, list); 401 } 402 403 int expand_set_str_generic(optexpand_T *args, int *numMatches, char ***matches) 404 { 405 size_t values_len; 406 const char **values = opt_values(args->oe_idx, &values_len); 407 return expand_set_opt_string(args, values, values_len, numMatches, matches); 408 } 409 410 const char *did_set_str_generic(optset_T *args) 411 { 412 return check_str_opt(args->os_idx, args->os_varp) != OK ? e_invarg : NULL; 413 } 414 415 /// An option which is a list of flags is set. Valid values are in "flags". 416 static const char *did_set_option_listflag(char *val, char *flags, char *errbuf, size_t errbuflen) 417 { 418 for (char *s = val; *s; s++) { 419 if (vim_strchr(flags, (uint8_t)(*s)) == NULL) { 420 return illegal_char(errbuf, errbuflen, (uint8_t)(*s)); 421 } 422 } 423 424 return NULL; 425 } 426 427 /// Expand an option that accepts a list of string values. 428 static int expand_set_opt_string(optexpand_T *args, const char **values, size_t numValues, 429 int *numMatches, char ***matches) 430 { 431 regmatch_T *regmatch = args->oe_regmatch; 432 bool include_orig_val = args->oe_include_orig_val; 433 char *option_val = args->oe_opt_value; 434 435 // Assume numValues is small since they are fixed enums, so just allocate 436 // upfront instead of needing two passes to calculate output size. 437 *matches = xmalloc(sizeof(char *) * (numValues + 1)); 438 439 int count = 0; 440 441 if (include_orig_val && *option_val != NUL) { 442 (*matches)[count++] = xstrdup(option_val); 443 } 444 445 for (const char **val = values; *val != NULL; val++) { 446 if (**val == NUL) { 447 continue; // Ignore empty 448 } else if (include_orig_val && *option_val != NUL) { 449 if (strcmp(*val, option_val) == 0) { 450 continue; 451 } 452 } 453 if (vim_regexec(regmatch, *val, 0)) { 454 (*matches)[count++] = xstrdup(*val); 455 } 456 } 457 if (count == 0) { 458 XFREE_CLEAR(*matches); 459 return FAIL; 460 } 461 *numMatches = count; 462 return OK; 463 } 464 465 static char *set_opt_callback_orig_option = NULL; 466 static char *((*set_opt_callback_func)(expand_T *, int)); 467 468 /// Callback used by expand_set_opt_generic to also include the original value. 469 static char *expand_set_opt_callback(expand_T *xp, int idx) 470 { 471 if (idx == 0) { 472 if (set_opt_callback_orig_option != NULL) { 473 return set_opt_callback_orig_option; 474 } else { 475 return ""; // empty strings are ignored 476 } 477 } 478 return set_opt_callback_func(xp, idx - 1); 479 } 480 481 /// Expand an option with a callback that iterates through a list of possible names. 482 static int expand_set_opt_generic(optexpand_T *args, CompleteListItemGetter func, int *numMatches, 483 char ***matches) 484 { 485 set_opt_callback_orig_option = args->oe_include_orig_val ? args->oe_opt_value : NULL; 486 set_opt_callback_func = func; 487 488 // not using fuzzy as currently EXPAND_STRING_SETTING doesn't use it 489 ExpandGeneric("", args->oe_xp, args->oe_regmatch, matches, numMatches, 490 expand_set_opt_callback, false); 491 492 set_opt_callback_orig_option = NULL; 493 set_opt_callback_func = NULL; 494 return OK; 495 } 496 497 /// Expand an option which is a list of flags. 498 static int expand_set_opt_listflag(optexpand_T *args, char *flags, int *numMatches, char ***matches) 499 { 500 char *option_val = args->oe_opt_value; 501 char *cmdline_val = args->oe_set_arg; 502 bool append = args->oe_append; 503 bool include_orig_val = args->oe_include_orig_val && (*option_val != NUL); 504 505 size_t num_flags = strlen(flags); 506 507 // Assume we only have small number of flags, so just allocate max size. 508 *matches = xmalloc(sizeof(char *) * (num_flags + 1)); 509 510 int count = 0; 511 512 if (include_orig_val) { 513 (*matches)[count++] = xstrdup(option_val); 514 } 515 516 for (char *flag = flags; *flag != NUL; flag++) { 517 if (append && vim_strchr(option_val, *flag) != NULL) { 518 continue; 519 } 520 521 if (vim_strchr(cmdline_val, *flag) == NULL) { 522 if (include_orig_val && option_val[1] == NUL && *flag == option_val[0]) { 523 // This value is already used as the first choice as it's the 524 // existing flag. Just skip it to avoid duplicate. 525 continue; 526 } 527 (*matches)[count++] = xmemdupz(flag, 1); 528 } 529 } 530 531 if (count == 0) { 532 XFREE_CLEAR(*matches); 533 return FAIL; 534 } 535 *numMatches = count; 536 return OK; 537 } 538 539 /// The 'ambiwidth' option is changed. 540 const char *did_set_ambiwidth(optset_T *args) 541 { 542 const char *errmsg = did_set_str_generic(args); 543 if (errmsg != NULL) { 544 return errmsg; 545 } 546 return check_chars_options(); 547 } 548 549 /// The 'emoji' option is changed. 550 const char *did_set_emoji(optset_T *args) 551 { 552 if (check_str_opt(kOptAmbiwidth, NULL) != OK) { 553 return e_invarg; 554 } 555 return check_chars_options(); 556 } 557 558 /// The 'background' option is changed. 559 const char *did_set_background(optset_T *args) 560 { 561 const char *errmsg = did_set_str_generic(args); 562 if (errmsg != NULL) { 563 return errmsg; 564 } 565 566 if (args->os_oldval.string.data[0] == *p_bg) { 567 // Value was not changed 568 return NULL; 569 } 570 571 int dark = (*p_bg == 'd'); 572 573 init_highlight(false, false); 574 575 if (dark != (*p_bg == 'd') && get_var_value("g:colors_name") != NULL) { 576 // The color scheme must have set 'background' back to another 577 // value, that's not what we want here. Disable the color 578 // scheme and set the colors again. 579 do_unlet(S_LEN("g:colors_name"), true); 580 free_string_option(p_bg); 581 p_bg = xstrdup((dark ? "dark" : "light")); 582 check_string_option(&p_bg); 583 init_highlight(false, false); 584 } 585 586 // Notify all terminal buffers that the background color changed so they can 587 // send a theme update notification 588 FOR_ALL_BUFFERS(buf) { 589 if (buf->terminal) { 590 terminal_notify_theme(buf->terminal, dark); 591 } 592 } 593 594 return NULL; 595 } 596 597 /// The 'backspace' option is changed. 598 const char *did_set_backspace(optset_T *args FUNC_ATTR_UNUSED) 599 { 600 if (ascii_isdigit(*p_bs)) { 601 if (*p_bs != '2') { 602 return e_invarg; 603 } 604 return NULL; 605 } 606 return did_set_str_generic(args); 607 } 608 609 /// The 'backupcopy' option is changed. 610 const char *did_set_backupcopy(optset_T *args) 611 { 612 buf_T *buf = (buf_T *)args->os_buf; 613 const char *oldval = args->os_oldval.string.data; 614 int opt_flags = args->os_flags; 615 char *bkc = p_bkc; 616 unsigned *flags = &bkc_flags; 617 618 if (opt_flags & OPT_LOCAL) { 619 bkc = buf->b_p_bkc; 620 flags = &buf->b_bkc_flags; 621 } else if (!(opt_flags & OPT_GLOBAL)) { 622 // When using :set, clear the local flags. 623 buf->b_bkc_flags = 0; 624 } 625 626 if ((opt_flags & OPT_LOCAL) && *bkc == NUL) { 627 // make the local value empty: use the global value 628 *flags = 0; 629 } else { 630 if (opt_strings_flags(bkc, opt_bkc_values, flags, true) != OK) { 631 return e_invarg; 632 } 633 634 if (((*flags & kOptBkcFlagAuto) != 0) 635 + ((*flags & kOptBkcFlagYes) != 0) 636 + ((*flags & kOptBkcFlagNo) != 0) != 1) { 637 // Must have exactly one of "auto", "yes" and "no". 638 opt_strings_flags(oldval, opt_bkc_values, flags, true); 639 return e_invarg; 640 } 641 } 642 643 return NULL; 644 } 645 646 /// The 'backupext' or the 'patchmode' option is changed. 647 const char *did_set_backupext_or_patchmode(optset_T *args FUNC_ATTR_UNUSED) 648 { 649 if (strcmp(*p_bex == '.' ? p_bex + 1 : p_bex, 650 *p_pm == '.' ? p_pm + 1 : p_pm) == 0) { 651 return e_backupext_and_patchmode_are_equal; 652 } 653 654 return NULL; 655 } 656 657 /// The 'breakat' option is changed. 658 const char *did_set_breakat(optset_T *args FUNC_ATTR_UNUSED) 659 { 660 for (int i = 0; i < 256; i++) { 661 breakat_flags[i] = false; 662 } 663 664 if (p_breakat != NULL) { 665 for (char *p = p_breakat; *p; p++) { 666 breakat_flags[(uint8_t)(*p)] = true; 667 } 668 } 669 670 return NULL; 671 } 672 673 /// The 'breakindentopt' option is changed. 674 const char *did_set_breakindentopt(optset_T *args) 675 { 676 win_T *win = (win_T *)args->os_win; 677 char **varp = (char **)args->os_varp; 678 679 if (briopt_check(*varp, varp == &win->w_p_briopt ? win : NULL) == FAIL) { 680 return e_invarg; 681 } 682 683 // list setting requires a redraw 684 if (varp == &win->w_p_briopt && win->w_briopt_list) { 685 redraw_all_later(UPD_NOT_VALID); 686 } 687 688 return NULL; 689 } 690 691 /// The 'bufhidden' option is changed. 692 const char *did_set_bufhidden(optset_T *args) 693 { 694 buf_T *buf = (buf_T *)args->os_buf; 695 return did_set_opt_flags(buf->b_p_bh, opt_bh_values, NULL, false); 696 } 697 698 /// The 'buftype' option is changed. 699 const char *did_set_buftype(optset_T *args) 700 { 701 buf_T *buf = (buf_T *)args->os_buf; 702 win_T *win = (win_T *)args->os_win; 703 // When 'buftype' is set, check for valid value. 704 if ((buf->terminal && buf->b_p_bt[0] != 't') 705 || (!buf->terminal && buf->b_p_bt[0] == 't') 706 || opt_strings_flags(buf->b_p_bt, opt_bt_values, NULL, false) != OK) { 707 return e_invarg; 708 } 709 // buftype=prompt: 710 if (buf->b_p_bt[0] == 'p') { 711 // Set default value for 'comments' 712 set_option_direct(kOptComments, STATIC_CSTR_AS_OPTVAL(""), OPT_LOCAL, SID_NONE); 713 // set the prompt start position to lastline. 714 pos_T next_prompt = { .lnum = buf->b_ml.ml_line_count, .col = buf->b_prompt_start.mark.col, 715 .coladd = 0 }; 716 RESET_FMARK(&buf->b_prompt_start, next_prompt, 0, ((fmarkv_T)INIT_FMARKV)); 717 } 718 if (win->w_status_height || global_stl_height()) { 719 win->w_redr_status = true; 720 redraw_later(win, UPD_VALID); 721 } 722 buf->b_help = (buf->b_p_bt[0] == 'h'); 723 redraw_titles(); 724 return NULL; 725 } 726 727 /// The global 'listchars' or 'fillchars' option is changed. 728 static const char *did_set_global_chars_option(win_T *win, char *val, CharsOption what, 729 int opt_flags, char *errbuf, size_t errbuflen) 730 { 731 const char *errmsg = NULL; 732 char **local_ptr = (what == kListchars) ? &win->w_p_lcs : &win->w_p_fcs; 733 734 // only apply the global value to "win" when it does not have a 735 // local value 736 errmsg = set_chars_option(win, val, what, 737 **local_ptr == NUL || !(opt_flags & OPT_GLOBAL), 738 errbuf, errbuflen); 739 if (errmsg != NULL) { 740 return errmsg; 741 } 742 743 // If the current window is set to use the global 744 // 'listchars'/'fillchars' value, clear the window-local value. 745 if (!(opt_flags & OPT_GLOBAL)) { 746 clear_string_option(local_ptr); 747 } 748 749 FOR_ALL_TAB_WINDOWS(tp, wp) { 750 // If the current window has a local value need to apply it 751 // again, it was changed when setting the global value. 752 // If no error was returned above, we don't expect an error 753 // here, so ignore the return value. 754 char *opt = (what == kListchars) ? wp->w_p_lcs : wp->w_p_fcs; 755 if (*opt == NUL) { 756 set_chars_option(wp, opt, what, true, errbuf, errbuflen); 757 } 758 } 759 760 redraw_all_later(UPD_NOT_VALID); 761 762 return NULL; 763 } 764 765 /// The 'fillchars' option or the 'listchars' option is changed. 766 const char *did_set_chars_option(optset_T *args) 767 { 768 win_T *win = (win_T *)args->os_win; 769 char **varp = (char **)args->os_varp; 770 const char *errmsg = NULL; 771 772 if (varp == &p_lcs) { // global 'listchars' 773 errmsg = did_set_global_chars_option(win, *varp, kListchars, args->os_flags, 774 args->os_errbuf, args->os_errbuflen); 775 } else if (varp == &p_fcs) { // global 'fillchars' 776 errmsg = did_set_global_chars_option(win, *varp, kFillchars, args->os_flags, 777 args->os_errbuf, args->os_errbuflen); 778 } else if (varp == &win->w_p_lcs) { // local 'listchars' 779 errmsg = set_chars_option(win, *varp, kListchars, true, 780 args->os_errbuf, args->os_errbuflen); 781 } else if (varp == &win->w_p_fcs) { // local 'fillchars' 782 errmsg = set_chars_option(win, *varp, kFillchars, true, 783 args->os_errbuf, args->os_errbuflen); 784 } 785 786 return errmsg; 787 } 788 789 /// Expand 'fillchars' or 'listchars' option value. 790 int expand_set_chars_option(optexpand_T *args, int *numMatches, char ***matches) 791 { 792 char **varp = (char **)args->oe_varp; 793 bool is_lcs = (varp == &p_lcs || varp == &curwin->w_p_lcs); 794 return expand_set_opt_generic(args, 795 is_lcs ? get_listchars_name : get_fillchars_name, 796 numMatches, 797 matches); 798 } 799 800 /// The 'cinoptions' option is changed. 801 const char *did_set_cinoptions(optset_T *args) 802 { 803 buf_T *buf = (buf_T *)args->os_buf; 804 // TODO(vim): recognize errors 805 parse_cino(buf); 806 807 return NULL; 808 } 809 810 /// The 'colorcolumn' option is changed. 811 const char *did_set_colorcolumn(optset_T *args) 812 { 813 win_T *win = (win_T *)args->os_win; 814 char **varp = (char **)args->os_varp; 815 return check_colorcolumn(*varp, varp == &win->w_p_cc ? win : NULL); 816 } 817 818 /// The 'comments' option is changed. 819 const char *did_set_comments(optset_T *args) 820 { 821 char **varp = (char **)args->os_varp; 822 char *errmsg = NULL; 823 for (char *s = *varp; *s;) { 824 while (*s && *s != ':') { 825 if (vim_strchr(COM_ALL, (uint8_t)(*s)) == NULL 826 && !ascii_isdigit(*s) && *s != '-') { 827 errmsg = illegal_char(args->os_errbuf, args->os_errbuflen, (uint8_t)(*s)); 828 break; 829 } 830 s++; 831 } 832 if (*s++ == NUL) { 833 errmsg = N_("E524: Missing colon"); 834 } else if (*s == ',' || *s == NUL) { 835 errmsg = N_("E525: Zero length string"); 836 } 837 if (errmsg != NULL) { 838 break; 839 } 840 while (*s && *s != ',') { 841 if (*s == '\\' && s[1] != NUL) { 842 s++; 843 } 844 s++; 845 } 846 s = skip_to_option_part(s); 847 } 848 return errmsg; 849 } 850 851 /// The 'commentstring' option is changed. 852 const char *did_set_commentstring(optset_T *args) 853 { 854 char **varp = (char **)args->os_varp; 855 856 if (**varp != NUL && strstr(*varp, "%s") == NULL) { 857 return N_("E537: 'commentstring' must be empty or contain %s"); 858 } 859 return NULL; 860 } 861 862 /// Check if value for 'complete' is valid when 'complete' option is changed. 863 const char *did_set_complete(optset_T *args) 864 { 865 char **varp = (char **)args->os_varp; 866 char buffer[LSIZE]; 867 uint8_t char_before = NUL; 868 869 for (char *p = *varp; *p;) { 870 memset(buffer, 0, LSIZE); 871 char *buf_ptr = buffer; 872 int escape = 0; 873 874 // Extract substring while handling escaped commas 875 while (*p && (*p != ',' || escape) && buf_ptr < (buffer + LSIZE - 1)) { 876 if (*p == '\\' && *(p + 1) == ',') { 877 escape = 1; // Mark escape mode 878 p++; // Skip '\' 879 } else { 880 escape = 0; 881 *buf_ptr++ = *p; 882 } 883 p++; 884 } 885 *buf_ptr = NUL; 886 887 if (vim_strchr(".wbuksid]tUfFo", (uint8_t)(*buffer)) == NULL) { 888 return illegal_char(args->os_errbuf, args->os_errbuflen, (uint8_t)(*buffer)); 889 } 890 891 if (vim_strchr("ksF", (uint8_t)(*buffer)) == NULL && *(buffer + 1) != NUL 892 && *(buffer + 1) != '^') { 893 char_before = (uint8_t)(*buffer); 894 } else { 895 char *t; 896 // Test for a number after '^' 897 if ((t = vim_strchr(buffer, '^')) != NULL) { 898 *t++ = NUL; 899 if (!*t) { 900 char_before = '^'; 901 } else { 902 for (; *t; t++) { 903 if (!ascii_isdigit(*t)) { 904 char_before = '^'; 905 break; 906 } 907 } 908 } 909 } 910 } 911 if (char_before != NUL) { 912 if (args->os_errbuf != NULL) { 913 return illegal_char_after_chr(args->os_errbuf, args->os_errbuflen, 914 char_before); 915 } 916 return NULL; 917 } 918 // Skip comma and spaces 919 while (*p == ',' || *p == ' ') { 920 p++; 921 } 922 } 923 924 if (set_cpt_callbacks(args) != OK) { 925 return illegal_char_after_chr(args->os_errbuf, args->os_errbuflen, 'F'); 926 } 927 return NULL; 928 } 929 930 /// The 'completeitemalign' option is changed. 931 const char *did_set_completeitemalign(optset_T *args) 932 { 933 char *p = p_cia; 934 unsigned new_cia_flags = 0; 935 bool seen[3] = { false, false, false }; 936 int count = 0; 937 char buf[10]; 938 while (*p) { 939 copy_option_part(&p, buf, sizeof(buf), ","); 940 if (count >= 3) { 941 return e_invarg; 942 } 943 if (strequal(buf, "abbr")) { 944 if (seen[CPT_ABBR]) { 945 return e_invarg; 946 } 947 new_cia_flags = new_cia_flags * 10 + CPT_ABBR; 948 seen[CPT_ABBR] = true; 949 count++; 950 } else if (strequal(buf, "kind")) { 951 if (seen[CPT_KIND]) { 952 return e_invarg; 953 } 954 new_cia_flags = new_cia_flags * 10 + CPT_KIND; 955 seen[CPT_KIND] = true; 956 count++; 957 } else if (strequal(buf, "menu")) { 958 if (seen[CPT_MENU]) { 959 return e_invarg; 960 } 961 new_cia_flags = new_cia_flags * 10 + CPT_MENU; 962 seen[CPT_MENU] = true; 963 count++; 964 } else { 965 return e_invarg; 966 } 967 } 968 if (new_cia_flags == 0 || count != 3) { 969 return e_invarg; 970 } 971 cia_flags = new_cia_flags; 972 return NULL; 973 } 974 975 /// The 'completeopt' option is changed. 976 const char *did_set_completeopt(optset_T *args FUNC_ATTR_UNUSED) 977 { 978 buf_T *buf = (buf_T *)args->os_buf; 979 char *cot = p_cot; 980 unsigned *flags = &cot_flags; 981 982 if (args->os_flags & OPT_LOCAL) { 983 cot = buf->b_p_cot; 984 flags = &buf->b_cot_flags; 985 } else if (!(args->os_flags & OPT_GLOBAL)) { 986 // When using :set, clear the local flags. 987 buf->b_cot_flags = 0; 988 } 989 990 if (opt_strings_flags(cot, opt_cot_values, flags, true) != OK) { 991 return e_invarg; 992 } 993 994 return NULL; 995 } 996 997 #ifdef BACKSLASH_IN_FILENAME 998 /// The 'completeslash' option is changed. 999 const char *did_set_completeslash(optset_T *args) 1000 { 1001 buf_T *buf = (buf_T *)args->os_buf; 1002 if (opt_strings_flags(p_csl, opt_csl_values, NULL, false) != OK 1003 || opt_strings_flags(buf->b_p_csl, opt_csl_values, NULL, false) != OK) { 1004 return e_invarg; 1005 } 1006 return NULL; 1007 } 1008 #endif 1009 1010 /// The 'concealcursor' option is changed. 1011 const char *did_set_concealcursor(optset_T *args) 1012 { 1013 char **varp = (char **)args->os_varp; 1014 1015 return did_set_option_listflag(*varp, COCU_ALL, args->os_errbuf, args->os_errbuflen); 1016 } 1017 1018 int expand_set_concealcursor(optexpand_T *args, int *numMatches, char ***matches) 1019 { 1020 return expand_set_opt_listflag(args, COCU_ALL, numMatches, matches); 1021 } 1022 1023 /// The 'cpoptions' option is changed. 1024 const char *did_set_cpoptions(optset_T *args) 1025 { 1026 char **varp = (char **)args->os_varp; 1027 1028 return did_set_option_listflag(*varp, CPO_VI, args->os_errbuf, args->os_errbuflen); 1029 } 1030 1031 int expand_set_cpoptions(optexpand_T *args, int *numMatches, char ***matches) 1032 { 1033 return expand_set_opt_listflag(args, CPO_VI, numMatches, matches); 1034 } 1035 1036 /// The 'cursorlineopt' option is changed. 1037 const char *did_set_cursorlineopt(optset_T *args) 1038 { 1039 win_T *win = (win_T *)args->os_win; 1040 char **varp = (char **)args->os_varp; 1041 1042 // This could be changed to use opt_strings_flags() instead. 1043 if (**varp == NUL || fill_culopt_flags(*varp, win) != OK) { 1044 return e_invarg; 1045 } 1046 1047 return NULL; 1048 } 1049 1050 /// The 'diffanchors' option is changed. 1051 const char *did_set_diffanchors(optset_T *args) 1052 { 1053 if (diffanchors_changed(args->os_flags & OPT_LOCAL) == FAIL) { 1054 return e_invarg; 1055 } 1056 1057 return NULL; 1058 } 1059 1060 /// The 'diffopt' option is changed. 1061 const char *did_set_diffopt(optset_T *args FUNC_ATTR_UNUSED) 1062 { 1063 return diffopt_changed() == FAIL ? e_invarg : NULL; 1064 } 1065 1066 int expand_set_diffopt(optexpand_T *args, int *numMatches, char ***matches) 1067 { 1068 expand_T *xp = args->oe_xp; 1069 1070 if (xp->xp_pattern > args->oe_set_arg && *(xp->xp_pattern - 1) == ':') { 1071 // Within "algorithm:", we have a subgroup of possible options. 1072 const size_t algo_len = strlen("algorithm:"); 1073 if (xp->xp_pattern - args->oe_set_arg >= (int)algo_len 1074 && strncmp(xp->xp_pattern - algo_len, "algorithm:", algo_len) == 0) { 1075 return expand_set_opt_string(args, 1076 opt_dip_algorithm_values, 1077 ARRAY_SIZE(opt_dip_algorithm_values) - 1, 1078 numMatches, 1079 matches); 1080 } 1081 // Within "inline:", we have a subgroup of possible options. 1082 const size_t inline_len = strlen("inline:"); 1083 if (xp->xp_pattern - args->oe_set_arg >= (int)inline_len 1084 && strncmp(xp->xp_pattern - inline_len, "inline:", inline_len) == 0) { 1085 return expand_set_opt_string(args, 1086 opt_dip_inline_values, 1087 ARRAY_SIZE(opt_dip_inline_values) - 1, 1088 numMatches, 1089 matches); 1090 } 1091 return FAIL; 1092 } 1093 1094 return expand_set_str_generic(args, numMatches, matches); 1095 } 1096 1097 /// The 'display' option is changed. 1098 const char *did_set_display(optset_T *args) 1099 { 1100 const char *errmsg = did_set_str_generic(args); 1101 if (errmsg != NULL) { 1102 return errmsg; 1103 } 1104 init_chartab(); 1105 msg_grid_validate(); 1106 return NULL; 1107 } 1108 1109 /// One of the 'encoding', 'fileencoding' or 'makeencoding' 1110 /// options is changed. 1111 const char *did_set_encoding(optset_T *args) 1112 { 1113 buf_T *buf = (buf_T *)args->os_buf; 1114 char **varp = (char **)args->os_varp; 1115 int opt_flags = args->os_flags; 1116 // Get the global option to compare with, otherwise we would have to check 1117 // two values for all local options. 1118 char **gvarp = (char **)get_option_varp_scope_from(args->os_idx, OPT_GLOBAL, buf, NULL); 1119 1120 if (gvarp == &p_fenc) { 1121 if (!MODIFIABLE(buf) && opt_flags != OPT_GLOBAL) { 1122 return e_modifiable; 1123 } 1124 1125 if (vim_strchr(*varp, ',') != NULL) { 1126 // No comma allowed in 'fileencoding'; catches confusing it 1127 // with 'fileencodings'. 1128 return e_invarg; 1129 } 1130 1131 // May show a "+" in the title now. 1132 redraw_titles(); 1133 // Add 'fileencoding' to the swap file. 1134 ml_setflags(buf); 1135 } 1136 1137 // canonize the value, so that strcmp() can be used on it 1138 char *p = enc_canonize(*varp); 1139 xfree(*varp); 1140 *varp = p; 1141 if (varp == &p_enc) { 1142 // only encoding=utf-8 allowed 1143 if (strcmp(p_enc, "utf-8") != 0) { 1144 return e_unsupportedoption; 1145 } 1146 spell_reload(); 1147 } 1148 return NULL; 1149 } 1150 1151 int expand_set_encoding(optexpand_T *args, int *numMatches, char ***matches) 1152 { 1153 return expand_set_opt_generic(args, get_encoding_name, numMatches, matches); 1154 } 1155 1156 /// The 'eventignore(win)' option is changed. 1157 const char *did_set_eventignore(optset_T *args) 1158 { 1159 char **varp = (char **)args->os_varp; 1160 1161 if (check_ei(*varp) == FAIL) { 1162 return e_invarg; 1163 } 1164 return NULL; 1165 } 1166 1167 static bool expand_eiw = false; 1168 1169 static char *get_eventignore_name(expand_T *xp, int idx) 1170 { 1171 bool subtract = *xp->xp_pattern == '-'; 1172 // 'eventignore(win)' allows special keyword "all" in addition to 1173 // all event names. 1174 if (!subtract && idx == 0) { 1175 return "all"; 1176 } 1177 1178 char *name = get_event_name_no_group(xp, idx - 1 + subtract, expand_eiw); 1179 if (name == NULL) { 1180 return NULL; 1181 } 1182 1183 snprintf(IObuff, IOSIZE, "%s%s", subtract ? "-" : "", name); 1184 return IObuff; 1185 } 1186 1187 int expand_set_eventignore(optexpand_T *args, int *numMatches, char ***matches) 1188 { 1189 expand_eiw = args->oe_varp != (char *)&p_ei; 1190 return expand_set_opt_generic(args, get_eventignore_name, numMatches, matches); 1191 } 1192 1193 /// The 'fileformat' option is changed. 1194 const char *did_set_fileformat(optset_T *args) 1195 { 1196 buf_T *buf = (buf_T *)args->os_buf; 1197 const char *oldval = args->os_oldval.string.data; 1198 int opt_flags = args->os_flags; 1199 if (!MODIFIABLE(buf) && !(opt_flags & OPT_GLOBAL)) { 1200 return e_modifiable; 1201 } 1202 1203 const char *errmsg = did_set_str_generic(args); 1204 if (errmsg != NULL) { 1205 return errmsg; 1206 } 1207 1208 redraw_titles(); 1209 // update flag in swap file 1210 ml_setflags(buf); 1211 // Redraw needed when switching to/from "mac": a CR in the text 1212 // will be displayed differently. 1213 if (get_fileformat(buf) == EOL_MAC || *oldval == 'm') { 1214 redraw_buf_later(buf, UPD_NOT_VALID); 1215 } 1216 return NULL; 1217 } 1218 1219 /// Function given to ExpandGeneric() to obtain the possible arguments of the 1220 /// fileformat options. 1221 char *get_fileformat_name(expand_T *xp FUNC_ATTR_UNUSED, int idx) 1222 { 1223 if (idx >= (int)ARRAY_SIZE(opt_ff_values)) { 1224 return NULL; 1225 } 1226 1227 return (char *)opt_ff_values[idx]; 1228 } 1229 1230 /// The 'filetype' or the 'syntax' option is changed. 1231 const char *did_set_filetype_or_syntax(optset_T *args) 1232 { 1233 char **varp = (char **)args->os_varp; 1234 1235 if (!valid_filetype(*varp)) { 1236 return e_invarg; 1237 } 1238 1239 args->os_value_changed = strcmp(args->os_oldval.string.data, *varp) != 0; 1240 1241 // Since we check the value, there is no need to set kOptFlagInsecure, 1242 // even when the value comes from a modeline. 1243 args->os_value_checked = true; 1244 1245 return NULL; 1246 } 1247 1248 /// The 'foldexpr' option is changed. 1249 const char *did_set_foldexpr(optset_T *args) 1250 { 1251 win_T *win = (win_T *)args->os_win; 1252 did_set_optexpr(args); 1253 if (foldmethodIsExpr(win)) { 1254 foldUpdateAll(win); 1255 } 1256 return NULL; 1257 } 1258 1259 /// The 'foldignore' option is changed. 1260 const char *did_set_foldignore(optset_T *args) 1261 { 1262 win_T *win = (win_T *)args->os_win; 1263 if (foldmethodIsIndent(win)) { 1264 foldUpdateAll(win); 1265 } 1266 return NULL; 1267 } 1268 1269 /// The 'foldmarker' option is changed. 1270 const char *did_set_foldmarker(optset_T *args) 1271 { 1272 win_T *win = (win_T *)args->os_win; 1273 char **varp = (char **)args->os_varp; 1274 char *p = vim_strchr(*varp, ','); 1275 1276 if (p == NULL) { 1277 return e_comma_required; 1278 } 1279 1280 if (p == *varp || p[1] == NUL) { 1281 return e_invarg; 1282 } 1283 1284 if (foldmethodIsMarker(win)) { 1285 foldUpdateAll(win); 1286 } 1287 1288 return NULL; 1289 } 1290 1291 /// The 'foldmethod' option is changed. 1292 const char *did_set_foldmethod(optset_T *args) 1293 { 1294 const char *errmsg = did_set_str_generic(args); 1295 if (errmsg != NULL) { 1296 return errmsg; 1297 } 1298 win_T *win = (win_T *)args->os_win; 1299 foldUpdateAll(win); 1300 if (foldmethodIsDiff(win)) { 1301 newFoldLevel(); 1302 } 1303 return NULL; 1304 } 1305 1306 /// The 'formatoptions' option is changed. 1307 const char *did_set_formatoptions(optset_T *args) 1308 { 1309 char **varp = (char **)args->os_varp; 1310 1311 return did_set_option_listflag(*varp, FO_ALL, args->os_errbuf, args->os_errbuflen); 1312 } 1313 1314 int expand_set_formatoptions(optexpand_T *args, int *numMatches, char ***matches) 1315 { 1316 return expand_set_opt_listflag(args, FO_ALL, numMatches, matches); 1317 } 1318 1319 /// The 'guicursor' option is changed. 1320 const char *did_set_guicursor(optset_T *args FUNC_ATTR_UNUSED) 1321 { 1322 const char *errmsg = parse_shape_opt(SHAPE_CURSOR); 1323 if (errmsg != NULL) { 1324 return errmsg; 1325 } 1326 if (VIsual_active) { 1327 // In Visual mode cursor may be drawn differently. 1328 redrawWinline(curwin, curwin->w_cursor.lnum); 1329 } 1330 return NULL; 1331 } 1332 1333 /// The 'helpfile' option is changed. 1334 const char *did_set_helpfile(optset_T *args FUNC_ATTR_UNUSED) 1335 { 1336 // May compute new values for $VIM and $VIMRUNTIME 1337 if (didset_vim) { 1338 vim_unsetenv_ext("VIM"); 1339 } 1340 if (didset_vimruntime) { 1341 vim_unsetenv_ext("VIMRUNTIME"); 1342 } 1343 return NULL; 1344 } 1345 1346 /// The 'helplang' option is changed. 1347 const char *did_set_helplang(optset_T *args FUNC_ATTR_UNUSED) 1348 { 1349 // Check for "", "ab", "ab,cd", etc. 1350 for (char *s = p_hlg; *s != NUL; s += 3) { 1351 if (s[1] == NUL || ((s[2] != ',' || s[3] == NUL) && s[2] != NUL)) { 1352 return e_invarg; 1353 } 1354 if (s[2] == NUL) { 1355 break; 1356 } 1357 } 1358 return NULL; 1359 } 1360 1361 /// The 'highlight' option is changed. 1362 const char *did_set_highlight(optset_T *args) 1363 { 1364 char **varp = (char **)args->os_varp; 1365 1366 if (strcmp(*varp, HIGHLIGHT_INIT) != 0) { 1367 return e_unsupportedoption; 1368 } 1369 return NULL; 1370 } 1371 1372 /// The 'iconstring' option is changed. 1373 const char *did_set_iconstring(optset_T *args) 1374 { 1375 return did_set_titleiconstring(args, STL_IN_ICON); 1376 } 1377 1378 /// The 'inccommand' option is changed. 1379 const char *did_set_inccommand(optset_T *args FUNC_ATTR_UNUSED) 1380 { 1381 if (cmdpreview) { 1382 return e_invarg; 1383 } 1384 return did_set_str_generic(args); 1385 } 1386 1387 /// The 'iskeyword' option is changed. 1388 const char *did_set_iskeyword(optset_T *args) 1389 { 1390 char **varp = (char **)args->os_varp; 1391 1392 if (varp == &p_isk) { // only check for global-value 1393 if (check_isopt(*varp) == FAIL) { 1394 return e_invarg; 1395 } 1396 } else { // fallthrough for local-value 1397 return did_set_isopt(args); 1398 } 1399 1400 return NULL; 1401 } 1402 1403 /// The 'isident' or the 'iskeyword' or the 'isprint' or the 'isfname' option is 1404 /// changed. 1405 const char *did_set_isopt(optset_T *args) 1406 { 1407 buf_T *buf = (buf_T *)args->os_buf; 1408 // 'isident', 'iskeyword', 'isprint' or 'isfname' option: refill g_chartab[] 1409 // If the new option is invalid, use old value. 1410 // 'lisp' option: refill g_chartab[] for '-' char 1411 if (buf_init_chartab(buf, true) == FAIL) { 1412 args->os_restore_chartab = true; // need to restore it below 1413 return e_invarg; // error in value 1414 } 1415 return NULL; 1416 } 1417 1418 /// The 'keymap' option has changed. 1419 const char *did_set_keymap(optset_T *args) 1420 { 1421 buf_T *buf = (buf_T *)args->os_buf; 1422 char **varp = (char **)args->os_varp; 1423 int opt_flags = args->os_flags; 1424 1425 if (!valid_filetype(*varp)) { 1426 return e_invarg; 1427 } 1428 1429 int secure_save = secure; 1430 1431 // Reset the secure flag, since the value of 'keymap' has 1432 // been checked to be safe. 1433 secure = 0; 1434 1435 // load or unload key mapping tables 1436 const char *errmsg = keymap_init(); 1437 1438 secure = secure_save; 1439 1440 // Since we check the value, there is no need to set kOptFlagInsecure, 1441 // even when the value comes from a modeline. 1442 args->os_value_checked = true; 1443 1444 if (errmsg == NULL) { 1445 if (*buf->b_p_keymap != NUL) { 1446 // Installed a new keymap, switch on using it. 1447 buf->b_p_iminsert = B_IMODE_LMAP; 1448 if (buf->b_p_imsearch != B_IMODE_USE_INSERT) { 1449 buf->b_p_imsearch = B_IMODE_LMAP; 1450 } 1451 } else { 1452 // Cleared the keymap, may reset 'iminsert' and 'imsearch'. 1453 if (buf->b_p_iminsert == B_IMODE_LMAP) { 1454 buf->b_p_iminsert = B_IMODE_NONE; 1455 } 1456 if (buf->b_p_imsearch == B_IMODE_LMAP) { 1457 buf->b_p_imsearch = B_IMODE_USE_INSERT; 1458 } 1459 } 1460 if ((opt_flags & OPT_LOCAL) == 0) { 1461 set_iminsert_global(buf); 1462 set_imsearch_global(buf); 1463 } 1464 status_redraw_buf(buf); 1465 } 1466 1467 return errmsg; 1468 } 1469 1470 /// The 'keymodel' option is changed. 1471 const char *did_set_keymodel(optset_T *args FUNC_ATTR_UNUSED) 1472 { 1473 const char *errmsg = did_set_str_generic(args); 1474 if (errmsg != NULL) { 1475 return errmsg; 1476 } 1477 km_stopsel = (vim_strchr(p_km, 'o') != NULL); 1478 km_startsel = (vim_strchr(p_km, 'a') != NULL); 1479 return NULL; 1480 } 1481 1482 /// The 'lispoptions' option is changed. 1483 const char *did_set_lispoptions(optset_T *args) 1484 { 1485 char **varp = (char **)args->os_varp; 1486 1487 if (**varp != NUL && strcmp(*varp, "expr:0") != 0 && strcmp(*varp, "expr:1") != 0) { 1488 return e_invarg; 1489 } 1490 return NULL; 1491 } 1492 1493 /// The 'matchpairs' option is changed. 1494 const char *did_set_matchpairs(optset_T *args) 1495 { 1496 char **varp = (char **)args->os_varp; 1497 1498 for (char *p = *varp; *p != NUL; p++) { 1499 int x2 = -1; 1500 int x3 = -1; 1501 1502 p += utfc_ptr2len(p); 1503 if (*p != NUL) { 1504 x2 = (unsigned char)(*p++); 1505 } 1506 if (*p != NUL) { 1507 x3 = utf_ptr2char(p); 1508 p += utfc_ptr2len(p); 1509 } 1510 if (x2 != ':' || x3 == -1 || (*p != NUL && *p != ',')) { 1511 return e_invarg; 1512 } 1513 if (*p == NUL) { 1514 break; 1515 } 1516 } 1517 return NULL; 1518 } 1519 1520 /// Process the updated 'messagesopt' option value. 1521 const char *did_set_messagesopt(optset_T *args FUNC_ATTR_UNUSED) 1522 { 1523 if (messagesopt_changed() == FAIL) { 1524 return e_invarg; 1525 } 1526 return NULL; 1527 } 1528 1529 /// The 'mkspellmem' option is changed. 1530 const char *did_set_mkspellmem(optset_T *args FUNC_ATTR_UNUSED) 1531 { 1532 if (spell_check_msm() != OK) { 1533 return e_invarg; 1534 } 1535 return NULL; 1536 } 1537 1538 /// The 'mouse' option is changed. 1539 const char *did_set_mouse(optset_T *args) 1540 { 1541 char **varp = (char **)args->os_varp; 1542 1543 return did_set_option_listflag(*varp, MOUSE_ALL, args->os_errbuf, args->os_errbuflen); 1544 } 1545 1546 int expand_set_mouse(optexpand_T *args, int *numMatches, char ***matches) 1547 { 1548 return expand_set_opt_listflag(args, MOUSE_ALL, numMatches, matches); 1549 } 1550 1551 /// Handle setting 'mousescroll'. 1552 /// @return error message, NULL if it's OK. 1553 const char *did_set_mousescroll(optset_T *args FUNC_ATTR_UNUSED) 1554 { 1555 OptInt vertical = -1; 1556 OptInt horizontal = -1; 1557 1558 char *string = p_mousescroll; 1559 1560 while (true) { 1561 char *end = vim_strchr(string, ','); 1562 size_t length = end ? (size_t)(end - string) : strlen(string); 1563 1564 // Both "ver:" and "hor:" are 4 bytes long. 1565 // They should be followed by at least one digit. 1566 if (length <= 4) { 1567 return e_invarg; 1568 } 1569 1570 OptInt *direction; 1571 1572 if (memcmp(string, "ver:", 4) == 0) { 1573 direction = &vertical; 1574 } else if (memcmp(string, "hor:", 4) == 0) { 1575 direction = &horizontal; 1576 } else { 1577 return e_invarg; 1578 } 1579 1580 // If the direction has already been set, this is a duplicate. 1581 if (*direction != -1) { 1582 return e_invarg; 1583 } 1584 1585 // Verify that only digits follow the colon. 1586 for (size_t i = 4; i < length; i++) { 1587 if (!ascii_isdigit(string[i])) { 1588 return N_("E5080: Digit expected"); 1589 } 1590 } 1591 1592 string += 4; 1593 *direction = getdigits_int(&string, false, -1); 1594 1595 // Num options are generally kept within the signed int range. 1596 // We know this number won't be negative because we've already checked for 1597 // a minus sign. We'll allow 0 as a means of disabling mouse scrolling. 1598 if (*direction == -1) { 1599 return e_invarg; 1600 } 1601 1602 if (!end) { 1603 break; 1604 } 1605 1606 string = end + 1; 1607 } 1608 1609 // If a direction wasn't set, fallback to the default value. 1610 p_mousescroll_vert = (vertical == -1) ? MOUSESCROLL_VERT_DFLT : vertical; 1611 p_mousescroll_hor = (horizontal == -1) ? MOUSESCROLL_HOR_DFLT : horizontal; 1612 1613 return NULL; 1614 } 1615 1616 /// One of the '*expr' options is changed:, 'diffexpr', 'foldexpr', 'foldtext', 1617 /// 'formatexpr', 'includeexpr', 'indentexpr', 'patchexpr' and 'charconvert'. 1618 const char *did_set_optexpr(optset_T *args) 1619 { 1620 char **varp = (char **)args->os_varp; 1621 1622 // If the option value starts with <SID> or s:, then replace that with 1623 // the script identifier. 1624 char *name = get_scriptlocal_funcname(*varp); 1625 if (name != NULL) { 1626 free_string_option(*varp); 1627 *varp = name; 1628 } 1629 return NULL; 1630 } 1631 1632 /// The 'rulerformat' option is changed. 1633 const char *did_set_rulerformat(optset_T *args) 1634 { 1635 return did_set_statustabline_rulerformat(args, true, false); 1636 } 1637 1638 /// The 'selection' option is changed. 1639 const char *did_set_selection(optset_T *args FUNC_ATTR_UNUSED) 1640 { 1641 const char *errmsg = did_set_str_generic(args); 1642 if (errmsg != NULL) { 1643 return errmsg; 1644 } 1645 if (VIsual_active) { 1646 // Visual selection may be drawn differently. 1647 redraw_curbuf_later(UPD_INVERTED); 1648 } 1649 return NULL; 1650 } 1651 1652 /// The 'sessionoptions' option is changed. 1653 const char *did_set_sessionoptions(optset_T *args) 1654 { 1655 const char *errmsg = did_set_str_generic(args); 1656 if (errmsg != NULL) { 1657 return errmsg; 1658 } 1659 if ((ssop_flags & kOptSsopFlagCurdir) && (ssop_flags & kOptSsopFlagSesdir)) { 1660 // Don't allow both "sesdir" and "curdir". 1661 const char *oldval = args->os_oldval.string.data; 1662 opt_strings_flags(oldval, opt_ssop_values, &ssop_flags, true); 1663 return e_invarg; 1664 } 1665 return NULL; 1666 } 1667 1668 const char *did_set_shada(optset_T *args) 1669 { 1670 char *errbuf = args->os_errbuf; 1671 size_t errbuflen = args->os_errbuflen; 1672 1673 for (char *s = p_shada; *s;) { 1674 // Check it's a valid character 1675 if (vim_strchr("!\"%'/:<@cfhnrs", (uint8_t)(*s)) == NULL) { 1676 return illegal_char(errbuf, errbuflen, (uint8_t)(*s)); 1677 } 1678 if (*s == 'n') { // name is always last one 1679 break; 1680 } else if (*s == 'r') { // skip until next ',' 1681 while (*++s && *s != ',') {} 1682 } else if (*s == '%') { 1683 // optional number 1684 while (ascii_isdigit(*++s)) {} 1685 } else if (*s == '!' || *s == 'h' || *s == 'c') { 1686 s++; // no extra chars 1687 } else { // must have a number 1688 while (ascii_isdigit(*++s)) {} 1689 1690 if (!ascii_isdigit(*(s - 1))) { 1691 if (errbuf != NULL) { 1692 vim_snprintf(errbuf, errbuflen, 1693 _("E526: Missing number after <%s>"), 1694 transchar_byte((uint8_t)(*(s - 1)))); 1695 return errbuf; 1696 } else { 1697 return ""; 1698 } 1699 } 1700 } 1701 if (*s == ',') { 1702 s++; 1703 } else if (*s) { 1704 if (errbuf != NULL) { 1705 return N_("E527: Missing comma"); 1706 } else { 1707 return ""; 1708 } 1709 } 1710 } 1711 if (*p_shada && get_shada_parameter('\'') < 0) { 1712 return N_("E528: Must specify a ' value"); 1713 } 1714 return NULL; 1715 } 1716 1717 /// The 'shortmess' option is changed. 1718 const char *did_set_shortmess(optset_T *args) 1719 { 1720 char **varp = (char **)args->os_varp; 1721 1722 return did_set_option_listflag(*varp, SHM_ALL, args->os_errbuf, args->os_errbuflen); 1723 } 1724 1725 int expand_set_shortmess(optexpand_T *args, int *numMatches, char ***matches) 1726 { 1727 return expand_set_opt_listflag(args, SHM_ALL, numMatches, matches); 1728 } 1729 1730 /// The 'showbreak' option is changed. 1731 const char *did_set_showbreak(optset_T *args) 1732 { 1733 char **varp = (char **)args->os_varp; 1734 1735 for (char *s = *varp; *s;) { 1736 if (ptr2cells(s) != 1) { 1737 return e_showbreak_contains_unprintable_or_wide_character; 1738 } 1739 MB_PTR_ADV(s); 1740 } 1741 return NULL; 1742 } 1743 1744 /// The 'showcmdloc' option is changed. 1745 const char *did_set_showcmdloc(optset_T *args FUNC_ATTR_UNUSED) 1746 { 1747 const char *errmsg = did_set_str_generic(args); 1748 1749 if (errmsg == NULL) { 1750 comp_col(); 1751 } 1752 1753 return errmsg; 1754 } 1755 1756 /// The 'signcolumn' option is changed. 1757 const char *did_set_signcolumn(optset_T *args) 1758 { 1759 win_T *win = (win_T *)args->os_win; 1760 char **varp = (char **)args->os_varp; 1761 const char *oldval = args->os_oldval.string.data; 1762 if (check_signcolumn(*varp, varp == &win->w_p_scl ? win : NULL) != OK) { 1763 return e_invarg; 1764 } 1765 // When changing the 'signcolumn' to or from 'number', recompute the 1766 // width of the number column if 'number' or 'relativenumber' is set. 1767 if ((*oldval == 'n' && *(oldval + 1) == 'u') || win->w_minscwidth == SCL_NUM) { 1768 win->w_nrwidth_line_count = 0; 1769 } 1770 return NULL; 1771 } 1772 1773 /// The 'spellcapcheck' option is changed. 1774 const char *did_set_spellcapcheck(optset_T *args) 1775 { 1776 win_T *win = (win_T *)args->os_win; 1777 // When 'spellcapcheck' is set compile the regexp program. 1778 return compile_cap_prog(win->w_s); 1779 } 1780 1781 /// The 'spellfile' option is changed. 1782 const char *did_set_spellfile(optset_T *args) 1783 { 1784 char **varp = (char **)args->os_varp; 1785 1786 // When there is a window for this buffer in which 'spell' 1787 // is set load the wordlists. 1788 if (!valid_spellfile(*varp)) { 1789 return e_invarg; 1790 } 1791 return did_set_spell_option(); 1792 } 1793 1794 /// The 'spelllang' option is changed. 1795 const char *did_set_spelllang(optset_T *args) 1796 { 1797 char **varp = (char **)args->os_varp; 1798 1799 // When there is a window for this buffer in which 'spell' 1800 // is set load the wordlists. 1801 if (!valid_spelllang(*varp)) { 1802 return e_invarg; 1803 } 1804 return did_set_spell_option(); 1805 } 1806 1807 /// The 'spelloptions' option is changed. 1808 const char *did_set_spelloptions(optset_T *args) 1809 { 1810 win_T *win = (win_T *)args->os_win; 1811 int opt_flags = args->os_flags; 1812 const char *val = args->os_newval.string.data; 1813 1814 if (!(opt_flags & OPT_LOCAL) 1815 && opt_strings_flags(val, opt_spo_values, &spo_flags, true) != OK) { 1816 return e_invarg; 1817 } 1818 if (!(opt_flags & OPT_GLOBAL) 1819 && opt_strings_flags(val, opt_spo_values, &win->w_s->b_p_spo_flags, true) != OK) { 1820 return e_invarg; 1821 } 1822 return NULL; 1823 } 1824 1825 /// The 'spellsuggest' option is changed. 1826 const char *did_set_spellsuggest(optset_T *args FUNC_ATTR_UNUSED) 1827 { 1828 if (spell_check_sps() != OK) { 1829 return e_invarg; 1830 } 1831 return NULL; 1832 } 1833 1834 /// The 'statuscolumn' option is changed. 1835 const char *did_set_statuscolumn(optset_T *args) 1836 { 1837 return did_set_statustabline_rulerformat(args, false, true); 1838 } 1839 1840 /// The 'statusline' option is changed. 1841 const char *did_set_statusline(optset_T *args) 1842 { 1843 return did_set_statustabline_rulerformat(args, false, false); 1844 } 1845 1846 /// The 'statusline', 'winbar', 'tabline', 'rulerformat' or 'statuscolumn' option is changed. 1847 /// 1848 /// @param rulerformat true if the 'rulerformat' option is changed 1849 /// @param statuscolumn true if the 'statuscolumn' option is changed 1850 static const char *did_set_statustabline_rulerformat(optset_T *args, bool rulerformat, 1851 bool statuscolumn) 1852 { 1853 win_T *win = (win_T *)args->os_win; 1854 char **varp = (char **)args->os_varp; 1855 if (rulerformat) { // reset ru_wid first 1856 ru_wid = 0; 1857 } else if (statuscolumn) { 1858 // reset 'statuscolumn' width 1859 win->w_nrwidth_line_count = 0; 1860 } 1861 const char *errmsg = NULL; 1862 char *s = *varp; 1863 bool is_stl = args->os_idx == kOptStatusline; 1864 1865 // reset statusline to default when setting global option and empty string is being set 1866 if (is_stl 1867 && ((args->os_flags & OPT_GLOBAL) || !(args->os_flags & OPT_LOCAL)) 1868 && s[0] == NUL) { 1869 xfree(*varp); 1870 *varp = xstrdup(get_option_default(args->os_idx, args->os_flags).data.string.data); 1871 s = *varp; 1872 } 1873 1874 // handle floating window statusline changes 1875 if (is_stl && win && win->w_floating) { 1876 win_config_float(win, win->w_config); 1877 } 1878 1879 if (rulerformat && *s == '%') { 1880 // set ru_wid if 'ruf' starts with "%99(" 1881 if (*++s == '-') { // ignore a '-' 1882 s++; 1883 } 1884 int wid = getdigits_int(&s, true, 0); 1885 if (wid && *s == '(' && (errmsg = check_stl_option(p_ruf)) == NULL) { 1886 ru_wid = wid; 1887 } else { 1888 // Validate the flags in 'rulerformat' only if it doesn't point to 1889 // a custom function ("%!" flag). 1890 if ((*varp)[1] != '!') { 1891 errmsg = check_stl_option(p_ruf); 1892 } 1893 } 1894 } else if (rulerformat || s[0] != '%' || s[1] != '!') { 1895 // check 'statusline', 'winbar', 'tabline' or 'statuscolumn' 1896 // only if it doesn't start with "%!" 1897 errmsg = check_stl_option(s); 1898 } 1899 if (rulerformat && errmsg == NULL) { 1900 comp_col(); 1901 } 1902 return errmsg; 1903 } 1904 1905 /// The 'tabline' option is changed. 1906 const char *did_set_tabline(optset_T *args) 1907 { 1908 return did_set_statustabline_rulerformat(args, false, false); 1909 } 1910 1911 /// The 'tagcase' option is changed. 1912 const char *did_set_tagcase(optset_T *args) 1913 { 1914 buf_T *buf = (buf_T *)args->os_buf; 1915 int opt_flags = args->os_flags; 1916 1917 unsigned *flags; 1918 char *p; 1919 1920 if (opt_flags & OPT_LOCAL) { 1921 p = buf->b_p_tc; 1922 flags = &buf->b_tc_flags; 1923 } else { 1924 p = p_tc; 1925 flags = &tc_flags; 1926 } 1927 1928 if ((opt_flags & OPT_LOCAL) && *p == NUL) { 1929 // make the local value empty: use the global value 1930 *flags = 0; 1931 } else if (opt_strings_flags(p, opt_tc_values, flags, false) != OK) { 1932 return e_invarg; 1933 } 1934 return NULL; 1935 } 1936 1937 /// The 'titlestring' or the 'iconstring' option is changed. 1938 static const char *did_set_titleiconstring(optset_T *args, int flagval) 1939 { 1940 char **varp = (char **)args->os_varp; 1941 1942 // NULL => statusline syntax 1943 if (vim_strchr(*varp, '%') && check_stl_option(*varp) == NULL) { 1944 stl_syntax |= flagval; 1945 } else { 1946 stl_syntax &= ~flagval; 1947 } 1948 did_set_title(); 1949 1950 return NULL; 1951 } 1952 1953 /// The 'titlestring' option is changed. 1954 const char *did_set_titlestring(optset_T *args) 1955 { 1956 return did_set_titleiconstring(args, STL_IN_TITLE); 1957 } 1958 1959 /// The 'varsofttabstop' option is changed. 1960 const char *did_set_varsofttabstop(optset_T *args) 1961 { 1962 buf_T *buf = (buf_T *)args->os_buf; 1963 char **varp = (char **)args->os_varp; 1964 1965 if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) { 1966 XFREE_CLEAR(buf->b_p_vsts_array); 1967 return NULL; 1968 } 1969 1970 for (char *cp = *varp; *cp; cp++) { 1971 if (ascii_isdigit(*cp)) { 1972 continue; 1973 } 1974 if (*cp == ',' && cp > *varp && *(cp - 1) != ',') { 1975 continue; 1976 } 1977 return e_invarg; 1978 } 1979 1980 colnr_T *oldarray = buf->b_p_vsts_array; 1981 if (tabstop_set(*varp, &(buf->b_p_vsts_array))) { 1982 xfree(oldarray); 1983 } else { 1984 return e_invarg; 1985 } 1986 return NULL; 1987 } 1988 1989 /// The 'varstabstop' option is changed. 1990 const char *did_set_vartabstop(optset_T *args) 1991 { 1992 buf_T *buf = (buf_T *)args->os_buf; 1993 win_T *win = (win_T *)args->os_win; 1994 char **varp = (char **)args->os_varp; 1995 1996 if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) { 1997 XFREE_CLEAR(buf->b_p_vts_array); 1998 return NULL; 1999 } 2000 2001 for (char *cp = *varp; *cp; cp++) { 2002 if (ascii_isdigit(*cp)) { 2003 continue; 2004 } 2005 if (*cp == ',' && cp > *varp && *(cp - 1) != ',') { 2006 continue; 2007 } 2008 return e_invarg; 2009 } 2010 2011 colnr_T *oldarray = buf->b_p_vts_array; 2012 if (tabstop_set(*varp, &(buf->b_p_vts_array))) { 2013 xfree(oldarray); 2014 if (foldmethodIsIndent(win)) { 2015 foldUpdateAll(win); 2016 } 2017 } else { 2018 return e_invarg; 2019 } 2020 return NULL; 2021 } 2022 2023 /// The 'verbosefile' option is changed. 2024 const char *did_set_verbosefile(optset_T *args) 2025 { 2026 verbose_stop(); 2027 if (*p_vfile != NUL && verbose_open() == FAIL) { 2028 return (char *)e_invarg; 2029 } 2030 return NULL; 2031 } 2032 2033 /// The 'virtualedit' option is changed. 2034 const char *did_set_virtualedit(optset_T *args) 2035 { 2036 win_T *win = (win_T *)args->os_win; 2037 2038 char *ve = p_ve; 2039 unsigned *flags = &ve_flags; 2040 2041 if (args->os_flags & OPT_LOCAL) { 2042 ve = win->w_p_ve; 2043 flags = &win->w_ve_flags; 2044 } 2045 2046 if ((args->os_flags & OPT_LOCAL) && *ve == NUL) { 2047 // make the local value empty: use the global value 2048 *flags = 0; 2049 } else { 2050 if (opt_strings_flags(ve, opt_ve_values, flags, true) != OK) { 2051 return e_invarg; 2052 } else if (strcmp(ve, args->os_oldval.string.data) != 0) { 2053 // Recompute cursor position in case the new 've' setting 2054 // changes something. 2055 validate_virtcol(win); 2056 coladvance(win, win->w_virtcol); 2057 } 2058 } 2059 return NULL; 2060 } 2061 2062 /// The 'whichwrap' option is changed. 2063 const char *did_set_whichwrap(optset_T *args) 2064 { 2065 char **varp = (char **)args->os_varp; 2066 2067 // Add ',' to the list flags because 'whichwrap' is a flag 2068 // list that is comma-separated. 2069 return did_set_option_listflag(*varp, WW_ALL ",", args->os_errbuf, args->os_errbuflen); 2070 } 2071 2072 int expand_set_whichwrap(optexpand_T *args, int *numMatches, char ***matches) 2073 { 2074 return expand_set_opt_listflag(args, WW_ALL, numMatches, matches); 2075 } 2076 2077 /// The 'wildmode' option is changed. 2078 const char *did_set_wildmode(optset_T *args FUNC_ATTR_UNUSED) 2079 { 2080 if (check_opt_wim() == FAIL) { 2081 return e_invarg; 2082 } 2083 return NULL; 2084 } 2085 2086 /// The 'winbar' option is changed. 2087 const char *did_set_winbar(optset_T *args) 2088 { 2089 return did_set_statustabline_rulerformat(args, false, false); 2090 } 2091 2092 static bool parse_border_opt(char *border_opt) 2093 { 2094 WinConfig fconfig = WIN_CONFIG_INIT; 2095 Error err = ERROR_INIT; 2096 bool result = true; 2097 if (!parse_winborder(&fconfig, border_opt, &err)) { 2098 result = false; 2099 } 2100 api_clear_error(&err); 2101 return result; 2102 } 2103 2104 /// The 'winborder' option is changed. 2105 const char *did_set_winborder(optset_T *args) 2106 { 2107 if (!parse_border_opt(p_winborder)) { 2108 return e_invarg; 2109 } 2110 return NULL; 2111 } 2112 2113 const char *did_set_pumborder(optset_T *args) 2114 { 2115 if (!parse_border_opt(p_pumborder)) { 2116 return e_invarg; 2117 } 2118 return NULL; 2119 } 2120 2121 /// The 'winhighlight' option is changed. 2122 const char *did_set_winhighlight(optset_T *args) 2123 { 2124 win_T *win = (win_T *)args->os_win; 2125 char **varp = (char **)args->os_varp; 2126 if (!parse_winhl_opt(*varp, varp == &win->w_p_winhl ? win : NULL)) { 2127 return e_invarg; 2128 } 2129 return NULL; 2130 } 2131 2132 int expand_set_winhighlight(optexpand_T *args, int *numMatches, char ***matches) 2133 { 2134 return expand_set_opt_generic(args, get_highlight_name, numMatches, matches); 2135 } 2136 2137 /// Handle an option that can be a range of string values. 2138 /// Set a flag in "*flagp" for each string present. 2139 /// 2140 /// @param val new value 2141 /// @param values array of valid string values 2142 /// @param list when true: accept a list of values 2143 /// 2144 /// @return OK for correct value, FAIL otherwise. Empty is always OK. 2145 static int opt_strings_flags(const char *val, const char **values, unsigned *flagp, bool list) 2146 { 2147 unsigned new_flags = 0; 2148 2149 // If not list and val is empty, then force one iteration of the while loop 2150 bool iter_one = (*val == NUL) && !list; 2151 2152 while (*val || iter_one) { 2153 for (unsigned i = 0;; i++) { 2154 if (values[i] == NULL) { // val not found in values[] 2155 return FAIL; 2156 } 2157 2158 size_t len = strlen(values[i]); 2159 if (strncmp(values[i], val, len) == 0 2160 && ((list && val[len] == ',') || val[len] == NUL)) { 2161 val += len + (val[len] == ','); 2162 assert(i < sizeof(new_flags) * 8); 2163 new_flags |= (1U << i); 2164 break; // check next item in val list 2165 } 2166 } 2167 if (iter_one) { 2168 break; 2169 } 2170 } 2171 if (flagp != NULL) { 2172 *flagp = new_flags; 2173 } 2174 2175 return OK; 2176 } 2177 2178 /// @return OK if "p" is a valid fileformat name, FAIL otherwise. 2179 int check_ff_value(char *p) 2180 { 2181 return opt_strings_flags(p, opt_ff_values, NULL, false); 2182 } 2183 2184 static const char e_conflicts_with_value_of_listchars[] 2185 = N_("E834: Conflicts with value of 'listchars'"); 2186 static const char e_conflicts_with_value_of_fillchars[] 2187 = N_("E835: Conflicts with value of 'fillchars'"); 2188 2189 /// Calls mb_cptr2char_adv(p) and returns the character. 2190 /// If "p" starts with "\x", "\u" or "\U" the hex or unicode value is used. 2191 /// Returns 0 for invalid hex or invalid UTF-8 byte. 2192 static schar_T get_encoded_char_adv(const char **p) 2193 { 2194 const char *s = *p; 2195 2196 if (s[0] == '\\' && (s[1] == 'x' || s[1] == 'u' || s[1] == 'U')) { 2197 int64_t num = 0; 2198 for (int bytes = s[1] == 'x' ? 1 : s[1] == 'u' ? 2 : 4; bytes > 0; bytes--) { 2199 *p += 2; 2200 int n = hexhex2nr(*p); 2201 if (n < 0) { 2202 return 0; 2203 } 2204 num = num * 256 + n; 2205 } 2206 *p += 2; 2207 return (char2cells((int)num) > 1) ? 0 : schar_from_char((int)num); 2208 } 2209 2210 int clen = utfc_ptr2len(s); 2211 int firstc; 2212 schar_T c = utfc_ptr2schar(s, &firstc); 2213 *p += clen; 2214 // Invalid UTF-8 byte or doublewidth not allowed 2215 return ((clen == 1 && firstc > 127) || char2cells(firstc) > 1) ? 0 : c; 2216 } 2217 2218 struct chars_tab { 2219 schar_T *cp; ///< char value 2220 String name; ///< char id 2221 const char *def; ///< default value 2222 const char *fallback; ///< default value when "def" isn't single-width 2223 }; 2224 2225 #define CHARSTAB_ENTRY(cp, name, def, fallback) \ 2226 { (cp), { name, STRLEN_LITERAL(name) }, def, fallback } 2227 2228 static fcs_chars_T fcs_chars; 2229 static const struct chars_tab fcs_tab[] = { 2230 CHARSTAB_ENTRY(&fcs_chars.stl, "stl", " ", NULL), 2231 CHARSTAB_ENTRY(&fcs_chars.stlnc, "stlnc", " ", NULL), 2232 CHARSTAB_ENTRY(&fcs_chars.wbr, "wbr", " ", NULL), 2233 CHARSTAB_ENTRY(&fcs_chars.horiz, "horiz", "─", "-"), 2234 CHARSTAB_ENTRY(&fcs_chars.horizup, "horizup", "┴", "-"), 2235 CHARSTAB_ENTRY(&fcs_chars.horizdown, "horizdown", "┬", "-"), 2236 CHARSTAB_ENTRY(&fcs_chars.vert, "vert", "│", "|"), 2237 CHARSTAB_ENTRY(&fcs_chars.vertleft, "vertleft", "┤", "|"), 2238 CHARSTAB_ENTRY(&fcs_chars.vertright, "vertright", "├", "|"), 2239 CHARSTAB_ENTRY(&fcs_chars.verthoriz, "verthoriz", "┼", "+"), 2240 CHARSTAB_ENTRY(&fcs_chars.fold, "fold", "·", "-"), 2241 CHARSTAB_ENTRY(&fcs_chars.foldopen, "foldopen", "-", NULL), 2242 CHARSTAB_ENTRY(&fcs_chars.foldclosed, "foldclose", "+", NULL), 2243 CHARSTAB_ENTRY(&fcs_chars.foldsep, "foldsep", "│", "|"), 2244 CHARSTAB_ENTRY(&fcs_chars.foldinner, "foldinner", NULL, NULL), 2245 CHARSTAB_ENTRY(&fcs_chars.diff, "diff", "-", NULL), 2246 CHARSTAB_ENTRY(&fcs_chars.msgsep, "msgsep", " ", NULL), 2247 CHARSTAB_ENTRY(&fcs_chars.eob, "eob", "~", NULL), 2248 CHARSTAB_ENTRY(&fcs_chars.lastline, "lastline", "@", NULL), 2249 CHARSTAB_ENTRY(&fcs_chars.trunc, "trunc", ">", NULL), 2250 CHARSTAB_ENTRY(&fcs_chars.truncrl, "truncrl", "<", NULL), 2251 }; 2252 2253 static lcs_chars_T lcs_chars; 2254 static const struct chars_tab lcs_tab[] = { 2255 CHARSTAB_ENTRY(&lcs_chars.eol, "eol", NULL, NULL), 2256 CHARSTAB_ENTRY(&lcs_chars.ext, "extends", NULL, NULL), 2257 CHARSTAB_ENTRY(&lcs_chars.nbsp, "nbsp", NULL, NULL), 2258 CHARSTAB_ENTRY(&lcs_chars.prec, "precedes", NULL, NULL), 2259 CHARSTAB_ENTRY(&lcs_chars.space, "space", NULL, NULL), 2260 CHARSTAB_ENTRY(&lcs_chars.tab2, "tab", NULL, NULL), 2261 CHARSTAB_ENTRY(&lcs_chars.leadtab2, "leadtab", NULL, NULL), 2262 CHARSTAB_ENTRY(&lcs_chars.lead, "lead", NULL, NULL), 2263 CHARSTAB_ENTRY(&lcs_chars.trail, "trail", NULL, NULL), 2264 CHARSTAB_ENTRY(&lcs_chars.conceal, "conceal", NULL, NULL), 2265 CHARSTAB_ENTRY(NULL, "multispace", NULL, NULL), 2266 CHARSTAB_ENTRY(NULL, "leadmultispace", NULL, NULL), 2267 }; 2268 2269 #undef CHARSTAB_ENTRY 2270 2271 static char *field_value_err(char *errbuf, size_t errbuflen, const char *fmt, const char *field) 2272 { 2273 if (errbuf == NULL) { 2274 return ""; 2275 } 2276 vim_snprintf(errbuf, errbuflen, _(fmt), field); 2277 return errbuf; 2278 } 2279 2280 /// Handle setting 'listchars' or 'fillchars'. 2281 /// Assume monocell characters 2282 /// 2283 /// @param value points to either the global or the window-local value. 2284 /// @param what kListchars or kFillchars 2285 /// @param apply if false, do not store the flags, only check for errors. 2286 /// @param errbuf buffer for error message, can be NULL if it won't be used. 2287 /// @param errbuflen size of error buffer. 2288 /// 2289 /// @return error message, NULL if it's OK. 2290 const char *set_chars_option(win_T *wp, const char *value, CharsOption what, bool apply, 2291 char *errbuf, size_t errbuflen) 2292 { 2293 const char *last_multispace = NULL; // Last occurrence of "multispace:" 2294 const char *last_lmultispace = NULL; // Last occurrence of "leadmultispace:" 2295 int multispace_len = 0; // Length of lcs-multispace string 2296 int lead_multispace_len = 0; // Length of lcs-leadmultispace string 2297 2298 const struct chars_tab *tab; 2299 int entries; 2300 if (what == kListchars) { 2301 tab = lcs_tab; 2302 entries = ARRAY_SIZE(lcs_tab); 2303 if (wp->w_p_lcs[0] == NUL) { 2304 value = p_lcs; // local value is empty, use the global value 2305 } 2306 } else { 2307 tab = fcs_tab; 2308 entries = ARRAY_SIZE(fcs_tab); 2309 if (wp->w_p_fcs[0] == NUL) { 2310 value = p_fcs; // local value is empty, use the global value 2311 } 2312 } 2313 2314 // first round: check for valid value, second round: assign values 2315 for (int round = 0; round <= (apply ? 1 : 0); round++) { 2316 if (round > 0) { 2317 // After checking that the value is valid: set defaults 2318 for (int i = 0; i < entries; i++) { 2319 if (tab[i].cp != NULL) { 2320 // XXX: Characters taking 2 columns is forbidden (TUI limitation?). 2321 // Set old defaults in this case. 2322 *(tab[i].cp) = schar_from_str((tab[i].def && ptr2cells(tab[i].def) == 1) 2323 ? tab[i].def : tab[i].fallback); 2324 } 2325 } 2326 2327 if (what == kListchars) { 2328 lcs_chars.tab1 = NUL; 2329 lcs_chars.tab3 = NUL; 2330 lcs_chars.leadtab1 = NUL; 2331 lcs_chars.leadtab3 = NUL; 2332 2333 if (multispace_len > 0) { 2334 lcs_chars.multispace = xmalloc(((size_t)multispace_len + 1) * sizeof(schar_T)); 2335 lcs_chars.multispace[multispace_len] = NUL; 2336 } else { 2337 lcs_chars.multispace = NULL; 2338 } 2339 2340 if (lead_multispace_len > 0) { 2341 lcs_chars.leadmultispace = xmalloc(((size_t)lead_multispace_len + 1) * sizeof(schar_T)); 2342 lcs_chars.leadmultispace[lead_multispace_len] = NUL; 2343 } else { 2344 lcs_chars.leadmultispace = NULL; 2345 } 2346 } 2347 } 2348 2349 const char *p = value; 2350 while (*p) { 2351 int i; 2352 for (i = 0; i < entries; i++) { 2353 if (!(strncmp(p, tab[i].name.data, 2354 tab[i].name.size) == 0 && p[tab[i].name.size] == ':')) { 2355 continue; 2356 } 2357 2358 const char *s = p + tab[i].name.size + 1; 2359 2360 if (what == kListchars && strcmp(tab[i].name.data, "multispace") == 0) { 2361 if (round == 0) { 2362 // Get length of lcs-multispace string in the first round 2363 last_multispace = p; 2364 multispace_len = 0; 2365 while (*s != NUL && *s != ',') { 2366 schar_T c1 = get_encoded_char_adv(&s); 2367 if (c1 == 0) { 2368 return field_value_err(errbuf, errbuflen, 2369 e_wrong_character_width_for_field_str, 2370 tab[i].name.data); 2371 } 2372 multispace_len++; 2373 } 2374 if (multispace_len == 0) { 2375 // lcs-multispace cannot be an empty string 2376 return field_value_err(errbuf, errbuflen, 2377 e_wrong_number_of_characters_for_field_str, 2378 tab[i].name.data); 2379 } 2380 } else { 2381 int multispace_pos = 0; 2382 while (*s != NUL && *s != ',') { 2383 schar_T c1 = get_encoded_char_adv(&s); 2384 if (p == last_multispace) { 2385 lcs_chars.multispace[multispace_pos++] = c1; 2386 } 2387 } 2388 } 2389 p = s; 2390 break; 2391 } 2392 2393 if (what == kListchars && strcmp(tab[i].name.data, "leadmultispace") == 0) { 2394 if (round == 0) { 2395 // Get length of lcs-leadmultispace string in first round 2396 last_lmultispace = p; 2397 lead_multispace_len = 0; 2398 while (*s != NUL && *s != ',') { 2399 schar_T c1 = get_encoded_char_adv(&s); 2400 if (c1 == 0) { 2401 return field_value_err(errbuf, errbuflen, 2402 e_wrong_character_width_for_field_str, 2403 tab[i].name.data); 2404 } 2405 lead_multispace_len++; 2406 } 2407 if (lead_multispace_len == 0) { 2408 // lcs-leadmultispace cannot be an empty string 2409 return field_value_err(errbuf, errbuflen, 2410 e_wrong_number_of_characters_for_field_str, 2411 tab[i].name.data); 2412 } 2413 } else { 2414 int multispace_pos = 0; 2415 while (*s != NUL && *s != ',') { 2416 schar_T c1 = get_encoded_char_adv(&s); 2417 if (p == last_lmultispace) { 2418 lcs_chars.leadmultispace[multispace_pos++] = c1; 2419 } 2420 } 2421 } 2422 p = s; 2423 break; 2424 } 2425 2426 if (*s == NUL) { 2427 return field_value_err(errbuf, errbuflen, 2428 e_wrong_number_of_characters_for_field_str, 2429 tab[i].name.data); 2430 } 2431 schar_T c1 = get_encoded_char_adv(&s); 2432 if (c1 == 0) { 2433 return field_value_err(errbuf, errbuflen, 2434 e_wrong_character_width_for_field_str, 2435 tab[i].name.data); 2436 } 2437 schar_T c2 = 0; 2438 schar_T c3 = 0; 2439 if (tab[i].cp == &lcs_chars.tab2 || tab[i].cp == &lcs_chars.leadtab2) { 2440 if (*s == NUL) { 2441 return field_value_err(errbuf, errbuflen, 2442 e_wrong_number_of_characters_for_field_str, 2443 tab[i].name.data); 2444 } 2445 c2 = get_encoded_char_adv(&s); 2446 if (c2 == 0) { 2447 return field_value_err(errbuf, errbuflen, 2448 e_wrong_character_width_for_field_str, 2449 tab[i].name.data); 2450 } 2451 if (!(*s == ',' || *s == NUL)) { 2452 c3 = get_encoded_char_adv(&s); 2453 if (c3 == 0) { 2454 return field_value_err(errbuf, errbuflen, 2455 e_wrong_character_width_for_field_str, 2456 tab[i].name.data); 2457 } 2458 } 2459 } 2460 2461 if (*s == ',' || *s == NUL) { 2462 if (round > 0) { 2463 if (tab[i].cp == &lcs_chars.tab2) { 2464 lcs_chars.tab1 = c1; 2465 lcs_chars.tab2 = c2; 2466 lcs_chars.tab3 = c3; 2467 } else if (tab[i].cp == &lcs_chars.leadtab2) { 2468 lcs_chars.leadtab1 = c1; 2469 lcs_chars.leadtab2 = c2; 2470 lcs_chars.leadtab3 = c3; 2471 } else if (tab[i].cp != NULL) { 2472 *(tab[i].cp) = c1; 2473 } 2474 } 2475 p = s; 2476 break; 2477 } else { 2478 return field_value_err(errbuf, errbuflen, 2479 e_wrong_number_of_characters_for_field_str, 2480 tab[i].name.data); 2481 } 2482 } 2483 2484 if (i == entries) { 2485 return e_invarg; 2486 } 2487 2488 if (*p == ',') { 2489 p++; 2490 } 2491 } 2492 } 2493 2494 if (what == kListchars && lcs_chars.leadtab2 != NUL && lcs_chars.tab2 == NUL) { 2495 return e_leadtab_requires_tab; 2496 } 2497 2498 if (apply) { 2499 if (what == kListchars) { 2500 xfree(wp->w_p_lcs_chars.multispace); 2501 xfree(wp->w_p_lcs_chars.leadmultispace); 2502 wp->w_p_lcs_chars = lcs_chars; 2503 } else { 2504 wp->w_p_fcs_chars = fcs_chars; 2505 } 2506 } 2507 2508 return NULL; // no error 2509 } 2510 2511 /// Function given to ExpandGeneric() to obtain possible arguments of the 2512 /// 'fillchars' option. 2513 char *get_fillchars_name(expand_T *xp FUNC_ATTR_UNUSED, int idx) 2514 { 2515 if (idx < 0 || idx >= (int)ARRAY_SIZE(fcs_tab)) { 2516 return NULL; 2517 } 2518 2519 return fcs_tab[idx].name.data; 2520 } 2521 2522 /// Function given to ExpandGeneric() to obtain possible arguments of the 2523 /// 'listchars' option. 2524 char *get_listchars_name(expand_T *xp FUNC_ATTR_UNUSED, int idx) 2525 { 2526 if (idx < 0 || idx >= (int)ARRAY_SIZE(lcs_tab)) { 2527 return NULL; 2528 } 2529 2530 return lcs_tab[idx].name.data; 2531 } 2532 2533 /// Check all global and local values of 'listchars' and 'fillchars'. 2534 /// May set different defaults in case character widths change. 2535 /// 2536 /// @return an untranslated error message if any of them is invalid, NULL otherwise. 2537 const char *check_chars_options(void) 2538 { 2539 if (set_chars_option(curwin, p_lcs, kListchars, false, NULL, 0) != NULL) { 2540 return e_conflicts_with_value_of_listchars; 2541 } 2542 if (set_chars_option(curwin, p_fcs, kFillchars, false, NULL, 0) != NULL) { 2543 return e_conflicts_with_value_of_fillchars; 2544 } 2545 FOR_ALL_TAB_WINDOWS(tp, wp) { 2546 if (set_chars_option(wp, wp->w_p_lcs, kListchars, true, NULL, 0) != NULL) { 2547 return e_conflicts_with_value_of_listchars; 2548 } 2549 if (set_chars_option(wp, wp->w_p_fcs, kFillchars, true, NULL, 0) != NULL) { 2550 return e_conflicts_with_value_of_fillchars; 2551 } 2552 } 2553 return NULL; 2554 }