diff options
44 files changed, 901 insertions, 1045 deletions
diff --git a/runtime/doc/mbyte.txt b/runtime/doc/mbyte.txt index 2d4a20ed72..d38c4fd019 100644 --- a/runtime/doc/mbyte.txt +++ b/runtime/doc/mbyte.txt @@ -1059,8 +1059,7 @@ widespread as file format. A composing or combining character is used to change the meaning of the character before it. The combining characters are drawn on top of the preceding character. -Up to two combining characters can be used by default. This can be changed -with the 'maxcombine' option. +Up to six combining characters can be displayed. When editing text a composing character is mostly considered part of the preceding character. For example "x" will delete a character and its following composing characters by default. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 7ff1281274..f924003e1f 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2393,6 +2393,7 @@ A jump table for the options with a short description can be found at |Q_op|. fold:c '·' or '-' filling 'foldtext' diff:c '-' deleted lines of the 'diff' option msgsep:c ' ' message separator 'display' + eob:c '~' empty lines at the end of a buffer Any one that is omitted will fall back to the default. For "stl" and "stlnc" the space will be used when there is highlighting, '^' or '=' @@ -2415,6 +2416,7 @@ A jump table for the options with a short description can be found at |Q_op|. vert:c VertSplit |hl-VertSplit| fold:c Folded |hl-Folded| diff:c DiffDelete |hl-DiffDelete| + eob:c EndOfBuffer |hl-EndOfBuffer| *'fixendofline'* *'fixeol'* *'nofixendofline'* *'nofixeol'* 'fixendofline' 'fixeol' boolean (default on) @@ -3888,16 +3890,10 @@ A jump table for the options with a short description can be found at |Q_op|. set a time. This is to be compatible with Nvi. *'maxcombine'* *'mco'* -'maxcombine' 'mco' number (default 2) - global - {only available when compiled with the |+multi_byte| - feature} - The maximum number of combining characters supported for displaying. - The default is OK for most languages. Hebrew may require 4. - Maximum value is 6. - Even when this option is set to 2 you can still edit text with more - combining characters, you just can't see them. Use |g8| or |ga|. - See |mbyte-combining|. +'maxcombine' 'mco' Removed. |vim-differences| {Nvim} + Nvim always displays up to 6 combining characters. You can still edit + text with more than 6 combining characters, you just can't see them. + Use |g8| or |ga|. See |mbyte-combining|. *'maxfuncdepth'* *'mfd'* 'maxfuncdepth' 'mfd' number (default 100) diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 1af91f24da..5394414947 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -180,7 +180,8 @@ Options: 'cpoptions' flags: |cpo-_| 'display' flag `msgsep` to minimize scrolling when showing messages 'guicursor' works in the terminal - 'fillchars' flag `msgsep` (see 'display' above) + 'fillchars' flags: `msgsep` (see 'display' above) + and `eob` for |EndOfBuffer| marker 'inccommand' shows interactive results for |:substitute|-like commands 'scrollback' 'statusline' supports unlimited alignment sections @@ -409,6 +410,7 @@ Options: *'macatsui'* 'maxmem' Nvim delegates memory-management to the OS. 'maxmemtot' Nvim delegates memory-management to the OS. + 'maxcombine' (6 is always used) *'restorescreen'* *'rs'* *'norestorescreen'* *'nors'* 'shelltype' *'shortname'* *'sn'* *'noshortname'* *'nosn'* diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 4f70bcc41a..99ced6c8c2 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -804,7 +804,7 @@ unsigned int win_linetabsize(win_T *wp, char_u *line, colnr_T len) for (char_u *s = line; *s != NUL && (len == MAXCOL || s < line + len); - mb_ptr_adv(s)) { + MB_PTR_ADV(s)) { col += win_lbr_chartabsize(wp, line, s, col, NULL); } @@ -971,7 +971,7 @@ int lbr_chartabsize_adv(char_u *line, char_u **s, colnr_T col) int retval; retval = lbr_chartabsize(line, *s, col); - mb_ptr_adv(*s); + MB_PTR_ADV(*s); return retval; } @@ -1038,7 +1038,7 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he for (;;) { ps = s; - mb_ptr_adv(s); + MB_PTR_ADV(s); c = *s; if (!(c != NUL @@ -1283,7 +1283,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, } vcol += incr; - mb_ptr_adv(ptr); + MB_PTR_ADV(ptr); } } else { for (;;) { @@ -1304,7 +1304,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, } vcol += incr; - mb_ptr_adv(ptr); + MB_PTR_ADV(ptr); } } diff --git a/src/nvim/charset.h b/src/nvim/charset.h index eb64b6128a..e657ce19b6 100644 --- a/src/nvim/charset.h +++ b/src/nvim/charset.h @@ -5,6 +5,7 @@ #include "nvim/pos.h" #include "nvim/buffer_defs.h" #include "nvim/eval/typval.h" +#include "nvim/option_defs.h" /// Return the folded-case equivalent of the given character /// @@ -34,4 +35,15 @@ typedef enum { #ifdef INCLUDE_GENERATED_DECLARATIONS # include "charset.h.generated.h" #endif + +static inline bool vim_isbreak(int c) + REAL_FATTR_CONST + REAL_FATTR_ALWAYS_INLINE; + +/// Check if `c` is one of the characters in 'breakat'. +/// Used very often if 'linebreak' is set +static inline bool vim_isbreak(int c) +{ + return breakat_flags[(char_u)c]; +} #endif // NVIM_CHARSET_H diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 0e97e2203f..177f167d74 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -144,7 +144,7 @@ static int coladvance2( while (col <= wcol && *ptr != NUL) { /* Count a tab for what it's worth (if list mode not on) */ csize = win_lbr_chartabsize(curwin, line, ptr, col, &head); - mb_ptr_adv(ptr); + MB_PTR_ADV(ptr); col += csize; } idx = (int)(ptr - line); diff --git a/src/nvim/edit.c b/src/nvim/edit.c index ccb5d6bac8..c20758cb0b 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -659,7 +659,7 @@ static int insert_execute(VimState *state, int key) char_u *p; if (str != NULL) { - for (p = str; *p != NUL; mb_ptr_adv(p)) { + for (p = str; *p != NUL; MB_PTR_ADV(p)) { ins_compl_addleader(PTR2CHAR(p)); } xfree(str); @@ -1197,7 +1197,7 @@ normalchar: if (str != NULL) { if (*str != NUL && stop_arrow() != FAIL) { // Insert the new value of v:char literally. - for (p = str; *p != NUL; mb_ptr_adv(p)) { + for (p = str; *p != NUL; MB_PTR_ADV(p)) { s->c = PTR2CHAR(p); if (s->c == CAR || s->c == K_KENTER || s->c == NL) { ins_eol(s->c); @@ -2009,8 +2009,8 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int const char_u *p = str; actual_len = 0; while (*p != NUL) { - mb_ptr_adv(p); - ++actual_len; + MB_PTR_ADV(p); + actual_len++; } } else actual_len = len; @@ -2020,8 +2020,8 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int const char_u *p = compl_orig_text; actual_compl_length = 0; while (*p != NUL) { - mb_ptr_adv(p); - ++actual_compl_length; + MB_PTR_ADV(p); + actual_compl_length++; } } else actual_compl_length = compl_length; @@ -2323,8 +2323,8 @@ static void ins_compl_longest_match(compl_T *match) break; } if (has_mbyte) { - mb_ptr_adv(p); - mb_ptr_adv(s); + MB_PTR_ADV(p); + MB_PTR_ADV(s); } else { ++p; ++s; @@ -2976,7 +2976,7 @@ static int ins_compl_bs(void) line = get_cursor_line_ptr(); p = line + curwin->w_cursor.col; - mb_ptr_back(line, p); + MB_PTR_BACK(line, p); // Stop completion when the whole word was deleted. For Omni completion // allow the word to be deleted, we won't match everything. @@ -3442,8 +3442,9 @@ static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg) ; if (len > 0) len -= (*mb_head_off)(p, p + len); - for (p += len; *p != NUL; mb_ptr_adv(p)) + for (p += len; *p != NUL; MB_PTR_ADV(p)) { AppendCharToRedobuff(K_BS); + } } else { len = 0; } @@ -4609,13 +4610,15 @@ static int ins_complete(int c, bool enable_pum) if (startcol > 0) { char_u *p = line + startcol; - mb_ptr_back(line, p); - while (p > line && vim_isfilec(PTR2CHAR(p))) - mb_ptr_back(line, p); - if (p == line && vim_isfilec(PTR2CHAR(p))) + MB_PTR_BACK(line, p); + while (p > line && vim_isfilec(PTR2CHAR(p))) { + MB_PTR_BACK(line, p); + } + if (p == line && vim_isfilec(PTR2CHAR(p))) { startcol = 0; - else + } else { startcol = (int)(p - line) + 1; + } } compl_col += startcol; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 30c17af8c9..eb437931d4 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -110,8 +110,8 @@ #define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */ -#define AUTOLOAD_CHAR '#' /* Character used as separator in autoload - function/variable names. */ +// Character used as separator in autoload function/variable names. +#define AUTOLOAD_CHAR '#' /* * Structure returned by get_lval() and used by set_var_lval(). @@ -728,8 +728,8 @@ static char_u *redir_varname = NULL; * Start recording command output to a variable * Returns OK if successfully completed the setup. FAIL otherwise. */ -int -var_redir_start ( +int +var_redir_start( char_u *name, int append /* append to an existing variable */ ) @@ -926,8 +926,8 @@ void eval_patch(const char *const origfile, const char *const difffile, * Sets "error" to TRUE if there was an error. * Return TRUE or FALSE. */ -int -eval_to_bool ( +int +eval_to_bool( char_u *arg, bool *error, char_u **nextcmd, @@ -1503,8 +1503,8 @@ void ex_let(exarg_T *eap) * or concatenate. * Returns OK or FAIL; */ -static int -ex_let_vars ( +static int +ex_let_vars( char_u *arg_start, typval_T *tv, int copy, /* copy values from "tv", don't move */ @@ -2581,9 +2581,10 @@ void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx) /* ":let var1 var2 ...": find last space. */ for (p = arg + STRLEN(arg); p >= arg; ) { xp->xp_pattern = p; - mb_ptr_back(arg, p); - if (ascii_iswhite(*p)) + MB_PTR_BACK(arg, p); + if (ascii_iswhite(*p)) { break; + } } return; } @@ -4307,8 +4308,8 @@ static int eval7( * "*arg" points to the '[' or '.'. * Returns FAIL or OK. "*arg" is advanced to after the ']'. */ -static int -eval_index ( +static int +eval_index( char_u **arg, typval_T *rettv, int evaluate, @@ -4653,7 +4654,7 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate) /* * Find the end of the string, skipping backslashed characters. */ - for (p = *arg + 1; *p != NUL && *p != '"'; mb_ptr_adv(p)) { + for (p = *arg + 1; *p != NUL && *p != '"'; MB_PTR_ADV(p)) { if (*p == '\\' && p[1] != NUL) { ++p; /* A "\<x>" form occupies at least 4 characters, and produces up @@ -4777,7 +4778,7 @@ static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate) /* * Find the end of the string, skipping ''. */ - for (p = *arg + 1; *p != NUL; mb_ptr_adv(p)) { + for (p = *arg + 1; *p != NUL; MB_PTR_ADV(p)) { if (*p == '\'') { if (p[1] != '\'') break; @@ -6061,15 +6062,15 @@ static char_u *deref_func_name(const char *name, int *lenp, * Allocate a variable for the result of a function. * Return OK or FAIL. */ -static int -get_func_tv ( - char_u *name, /* name of the function */ - int len, /* length of "name" */ +static int +get_func_tv( + char_u *name, // name of the function + int len, // length of "name" typval_T *rettv, - char_u **arg, /* argument, pointing to the '(' */ - linenr_T firstline, /* first line of range */ - linenr_T lastline, /* last line of range */ - int *doesrange, /* return: function handled range */ + char_u **arg, // argument, pointing to the '(' + linenr_T firstline, // first line of range + linenr_T lastline, // last line of range + int *doesrange, // return: function handled range int evaluate, partial_T *partial, // for extra arguments dict_T *selfdict // Dictionary for "self" @@ -9961,8 +9962,8 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) while (cur != NULL) { dict_T *dict = tv_dict_alloc(); if (cur->match.regprog == NULL) { - // match added with matchaddpos() - for (i = 0; i < MAXPOSMATCH; ++i) { + // match added with matchaddpos() + for (i = 0; i < MAXPOSMATCH; i++) { llpos_T *llpos; char buf[6]; @@ -10380,8 +10381,8 @@ static void f_getwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) /* * getwinvar() and gettabwinvar() */ -static void -getwinvar ( +static void +getwinvar( typval_T *argvars, typval_T *rettv, int off /* 1 for gettabwinvar() */ @@ -13994,10 +13995,7 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) c = -1; } else { off = LineOffset[row] + col; - if (enc_utf8 && ScreenLinesUC[off] != 0) - c = ScreenLinesUC[off]; - else - c = ScreenLines[off]; + c = utf_ptr2char(ScreenLines[off]); } rettv->vval.v_number = c; } @@ -14163,17 +14161,17 @@ static void f_searchpairpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) * Used by searchpair(), see its documentation for the details. * Returns 0 or -1 for no match, */ -long -do_searchpair ( - char_u *spat, /* start pattern */ - char_u *mpat, /* middle pattern */ - char_u *epat, /* end pattern */ - int dir, /* BACKWARD or FORWARD */ - char_u *skip, /* skip expression */ - int flags, /* SP_SETPCMARK and other SP_ values */ +long +do_searchpair( + char_u *spat, // start pattern + char_u *mpat, // middle pattern + char_u *epat, // end pattern + int dir, // BACKWARD or FORWARD + char_u *skip, // skip expression + int flags, // SP_SETPCMARK and other SP_ values pos_T *match_pos, - linenr_T lnum_stop, /* stop at this line if not zero */ - long time_limit /* stop after this many msec */ + linenr_T lnum_stop, // stop at this line if not zero + long time_limit // stop after this many msec ) { char_u *save_cpo; @@ -16401,7 +16399,7 @@ static list_T *string_to_list(const char *str, size_t len, const bool keepempty) return list; } -static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, +static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist) { rettv->v_type = VAR_STRING; @@ -18064,16 +18062,17 @@ static const char_u *find_name_end(const char_u *arg, const char_u **expr_start, || *p == '{' || ((flags & FNE_INCL_BR) && (*p == '[' || *p == '.')) || mb_nest != 0 - || br_nest != 0); mb_ptr_adv(p)) { + || br_nest != 0); MB_PTR_ADV(p)) { if (*p == '\'') { - /* skip over 'string' to avoid counting [ and ] inside it. */ - for (p = p + 1; *p != NUL && *p != '\''; mb_ptr_adv(p)) - ; - if (*p == NUL) + // skip over 'string' to avoid counting [ and ] inside it. + for (p = p + 1; *p != NUL && *p != '\''; MB_PTR_ADV(p)) { + } + if (*p == NUL) { break; + } } else if (*p == '"') { // skip over "str\"ing" to avoid counting [ and ] inside it. - for (p = p + 1; *p != NUL && *p != '"'; mb_ptr_adv(p)) { + for (p = p + 1; *p != NUL && *p != '"'; MB_PTR_ADV(p)) { if (*p == '\\' && p[1] != NUL) { ++p; } @@ -20696,8 +20695,8 @@ void func_dump_profile(FILE *fd) xfree(sorttab); } -static void -prof_sort_list ( +static void +prof_sort_list( FILE *fd, ufunc_T **sorttab, int st_len, @@ -21554,8 +21553,8 @@ static int can_free_funccal(funccall_T *fc, int copyID) /* * Free "fc" and what it contains. */ -static void -free_funccal ( +static void +free_funccal( funccall_T *fc, int free_val /* a: vars were allocated */ ) @@ -22109,13 +22108,13 @@ void reset_v_option_vars(void) * Returns VALID_ flags or -1 for failure. * When there is an error, *fnamep is set to NULL. */ -int -modify_fname ( - char_u *src, /* string with modifiers */ - size_t *usedlen, /* characters after src that are used */ - char_u **fnamep, /* file name so far */ - char_u **bufp, /* buffer for allocated file name or NULL */ - size_t *fnamelen /* length of fnamep */ +int +modify_fname( + char_u *src, // string with modifiers + size_t *usedlen, // characters after src that are used + char_u **fnamep, // file name so far + char_u **bufp, // buffer for allocated file name or NULL + size_t *fnamelen // length of fnamep ) { int valid = 0; @@ -22151,15 +22150,16 @@ repeat: return -1; } - /* When "/." or "/.." is used: force expansion to get rid of it. */ - for (p = *fnamep; *p != NUL; mb_ptr_adv(p)) { + // When "/." or "/.." is used: force expansion to get rid of it. + for (p = *fnamep; *p != NUL; MB_PTR_ADV(p)) { if (vim_ispathsep(*p) && p[1] == '.' && (p[2] == NUL || vim_ispathsep(p[2]) || (p[2] == '.' - && (p[3] == NUL || vim_ispathsep(p[3]))))) + && (p[3] == NUL || vim_ispathsep(p[3]))))) { break; + } } /* FullName_save() is slow, don't use it when not needed. */ @@ -22239,8 +22239,9 @@ repeat: valid |= VALID_HEAD; *usedlen += 2; s = get_past_head(*fnamep); - while (tail > s && after_pathsep((char *)s, (char *)tail)) - mb_ptr_back(*fnamep, tail); + while (tail > s && after_pathsep((char *)s, (char *)tail)) { + MB_PTR_BACK(*fnamep, tail); + } *fnamelen = (size_t)(tail - *fnamep); if (*fnamelen == 0) { /* Result is empty. Turn it into "." to make ":cd %:h" work. */ @@ -22248,8 +22249,9 @@ repeat: *bufp = *fnamep = tail = vim_strsave((char_u *)"."); *fnamelen = 1; } else { - while (tail > s && !after_pathsep((char *)s, (char *)tail)) - mb_ptr_back(*fnamep, tail); + while (tail > s && !after_pathsep((char *)s, (char *)tail)) { + MB_PTR_BACK(*fnamep, tail); + } } } diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 4e0bdb777c..98eda8dcb8 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3269,9 +3269,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, *cmd++ = NUL; /* replace it with a NUL */ break; } - if (cmd[0] == '\\' && cmd[1] != 0) /* skip escaped characters */ - ++cmd; - mb_ptr_adv(cmd); + if (cmd[0] == '\\' && cmd[1] != 0) { // skip escaped characters + cmd++; + } + MB_PTR_ADV(cmd); } if (!eap->skip && !preview) { diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index e95890adbf..46a7c869e1 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2532,7 +2532,7 @@ static void add_pack_plugin(char_u *fname, void *cookie) if (cookie != &APP_LOAD && strstr((char *)p_rtp, ffname) == NULL) { // directory is not yet in 'runtimepath', add it p4 = p3 = p2 = p1 = get_past_head((char_u *)ffname); - for (p = p1; *p; mb_ptr_adv(p)) { + for (p = p1; *p; MB_PTR_ADV(p)) { if (vim_ispathsep_nocolon(*p)) { p4 = p3; p3 = p2; p2 = p1; p1 = p; } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 709dc60b13..9590a3715e 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2872,7 +2872,7 @@ const char * set_one_cmd_context( return NULL; /* It's a comment */ } } - mb_ptr_adv(p); + MB_PTR_ADV(p); } } @@ -2890,9 +2890,10 @@ const char * set_one_cmd_context( // Argument starts after a space. xp->xp_pattern = (char_u *)++p; } else { - if (*p == '\\' && *(p + 1) != NUL) - ++p; /* skip over escaped character */ - mb_ptr_adv(p); + if (*p == '\\' && *(p + 1) != NUL) { + p++; // skip over escaped character + } + MB_PTR_ADV(p); } } @@ -2943,7 +2944,7 @@ const char * set_one_cmd_context( } else { len = 1; } - mb_ptr_adv(p); + MB_PTR_ADV(p); } if (in_quote) { bow = p; @@ -2952,7 +2953,7 @@ const char * set_one_cmd_context( } p -= len; } - mb_ptr_adv(p); + MB_PTR_ADV(p); } /* @@ -3336,12 +3337,13 @@ const char * set_one_cmd_context( // Find start of last argument. p = arg; while (*p) { - if (*p == ' ') - /* argument starts after a space */ + if (*p == ' ') { + // argument starts after a space arg = p + 1; - else if (*p == '\\' && *(p + 1) != NUL) - ++p; /* skip over escaped character */ - mb_ptr_adv(p); + } else if (*p == '\\' && *(p + 1) != NUL) { + p++; // skip over escaped character + } + MB_PTR_ADV(p); } xp->xp_pattern = (char_u *)arg; } @@ -4238,7 +4240,7 @@ void separate_nextcmd(exarg_T *eap) p = skip_grep_pat(eap); - for (; *p; mb_ptr_adv(p)) { + for (; *p; MB_PTR_ADV(p)) { if (*p == Ctrl_V) { if (eap->argt & (USECTRLV | XFILE)) ++p; /* skip CTRL-V and next char */ @@ -4322,7 +4324,7 @@ skip_cmd_arg ( else ++p; } - mb_ptr_adv(p); + MB_PTR_ADV(p); } return p; } @@ -9486,10 +9488,12 @@ static int ses_put_fname(FILE *fd, char_u *name, unsigned *flagp) char_u *sname = home_replace_save(NULL, name); if (*flagp & SSOP_SLASH) { - /* change all backslashes to forward slashes */ - for (p = sname; *p != NUL; mb_ptr_adv(p)) - if (*p == '\\') + // change all backslashes to forward slashes + for (p = sname; *p != NUL; MB_PTR_ADV(p)) { + if (*p == '\\') { *p = '/'; + } + } } // Escape special characters. diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index d2db309c4f..9b1dcfcafb 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2934,7 +2934,7 @@ static void ui_ext_cmdline_show(CmdlineInfo *line) Array content = ARRAY_DICT_INIT; if (cmdline_star) { size_t len = 0; - for (char_u *p = ccline.cmdbuff; *p; mb_ptr_adv(p)) { + for (char_u *p = ccline.cmdbuff; *p; MB_PTR_ADV(p)) { len++; } char *buf = xmallocz(len); @@ -4254,7 +4254,7 @@ char_u *sm_gettail(char_u *s) t = p; had_sep = FALSE; } - mb_ptr_adv(p); + MB_PTR_ADV(p); } return t; } @@ -5179,7 +5179,7 @@ static int ExpandRTDir(char_u *pat, int flags, int *num_file, char_u ***file, char_u *e = s + STRLEN(s); if (e - s > 4 && STRNICMP(e - 4, ".vim", 4) == 0) { e -= 4; - for (s = e; s > match; mb_ptr_back(match, s)) { + for (s = e; s > match; MB_PTR_BACK(match, s)) { if (vim_ispathsep(*s)) { break; } diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 0417c3daed..3b6a3a7995 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -4404,7 +4404,7 @@ char *modname(const char *fname, const char *ext, bool prepend_dot) // Search backwards until we hit a '/', '\' or ':'. // Then truncate what is after the '/', '\' or ':' to BASENAMELEN characters. char *ptr = NULL; - for (ptr = retval + fnamelen; ptr > retval; mb_ptr_back(retval, ptr)) { + for (ptr = retval + fnamelen; ptr > retval; MB_PTR_BACK(retval, ptr)) { if (vim_ispathsep(*ptr)) { ptr++; break; @@ -6653,7 +6653,7 @@ bool trigger_cursorhold(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT /// Return true if "event" autocommand is defined. /// /// @param event the autocommand to check -bool has_event(int event) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +bool has_event(event_T event) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return first_autopat[event] != NULL; } diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 282b72b67a..aea7cdd302 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -1883,7 +1883,7 @@ void foldtext_cleanup(char_u *str) ++len; STRMOVE(s, s + len); } else { - mb_ptr_adv(s); + MB_PTR_ADV(s); } } } @@ -3054,10 +3054,12 @@ static void foldlevelMarker(fline_T *flp) if (flp->lvl_next > start_lvl) flp->lvl_next = start_lvl; } - } else - --flp->lvl_next; - } else - mb_ptr_adv(s); + } else { + flp->lvl_next--; + } + } else { + MB_PTR_ADV(s); + } } /* The level can't go negative, must be missing a start marker. */ diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 690a83af50..1c0464b575 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -4249,7 +4249,7 @@ static bool typebuf_match_len(const uint8_t *str, int *mlen) mapblock_T *get_maphash(int index, buf_T *buf) FUNC_ATTR_PURE { - if (index > MAX_MAPHASH) { + if (index >= MAX_MAPHASH) { return NULL; } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 51bc3f1289..d9103f516c 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -131,38 +131,39 @@ typedef off_t off_T; /* * The characters and attributes cached for the screen. */ -typedef char_u schar_T; -typedef unsigned short sattr_T; - -/* - * The characters that are currently on the screen are kept in ScreenLines[]. - * It is a single block of characters, the size of the screen plus one line. - * The attributes for those characters are kept in ScreenAttrs[]. - * - * "LineOffset[n]" is the offset from ScreenLines[] for the start of line 'n'. - * The same value is used for ScreenLinesUC[] and ScreenAttrs[]. - * - * Note: before the screen is initialized and when out of memory these can be - * NULL. - */ +typedef char_u schar_T[(MAX_MCO+1) * 4 + 1]; +typedef int16_t sattr_T; + +/// ScreenLines[] contains a copy of the whole screen, as it currently is +/// displayed. It is a single block of screen cells, the size of the screen +/// plus one line. The extra line used as a buffer while redrawing a window +/// line, so it can be compared with the previous state of that line. This way +/// we can avoid sending bigger updates than neccessary to the Ul layer. +/// +/// Screen cells are stored as NUL-terminated UTF-8 strings, and a cell can +/// contain up to MAX_MCO composing characters after the base character. +/// The composing characters are to be drawn on top of the original character. +/// The content after the NUL is not defined (so comparison must be done a +/// single cell at a time). Double-width characters are stored in the left cell, +/// and the right cell should only contain the empty string. When a part of the +/// screen is cleared, the cells should be filled with a single whitespace char. +/// +/// ScreenAttrs[] contains the highlighting attribute for each cell. +/// LineOffset[n] is the offset from ScreenLines[] and ScreenAttrs[] for the +/// start of line 'n'. These offsets are in general not linear, as full screen +/// scrolling is implemented by rotating the offsets in the LineOffset array. +/// LineWraps[] is an array of boolean flags indicating if the screen line wraps +/// to the next line. It can only be true if a window occupies the entire screen +/// width. +/// +/// +/// Note: before the screen is initialized and when out of memory these can be +/// NULL. EXTERN schar_T *ScreenLines INIT(= NULL); EXTERN sattr_T *ScreenAttrs INIT(= NULL); EXTERN unsigned *LineOffset INIT(= NULL); EXTERN char_u *LineWraps INIT(= NULL); /* line wraps to next line */ -/* - * When using Unicode characters (in UTF-8 encoding) the character in - * ScreenLinesUC[] contains the Unicode for the character at this position, or - * NUL when the character in ScreenLines[] is to be used (ASCII char). - * The composing characters are to be drawn on top of the original character. - * ScreenLinesC[0][off] is only to be used when ScreenLinesUC[off] != 0. - * Note: These three are only allocated when enc_utf8 is set! - */ -EXTERN u8char_T *ScreenLinesUC INIT(= NULL); /* decoded UTF-8 characters */ -EXTERN u8char_T *ScreenLinesC[MAX_MCO]; /* composing characters */ -EXTERN int Screen_mco INIT(= 0); /* value of p_mco used when - allocating ScreenLinesC[] */ - EXTERN int screen_Rows INIT(= 0); /* actual size of ScreenLines[] */ EXTERN int screen_Columns INIT(= 0); /* actual size of ScreenLines[] */ @@ -871,8 +872,6 @@ enum { }; -EXTERN char breakat_flags[256]; /* which characters are in 'breakat' */ - /* * Some file names are stored in pathdef.c, which is generated from the * Makefile to make their value depend on the Makefile. @@ -907,6 +906,7 @@ EXTERN int fill_vert INIT(= 9474); // │ EXTERN int fill_fold INIT(= 183); // · EXTERN int fill_diff INIT(= '-'); EXTERN int fill_msgsep INIT(= ' '); +EXTERN int fill_eob INIT(= '~'); /* Whether 'keymodel' contains "stopsel" and "startsel". */ EXTERN int km_stopsel INIT(= FALSE); diff --git a/src/nvim/macros.h b/src/nvim/macros.h index 348df2d9b6..7eb58bea2a 100644 --- a/src/nvim/macros.h +++ b/src/nvim/macros.h @@ -82,12 +82,6 @@ } \ } while (0) -/* - * vim_isbreak() is used very often if 'linebreak' is set, use a macro to make - * it work fast. - */ -#define vim_isbreak(c) (breakat_flags[(char_u)(c)]) - #define WRITEBIN "wb" /* no CR-LF translation */ #define READBIN "rb" #define APPENDBIN "ab" @@ -110,9 +104,9 @@ /* Whether to draw the vertical bar on the right side of the cell. */ # define CURSOR_BAR_RIGHT (curwin->w_p_rl && (!(State & CMDLINE) || cmdmsg_rl)) -// mb_ptr_adv(): advance a pointer to the next character, taking care of +// MB_PTR_ADV(): advance a pointer to the next character, taking care of // multi-byte characters if needed. -// mb_ptr_back(): backup a pointer to the previous character, taking care of +// MB_PTR_BACK(): backup a pointer to the previous character, taking care of // multi-byte characters if needed. // MB_COPY_CHAR(f, t): copy one char from "f" to "t" and advance the pointers. // PTR2CHAR(): get character from pointer. @@ -120,11 +114,11 @@ // Get the length of the character p points to # define MB_PTR2LEN(p) mb_ptr2len(p) // Advance multi-byte pointer, skip over composing chars. -# define mb_ptr_adv(p) (p += mb_ptr2len((char_u *)p)) +# define MB_PTR_ADV(p) (p += mb_ptr2len((char_u *)p)) // Advance multi-byte pointer, do not skip over composing chars. -# define mb_cptr_adv(p) (p += utf_ptr2len(p)) +# define MB_CPTR_ADV(p) (p += utf_ptr2len(p)) // Backup multi-byte pointer. Only use with "p" > "s" ! -# define mb_ptr_back(s, p) (p -= mb_head_off((char_u *)s, (char_u *)p - 1) + 1) +# define MB_PTR_BACK(s, p) (p -= mb_head_off((char_u *)s, (char_u *)p - 1) + 1) // get length of multi-byte char, not including composing chars # define MB_CPTR2LEN(p) utf_ptr2len(p) diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 7cfe3f4a18..49e60b5166 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -603,7 +603,7 @@ static char_u *mark_line(pos_T *mp, int lead_len) /* Truncate the line to fit it in the window */ len = 0; - for (p = s; *p != NUL; mb_ptr_adv(p)) { + for (p = s; *p != NUL; MB_PTR_ADV(p)) { len += ptr2cells(p); if (len >= Columns - lead_len) break; diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 05e326104b..a8781ffbb8 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -560,7 +560,7 @@ size_t mb_string2cells(const char_u *str) /// We make sure that the offset used is less than "max_off". int utf_off2cells(unsigned off, unsigned max_off) { - return (off + 1 < max_off && ScreenLines[off + 1] == 0) ? 2 : 1; + return (off + 1 < max_off && ScreenLines[off + 1][0] == 0) ? 2 : 1; } /// Convert a UTF-8 byte sequence to a wide character @@ -790,27 +790,6 @@ int utfc_ptr2char_len(const char_u *p, int *pcc, int maxlen) #undef ISCOMPOSING } -/* - * Convert the character at screen position "off" to a sequence of bytes. - * Includes the composing characters. - * "buf" must at least have the length MB_MAXBYTES + 1. - * Only to be used when ScreenLinesUC[off] != 0. - * Returns the produced number of bytes. - */ -int utfc_char2bytes(int off, char_u *buf) -{ - int len; - int i; - - len = utf_char2bytes(ScreenLinesUC[off], buf); - for (i = 0; i < Screen_mco; ++i) { - if (ScreenLinesC[i][off] == 0) - break; - len += utf_char2bytes(ScreenLinesC[i][off], buf + len); - } - return len; -} - /// Get the length of a UTF-8 byte sequence representing a single codepoint /// /// @param[in] p UTF-8 string. @@ -1743,8 +1722,9 @@ char_u * mb_prevptr( char_u *p ) { - if (p > line) - mb_ptr_back(line, p); + if (p > line) { + MB_PTR_BACK(line, p); + } return p; } @@ -1853,7 +1833,7 @@ int mb_fix_col(int col, int row) col = check_col(col); row = check_row(row); if (ScreenLines != NULL && col > 0 - && ScreenLines[LineOffset[row] + col] == 0) { + && ScreenLines[LineOffset[row] + col][0] == 0) { return col - 1; } return col; diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 0449af1e2b..b156f2c513 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -1432,7 +1432,7 @@ static char *make_percent_swname(const char *dir, char *name) char *f = fix_fname(name != NULL ? name : ""); if (f != NULL) { char *s = xstrdup(f); - for (d = s; *d != NUL; mb_ptr_adv(d)) { + for (d = s; *d != NUL; MB_PTR_ADV(d)) { if (vim_ispathsep(*d)) { *d = '%'; } diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 1bbd07686b..968962c894 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -97,7 +97,7 @@ ex_menu(exarg_T *eap) while (*arg != NUL && *arg != ' ') { if (*arg == '\\') STRMOVE(arg, arg + 1); - mb_ptr_adv(arg); + MB_PTR_ADV(arg); } if (*arg != NUL) { *arg++ = NUL; @@ -1099,7 +1099,7 @@ char_u *menu_name_skip(char_u *const name) { char_u *p; - for (p = name; *p && *p != '.'; mb_ptr_adv(p)) { + for (p = name; *p && *p != '.'; MB_PTR_ADV(p)) { if (*p == '\\' || *p == Ctrl_V) { STRMOVE(p, p + 1); if (*p == NUL) @@ -1549,9 +1549,11 @@ static void menu_unescape_name(char_u *name) { char_u *p; - for (p = name; *p && *p != '.'; mb_ptr_adv(p)) - if (*p == '\\') + for (p = name; *p && *p != '.'; MB_PTR_ADV(p)) { + if (*p == '\\') { STRMOVE(p, p + 1); + } + } } /* diff --git a/src/nvim/message.c b/src/nvim/message.c index 7935bcbc2f..2643d644a9 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -2935,7 +2935,7 @@ static char_u * console_dialog_alloc(const char_u *message, } // Advance to the next character - mb_ptr_adv(r); + MB_PTR_ADV(r); } len += (int)(STRLEN(message) @@ -3053,7 +3053,7 @@ static void copy_hotkeys_and_msg(const char_u *message, char_u *buttons, } // advance to the next character - mb_ptr_adv(r); + MB_PTR_ADV(r); } *msgp++ = ':'; diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index a5da9d3220..5678360ef0 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -531,7 +531,7 @@ open_line ( int l; while (old_size < repl_size && p > leader) { - mb_ptr_back(leader, p); + MB_PTR_BACK(leader, p); old_size += ptr2cells(p); } l = lead_repl_len - (int)(endp - p); @@ -1311,7 +1311,7 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column) colnr_T col = 0; while (*s != NUL && --column >= 0) { col += win_lbr_chartabsize(wp, line, s, col, NULL); - mb_ptr_adv(s); + MB_PTR_ADV(s); } // If *s is a TAB, and the TAB is not displayed as ^I, and we're not in diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 6f636f643a..8c615f52e7 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -108,12 +108,13 @@ retnomove: goto retnomove; // ugly goto... // Remember the character under the mouse, it might be a '-' or '+' in the - // fold column. + // fold column. NB: only works for ASCII chars! if (row >= 0 && row < Rows && col >= 0 && col <= Columns - && ScreenLines != NULL) - mouse_char = ScreenLines[LineOffset[row] + (unsigned)col]; - else + && ScreenLines != NULL) { + mouse_char = ScreenLines[LineOffset[row] + (unsigned)col][0]; + } else { mouse_char = ' '; + } old_curwin = curwin; old_cursor = curwin->w_cursor; @@ -525,7 +526,7 @@ static colnr_T scroll_line_len(linenr_T lnum) if (*line != NUL) { for (;;) { int numchar = chartabsize(line, col); - mb_ptr_adv(line); + MB_PTR_ADV(line); if (*line == NUL) { // don't count the last character break; } diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 45de76f80a..c95345f9b2 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -406,8 +406,9 @@ static void shift_block(oparg_T *oap, int amount) /* If "bd.startspaces" is set, "bd.textstart" points to the character, * the part of which is displayed at the block's beginning. Let's start * searching from the next character. */ - if (bd.startspaces) - mb_ptr_adv(non_white); + if (bd.startspaces) { + MB_PTR_ADV(non_white); + } /* The character's column is in "bd.start_vcol". */ non_white_col = bd.start_vcol; @@ -443,7 +444,7 @@ static void shift_block(oparg_T *oap, int amount) if (verbatim_copy_width + incr > destination_col) break; verbatim_copy_width += incr; - mb_ptr_adv(verbatim_copy_end); + MB_PTR_ADV(verbatim_copy_end); } /* If "destination_col" is different from the width of the initial @@ -2820,7 +2821,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } char_u *p = get_cursor_pos_ptr(); if (dir == FORWARD && *p != NUL) { - mb_ptr_adv(p); + MB_PTR_ADV(p); } ptr = vim_strsave(p); ml_append(curwin->w_cursor.lnum, ptr, (colnr_T)0, false); @@ -2829,7 +2830,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) oldp = get_cursor_line_ptr(); p = oldp + curwin->w_cursor.col; if (dir == FORWARD && *p != NUL) { - mb_ptr_adv(p); + MB_PTR_ADV(p); } ptr = vim_strnsave(oldp, (size_t)(p - oldp)); ml_replace(curwin->w_cursor.lnum, ptr, false); @@ -3632,10 +3633,10 @@ int do_join(size_t count, if (insert_space && currsize > 0) { if (has_mbyte) { cend = curr + currsize; - mb_ptr_back(curr, cend); + MB_PTR_BACK(curr, cend); endcurr1 = (*mb_ptr2char)(cend); if (cend > curr) { - mb_ptr_back(curr, cend); + MB_PTR_BACK(curr, cend); endcurr2 = (*mb_ptr2char)(cend); } } else { @@ -4272,7 +4273,7 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, int i bdp->pre_whitesp_c = 0; } prev_pstart = pstart; - mb_ptr_adv(pstart); + MB_PTR_ADV(pstart); } bdp->start_char_vcols = incr; if (bdp->start_vcol < oap->start_vcol) { /* line too short */ diff --git a/src/nvim/option.c b/src/nvim/option.c index 882289c8b8..0851e6cc5f 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2830,7 +2830,7 @@ did_set_string_option ( for (s = p_sbr; *s; ) { if (ptr2cells(s) != 1) errmsg = (char_u *)N_("E595: contains unprintable or wide character"); - mb_ptr_adv(s); + MB_PTR_ADV(s); } } @@ -3386,6 +3386,7 @@ static char_u *set_chars_option(char_u **varp) { &fill_fold, "fold" , 183 }, // · { &fill_diff, "diff" , '-' }, { &fill_msgsep, "msgsep", ' ' }, + { &fill_eob, "eob", '~' }, }; static struct charstab lcstab[] = { { &lcs_eol, "eol", NUL }, @@ -3437,16 +3438,20 @@ static char_u *set_chars_option(char_u **varp) && p[len] == ':' && p[len + 1] != NUL) { s = p + len + 1; - c1 = mb_ptr2char_adv((const char_u **)&s); - if (mb_char2cells(c1) > 1) { + + // TODO(bfredl): use schar_T representation and utfc_ptr2len + int c1len = utf_ptr2len(s); + c1 = mb_cptr2char_adv((const char_u **)&s); + if (mb_char2cells(c1) > 1 || (c1len == 1 && c1 > 127)) { continue; } if (tab[i].cp == &lcs_tab2) { if (*s == NUL) { continue; } - c2 = mb_ptr2char_adv((const char_u **)&s); - if (mb_char2cells(c2) > 1) { + int c2len = utf_ptr2len(s); + c2 = mb_cptr2char_adv((const char_u **)&s); + if (mb_char2cells(c2) > 1 || (c2len == 1 && c2 > 127)) { continue; } } @@ -4102,11 +4107,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, errmsg = e_winwidth; } } else if (pp == &p_mco) { - if (value > MAX_MCO) { - errmsg = e_invarg; - } else if (value < 0) { - errmsg = e_positive; - } + value = MAX_MCO; } else if (pp == &p_titlelen) { if (value < 0) { errmsg = e_positive; @@ -4268,8 +4269,6 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, if (pp == &curbuf->b_p_sw || curbuf->b_p_sw == 0) { parse_cino(curbuf); } - } else if (pp == &p_mco) { - screenclear(); // will re-allocate the screen } else if (pp == &curbuf->b_p_iminsert) { showmode(); // Show/unshow value of 'keymap' in status lines. @@ -6210,13 +6209,15 @@ void ExpandOldSetting(int *num_file, char_u ***file) #ifdef BACKSLASH_IN_FILENAME /* For MS-Windows et al. we don't double backslashes at the start and * before a file name character. */ - for (var = buf; *var != NUL; mb_ptr_adv(var)) + for (var = buf; *var != NUL; MB_PTR_ADV(var)) { if (var[0] == '\\' && var[1] == '\\' && expand_option_idx >= 0 && (options[expand_option_idx].flags & P_EXPAND) && vim_isfilec(var[2]) - && (var[2] != '\\' || (var == buf && var[4] != '\\'))) + && (var[2] != '\\' || (var == buf && var[4] != '\\'))) { STRMOVE(var, var + 1); + } + } #endif *file[0] = buf; @@ -6384,9 +6385,10 @@ static void langmap_set(void) for (p = p_langmap; p[0] != NUL; ) { for (p2 = p; p2[0] != NUL && p2[0] != ',' && p2[0] != ';'; - mb_ptr_adv(p2)) { - if (p2[0] == '\\' && p2[1] != NUL) - ++p2; + MB_PTR_ADV(p2)) { + if (p2[0] == '\\' && p2[1] != NUL) { + p2++; + } } if (p2[0] == ';') ++p2; /* abcd;ABCD form, p2 points to A */ @@ -6402,7 +6404,7 @@ static void langmap_set(void) from = (*mb_ptr2char)(p); to = NUL; if (p2 == NULL) { - mb_ptr_adv(p); + MB_PTR_ADV(p); if (p[0] != ',') { if (p[0] == '\\') ++p; @@ -6428,10 +6430,10 @@ static void langmap_set(void) langmap_mapchar[from & 255] = (char_u)to; } - /* Advance to next pair */ - mb_ptr_adv(p); + // Advance to next pair + MB_PTR_ADV(p); if (p2 != NULL) { - mb_ptr_adv(p2); + MB_PTR_ADV(p2); if (*p == ';') { p = p2; if (p[0] != NUL) { diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index f7dfa65053..6a0d0e32e0 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -314,9 +314,10 @@ static char *(p_bkc_values[]) = # define BKC_NO 0x004 # define BKC_BREAKSYMLINK 0x008 # define BKC_BREAKHARDLINK 0x010 -EXTERN char_u *p_bdir; /* 'backupdir' */ -EXTERN char_u *p_bex; /* 'backupext' */ -EXTERN char_u *p_bo; // 'belloff' +EXTERN char_u *p_bdir; // 'backupdir' +EXTERN char_u *p_bex; // 'backupext' +EXTERN char_u *p_bo; // 'belloff' +EXTERN char breakat_flags[256]; // which characters are in 'breakat' EXTERN unsigned bo_flags; # ifdef IN_OPTION_C static char *(p_bo_values[]) = {"all", "backspace", "cursor", "complete", diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 47c9f5aa78..30ddb977bb 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -1493,9 +1493,8 @@ return { full_name='maxcombine', abbreviation='mco', type='number', scope={'global'}, vi_def=true, - redraw={'curswant'}, varname='p_mco', - defaults={if_true={vi=2}} + defaults={if_true={vi=6}} }, { full_name='maxfuncdepth', abbreviation='mfd', diff --git a/src/nvim/path.c b/src/nvim/path.c index 61cfaea84a..b1e1bf3b2f 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -101,7 +101,7 @@ char_u *path_tail(const char_u *fname) if (vim_ispathsep_nocolon(*p)) { tail = p + 1; } - mb_ptr_adv(p); + MB_PTR_ADV(p); } return (char_u *)tail; } @@ -144,7 +144,7 @@ const char_u *invocation_path_tail(const char_u *invocation, size_t *len) const char_u *p = tail; while (*p != NUL && *p != ' ') { bool was_sep = vim_ispathsep_nocolon(*p); - mb_ptr_adv(p); + MB_PTR_ADV(p); if (was_sep) { tail = p; // Now tail points one past the separator. } @@ -166,7 +166,7 @@ const char *path_next_component(const char *fname) { assert(fname != NULL); while (*fname != NUL && !vim_ispathsep(*fname)) { - mb_ptr_adv(fname); + MB_PTR_ADV(fname); } if (*fname != NUL) { fname++; @@ -468,7 +468,7 @@ char_u *save_abs_path(const char_u *name) bool path_has_wildcard(const char_u *p) FUNC_ATTR_NONNULL_ALL { - for (; *p; mb_ptr_adv(p)) { + for (; *p; MB_PTR_ADV(p)) { #if defined(UNIX) if (p[0] == '\\' && p[1] != NUL) { p++; @@ -503,7 +503,7 @@ static int pstrcmp(const void *a, const void *b) bool path_has_exp_wildcard(const char_u *p) FUNC_ATTR_NONNULL_ALL { - for (; *p != NUL; mb_ptr_adv(p)) { + for (; *p != NUL; MB_PTR_ADV(p)) { #if defined(UNIX) if (p[0] == '\\' && p[1] != NUL) { p++; @@ -744,7 +744,7 @@ static int find_previous_pathsep(char_u *path, char_u **psep) while (*psep > path) { if (vim_ispathsep(**psep)) return OK; - mb_ptr_back(path, *psep); + MB_PTR_BACK(path, *psep); } return FAIL; @@ -859,10 +859,12 @@ static char_u *get_path_cutoff(char_u *fname, garray_T *gap) } } - /* skip to the file or directory name */ - if (cutoff != NULL) - while (vim_ispathsep(*cutoff)) - mb_ptr_adv(cutoff); + // skip to the file or directory name + if (cutoff != NULL) { + while (vim_ispathsep(*cutoff)) { + MB_PTR_ADV(cutoff); + } + } return cutoff; } @@ -1038,7 +1040,7 @@ const char *gettail_dir(const char *const fname) dir_end = next_dir_end; look_for_sep = true; } - mb_ptr_adv(p); + MB_PTR_ADV(p); } return dir_end; } @@ -1085,13 +1087,12 @@ expand_in_path ( */ static bool has_env_var(char_u *p) { - for (; *p; mb_ptr_adv(p)) { - if (*p == '\\' && p[1] != NUL) - ++p; - else if (vim_strchr((char_u *) - "$" - , *p) != NULL) + for (; *p; MB_PTR_ADV(p)) { + if (*p == '\\' && p[1] != NUL) { + p++; + } else if (vim_strchr((char_u *) "$" , *p) != NULL) { return true; + } } return false; } @@ -1102,7 +1103,7 @@ static bool has_env_var(char_u *p) // cannot expand, requires using a shell. static bool has_special_wildchar(char_u *p) { - for (; *p; mb_ptr_adv(p)) { + for (; *p; MB_PTR_ADV(p)) { // Allow for escaping if (*p == '\\' && p[1] != NUL) { p++; @@ -1343,7 +1344,7 @@ void slash_adjust(char_u *p) if (*p == (char_u)psepcN) { *p = (char_u)psepc; } - mb_ptr_adv(p); + MB_PTR_ADV(p); } } #endif @@ -1444,19 +1445,22 @@ void simplify_filename(char_u *filename) * we are after "start", or strip "." if we are at the beginning * of an absolute path name . */ tail = p + 1; - if (p[1] != NUL) - while (vim_ispathsep(*tail)) - mb_ptr_adv(tail); - else if (p > start) - --p; /* strip preceding path separator */ + if (p[1] != NUL) { + while (vim_ispathsep(*tail)) { + MB_PTR_ADV(tail); + } + } else if (p > start) { + p--; // strip preceding path separator + } STRMOVE(p, tail); } } else if (p[0] == '.' && p[1] == '.' && (vim_ispathsep(p[2]) || p[2] == NUL)) { // Skip to after ".." or "../" or "..///". tail = p + 2; - while (vim_ispathsep(*tail)) - mb_ptr_adv(tail); + while (vim_ispathsep(*tail)) { + MB_PTR_ADV(tail); + } if (components > 0) { /* strip one preceding component */ bool do_strip = false; @@ -1475,10 +1479,11 @@ void simplify_filename(char_u *filename) } p[-1] = saved_char; - --p; - /* Skip back to after previous '/'. */ - while (p > start && !after_pathsep((char *)start, (char *)p)) - mb_ptr_back(start, p); + p--; + // Skip back to after previous '/'. + while (p > start && !after_pathsep((char *)start, (char *)p)) { + MB_PTR_BACK(start, p); + } if (!do_strip) { /* If the component exists in the file system, check diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index 4c2fc6d906..1182d3d902 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -374,7 +374,7 @@ void pum_redraw(void) } if (p != NULL) { - for (;; mb_ptr_adv(p)) { + for (;; MB_PTR_ADV(p)) { if (s == NULL) { s = p; } @@ -398,7 +398,7 @@ void pum_redraw(void) if (size > pum_width) { do { size -= has_mbyte ? (*mb_ptr2cells)(rt) : 1; - mb_ptr_adv(rt); + MB_PTR_ADV(rt); } while (size > pum_width); if (size < pum_width) { diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index ef02b6529c..d76da62c6d 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -1142,24 +1142,28 @@ static char_u *skip_anyof(char_u *p) if (*p == ']' || *p == '-') ++p; while (*p != NUL && *p != ']') { - if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1) + if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1) { p += l; - else if (*p == '-') { - ++p; - if (*p != ']' && *p != NUL) - mb_ptr_adv(p); + } else if (*p == '-') { + p++; + if (*p != ']' && *p != NUL) { + MB_PTR_ADV(p); + } } else if (*p == '\\' && (vim_strchr(REGEXP_INRANGE, p[1]) != NULL - || (!reg_cpo_lit && vim_strchr(REGEXP_ABBR, p[1]) != NULL))) + || (!reg_cpo_lit + && vim_strchr(REGEXP_ABBR, p[1]) != NULL))) { p += 2; - else if (*p == '[') { + } else if (*p == '[') { if (get_char_class(&p) == CLASS_NONE && get_equi_class(&p) == 0 && get_coll_element(&p) == 0 - && *p != NUL) - ++p; /* It is not a class name and not NUL */ - } else - ++p; + && *p != NUL) { + p++; // It is not a class name and not NUL + } + } else { + p++; + } } return p; @@ -1185,9 +1189,10 @@ char_u *skip_regexp(char_u *startp, int dirc, int magic, char_u **newp) mymagic = MAGIC_OFF; get_cpo_flags(); - for (; p[0] != NUL; mb_ptr_adv(p)) { - if (p[0] == dirc) /* found end of regexp */ + for (; p[0] != NUL; MB_PTR_ADV(p)) { + if (p[0] == dirc) { // found end of regexp break; + } if ((p[0] == '[' && mymagic >= MAGIC_ON) || (p[0] == '\\' && p[1] == '[' && mymagic <= MAGIC_OFF)) { p = skip_anyof(p + 1); @@ -3476,14 +3481,14 @@ static long bt_regexec_both(char_u *line, if (cstrncmp(s, prog->regmust, &prog->regmlen) == 0) { break; // Found it. } - mb_ptr_adv(s); + MB_PTR_ADV(s); } } else { while ((s = cstrchr(s, c)) != NULL) { if (cstrncmp(s, prog->regmust, &prog->regmlen) == 0) { break; // Found it. } - mb_ptr_adv(s); + MB_PTR_ADV(s); } } if (s == NULL) { // Not present. @@ -3753,7 +3758,7 @@ static int reg_match_visual(void) return TRUE; } -#define ADVANCE_REGINPUT() mb_ptr_adv(reginput) +#define ADVANCE_REGINPUT() MB_PTR_ADV(reginput) /* * The arguments from BRACE_LIMITS are stored here. They are actually local @@ -4287,7 +4292,7 @@ regmatch ( if (enc_utf8) { // Skip composing characters. while (utf_iscomposing(utf_ptr2char(reginput))) { - mb_cptr_adv(reginput); + MB_CPTR_ADV(reginput); } } break; @@ -4951,21 +4956,24 @@ regmatch ( (colnr_T)STRLEN(regline); } } else { - if (has_mbyte) + if (has_mbyte) { rp->rs_un.regsave.rs_u.pos.col -= (*mb_head_off)(regline, regline + rp->rs_un.regsave.rs_u.pos.col - 1) + 1; - else - --rp->rs_un.regsave.rs_u.pos.col; + } else { + rp->rs_un.regsave.rs_u.pos.col--; + } } } else { - if (rp->rs_un.regsave.rs_u.ptr == regline) + if (rp->rs_un.regsave.rs_u.ptr == regline) { no = FAIL; - else { - mb_ptr_back(regline, rp->rs_un.regsave.rs_u.ptr); - if (limit > 0 && (long)(behind_pos.rs_u.ptr - - rp->rs_un.regsave.rs_u.ptr) > limit) + } else { + MB_PTR_BACK(regline, rp->rs_un.regsave.rs_u.ptr); + if (limit > 0 + && (long)(behind_pos.rs_u.ptr + - rp->rs_un.regsave.rs_u.ptr) > limit) { no = FAIL; + } } } if (no == OK) { @@ -5025,17 +5033,18 @@ regmatch ( if (--rst->count < rst->minval) break; if (reginput == regline) { - /* backup to last char of previous line */ - --reglnum; + // backup to last char of previous line + reglnum--; regline = reg_getline(reglnum); - /* Just in case regrepeat() didn't count - * right. */ - if (regline == NULL) + // Just in case regrepeat() didn't count right. + if (regline == NULL) { break; + } reginput = regline + STRLEN(regline); fast_breakcheck(); - } else - mb_ptr_back(regline, reginput); + } else { + MB_PTR_BACK(regline, reginput); + } } else { /* Range is backwards, use shortest match first. * Careful: maxval and minval are exchanged! @@ -5165,8 +5174,8 @@ regrepeat ( /* Matching anything means we continue until end-of-line (or * end-of-file for ANY + ADD_NL), only limited by maxcount. */ while (*scan != NUL && count < maxcount) { - ++count; - mb_ptr_adv(scan); + count++; + MB_PTR_ADV(scan); } if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline || rex.reg_line_lbr || count == maxcount) { @@ -5188,7 +5197,7 @@ regrepeat ( case SIDENT + ADD_NL: while (count < maxcount) { if (vim_isIDc(PTR2CHAR(scan)) && (testval || !ascii_isdigit(*scan))) { - mb_ptr_adv(scan); + MB_PTR_ADV(scan); } else if (*scan == NUL) { if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline || rex.reg_line_lbr) { @@ -5216,7 +5225,7 @@ regrepeat ( while (count < maxcount) { if (vim_iswordp_buf(scan, rex.reg_buf) && (testval || !ascii_isdigit(*scan))) { - mb_ptr_adv(scan); + MB_PTR_ADV(scan); } else if (*scan == NUL) { if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline || rex.reg_line_lbr) { @@ -5244,7 +5253,7 @@ regrepeat ( case SFNAME + ADD_NL: while (count < maxcount) { if (vim_isfilec(PTR2CHAR(scan)) && (testval || !ascii_isdigit(*scan))) { - mb_ptr_adv(scan); + MB_PTR_ADV(scan); } else if (*scan == NUL) { if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline || rex.reg_line_lbr) { @@ -5283,7 +5292,7 @@ regrepeat ( } } else if (vim_isprintc(PTR2CHAR(scan)) == 1 && (testval || !ascii_isdigit(*scan))) { - mb_ptr_adv(scan); + MB_PTR_ADV(scan); } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) { scan++; } else { @@ -6679,7 +6688,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, if (eval_result != NULL) { int had_backslash = FALSE; - for (s = eval_result; *s != NUL; mb_ptr_adv(s)) { + for (s = eval_result; *s != NUL; MB_PTR_ADV(s)) { // Change NL to CR, so that it becomes a line break, // unless called from vim_regexec_nl(). // Skip over a backslashed character. diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index c2b1b97ce9..2eb0ca9313 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -1323,7 +1323,7 @@ static int nfa_regatom(void) EMSG(_(e_nopresub)); return FAIL; } - for (lp = reg_prev_sub; *lp != NUL; mb_cptr_adv(lp)) { + for (lp = reg_prev_sub; *lp != NUL; MB_CPTR_ADV(lp)) { EMIT(PTR2CHAR(lp)); if (lp != reg_prev_sub) EMIT(NFA_CONCAT); @@ -1557,7 +1557,7 @@ collection: } else EMIT(result); regparse = endp; - mb_ptr_adv(regparse); + MB_PTR_ADV(regparse); return OK; } /* @@ -1565,10 +1565,10 @@ collection: * version that turns [abc] into 'a' OR 'b' OR 'c' */ startc = endc = oldstartc = -1; - negated = FALSE; - if (*regparse == '^') { /* negated range */ - negated = TRUE; - mb_ptr_adv(regparse); + negated = false; + if (*regparse == '^') { // negated range + negated = true; + MB_PTR_ADV(regparse); EMIT(NFA_START_NEG_COLL); } else EMIT(NFA_START_COLL); @@ -1576,7 +1576,7 @@ collection: startc = '-'; EMIT(startc); EMIT(NFA_CONCAT); - mb_ptr_adv(regparse); + MB_PTR_ADV(regparse); } /* Emit the OR branches for each character in the [] */ emit_range = FALSE; @@ -1666,8 +1666,8 @@ collection: if (*regparse == '-' && oldstartc != -1) { emit_range = TRUE; startc = oldstartc; - mb_ptr_adv(regparse); - continue; /* reading the end of the range */ + MB_PTR_ADV(regparse); + continue; // reading the end of the range } /* Now handle simple and escaped characters. @@ -1683,7 +1683,7 @@ collection: != NULL) ) ) { - mb_ptr_adv(regparse); + MB_PTR_ADV(regparse); if (*regparse == 'n') startc = reg_string ? NL : NFA_NEWL; @@ -1695,8 +1695,8 @@ collection: ) { /* TODO(RE) This needs more testing */ startc = coll_get_char(); - got_coll_char = TRUE; - mb_ptr_back(old_regparse, regparse); + got_coll_char = true; + MB_PTR_BACK(old_regparse, regparse); } else { /* \r,\t,\e,\b */ startc = backslash_trans(*regparse); @@ -1768,18 +1768,18 @@ collection: } } - mb_ptr_adv(regparse); - } /* while (p < endp) */ + MB_PTR_ADV(regparse); + } // while (p < endp) - mb_ptr_back(old_regparse, regparse); - if (*regparse == '-') { /* if last, '-' is just a char */ + MB_PTR_BACK(old_regparse, regparse); + if (*regparse == '-') { // if last, '-' is just a char EMIT('-'); EMIT(NFA_CONCAT); } /* skip the trailing ] */ regparse = endp; - mb_ptr_adv(regparse); + MB_PTR_ADV(regparse); /* Mark end of the collection. */ if (negated == TRUE) diff --git a/src/nvim/screen.c b/src/nvim/screen.c index f36d408b25..4cb6575502 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1,83 +1,65 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -/* - * screen.c: code for displaying on the screen - * - * Output to the screen (console, terminal emulator or GUI window) is minimized - * by remembering what is already on the screen, and only updating the parts - * that changed. - * - * ScreenLines[off] Contains a copy of the whole screen, as it is currently - * displayed (excluding text written by external commands). - * ScreenAttrs[off] Contains the associated attributes. - * LineOffset[row] Contains the offset into ScreenLines*[] and ScreenAttrs[] - * for each line. - * LineWraps[row] Flag for each line whether it wraps to the next line. - * - * For double-byte characters, two consecutive bytes in ScreenLines[] can form - * one character which occupies two display cells. - * For UTF-8 a multi-byte character is converted to Unicode and stored in - * ScreenLinesUC[]. ScreenLines[] contains the first byte only. For an ASCII - * character without composing chars ScreenLinesUC[] will be 0 and - * ScreenLinesC[][] is not used. When the character occupies two display - * cells the next byte in ScreenLines[] is 0. - * ScreenLinesC[][] contain up to 'maxcombine' composing characters - * (drawn on top of the first character). There is 0 after the last one used. - * - * The screen_*() functions write to the screen and handle updating - * ScreenLines[]. - * - * update_screen() is the function that updates all windows and status lines. - * It is called form the main loop when must_redraw is non-zero. It may be - * called from other places when an immediate screen update is needed. - * - * The part of the buffer that is displayed in a window is set with: - * - w_topline (first buffer line in window) - * - w_topfill (filler lines above the first line) - * - w_leftcol (leftmost window cell in window), - * - w_skipcol (skipped window cells of first line) - * - * Commands that only move the cursor around in a window, do not need to take - * action to update the display. The main loop will check if w_topline is - * valid and update it (scroll the window) when needed. - * - * Commands that scroll a window change w_topline and must call - * check_cursor() to move the cursor into the visible part of the window, and - * call redraw_later(VALID) to have the window displayed by update_screen() - * later. - * - * Commands that change text in the buffer must call changed_bytes() or - * changed_lines() to mark the area that changed and will require updating - * later. The main loop will call update_screen(), which will update each - * window that shows the changed buffer. This assumes text above the change - * can remain displayed as it is. Text after the change may need updating for - * scrolling, folding and syntax highlighting. - * - * Commands that change how a window is displayed (e.g., setting 'list') or - * invalidate the contents of a window in another way (e.g., change fold - * settings), must call redraw_later(NOT_VALID) to have the whole window - * redisplayed by update_screen() later. - * - * Commands that change how a buffer is displayed (e.g., setting 'tabstop') - * must call redraw_curbuf_later(NOT_VALID) to have all the windows for the - * buffer redisplayed by update_screen() later. - * - * Commands that change highlighting and possibly cause a scroll too must call - * redraw_later(SOME_VALID) to update the whole window but still use scrolling - * to avoid redrawing everything. But the length of displayed lines must not - * change, use NOT_VALID then. - * - * Commands that move the window position must call redraw_later(NOT_VALID). - * TODO: should minimize redrawing by scrolling when possible. - * - * Commands that change everything (e.g., resizing the screen) must call - * redraw_all_later(NOT_VALID) or redraw_all_later(CLEAR). - * - * Things that are handled indirectly: - * - When messages scroll the screen up, msg_scrolled will be set and - * update_screen() called to redraw. - */ +// screen.c: code for displaying on the screen +// +// Output to the screen (console, terminal emulator or GUI window) is minimized +// by remembering what is already on the screen, and only updating the parts +// that changed. +// +// The screen_*() functions write to the screen and handle updating +// ScreenLines[]. +// +// update_screen() is the function that updates all windows and status lines. +// It is called from the main loop when must_redraw is non-zero. It may be +// called from other places when an immediate screen update is needed. +// +// The part of the buffer that is displayed in a window is set with: +// - w_topline (first buffer line in window) +// - w_topfill (filler lines above the first line) +// - w_leftcol (leftmost window cell in window), +// - w_skipcol (skipped window cells of first line) +// +// Commands that only move the cursor around in a window, do not need to take +// action to update the display. The main loop will check if w_topline is +// valid and update it (scroll the window) when needed. +// +// Commands that scroll a window change w_topline and must call +// check_cursor() to move the cursor into the visible part of the window, and +// call redraw_later(VALID) to have the window displayed by update_screen() +// later. +// +// Commands that change text in the buffer must call changed_bytes() or +// changed_lines() to mark the area that changed and will require updating +// later. The main loop will call update_screen(), which will update each +// window that shows the changed buffer. This assumes text above the change +// can remain displayed as it is. Text after the change may need updating for +// scrolling, folding and syntax highlighting. +// +// Commands that change how a window is displayed (e.g., setting 'list') or +// invalidate the contents of a window in another way (e.g., change fold +// settings), must call redraw_later(NOT_VALID) to have the whole window +// redisplayed by update_screen() later. +// +// Commands that change how a buffer is displayed (e.g., setting 'tabstop') +// must call redraw_curbuf_later(NOT_VALID) to have all the windows for the +// buffer redisplayed by update_screen() later. +// +// Commands that change highlighting and possibly cause a scroll too must call +// redraw_later(SOME_VALID) to update the whole window but still use scrolling +// to avoid redrawing everything. But the length of displayed lines must not +// change, use NOT_VALID then. +// +// Commands that move the window position must call redraw_later(NOT_VALID). +// TODO(neovim): should minimize redrawing by scrolling when possible. +// +// Commands that change everything (e.g., resizing the screen) must call +// redraw_all_later(NOT_VALID) or redraw_all_later(CLEAR). +// +// Things that are handled indirectly: +// - When messages scroll the screen up, msg_scrolled will be set and +// update_screen() called to redraw. +/// #include <assert.h> #include <inttypes.h> @@ -1550,9 +1532,9 @@ static void win_update(win_T *wp) } else if (dollar_vcol == -1) wp->w_botline = lnum; - /* make sure the rest of the screen is blank */ - /* put '~'s on rows that aren't part of the file. */ - win_draw_end(wp, '~', ' ', row, wp->w_height, HLF_EOB); + // make sure the rest of the screen is blank + // write the 'fill_eob' character to rows that aren't part of the file. + win_draw_end(wp, fill_eob, ' ', row, wp->w_height, HLF_EOB); } /* Reset the type of redrawing required, the window has been updated. */ @@ -1752,9 +1734,8 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T * Ignores 'rightleft', this window is never right-left. */ if (cmdwin_type != 0 && wp == curwin) { - ScreenLines[off] = cmdwin_type; + schar_from_ascii(ScreenLines[off], cmdwin_type); ScreenAttrs[off] = win_hl_attr(wp, HLF_AT); - ScreenLinesUC[off] = 0; col++; } @@ -1770,7 +1751,7 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T win_hl_attr(wp, HLF_FC)); // reverse the fold column for (i = 0; i < fdc; i++) { - ScreenLines[off + wp->w_width - i - 1 - col] = buf[i]; + schar_from_ascii(ScreenLines[off + wp->w_width - i - 1 - col], buf[i]); } } else { copy_text_attr(off + col, buf, fdc, win_hl_attr(wp, HLF_FC)); @@ -1854,111 +1835,76 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T * Right-left text is put in columns 0 - number-col, normal text is put * in columns number-col - window-width. */ - if (has_mbyte) { - int cells; - int u8c, u8cc[MAX_MCO]; - int i; - int idx; - int c_len; - char_u *p; - int prev_c = 0; /* previous Arabic character */ - int prev_c1 = 0; /* first composing char for prev_c */ - - if (wp->w_p_rl) - idx = off; - else - idx = off + col; - - /* Store multibyte characters in ScreenLines[] et al. correctly. */ - for (p = text; *p != NUL; ) { - cells = (*mb_ptr2cells)(p); - c_len = (*mb_ptr2len)(p); - if (col + cells > wp->w_width - - (wp->w_p_rl ? col : 0) - ) - break; - ScreenLines[idx] = *p; - if (enc_utf8) { - u8c = utfc_ptr2char(p, u8cc); - if (*p < 0x80 && u8cc[0] == 0) { - ScreenLinesUC[idx] = 0; - prev_c = u8c; - } else { - if (p_arshape && !p_tbidi && arabic_char(u8c)) { - /* Do Arabic shaping. */ - int pc, pc1, nc; - int pcc[MAX_MCO]; - int firstbyte = *p; + int cells; + int u8c, u8cc[MAX_MCO]; + int idx; + int c_len; + char_u *p; + int prev_c = 0; // previous Arabic character + int prev_c1 = 0; // first composing char for prev_c - /* The idea of what is the previous and next - * character depends on 'rightleft'. */ - if (wp->w_p_rl) { - pc = prev_c; - pc1 = prev_c1; - nc = utf_ptr2char(p + c_len); - prev_c1 = u8cc[0]; - } else { - pc = utfc_ptr2char(p + c_len, pcc); - nc = prev_c; - pc1 = pcc[0]; - } - prev_c = u8c; + if (wp->w_p_rl) { + idx = off; + } else { + idx = off + col; + } - u8c = arabic_shape(u8c, &firstbyte, &u8cc[0], - pc, pc1, nc); - ScreenLines[idx] = firstbyte; - } else - prev_c = u8c; - /* Non-BMP character: display as ? or fullwidth ?. */ - ScreenLinesUC[idx] = u8c; - for (i = 0; i < Screen_mco; ++i) { - ScreenLinesC[i][idx] = u8cc[i]; - if (u8cc[i] == 0) - break; - } + // Store multibyte characters in ScreenLines[] et al. correctly. + for (p = text; *p != NUL; ) { + cells = (*mb_ptr2cells)(p); + c_len = (*mb_ptr2len)(p); + if (col + cells > wp->w_width - (wp->w_p_rl ? col : 0)) { + break; + } + u8c = utfc_ptr2char(p, u8cc); + if (*p < 0x80 && u8cc[0] == 0) { + schar_from_ascii(ScreenLines[idx], *p); + prev_c = u8c; + } else { + if (p_arshape && !p_tbidi && arabic_char(u8c)) { + // Do Arabic shaping. + int pc, pc1, nc; + int pcc[MAX_MCO]; + int firstbyte = *p; + + // The idea of what is the previous and next + // character depends on 'rightleft'. + if (wp->w_p_rl) { + pc = prev_c; + pc1 = prev_c1; + nc = utf_ptr2char(p + c_len); + prev_c1 = u8cc[0]; + } else { + pc = utfc_ptr2char(p + c_len, pcc); + nc = prev_c; + pc1 = pcc[0]; } - if (cells > 1) - ScreenLines[idx + 1] = 0; - } else if (cells > 1) { - // Double-width character. - ScreenLines[idx + 1] = p[1]; + prev_c = u8c; + + u8c = arabic_shape(u8c, &firstbyte, &u8cc[0], pc, pc1, nc); + } else { + prev_c = u8c; } - col += cells; - idx += cells; - p += c_len; + schar_from_cc(ScreenLines[idx], u8c, u8cc); } - } else { - len = (int)STRLEN(text); - if (len > wp->w_width - col) - len = wp->w_width - col; - if (len > 0) { - if (wp->w_p_rl) - STRNCPY(current_ScreenLine, text, len); - else - STRNCPY(current_ScreenLine + col, text, len); - col += len; + if (cells > 1) { + ScreenLines[idx + 1][0] = 0; } + col += cells; + idx += cells; + p += c_len; } /* Fill the rest of the line with the fold filler */ if (wp->w_p_rl) col -= txtcol; + + schar_T sc; + schar_from_char(sc, fill_fold); while (col < wp->w_width - (wp->w_p_rl ? txtcol : 0) ) { - if (enc_utf8) { - if (fill_fold >= 0x80) { - ScreenLinesUC[off + col] = fill_fold; - ScreenLinesC[0][off + col] = 0; - ScreenLines[off + col] = 0x80; // avoid storing zero - } else { - ScreenLinesUC[off + col] = 0; - ScreenLines[off + col] = fill_fold; - } - col++; - } else { - ScreenLines[off + col++] = fill_fold; - } + schar_copy(ScreenLines[off+col++], sc); } if (text != buf) @@ -2059,18 +2005,18 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T } } -/* - * Copy "buf[len]" to ScreenLines["off"] and set attributes to "attr". - */ + +/// Copy "buf[len]" to ScreenLines["off"] and set attributes to "attr". +/// +/// Only works for ASCII text! static void copy_text_attr(int off, char_u *buf, int len, int attr) { int i; - memmove(ScreenLines + off, buf, (size_t)len); - if (enc_utf8) - memset(ScreenLinesUC + off, 0, sizeof(u8char_T) * (size_t)len); - for (i = 0; i < len; ++i) + for (i = 0; i < len; i++) { + schar_from_ascii(ScreenLines[off + i], buf[i]); ScreenAttrs[off + i] = attr; + } } /* @@ -2528,7 +2474,7 @@ win_line ( c = win_lbr_chartabsize(wp, line, ptr, (colnr_T)vcol, NULL); vcol += c; prev_ptr = ptr; - mb_ptr_adv(ptr); + MB_PTR_ADV(ptr); } // When: @@ -3129,13 +3075,6 @@ win_line ( mb_utf8 = true; c = 0xc0; } - } else { - /* if this is a DBCS character, put it in "mb_c" */ - mb_l = MB_BYTE2LEN(c); - if (mb_l >= n_extra) - mb_l = 1; - else if (mb_l > 1) - mb_c = (c << 8) + p_extra[1]; } if (mb_l == 0) /* at the NUL at end-of-line */ mb_l = 1; @@ -3195,8 +3134,9 @@ win_line ( if (utf_iscomposing(mb_c)) { int i; - for (i = Screen_mco - 1; i > 0; --i) + for (i = MAX_MCO - 1; i > 0; i--) { u8cc[i] = u8cc[i - 1]; + } u8cc[0] = mb_c; mb_c = ' '; } @@ -3880,10 +3820,8 @@ win_line ( off += n; col += n; } else { - /* Add a blank character to highlight. */ - ScreenLines[off] = ' '; - if (enc_utf8) - ScreenLinesUC[off] = 0; + // Add a blank character to highlight. + schar_from_ascii(ScreenLines[off], ' '); } if (area_attr == 0) { /* Use attributes from match with highest priority among @@ -3978,13 +3916,11 @@ win_line ( int mc_attr = win_hl_attr(wp, HLF_MC); while (col < wp->w_width) { - ScreenLines[off] = ' '; - if (enc_utf8) - ScreenLinesUC[off] = 0; - ++col; - if (draw_color_col) - draw_color_col = advance_color_col(VCOL_HLC, - &color_cols); + schar_from_ascii(ScreenLines[off], ' '); + col++; + if (draw_color_col) { + draw_color_col = advance_color_col(VCOL_HLC, &color_cols); + } if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol) { ScreenAttrs[off++] = cuc_attr; @@ -4006,10 +3942,7 @@ win_line ( // terminal buffers may need to highlight beyond the end of the // logical line while (col < wp->w_width) { - ScreenLines[off] = ' '; - if (enc_utf8) { - ScreenLinesUC[off] = 0; - } + schar_from_ascii(ScreenLines[off], ' '); ScreenAttrs[off++] = term_attrs[vcol++]; col++; } @@ -4088,20 +4021,10 @@ win_line ( --off; --col; } - ScreenLines[off] = c; if (mb_utf8) { - ScreenLinesUC[off] = mb_c; - if ((c & 0xff) == 0) { - ScreenLines[off] = 0x80; // Avoid storing zero. - } - for (int i = 0; i < Screen_mco; i++) { - ScreenLinesC[i][off] = u8cc[i]; - if (u8cc[i] == 0) { - break; - } - } + schar_from_cc(ScreenLines[off], mb_c, u8cc); } else { - ScreenLinesUC[off] = 0; + schar_from_ascii(ScreenLines[off], c); } if (multi_attr) { ScreenAttrs[off] = multi_attr; @@ -4113,13 +4036,8 @@ win_line ( // Need to fill two screen columns. off++; col++; - if (enc_utf8) { - // UTF-8: Put a 0 in the second screen char. - ScreenLines[off] = 0; - } else { - // DBCS: Put second byte in the second screen char. - ScreenLines[off] = mb_c & 0xff; - } + // UTF-8: Put a 0 in the second screen char. + ScreenLines[off][0] = 0; if (draw_state > WL_NR && filler_todo <= 0) { vcol++; } @@ -4297,12 +4215,11 @@ win_line ( /* When there is a multi-byte character, just output a * space to keep it simple. */ - if (has_mbyte && MB_BYTE2LEN(ScreenLines[LineOffset[ - screen_row - - 1] + (Columns - 1)]) > 1) { + if (ScreenLines[LineOffset[screen_row - 1] + + (Columns - 1)][1] != 0) { ui_putc(' '); } else { - ui_putc(ScreenLines[LineOffset[screen_row - 1] + (Columns - 1)]); + ui_puts(ScreenLines[LineOffset[screen_row - 1] + (Columns - 1)]); } /* force a redraw of the first char on the next line */ ScreenAttrs[LineOffset[screen_row]] = (sattr_T)-1; @@ -4347,23 +4264,6 @@ win_line ( /* - * Return if the composing characters at "off_from" and "off_to" differ. - * Only to be used when ScreenLinesUC[off_from] != 0. - */ -static int comp_char_differs(int off_from, int off_to) -{ - int i; - - for (i = 0; i < Screen_mco; ++i) { - if (ScreenLinesC[i][off_from] != ScreenLinesC[i][off_to]) - return TRUE; - if (ScreenLinesC[i][off_from] == 0) - break; - } - return FALSE; -} - -/* * Check whether the given character needs redrawing: * - the (first byte of the) character is different * - the attributes are different @@ -4373,13 +4273,11 @@ static int comp_char_differs(int off_from, int off_to) static int char_needs_redraw(int off_from, int off_to, int cols) { return (cols > 0 - && (ScreenLines[off_from] != ScreenLines[off_to] - || ScreenAttrs[off_from] != ScreenAttrs[off_to] - || ScreenLinesUC[off_from] != ScreenLinesUC[off_to] - || (ScreenLinesUC[off_from] != 0 - && comp_char_differs(off_from, off_to)) - || (utf_off2cells(off_from, off_from + cols) > 1 - && ScreenLines[off_from + 1] != ScreenLines[off_to + 1]) + && ((schar_cmp(ScreenLines[off_from], ScreenLines[off_to]) + || ScreenAttrs[off_from] != ScreenAttrs[off_to] + || ((*mb_off2cells)(off_from, off_from + cols) > 1 + && schar_cmp(ScreenLines[off_from + 1], + ScreenLines[off_to + 1]))) || p_wd < 0)); } @@ -4427,9 +4325,9 @@ static void screen_line(int row, int coloff, int endcol, if (rlflag) { /* Clear rest first, because it's left of the text. */ if (clear_width > 0) { - while (col <= endcol && ScreenLines[off_to] == ' ' + while (col <= endcol && ScreenLines[off_to][0] == ' ' + && ScreenLines[off_to][1] == NUL && ScreenAttrs[off_to] == bg_attr - && (!enc_utf8 || ScreenLinesUC[off_to] == 0) ) { ++off_to; ++col; @@ -4466,54 +4364,24 @@ static void screen_line(int row, int coloff, int endcol, if (redraw_this) { - if (enc_dbcs != 0) { - /* Check if overwriting a double-byte with a single-byte or - * the other way around requires another character to be - * redrawn. For UTF-8 this isn't needed, because comparing - * ScreenLinesUC[] is sufficient. */ - if (char_cells == 1 - && col + 1 < endcol - && (*mb_off2cells)(off_to, max_off_to) > 1) { - /* Writing a single-cell character over a double-cell - * character: need to redraw the next cell. */ - ScreenLines[off_to + 1] = 0; - redraw_next = TRUE; - } else if (char_cells == 2 - && col + 2 < endcol - && (*mb_off2cells)(off_to, max_off_to) == 1 - && (*mb_off2cells)(off_to + 1, max_off_to) > 1) { - /* Writing the second half of a double-cell character over - * a double-cell character: need to redraw the second - * cell. */ - ScreenLines[off_to + 2] = 0; - redraw_next = TRUE; - } - } - /* When writing a single-width character over a double-width - * character and at the end of the redrawn text, need to clear out - * the right halve of the old character. - * Also required when writing the right halve of a double-width - * char over the left halve of an existing one. */ + // When writing a single-width character over a double-width + // character and at the end of the redrawn text, need to clear out + // the right halve of the old character. + // Also required when writing the right halve of a double-width + // char over the left halve of an existing one if (has_mbyte && col + char_cells == endcol && ((char_cells == 1 && (*mb_off2cells)(off_to, max_off_to) > 1) || (char_cells == 2 && (*mb_off2cells)(off_to, max_off_to) == 1 - && (*mb_off2cells)(off_to + 1, max_off_to) > 1))) - clear_next = TRUE; - - ScreenLines[off_to] = ScreenLines[off_from]; - if (enc_utf8) { - ScreenLinesUC[off_to] = ScreenLinesUC[off_from]; - if (ScreenLinesUC[off_from] != 0) { - int i; + && (*mb_off2cells)(off_to + 1, max_off_to) > 1))) { + clear_next = true; + } - for (i = 0; i < Screen_mco; ++i) - ScreenLinesC[i][off_to] = ScreenLinesC[i][off_from]; - } + schar_copy(ScreenLines[off_to], ScreenLines[off_from]); + if (char_cells == 2) { + schar_copy(ScreenLines[off_to+1], ScreenLines[off_from+1]); } - if (char_cells == 2) - ScreenLines[off_to + 1] = ScreenLines[off_from + 1]; ScreenAttrs[off_to] = ScreenAttrs[off_from]; /* For simplicity set the attributes of second half of a @@ -4521,10 +4389,7 @@ static void screen_line(int row, int coloff, int endcol, if (char_cells == 2) ScreenAttrs[off_to + 1] = ScreenAttrs[off_from]; - if (enc_dbcs != 0 && char_cells == 2) - screen_char_2(off_to, row, col + coloff); - else - screen_char(off_to, row, col + coloff); + screen_char(off_to, row, col + coloff); } off_to += CHAR_CELLS; @@ -4535,20 +4400,15 @@ static void screen_line(int row, int coloff, int endcol, if (clear_next) { /* Clear the second half of a double-wide character of which the left * half was overwritten with a single-wide character. */ - ScreenLines[off_to] = ' '; - if (enc_utf8) - ScreenLinesUC[off_to] = 0; + schar_from_ascii(ScreenLines[off_to], ' '); screen_char(off_to, row, col + coloff); } - if (clear_width > 0 - && !rlflag - ) { - - /* blank out the rest of the line */ - while (col < clear_width && ScreenLines[off_to] == ' ' + if (clear_width > 0 && !rlflag) { + // blank out the rest of the line + while (col < clear_width && ScreenLines[off_to][0] == ' ' + && ScreenLines[off_to][1] == NUL && ScreenAttrs[off_to] == bg_attr - && (!enc_utf8 || ScreenLinesUC[off_to] == 0) ) { ++off_to; ++col; @@ -4564,22 +4424,14 @@ static void screen_line(int row, int coloff, int endcol, if (clear_width > 0) { // For a window that's left of another, draw the separator char. if (col + coloff < Columns && wp->w_vsep_width > 0) { - int c; + int c = fillchar_vsep(wp, &hl); + schar_T sc; + schar_from_char(sc, c); - c = fillchar_vsep(wp, &hl); - if (ScreenLines[off_to] != (schar_T)c - || (enc_utf8 && (int)ScreenLinesUC[off_to] - != (c >= 0x80 ? c : 0)) + if (schar_cmp(ScreenLines[off_to], sc) || ScreenAttrs[off_to] != hl) { - ScreenLines[off_to] = c; + schar_copy(ScreenLines[off_to], sc); ScreenAttrs[off_to] = hl; - if (enc_utf8) { - if (c >= 0x80) { - ScreenLinesUC[off_to] = c; - ScreenLinesC[0][off_to] = 0; - } else - ScreenLinesUC[off_to] = 0; - } screen_char(off_to, row, col + coloff); } } else @@ -4697,7 +4549,7 @@ static int status_match_len(expand_T *xp, char_u *s) while (*s != NUL) { s += skip_status_match_char(xp, s); len += ptr2cells(s); - mb_ptr_adv(s); + MB_PTR_ADV(s); } return len; @@ -5279,6 +5131,50 @@ theend: entered = FALSE; } +// Low-level functions to manipulate invidual character cells on the +// screen grid. + +/// Put a ASCII character in a screen cell. +static void schar_from_ascii(char_u *p, const char c) +{ + p[0] = c; + p[1] = 0; +} + +/// Put a unicode character in a screen cell. +static int schar_from_char(char_u *p, int c) +{ + int len = utf_char2bytes(c, p); + p[len] = NUL; + return len; +} + +/// Put a unicode char, and up to MAX_MCO composing chars, in a screen cell. +static int schar_from_cc(char_u *p, int c, int u8cc[MAX_MCO]) +{ + int len = utf_char2bytes(c, p); + for (int i = 0; i < MAX_MCO; i++) { + if (u8cc[i] == 0) { + break; + } + len += utf_char2bytes(u8cc[i], p + len); + } + p[len] = 0; + return len; +} + +/// compare the contents of two screen cells. +static int schar_cmp(char_u *sc1, char_u *sc2) +{ + return STRNCMP(sc1, sc2, sizeof(schar_T)); +} + +/// copy the contents of screen cell `sc2` into cell `sc1` +static void schar_copy(char_u *sc1, char_u *sc2) +{ + STRLCPY(sc1, sc2, sizeof(schar_T)); +} + /* * Output a single character directly to the screen and update ScreenLines. @@ -5287,12 +5183,7 @@ void screen_putchar(int c, int row, int col, int attr) { char_u buf[MB_MAXBYTES + 1]; - if (has_mbyte) - buf[(*mb_char2bytes)(c, buf)] = NUL; - else { - buf[0] = c; - buf[1] = NUL; - } + buf[(*mb_char2bytes)(c, buf)] = NUL; screen_puts(buf, row, col, attr); } @@ -5308,35 +5199,12 @@ void screen_getbytes(int row, int col, char_u *bytes, int *attrp) if (ScreenLines != NULL && row < screen_Rows && col < screen_Columns) { off = LineOffset[row] + col; *attrp = ScreenAttrs[off]; - bytes[0] = ScreenLines[off]; - bytes[1] = NUL; - - if (ScreenLinesUC[off] != 0) { - bytes[utfc_char2bytes(off, bytes)] = NUL; - } + schar_copy(bytes, ScreenLines[off]); } } /* - * Return TRUE if composing characters for screen posn "off" differs from - * composing characters in "u8cc". - * Only to be used when ScreenLinesUC[off] != 0. - */ -static int screen_comp_differs(int off, int *u8cc) -{ - int i; - - for (i = 0; i < Screen_mco; ++i) { - if (ScreenLinesC[i][off] != (u8char_T)u8cc[i]) - return TRUE; - if (u8cc[i] == 0) - break; - } - return FALSE; -} - -/* * Put string '*text' on the screen at position 'row' and 'col', with * attributes 'attr', and update ScreenLines[] and ScreenAttrs[]. * Note: only outputs within one row, message is truncated at screen boundary! @@ -5370,28 +5238,16 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr) int force_redraw_next = FALSE; int need_redraw; - const bool l_has_mbyte = has_mbyte; - const bool l_enc_utf8 = enc_utf8; - const int l_enc_dbcs = enc_dbcs; - - assert((l_has_mbyte == (l_enc_utf8 || l_enc_dbcs)) - && !(l_enc_utf8 && l_enc_dbcs)); - if (ScreenLines == NULL || row >= screen_Rows) /* safety check */ return; off = LineOffset[row] + col; /* When drawing over the right halve of a double-wide char clear out the * left halve. Only needed in a terminal. */ - if (l_has_mbyte && col > 0 && col < screen_Columns - && mb_fix_col(col, row) != col) { - ScreenLines[off - 1] = ' '; + if (col > 0 && col < screen_Columns && mb_fix_col(col, row) != col) { + schar_from_ascii(ScreenLines[off - 1], ' '); ScreenAttrs[off - 1] = 0; - if (l_enc_utf8) { - ScreenLinesUC[off - 1] = 0; - ScreenLinesC[0][off - 1] = 0; - } - /* redraw the previous cell, make it empty */ + // redraw the previous cell, make it empty screen_char(off - 1, row, col - 1); /* force the cell at "col" to be redrawn */ force_redraw_next = TRUE; @@ -5402,124 +5258,86 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr) && (len < 0 || (int)(ptr - text) < len) && *ptr != NUL) { c = *ptr; - /* check if this is the first byte of a multibyte */ - if (l_has_mbyte) { - if (len > 0) { - mbyte_blen = utfc_ptr2len_len(ptr, (int)((text + len) - ptr)); - } else { - mbyte_blen = utfc_ptr2len(ptr); - } - if (len >= 0) { - u8c = utfc_ptr2char_len(ptr, u8cc, (int)((text + len) - ptr)); - } else { - u8c = utfc_ptr2char(ptr, u8cc); - } - mbyte_cells = utf_char2cells(u8c); - if (p_arshape && !p_tbidi && arabic_char(u8c)) { - // Do Arabic shaping. - if (len >= 0 && (int)(ptr - text) + mbyte_blen >= len) { - // Past end of string to be displayed. - nc = NUL; - nc1 = NUL; - } else { - nc = utfc_ptr2char_len(ptr + mbyte_blen, pcc, - (int)((text + len) - ptr - mbyte_blen)); - nc1 = pcc[0]; - } - pc = prev_c; - prev_c = u8c; - u8c = arabic_shape(u8c, &c, &u8cc[0], nc, nc1, pc); + // check if this is the first byte of a multibyte + if (len > 0) { + mbyte_blen = utfc_ptr2len_len(ptr, (int)((text + len) - ptr)); + } else { + mbyte_blen = utfc_ptr2len(ptr); + } + if (len >= 0) { + u8c = utfc_ptr2char_len(ptr, u8cc, (int)((text + len) - ptr)); + } else { + u8c = utfc_ptr2char(ptr, u8cc); + } + mbyte_cells = utf_char2cells(u8c); + if (p_arshape && !p_tbidi && arabic_char(u8c)) { + // Do Arabic shaping. + if (len >= 0 && (int)(ptr - text) + mbyte_blen >= len) { + // Past end of string to be displayed. + nc = NUL; + nc1 = NUL; } else { - prev_c = u8c; - } - if (col + mbyte_cells > screen_Columns) { - // Only 1 cell left, but character requires 2 cells: - // display a '>' in the last column to avoid wrapping. */ - c = '>'; - mbyte_cells = 1; + nc = utfc_ptr2char_len(ptr + mbyte_blen, pcc, + (int)((text + len) - ptr - mbyte_blen)); + nc1 = pcc[0]; } + pc = prev_c; + prev_c = u8c; + u8c = arabic_shape(u8c, &c, &u8cc[0], nc, nc1, pc); + } else { + prev_c = u8c; } + if (col + mbyte_cells > screen_Columns) { + // Only 1 cell left, but character requires 2 cells: + // display a '>' in the last column to avoid wrapping. */ + c = '>'; + mbyte_cells = 1; + } + + schar_T buf; + schar_from_cc(buf, u8c, u8cc); + force_redraw_this = force_redraw_next; force_redraw_next = FALSE; - need_redraw = ScreenLines[off] != c - || (mbyte_cells == 2 && ScreenLines[off + 1] != 0) - || (ScreenLinesUC[off] != - (u8char_T)(c < 0x80 && u8cc[0] == 0 ? 0 : u8c) - || (ScreenLinesUC[off] != 0 - && screen_comp_differs(off, u8cc))) + need_redraw = schar_cmp(ScreenLines[off], buf) + || (mbyte_cells == 2 && ScreenLines[off + 1][0] != 0) || ScreenAttrs[off] != attr || exmode_active; - if (need_redraw - || force_redraw_this - ) { - /* When at the end of the text and overwriting a two-cell - * character with a one-cell character, need to clear the next - * cell. Also when overwriting the left halve of a two-cell char - * with the right halve of a two-cell char. Do this only once - * (mb_off2cells() may return 2 on the right halve). */ - if (clear_next_cell) - clear_next_cell = FALSE; - else if (l_has_mbyte - && (len < 0 ? ptr[mbyte_blen] == NUL - : ptr + mbyte_blen >= text + len) - && ((mbyte_cells == 1 && (*mb_off2cells)(off, max_off) > 1) - || (mbyte_cells == 2 - && (*mb_off2cells)(off, max_off) == 1 - && (*mb_off2cells)(off + 1, max_off) > 1))) - clear_next_cell = TRUE; - - /* Make sure we never leave a second byte of a double-byte behind, - * it confuses mb_off2cells(). */ - if (l_enc_dbcs - && ((mbyte_cells == 1 && (*mb_off2cells)(off, max_off) > 1) - || (mbyte_cells == 2 - && (*mb_off2cells)(off, max_off) == 1 - && (*mb_off2cells)(off + 1, max_off) > 1))) - ScreenLines[off + mbyte_blen] = 0; - ScreenLines[off] = c; - ScreenAttrs[off] = attr; - if (l_enc_utf8) { - if (c < 0x80 && u8cc[0] == 0) - ScreenLinesUC[off] = 0; - else { - int i; + if (need_redraw || force_redraw_this) { + // When at the end of the text and overwriting a two-cell + // character with a one-cell character, need to clear the next + // cell. Also when overwriting the left halve of a two-cell char + // with the right halve of a two-cell char. Do this only once + // (mb_off2cells() may return 2 on the right halve). + if (clear_next_cell) { + clear_next_cell = false; + } else if ((len < 0 ? ptr[mbyte_blen] == NUL + : ptr + mbyte_blen >= text + len) + && ((mbyte_cells == 1 && (*mb_off2cells)(off, max_off) > 1) + || (mbyte_cells == 2 + && (*mb_off2cells)(off, max_off) == 1 + && (*mb_off2cells)(off + 1, max_off) > 1))) { + clear_next_cell = true; + } - ScreenLinesUC[off] = u8c; - for (i = 0; i < Screen_mco; ++i) { - ScreenLinesC[i][off] = u8cc[i]; - if (u8cc[i] == 0) - break; - } - } - if (mbyte_cells == 2) { - ScreenLines[off + 1] = 0; - ScreenAttrs[off + 1] = attr; - } - screen_char(off, row, col); - } else if (mbyte_cells == 2) { - ScreenLines[off + 1] = ptr[1]; + schar_copy(ScreenLines[off], buf); + ScreenAttrs[off] = attr; + if (mbyte_cells == 2) { + ScreenLines[off + 1][0] = 0; ScreenAttrs[off + 1] = attr; - screen_char_2(off, row, col); - } else { - screen_char(off, row, col); } + screen_char(off, row, col); } - if (l_has_mbyte) { - off += mbyte_cells; - col += mbyte_cells; - ptr += mbyte_blen; - if (clear_next_cell) { - // This only happens at the end, display one space next. - ptr = (char_u *)" "; - len = -1; - } - } else { - ++off; - ++col; - ++ptr; + off += mbyte_cells; + col += mbyte_cells; + ptr += mbyte_blen; + if (clear_next_cell) { + // This only happens at the end, display one space next. + ptr = (char_u *)" "; + len = -1; } } @@ -5878,40 +5696,7 @@ static void screen_char(unsigned off, int row, int col) ui_cursor_goto(row, col); ui_set_highlight(ScreenAttrs[off]); - if (ScreenLinesUC[off] != 0) { - char_u buf[MB_MAXBYTES + 1]; - - // Convert UTF-8 character to bytes and write it. - buf[utfc_char2bytes(off, buf)] = NUL; - ui_puts(buf); - } else { - ui_putc(ScreenLines[off]); - } -} - -/* - * Used for enc_dbcs only: Put one double-wide character at ScreenLines["off"] - * on the screen at position 'row' and 'col'. - * The attributes of the first byte is used for all. This is required to - * output the two bytes of a double-byte character with nothing in between. - */ -static void screen_char_2(unsigned off, int row, int col) -{ - /* Check for illegal values (could be wrong when screen was resized). */ - if (off + 1 >= (unsigned)(screen_Rows * screen_Columns)) - return; - - /* Outputting the last character on the screen may scrollup the screen. - * Don't to it! Mark the character invalid (update it when scrolled up) */ - if (row == screen_Rows - 1 && col >= screen_Columns - 2) { - ScreenAttrs[off] = (sattr_T)-1; - return; - } - - /* Output the first byte normally (positions the cursor), then write the - * second byte directly. */ - screen_char(off, row, col); - ui_putc(ScreenLines[off + 1]); + ui_puts(ScreenLines[off]); } /* @@ -5927,6 +5712,7 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1, int end_off; int did_delete; int c; + schar_T sc; if (end_row > screen_Rows) /* safety check */ end_row = screen_Rows; @@ -5970,25 +5756,19 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1, off = LineOffset[row] + col; end_off = LineOffset[row] + end_col; - /* skip blanks (used often, keep it fast!) */ - if (enc_utf8) - while (off < end_off && ScreenLines[off] == ' ' - && ScreenAttrs[off] == 0 && ScreenLinesUC[off] == 0) - ++off; - else - while (off < end_off && ScreenLines[off] == ' ' - && ScreenAttrs[off] == 0) - ++off; - if (off < end_off) { /* something to be cleared */ + // skip blanks (used often, keep it fast!) + while (off < end_off && ScreenLines[off][0] == ' ' + && ScreenLines[off][1] == 0 && ScreenAttrs[off] == 0) { + off++; + } + if (off < end_off) { // something to be cleared col = off - LineOffset[row]; ui_clear_highlight(); ui_cursor_goto(row, col); // clear rest of this screen line ui_call_eol_clear(); col = end_col - col; - while (col--) { /* clear chars in ScreenLines */ - ScreenLines[off] = ' '; - if (enc_utf8) - ScreenLinesUC[off] = 0; + while (col--) { // clear chars in ScreenLines + schar_from_ascii(ScreenLines[off], ' '); ScreenAttrs[off] = 0; ++off; } @@ -5998,20 +5778,10 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1, off = LineOffset[row] + start_col; c = c1; - for (col = start_col; col < end_col; ++col) { - if (ScreenLines[off] != c - || (enc_utf8 && (int)ScreenLinesUC[off] - != (c >= 0x80 ? c : 0)) - || ScreenAttrs[off] != attr - ) { - ScreenLines[off] = c; - if (enc_utf8) { - if (c >= 0x80) { - ScreenLinesUC[off] = c; - ScreenLinesC[0][off] = 0; - } else - ScreenLinesUC[off] = 0; - } + schar_from_char(sc, c); + for (col = start_col; col < end_col; col++) { + if (schar_cmp(ScreenLines[off], sc) || ScreenAttrs[off] != attr) { + schar_copy(ScreenLines[off], sc); ScreenAttrs[off] = attr; if (!did_delete || c != ' ') screen_char(off, row, col); @@ -6021,6 +5791,7 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1, if (did_delete) break; c = c2; + schar_from_char(sc, c); } } if (end_col == Columns) @@ -6089,8 +5860,7 @@ retry: if ((ScreenLines != NULL && Rows == screen_Rows && Columns == screen_Columns - && ScreenLinesUC != NULL - && p_mco == Screen_mco) + ) || Rows == 0 || Columns == 0 || (!full_screen && ScreenLines == NULL)) { @@ -6133,18 +5903,11 @@ retry: if (aucmd_win != NULL) win_free_lsize(aucmd_win); - schar_T *new_ScreenLines = xmalloc( - (size_t)((Rows + 1) * Columns * sizeof(*new_ScreenLines))); - u8char_T *new_ScreenLinesC[MAX_MCO]; - memset(new_ScreenLinesC, 0, sizeof(new_ScreenLinesC)); - u8char_T *new_ScreenLinesUC = xmalloc( - (size_t)((Rows + 1) * Columns * sizeof(*new_ScreenLinesUC))); - for (int i = 0; i < p_mco; i++) { - new_ScreenLinesC[i] = xcalloc( - (size_t)((Rows + 1) * Columns), sizeof(new_ScreenLinesC[0][0])); - } - sattr_T *new_ScreenAttrs = xmalloc( - (size_t)((Rows + 1) * Columns * sizeof(*new_ScreenAttrs))); + // Allocate space for an extra row as scratch space, so that a redrawn + // line can be compared with the previous screen line state. + size_t ncells = (size_t)((Rows+1) * Columns); + schar_T *new_ScreenLines = xmalloc(ncells * sizeof(*new_ScreenLines)); + sattr_T *new_ScreenAttrs = xmalloc(ncells * sizeof(*new_ScreenAttrs)); unsigned *new_LineOffset = xmalloc((size_t)(Rows * sizeof(*new_LineOffset))); char_u *new_LineWraps = xmalloc((size_t)(Rows * sizeof(*new_LineWraps))); StlClickDefinition *new_tab_page_click_defs = xcalloc( @@ -6157,13 +5920,11 @@ retry: win_alloc_lines(aucmd_win); } - int i; - for (i = 0; i < p_mco; i++) { - if (new_ScreenLinesC[i] == NULL) { - break; - } - } - if (i != p_mco) { + if (new_ScreenLines == NULL + || new_ScreenAttrs == NULL + || new_LineOffset == NULL + || new_LineWraps == NULL + || new_tab_page_click_defs == NULL) { if (ScreenLines != NULL || !done_outofmem_msg) { // Guess the size. do_outofmem_msg((Rows + 1) * Columns); @@ -6174,12 +5935,6 @@ retry: } xfree(new_ScreenLines); new_ScreenLines = NULL; - xfree(new_ScreenLinesUC); - new_ScreenLinesUC = NULL; - for (i = 0; i < p_mco; ++i) { - xfree(new_ScreenLinesC[i]); - new_ScreenLinesC[i] = NULL; - } xfree(new_ScreenAttrs); new_ScreenAttrs = NULL; xfree(new_LineOffset); @@ -6202,39 +5957,21 @@ retry: * executing an external command, for the GUI). */ if (!doclear) { - memset(new_ScreenLines + new_row * Columns, - ' ', (size_t)Columns * sizeof(new_ScreenLines[0])); - memset(new_ScreenLinesUC + new_row * Columns, - 0, (size_t)Columns * sizeof(new_ScreenLinesUC[0])); - for (i = 0; i < p_mco; i++) { - memset(new_ScreenLinesC[i] + new_row * Columns, - 0, (size_t)Columns * sizeof(new_ScreenLinesC[0][0])); + for (int col = 0; col < Columns; col++) { + schar_from_ascii(new_ScreenLines[new_row * Columns + col], ' '); } memset(new_ScreenAttrs + new_row * Columns, - 0, (size_t)Columns * sizeof(new_ScreenAttrs[0])); + 0, (size_t)Columns * sizeof(*new_ScreenAttrs)); old_row = new_row + (screen_Rows - Rows); if (old_row >= 0 && ScreenLines != NULL) { if (screen_Columns < Columns) len = screen_Columns; else len = Columns; - // When switching to utf-8 don't copy characters, they - // may be invalid now. Also when p_mco changes. - if (ScreenLinesUC != NULL && p_mco == Screen_mco) { - memmove(new_ScreenLines + new_LineOffset[new_row], - ScreenLines + LineOffset[old_row], - (size_t)len * sizeof(new_ScreenLines[0])); - } - if (ScreenLinesUC != NULL && p_mco == Screen_mco) { - memmove(new_ScreenLinesUC + new_LineOffset[new_row], - ScreenLinesUC + LineOffset[old_row], - (size_t)len * sizeof(new_ScreenLinesUC[0])); - for (i = 0; i < p_mco; i++) { - memmove(new_ScreenLinesC[i] + new_LineOffset[new_row], - ScreenLinesC[i] + LineOffset[old_row], - (size_t)len * sizeof(new_ScreenLinesC[0][0])); - } - } + + memmove(new_ScreenLines + new_LineOffset[new_row], + ScreenLines + LineOffset[old_row], + (size_t)len * sizeof(schar_T)); memmove(new_ScreenAttrs + new_LineOffset[new_row], ScreenAttrs + LineOffset[old_row], (size_t)len * sizeof(new_ScreenAttrs[0])); @@ -6248,10 +5985,6 @@ retry: free_screenlines(); ScreenLines = new_ScreenLines; - ScreenLinesUC = new_ScreenLinesUC; - for (i = 0; i < p_mco; ++i) - ScreenLinesC[i] = new_ScreenLinesC[i]; - Screen_mco = p_mco; ScreenAttrs = new_ScreenAttrs; LineOffset = new_LineOffset; LineWraps = new_LineWraps; @@ -6285,10 +6018,6 @@ retry: void free_screenlines(void) { - xfree(ScreenLinesUC); - for (int i = 0; i < Screen_mco; i++) { - xfree(ScreenLinesC[i]); - } xfree(ScreenLines); xfree(ScreenAttrs); xfree(LineOffset); @@ -6360,10 +6089,9 @@ static void screenclear2(void) */ static void lineclear(unsigned off, int width) { - (void)memset(ScreenLines + off, ' ', (size_t)width * sizeof(schar_T)); - if (enc_utf8) - (void)memset(ScreenLinesUC + off, 0, - (size_t)width * sizeof(u8char_T)); + for (int col = 0; col < width; col++) { + schar_from_ascii(ScreenLines[off + col], ' '); + } (void)memset(ScreenAttrs + off, 0, (size_t)width * sizeof(sattr_T)); } @@ -6376,15 +6104,7 @@ static void linecopy(int to, int from, win_T *wp) const unsigned off_from = LineOffset[from] + wp->w_wincol; memmove(ScreenLines + off_to, ScreenLines + off_from, - wp->w_width * sizeof(ScreenLines[0])); - - memmove(ScreenLinesUC + off_to, ScreenLinesUC + off_from, - wp->w_width * sizeof(ScreenLinesUC[0])); - for (int i = 0; i < p_mco; i++) { - memmove(ScreenLinesC[i] + off_to, ScreenLinesC[i] + off_from, - wp->w_width * sizeof(ScreenLinesC[0])); - } - + wp->w_width * sizeof(schar_T)); memmove(ScreenAttrs + off_to, ScreenAttrs + off_from, wp->w_width * sizeof(ScreenAttrs[0])); } @@ -6951,7 +6671,7 @@ static void draw_tabline(void) if (has_mbyte) while (len > room) { len -= ptr2cells(p); - mb_ptr_adv(p); + MB_PTR_ADV(p); } else if (len > room) { p += len - room; diff --git a/src/nvim/search.c b/src/nvim/search.c index cb59eb6d04..6c974850ac 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -3239,35 +3239,41 @@ static int in_html_tag(int end_tag) /* We search forward until the cursor, because searching backwards is * very slow for DBCS encodings. */ - for (p = line; p < line + curwin->w_cursor.col; mb_ptr_adv(p)) + for (p = line; p < line + curwin->w_cursor.col; MB_PTR_ADV(p)) { if (*p == '>' || *p == '<') { lc = *p; lp = p; } - if (*p != '<') { /* check for '<' under cursor */ - if (lc != '<') - return FALSE; + } + if (*p != '<') { // check for '<' under cursor + if (lc != '<') { + return false; + } p = lp; } } else { for (p = line + curwin->w_cursor.col; p > line; ) { - if (*p == '<') /* find '<' under/before cursor */ + if (*p == '<') { // find '<' under/before cursor break; - mb_ptr_back(line, p); - if (*p == '>') /* find '>' before cursor */ + } + MB_PTR_BACK(line, p); + if (*p == '>') { // find '>' before cursor break; + } + } + if (*p != '<') { + return false; } - if (*p != '<') - return FALSE; } pos.lnum = curwin->w_cursor.lnum; pos.col = (colnr_T)(p - line); - mb_ptr_adv(p); - if (end_tag) - /* check that there is a '/' after the '<' */ + MB_PTR_ADV(p); + if (end_tag) { + // check that there is a '/' after the '<' return *p == '/'; + } /* check that there is no '/' after the '<' */ if (*p == '/') @@ -3371,8 +3377,10 @@ again: */ inc_cursor(); p = get_cursor_pos_ptr(); - for (cp = p; *cp != NUL && *cp != '>' && !ascii_iswhite(*cp); mb_ptr_adv(cp)) - ; + for (cp = p; + *cp != NUL && *cp != '>' && !ascii_iswhite(*cp); + MB_PTR_ADV(cp)) { + } len = (int)(cp - p); if (len == 0) { curwin->w_cursor = old_pos; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 0db1578e8d..0714eb3137 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -395,7 +395,7 @@ size_t spell_check( mi.mi_fend = ptr; if (spell_iswordp(mi.mi_fend, wp)) { do { - mb_ptr_adv(mi.mi_fend); + MB_PTR_ADV(mi.mi_fend); } while (*mi.mi_fend != NUL && spell_iswordp(mi.mi_fend, wp)); if (capcol != NULL && *capcol == 0 && wp->w_s->b_cap_prog != NULL) { @@ -422,7 +422,7 @@ size_t spell_check( // case-fold the word with one non-word character, so that we can check // for the word end. if (*mi.mi_fend != NUL) { - mb_ptr_adv(mi.mi_fend); + MB_PTR_ADV(mi.mi_fend); } (void)spell_casefold(ptr, (int)(mi.mi_fend - ptr), mi.mi_fword, MAXWLEN + 1); @@ -499,7 +499,7 @@ size_t spell_check( } else if (mi.mi_end == ptr) { // Always include at least one character. Required for when there // is a mixup in "midword". - mb_ptr_adv(mi.mi_end); + MB_PTR_ADV(mi.mi_end); } else if (mi.mi_result == SP_BAD && LANGP_ENTRY(wp->w_s->b_langp, 0)->lp_slang->sl_nobreak) { char_u *p, *fp; @@ -512,8 +512,8 @@ size_t spell_check( p = mi.mi_word; fp = mi.mi_fword; for (;;) { - mb_ptr_adv(p); - mb_ptr_adv(fp); + MB_PTR_ADV(p); + MB_PTR_ADV(fp); if (p >= mi.mi_end) { break; } @@ -706,8 +706,9 @@ static void find_word(matchinf_T *mip, int mode) // case-folded word is equal to the keep-case word. p = mip->mi_word; if (STRNCMP(ptr, p, wlen) != 0) { - for (char_u *s = ptr; s < ptr + wlen; mb_ptr_adv(s)) - mb_ptr_adv(p); + for (char_u *s = ptr; s < ptr + wlen; MB_PTR_ADV(s)) { + MB_PTR_ADV(p); + } wlen = (int)(p - mip->mi_word); } } @@ -814,10 +815,12 @@ static void find_word(matchinf_T *mip, int mode) mip->mi_compoff) != 0) { // case folding may have changed the length p = mip->mi_word; - for (char_u *s = ptr; s < ptr + mip->mi_compoff; mb_ptr_adv(s)) - mb_ptr_adv(p); - } else + for (char_u *s = ptr; s < ptr + mip->mi_compoff; MB_PTR_ADV(s)) { + MB_PTR_ADV(p); + } + } else { p = mip->mi_word + mip->mi_compoff; + } capflags = captype(p, mip->mi_word + wlen); if (capflags == WF_KEEPCAP || (capflags == WF_ALLCAP && (flags & WF_FIXCAP) != 0)) @@ -828,12 +831,13 @@ static void find_word(matchinf_T *mip, int mode) // character we do not accept a Onecap word. We do // accept a no-caps word, even when the dictionary // word specifies ONECAP. - mb_ptr_back(mip->mi_word, p); + MB_PTR_BACK(mip->mi_word, p); if (spell_iswordp_nmw(p, mip->mi_win) ? capflags == WF_ONECAP : (flags & WF_ONECAP) != 0 - && capflags != WF_ONECAP) + && capflags != WF_ONECAP) { continue; + } } } @@ -888,8 +892,9 @@ static void find_word(matchinf_T *mip, int mode) // the case-folded word is equal to the keep-case word. p = mip->mi_fword; if (STRNCMP(ptr, p, wlen) != 0) { - for (char_u *s = ptr; s < ptr + wlen; mb_ptr_adv(s)) - mb_ptr_adv(p); + for (char_u *s = ptr; s < ptr + wlen; MB_PTR_ADV(s)) { + MB_PTR_ADV(p); + } mip->mi_compoff = (int)(p - mip->mi_fword); } } @@ -1288,12 +1293,13 @@ static int fold_more(matchinf_T *mip) p = mip->mi_fend; do { - mb_ptr_adv(mip->mi_fend); + MB_PTR_ADV(mip->mi_fend); } while (*mip->mi_fend != NUL && spell_iswordp(mip->mi_fend, mip->mi_win)); // Include the non-word character so that we can check for the word end. - if (*mip->mi_fend != NUL) - mb_ptr_adv(mip->mi_fend); + if (*mip->mi_fend != NUL) { + MB_PTR_ADV(mip->mi_fend); + } (void)spell_casefold(p, (int)(mip->mi_fend - p), mip->mi_fword + mip->mi_fwordlen, @@ -2312,9 +2318,11 @@ int captype(char_u *word, char_u *end) bool past_second = false; // past second word char // find first letter - for (p = word; !spell_iswordp_nmw(p, curwin); mb_ptr_adv(p)) - if (end == NULL ? *p == NUL : p >= end) + for (p = word; !spell_iswordp_nmw(p, curwin); MB_PTR_ADV(p)) { + if (end == NULL ? *p == NUL : p >= end) { return 0; // only non-word characters, illegal word + } + } if (has_mbyte) { c = mb_ptr2char_adv((const char_u **)&p); } else { @@ -2324,19 +2332,22 @@ int captype(char_u *word, char_u *end) // Need to check all letters to find a word with mixed upper/lower. // But a word with an upper char only at start is a ONECAP. - for (; end == NULL ? *p != NUL : p < end; mb_ptr_adv(p)) + for (; end == NULL ? *p != NUL : p < end; MB_PTR_ADV(p)) { if (spell_iswordp_nmw(p, curwin)) { c = PTR2CHAR(p); if (!SPELL_ISUPPER(c)) { // UUl -> KEEPCAP - if (past_second && allcap) + if (past_second && allcap) { return WF_KEEPCAP; + } allcap = false; - } else if (!allcap) + } else if (!allcap) { // UlU -> KEEPCAP return WF_KEEPCAP; + } past_second = true; } + } if (allcap) return WF_ALLCAP; @@ -2360,7 +2371,7 @@ static int badword_captype(char_u *word, char_u *end) // Count the number of UPPER and lower case letters. l = u = 0; first = false; - for (p = word; p < end; mb_ptr_adv(p)) { + for (p = word; p < end; MB_PTR_ADV(p)) { c = PTR2CHAR(p); if (SPELL_ISUPPER(c)) { ++u; @@ -2764,11 +2775,12 @@ void spell_suggest(int count) return; } badlen = (int)curwin->w_cursor.col - (int)VIsual.col; - if (badlen < 0) + if (badlen < 0) { badlen = -badlen; - else + } else { curwin->w_cursor.col = VIsual.col; - ++badlen; + } + badlen++; end_visual_mode(); } else // Find the start of the badly spelled word. @@ -2780,11 +2792,13 @@ void spell_suggest(int count) line = get_cursor_line_ptr(); p = line + curwin->w_cursor.col; // Backup to before start of word. - while (p > line && spell_iswordp_nmw(p, curwin)) - mb_ptr_back(line, p); + while (p > line && spell_iswordp_nmw(p, curwin)) { + MB_PTR_BACK(line, p); + } // Forward to start of word. - while (*p != NUL && !spell_iswordp_nmw(p, curwin)) - mb_ptr_adv(p); + while (*p != NUL && !spell_iswordp_nmw(p, curwin)) { + MB_PTR_ADV(p); + } if (!spell_iswordp_nmw(p, curwin)) { // No word found. beep_flush(); @@ -2973,8 +2987,9 @@ static bool check_need_cap(linenr_T lnum, colnr_T col) endcol = (colnr_T)STRLEN(line); } } - } else + } else { endcol = col; + } if (endcol > 0) { // Check if sentence ends before the bad word. @@ -2982,9 +2997,10 @@ static bool check_need_cap(linenr_T lnum, colnr_T col) regmatch.rm_ic = FALSE; p = line + endcol; for (;; ) { - mb_ptr_back(line, p); - if (p == line || spell_iswordp_nmw(p, curwin)) + MB_PTR_BACK(line, p); + if (p == line || spell_iswordp_nmw(p, curwin)) { break; + } if (vim_regexec(®match, p, 0) && regmatch.endp[0] == line + endcol) { need_cap = true; @@ -3861,7 +3877,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // Get pointer to last char of previous word. p = preword + sp->ts_prewordlen; - mb_ptr_back(preword, p); + MB_PTR_BACK(preword, p); } } @@ -3947,12 +3963,13 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // Give a penalty when changing non-word char to word // char, e.g., "thes," -> "these". p = fword + sp->ts_fidx; - mb_ptr_back(fword, p); + MB_PTR_BACK(fword, p); if (!spell_iswordp(p, curwin)) { p = preword + STRLEN(preword); - mb_ptr_back(preword, p); - if (spell_iswordp(p, curwin)) + MB_PTR_BACK(preword, p); + if (spell_iswordp(p, curwin)) { newscore += SCORE_NONWORD; + } } // Give a bonus to words seen before. @@ -4301,10 +4318,10 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // to the score. Also for the soundfold // tree (might seem illogical but does // give better scores). - mb_ptr_back(tword, p); - if (c == mb_ptr2char(p)) - sp->ts_score -= SCORE_INS - - SCORE_INSDUP; + MB_PTR_BACK(tword, p); + if (c == mb_ptr2char(p)) { + sp->ts_score -= SCORE_INS - SCORE_INSDUP; + } } } @@ -4894,10 +4911,12 @@ static int nofold_len(char_u *fword, int flen, char_u *word) char_u *p; int i = 0; - for (p = fword; p < fword + flen; mb_ptr_adv(p)) - ++i; - for (p = word; i > 0; mb_ptr_adv(p)) - --i; + for (p = fword; p < fword + flen; MB_PTR_ADV(p)) { + i++; + } + for (p = word; i > 0; MB_PTR_ADV(p)) { + i--; + } return (int)(p - word); } @@ -5630,8 +5649,8 @@ add_suggestion ( badlen = (int)(pbad - su->su_badptr); if (goodlen <= 0 || badlen <= 0) break; - mb_ptr_back(goodword, pgood); - mb_ptr_back(su->su_badptr, pbad); + MB_PTR_BACK(goodword, pgood); + MB_PTR_BACK(su->su_badptr, pbad); if (has_mbyte) { if (mb_ptr2char(pgood) != mb_ptr2char(pbad)) break; @@ -7517,8 +7536,9 @@ char_u *spell_to_word_end(char_u *start, win_T *win) { char_u *p = start; - while (*p != NUL && spell_iswordp(p, win)) - mb_ptr_adv(p); + while (*p != NUL && spell_iswordp(p, win)) { + MB_PTR_ADV(p); + } return p; } @@ -7533,23 +7553,26 @@ int spell_word_start(int startcol) char_u *p; int col = 0; - if (no_spell_checking(curwin)) + if (no_spell_checking(curwin)) { return startcol; + } // Find a word character before "startcol". line = get_cursor_line_ptr(); for (p = line + startcol; p > line; ) { - mb_ptr_back(line, p); - if (spell_iswordp_nmw(p, curwin)) + MB_PTR_BACK(line, p); + if (spell_iswordp_nmw(p, curwin)) { break; + } } // Go back to start of the word. while (p > line) { col = (int)(p - line); - mb_ptr_back(line, p); - if (!spell_iswordp(p, curwin)) + MB_PTR_BACK(line, p); + if (!spell_iswordp(p, curwin)) { break; + } col = 0; } diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index b844fd9ab8..69fa95107e 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -1507,9 +1507,10 @@ static int set_sofo(slang_T *lp, char_u *from, char_u *to) // sl_sal_first[] for this. for (p = from, s = to; *p != NUL && *s != NUL; ) { c = mb_cptr2char_adv((const char_u **)&p); - mb_cptr_adv(s); - if (c >= 256) - ++lp->sl_sal_first[c & 0xff]; + MB_CPTR_ADV(s); + if (c >= 256) { + lp->sl_sal_first[c & 0xff]++; + } } if (*p != NUL || *s != NUL) // lengths differ return SP_FORMERROR; @@ -2427,7 +2428,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) || PTR2CHAR(aff_entry->ae_cond) == c)) { p = aff_entry->ae_add + STRLEN(aff_entry->ae_add); - mb_ptr_back(aff_entry->ae_add, p); + MB_PTR_BACK(aff_entry->ae_add, p); if (PTR2CHAR(p) == c_up) { upper = true; aff_entry->ae_chop = NULL; @@ -2528,12 +2529,16 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) if (items[0][3] == 'S' ? do_repsal : do_rep) { // Replace underscore with space (can't include a space // directly). - for (p = items[1]; *p != NUL; mb_ptr_adv(p)) - if (*p == '_') + for (p = items[1]; *p != NUL; MB_PTR_ADV(p)) { + if (*p == '_') { *p = ' '; - for (p = items[2]; *p != NUL; mb_ptr_adv(p)) - if (*p == '_') + } + } + for (p = items[2]; *p != NUL; MB_PTR_ADV(p)) { + if (*p == '_') { *p = ' '; + } + } add_fromto(spin, items[0][3] == 'S' ? &spin->si_repsal : &spin->si_rep, items[1], items[2]); @@ -3070,10 +3075,10 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile) // Truncate the word at the "/", set "afflist" to what follows. // Replace "\/" by "/" and "\\" by "\". afflist = NULL; - for (p = w; *p != NUL; mb_ptr_adv(p)) { - if (*p == '\\' && (p[1] == '\\' || p[1] == '/')) + for (p = w; *p != NUL; MB_PTR_ADV(p)) { + if (*p == '\\' && (p[1] == '\\' || p[1] == '/')) { STRMOVE(p, p + 1); - else if (*p == '/') { + } else if (*p == '/') { *p = NUL; afflist = p + 1; break; @@ -3343,19 +3348,22 @@ store_aff_word ( // Match. Remove the chop and add the affix. if (xht == NULL) { // prefix: chop/add at the start of the word - if (ae->ae_add == NULL) + if (ae->ae_add == NULL) { *newword = NUL; - else + } else { STRLCPY(newword, ae->ae_add, MAXWLEN); + } p = word; if (ae->ae_chop != NULL) { // Skip chop string. if (has_mbyte) { i = mb_charlen(ae->ae_chop); - for (; i > 0; --i) - mb_ptr_adv(p); - } else + for (; i > 0; i--) { + MB_PTR_ADV(p); + } + } else { p += STRLEN(ae->ae_chop); + } } STRCAT(newword, p); } else { @@ -3365,8 +3373,9 @@ store_aff_word ( // Remove chop string. p = newword + STRLEN(newword); i = (int)MB_CHARLEN(ae->ae_chop); - for (; i > 0; --i) - mb_ptr_back(newword, p); + for (; i > 0; i--) { + MB_PTR_BACK(newword, p); + } *p = NUL; } if (ae->ae_add != NULL) diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 52677738e8..3f31914c03 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -207,7 +207,7 @@ char_u *vim_strsave_shellescape(const char_u *string, /* First count the number of extra bytes required. */ size_t length = STRLEN(string) + 3; // two quotes and a trailing NUL - for (const char_u *p = string; *p != NUL; mb_ptr_adv(p)) { + for (const char_u *p = string; *p != NUL; MB_PTR_ADV(p)) { #ifdef WIN32 if (!p_ssl) { if (*p == '"') { @@ -469,7 +469,7 @@ char_u *vim_strrchr(const char_u *string, int c) while (*p) { if (*p == c) retval = p; - mb_ptr_adv(p); + MB_PTR_ADV(p); } return (char_u *) retval; } diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 68f0422f7d..b8cbf3b2a8 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -2824,11 +2824,12 @@ syn_add_end_off ( base = ml_get_buf(syn_buf, result->lnum, FALSE); p = base + col; if (off > 0) { - while (off-- > 0 && *p != NUL) - mb_ptr_adv(p); + while (off-- > 0 && *p != NUL) { + MB_PTR_ADV(p); + } } else { while (off++ < 0 && base < p) { - mb_ptr_back(base, p); + MB_PTR_BACK(base, p); } } col = (int)(p - base); @@ -2873,11 +2874,11 @@ syn_add_start_off ( p = base + col; if (off > 0) { while (off-- && *p != NUL) { - mb_ptr_adv(p); + MB_PTR_ADV(p); } } else { while (off++ && base < p) { - mb_ptr_back(base, p); + MB_PTR_BACK(base, p); } } col = (int)(p - base); diff --git a/src/nvim/undo.c b/src/nvim/undo.c index c5ec077d01..3025e01439 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -703,7 +703,7 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading) if (has_directory) { if (munged_name == NULL) { munged_name = xstrdup(ffname); - for (char *p = munged_name; *p != NUL; mb_ptr_adv(p)) { + for (char *p = munged_name; *p != NUL; MB_PTR_ADV(p)) { if (vim_ispathsep(*p)) { *p = '%'; } diff --git a/src/nvim/window.c b/src/nvim/window.c index ebde81bca6..274bf72f3b 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5101,11 +5101,13 @@ file_name_in_line ( * search forward for what could be the start of a file name */ ptr = line + col; - while (*ptr != NUL && !vim_isfilec(*ptr)) - mb_ptr_adv(ptr); - if (*ptr == NUL) { /* nothing found */ - if (options & FNAME_MESS) + while (*ptr != NUL && !vim_isfilec(*ptr)) { + MB_PTR_ADV(ptr); + } + if (*ptr == NUL) { // nothing found + if (options & FNAME_MESS) { EMSG(_("E446: No file name under cursor")); + } return NULL; } diff --git a/test/functional/options/fillchars_spec.lua b/test/functional/options/fillchars_spec.lua new file mode 100644 index 0000000000..ab61935d4c --- /dev/null +++ b/test/functional/options/fillchars_spec.lua @@ -0,0 +1,66 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local clear, command = helpers.clear, helpers.command +local eval = helpers.eval +local eq = helpers.eq +local exc_exec = helpers.exc_exec + +describe("'fillchars'", function() + local screen + + before_each(function() + clear() + screen = Screen.new(25, 5) + screen:attach() + end) + + after_each(function() + screen:detach() + end) + + local function shouldfail(val,errval) + errval = errval or val + eq('Vim(set):E474: Invalid argument: fillchars='..errval, + exc_exec('set fillchars='..val)) + end + + describe('"eob" flag', function() + it("uses '~' by default", function() + eq('', eval('&fillchars')) + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + | + ]]) + end) + it('supports whitespace', function() + command('set fillchars=eob:\\ ') + screen:expect([[ + ^ | + | + | + | + | + ]]) + end) + it('supports multibyte char', function() + command('set fillchars=eob:ñ') + screen:expect([[ + ^ | + ñ | + ñ | + ñ | + | + ]]) + end) + it('handles invalid values', function() + shouldfail('eob:') -- empty string + shouldfail('eob:馬') -- doublewidth char + shouldfail('eob:å̲') -- composing chars + shouldfail('eob:xy') -- two ascii chars + shouldfail('eob:\255', 'eob:<ff>') -- invalid UTF-8 + end) + end) +end) diff --git a/test/functional/options/num_options_spec.lua b/test/functional/options/num_options_spec.lua index ed17ffdd3c..fb0559054d 100644 --- a/test/functional/options/num_options_spec.lua +++ b/test/functional/options/num_options_spec.lua @@ -51,7 +51,6 @@ describe(':set validation', function() should_succeed('winminheight', 0) should_fail('winwidth', 0, 'E487') should_fail('helpheight', -1, 'E487') - should_fail('maxcombine', 7, 'E474') should_fail('iminsert', 3, 'E474') should_fail('imsearch', 3, 'E474') should_fail('titlelen', -1, 'E487') @@ -94,4 +93,18 @@ describe(':set validation', function() feed_command('set winminwidth=3') eq('E592', eval("v:errmsg"):match("E%d*")) end) + + it('set maxcombine resets to 6', function() + local function setto(value) + feed_command('setglobal maxcombine=' .. value) + feed_command('setlocal maxcombine=' .. value) + meths.set_option('maxcombine', value) + eq(6, meths.get_option('maxcombine')) + eq('', eval("v:errmsg")) + end + setto(0) + setto(1) + setto(6) + setto(7) + end) end) diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua index 40089d217d..9c5a59b58d 100644 --- a/test/functional/ui/fold_spec.lua +++ b/test/functional/ui/fold_spec.lua @@ -27,9 +27,8 @@ describe("folded lines", function() end) it("works with multibyte text", function() - -- Soon, we will always use the maximum value of 'maxcombine'. - feed_command("set maxcombine=6") - + -- Currently the only allowed value of 'maxcombine' + eq(6, meths.get_option('maxcombine')) eq(true, meths.get_option('arabicshape')) insert([[ å 语 x̨̣̘̫̲͚͎̎͂̀̂͛͛̾͢͟ العَرَبِيَّة diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua index 02ca566bd8..93d8965cb1 100644 --- a/test/functional/ui/output_spec.lua +++ b/test/functional/ui/output_spec.lua @@ -111,10 +111,10 @@ describe("shell command :!", function() feed([[<CR>]]) -- Print BELL control code. #4338 screen.bell = false - feed([[:!printf '\x07\x07\x07\x07text'<CR>]]) + feed([[:!printf '\007\007\007\007text'<CR>]]) screen:expect([[ ~ | - :!printf '\x07\x07\x07\x07text' | + :!printf '\007\007\007\007text' | text | Press ENTER or type command to continue^ | ]], nil, nil, function() @@ -122,7 +122,7 @@ describe("shell command :!", function() end) feed([[<CR>]]) -- Print BS control code. - feed([[:echo system('printf ''\x08\n''')<CR>]]) + feed([[:echo system('printf ''\010\n''')<CR>]]) screen:expect([[ ~ | ^H | |