diff options
author | Marco Hinz <mh.codebro+github@gmail.com> | 2019-01-28 00:45:59 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-01-28 00:45:59 +0100 |
commit | 28f87c505d895df8d00a919f6324f5305f02166e (patch) | |
tree | 3a642b40f51833605b14a0184d968dae4bb2cdfe | |
parent | 7e6980a161011ab58c69ee67f35522fd86e6cb25 (diff) | |
parent | 88b0899eb40581ad5b8071823594852a7cdf2043 (diff) | |
download | rneovim-28f87c505d895df8d00a919f6324f5305f02166e.tar.gz rneovim-28f87c505d895df8d00a919f6324f5305f02166e.tar.bz2 rneovim-28f87c505d895df8d00a919f6324f5305f02166e.zip |
Merge #9539 "options: make 'listchars' and 'fillchars' local to window"
-rw-r--r-- | runtime/doc/options.txt | 27 | ||||
-rw-r--r-- | runtime/doc/vim_diff.txt | 7 | ||||
-rw-r--r-- | src/nvim/buffer.c | 6 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 29 | ||||
-rw-r--r-- | src/nvim/charset.c | 8 | ||||
-rw-r--r-- | src/nvim/eval.c | 4 | ||||
-rw-r--r-- | src/nvim/globals.h | 20 | ||||
-rw-r--r-- | src/nvim/indent.c | 3 | ||||
-rw-r--r-- | src/nvim/message.c | 64 | ||||
-rw-r--r-- | src/nvim/misc1.c | 12 | ||||
-rw-r--r-- | src/nvim/mouse.c | 2 | ||||
-rw-r--r-- | src/nvim/option.c | 135 | ||||
-rw-r--r-- | src/nvim/option_defs.h | 4 | ||||
-rw-r--r-- | src/nvim/options.lua | 12 | ||||
-rw-r--r-- | src/nvim/screen.c | 212 | ||||
-rw-r--r-- | src/nvim/testdir/test_listchars.vim | 32 | ||||
-rw-r--r-- | test/functional/options/chars_spec.lua (renamed from test/functional/options/fillchars_spec.lua) | 48 | ||||
-rw-r--r-- | test/functional/ui/mouse_spec.lua | 47 |
18 files changed, 426 insertions, 246 deletions
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index a26e8faa2c..bf75bebb80 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -3653,7 +3653,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'listchars'* *'lcs'* 'listchars' 'lcs' string (default: "tab:> ,trail:-,nbsp:+" Vi default: "eol:$") - global + local to window Strings to use in 'list' mode and for the |:list| command. It is a comma separated list of string settings. @@ -3662,11 +3662,26 @@ A jump table for the options with a short description can be found at |Q_op|. omitted, there is no extra character at the end of the line. *lcs-tab* - tab:xy Two characters to be used to show a tab. The first - char is used once. The second char is repeated to - fill the space that the tab normally occupies. - "tab:>-" will show a tab that takes four spaces as - ">---". When omitted, a tab is show as ^I. + tab:xy[z] Two or three characters to be used to show a tab. + The third character is optional. + + tab:xy The 'x' is always used, then 'y' as many times as will + fit. Thus "tab:>-" displays: + > + >- + >-- + etc. + + tab:xyz The 'z' is always used, then 'x' is prepended, and + then 'y' is used as many times as will fit. Thus + "tab:<->" displays: + > + <> + <-> + <--> + etc. + + When "tab:" is omitted, a tab is shown as ^I. *lcs-space* space:c Character to show for a space. When omitted, spaces are left blank. diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 0e6c682b5c..36630d14ac 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -37,8 +37,8 @@ a complete and centralized reference of those differences. - 'display' defaults to "lastline,msgsep" - 'encoding' is UTF-8 (cf. 'fileencoding' for file-content encoding) - 'fillchars' defaults (in effect) to "vert:│,fold:·" -- 'fsync' is disabled - 'formatoptions' defaults to "tcqj" +- 'fsync' is disabled - 'history' defaults to 10000 (the maximum) - 'hlsearch' is set by default - 'incsearch' is set by default @@ -184,9 +184,10 @@ Options: 'cpoptions' flags: |cpo-_| 'display' flag `msgsep` to minimize scrolling when showing messages 'guicursor' works in the terminal - 'fillchars' flags: `msgsep` (see 'display' above) - and `eob` for |hl-EndOfBuffer| marker + 'fillchars' local to window. flags: `msgsep` (see 'display' above) and `eob` + for |hl-EndOfBuffer| marker 'inccommand' shows interactive results for |:substitute|-like commands + 'listchars' local to window 'scrollback' 'statusline' supports unlimited alignment sections 'tabline' %@Func@foo%X can call any function on mouse-click diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 18706f0cbb..7b90cbe4f4 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -3680,10 +3680,10 @@ int build_stl_str_hl( { // In list mode virtcol needs to be recomputed colnr_T virtcol = wp->w_virtcol; - if (wp->w_p_list && lcs_tab1 == NUL) { - wp->w_p_list = FALSE; + if (wp->w_p_list && wp->w_p_lcs_chars.tab1 == NUL) { + wp->w_p_list = false; getvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL); - wp->w_p_list = TRUE; + wp->w_p_list = true; } ++virtcol; // Don't display %V if it's the same as %c. diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 076d1dfdc4..b3977e0176 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -248,6 +248,10 @@ typedef struct { # define w_p_scl w_onebuf_opt.wo_scl // 'signcolumn' char_u *wo_winhl; # define w_p_winhl w_onebuf_opt.wo_winhl // 'winhighlight' + char_u *wo_fcs; +# define w_p_fcs w_onebuf_opt.wo_fcs // 'fillchars' + char_u *wo_lcs; +# define w_p_lcs w_onebuf_opt.wo_lcs // 'listchars' LastSet wo_scriptID[WV_COUNT]; // SIDs for window-local options # define w_p_scriptID w_onebuf_opt.wo_scriptID @@ -1003,6 +1007,31 @@ struct window_S { colnr_T w_old_visual_col; ///< last known start of visual part colnr_T w_old_curswant; ///< last known value of Curswant + // 'listchars' characters. Defaults set in set_chars_option(). + struct { + int eol; + int ext; + int prec; + int nbsp; + int space; + int tab1; ///< first tab character + int tab2; ///< second tab character + int tab3; ///< third tab character + int trail; + int conceal; + } w_p_lcs_chars; + + // 'fillchars' characters. Defaults set in set_chars_option(). + struct { + int stl; + int stlnc; + int vert; + int fold; + int diff; + int msgsep; + int eob; + } w_p_fcs_chars; + /* * "w_topline", "w_leftcol" and "w_skipcol" specify the offsets for * displaying the buffer. diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 4b192e7e94..08ecff149c 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -749,8 +749,8 @@ int vim_strnsize(char_u *s, int len) /// /// @return Number of characters. #define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \ - if (*(p) == TAB && (!(wp)->w_p_list || lcs_tab1)) { \ - const int ts = (int) (buf)->b_p_ts; \ + if (*(p) == TAB && (!(wp)->w_p_list || wp->w_p_lcs_chars.tab1)) { \ + const int ts = (int)(buf)->b_p_ts; \ return (ts - (int)(col % ts)); \ } else { \ return ptr2cells(p); \ @@ -1149,7 +1149,7 @@ static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp) { int n; - if ((*s == TAB) && (!wp->w_p_list || lcs_tab1)) { + if ((*s == TAB) && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) { n = (int)wp->w_buffer->b_p_ts; return n - (col % n); } @@ -1241,7 +1241,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, // When 'list', 'linebreak', 'showbreak' and 'breakindent' are not set // use a simple loop. // Also use this when 'list' is set but tabs take their normal size. - if ((!wp->w_p_list || (lcs_tab1 != NUL)) + if ((!wp->w_p_list || (wp->w_p_lcs_chars.tab1 != NUL)) && !wp->w_p_lbr && (*p_sbr == NUL) && !wp->w_p_bri ) { diff --git a/src/nvim/eval.c b/src/nvim/eval.c index dcb583ce8a..3980516d32 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -16425,7 +16425,9 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) if ((syntax_flags & HL_CONCEAL) && curwin->w_p_cole < 3) { cchar = syn_get_sub_char(); if (cchar == NUL && curwin->w_p_cole == 1) { - cchar = (lcs_conceal == NUL) ? ' ' : lcs_conceal; + cchar = (curwin->w_p_lcs_chars.conceal == NUL) + ? ' ' + : curwin->w_p_lcs_chars.conceal; } if (cchar != NUL) { utf_char2bytes(cchar, str); diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 8a76ca2bee..ccdf8f87ab 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -842,26 +842,6 @@ extern char_u *compiled_sys; * directory is not a local directory, globaldir is NULL. */ EXTERN char_u *globaldir INIT(= NULL); -// 'listchars' characters. Defaults are overridden in set_chars_option(). -EXTERN int lcs_eol INIT(= '$'); -EXTERN int lcs_ext INIT(= NUL); -EXTERN int lcs_prec INIT(= NUL); -EXTERN int lcs_nbsp INIT(= NUL); -EXTERN int lcs_space INIT(= NUL); -EXTERN int lcs_tab1 INIT(= NUL); -EXTERN int lcs_tab2 INIT(= NUL); -EXTERN int lcs_trail INIT(= NUL); -EXTERN int lcs_conceal INIT(= ' '); - -// 'fillchars' characters. Defaults are overridden in set_chars_option(). -EXTERN int fill_stl INIT(= ' '); -EXTERN int fill_stlnc INIT(= ' '); -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); EXTERN int km_startsel INIT(= FALSE); diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 9305e85f38..8e20aa5be4 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -61,7 +61,8 @@ int get_indent_str(char_u *ptr, int ts, int list) for (; *ptr; ++ptr) { // Count a tab for what it is worth. if (*ptr == TAB) { - if (!list || lcs_tab1) { // count a tab for what it is worth + if (!list || curwin->w_p_lcs_chars.tab1) { + // count a tab for what it is worth count += ts - (count % ts); } else { // In list mode, when tab is not set, count screen char width diff --git a/src/nvim/message.c b/src/nvim/message.c index 0f8ff74b12..1330460867 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1517,31 +1517,36 @@ void msg_prt_line(char_u *s, int list) int col = 0; int n_extra = 0; int c_extra = 0; - char_u *p_extra = NULL; /* init to make SASC shut up */ + int c_final = 0; + char_u *p_extra = NULL; // init to make SASC shut up int n; int attr = 0; - char_u *trail = NULL; + char_u *trail = NULL; int l; - if (curwin->w_p_list) - list = TRUE; + if (curwin->w_p_list) { + list = true; + } - /* find start of trailing whitespace */ - if (list && lcs_trail) { + // find start of trailing whitespace + if (list && curwin->w_p_lcs_chars.trail) { trail = s + STRLEN(s); - while (trail > s && ascii_iswhite(trail[-1])) - --trail; + while (trail > s && ascii_iswhite(trail[-1])) { + trail--; + } } - /* output a space for an empty line, otherwise the line will be - * overwritten */ - if (*s == NUL && !(list && lcs_eol != NUL)) + // output a space for an empty line, otherwise the line will be overwritten + if (*s == NUL && !(list && curwin->w_p_lcs_chars.eol != NUL)) { msg_putchar(' '); + } while (!got_int) { if (n_extra > 0) { n_extra--; - if (c_extra) { + if (n_extra == 0 && c_final) { + c = c_final; + } else if (c_extra) { c = c_extra; } else { assert(p_extra != NULL); @@ -1550,9 +1555,9 @@ void msg_prt_line(char_u *s, int list) } else if ((l = utfc_ptr2len(s)) > 1) { col += utf_ptr2cells(s); char buf[MB_MAXBYTES + 1]; - if (lcs_nbsp != NUL && list + if (curwin->w_p_lcs_chars.nbsp != NUL && list && (utf_ptr2char(s) == 160 || utf_ptr2char(s) == 0x202f)) { - utf_char2bytes(lcs_nbsp, (char_u *)buf); + utf_char2bytes(curwin->w_p_lcs_chars.nbsp, (char_u *)buf); buf[utfc_ptr2len((char_u *)buf)] = NUL; } else { memmove(buf, s, (size_t)l); @@ -1564,40 +1569,46 @@ void msg_prt_line(char_u *s, int list) } else { attr = 0; c = *s++; - if (c == TAB && (!list || lcs_tab1)) { - /* tab amount depends on current column */ + if (c == TAB && (!list || curwin->w_p_lcs_chars.tab1)) { + // tab amount depends on current column n_extra = curbuf->b_p_ts - col % curbuf->b_p_ts - 1; if (!list) { c = ' '; c_extra = ' '; + c_final = NUL; } else { - c = lcs_tab1; - c_extra = lcs_tab2; + c = (n_extra == 0 && curwin->w_p_lcs_chars.tab3) + ? curwin->w_p_lcs_chars.tab3 + : curwin->w_p_lcs_chars.tab1; + c_extra = curwin->w_p_lcs_chars.tab2; + c_final = curwin->w_p_lcs_chars.tab3; attr = HL_ATTR(HLF_8); } - } else if (c == 160 && list && lcs_nbsp != NUL) { - c = lcs_nbsp; + } else if (c == 160 && list && curwin->w_p_lcs_chars.nbsp != NUL) { + c = curwin->w_p_lcs_chars.nbsp; attr = HL_ATTR(HLF_8); - } else if (c == NUL && list && lcs_eol != NUL) { + } else if (c == NUL && list && curwin->w_p_lcs_chars.eol != NUL) { p_extra = (char_u *)""; c_extra = NUL; + c_final = NUL; n_extra = 1; - c = lcs_eol; + c = curwin->w_p_lcs_chars.eol; attr = HL_ATTR(HLF_AT); s--; } else if (c != NUL && (n = byte2cells(c)) > 1) { n_extra = n - 1; p_extra = transchar_byte(c); c_extra = NUL; + c_final = NUL; c = *p_extra++; /* Use special coloring to be able to distinguish <hex> from * the same in plain text. */ attr = HL_ATTR(HLF_8); } else if (c == ' ' && trail != NULL && s > trail) { - c = lcs_trail; + c = curwin->w_p_lcs_chars.trail; attr = HL_ATTR(HLF_8); - } else if (c == ' ' && list && lcs_space != NUL) { - c = lcs_space; + } else if (c == ' ' && list && curwin->w_p_lcs_chars.space != NUL) { + c = curwin->w_p_lcs_chars.space; attr = HL_ATTR(HLF_8); } } @@ -1955,7 +1966,8 @@ static void msg_scroll_up(void) if (dy_flags & DY_MSGSEP) { if (msg_scrolled == 0) { grid_fill(&default_grid, Rows-p_ch-1, Rows-p_ch, 0, (int)Columns, - fill_msgsep, fill_msgsep, HL_ATTR(HLF_MSGSEP)); + curwin->w_p_fcs_chars.msgsep, curwin->w_p_fcs_chars.msgsep, + HL_ATTR(HLF_MSGSEP)); } int nscroll = MIN(msg_scrollsize()+1, Rows); grid_del_lines(&default_grid, Rows-nscroll, 1, Rows, 0, Columns); diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 7f19bdf145..e752910a4b 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -1282,12 +1282,11 @@ int plines_win_nofold(win_T *wp, linenr_T lnum) return 1; col = win_linetabsize(wp, s, (colnr_T)MAXCOL); - /* - * If list mode is on, then the '$' at the end of the line may take up one - * extra column. - */ - if (wp->w_p_list && lcs_eol != NUL) + // If list mode is on, then the '$' at the end of the line may take up one + // extra column. + if (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL) { col += 1; + } /* * Add column offset for 'number', 'relativenumber' and 'foldcolumn'. @@ -1336,7 +1335,8 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column) // screen position of the TAB. This only fixes an error when the TAB wraps // from one screen line to the next (when 'columns' is not a multiple of // 'ts') -- webb. - if (*s == TAB && (State & NORMAL) && (!wp->w_p_list || lcs_tab1)) { + if (*s == TAB && (State & NORMAL) + && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) { col += win_lbr_chartabsize(wp, line, s, col, NULL) - 1; } diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 6b282ffbbf..50dba92eca 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -731,7 +731,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col) } else { if (!(row > 0 && ptr == ptr_row_offset) && (wp->w_p_cole == 1 || (wp->w_p_cole == 2 - && (lcs_conceal != NUL + && (wp->w_p_lcs_chars.conceal != NUL || syn_get_sub_char() != NUL)))) { // At least one placeholder character will be displayed. decr(); diff --git a/src/nvim/option.c b/src/nvim/option.c index 88401d3974..c57e6649bb 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2121,10 +2121,10 @@ static void didset_options2(void) (void)opt_strings_flags(p_cb, p_cb_values, &cb_flags, true); // Parse default for 'fillchars'. - (void)set_chars_option(&p_fcs); + (void)set_chars_option(curwin, &curwin->w_p_fcs); // Parse default for 'listchars'. - (void)set_chars_option(&p_lcs); + (void)set_chars_option(curwin, &curwin->w_p_lcs); // Parse default for 'wildmode'. check_opt_wim(); @@ -2567,10 +2567,19 @@ did_set_string_option ( // 'ambiwidth' if (check_opt_strings(p_ambw, p_ambw_values, false) != OK) { errmsg = e_invarg; - } else if (set_chars_option(&p_lcs) != NULL) { - errmsg = (char_u *)_("E834: Conflicts with value of 'listchars'"); - } else if (set_chars_option(&p_fcs) != NULL) { - errmsg = (char_u *)_("E835: Conflicts with value of 'fillchars'"); + } else { + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (set_chars_option(wp, &wp->w_p_lcs) != NULL) { + errmsg = (char_u *)_("E834: Conflicts with value of 'listchars'"); + goto ambw_end; + } + if (set_chars_option(wp, &wp->w_p_fcs) != NULL) { + errmsg = (char_u *)_("E835: Conflicts with value of 'fillchars'"); + goto ambw_end; + } + } +ambw_end: + {} // clint prefers {} over ; as an empty statement } } /* 'background' */ @@ -2756,14 +2765,10 @@ did_set_string_option ( } s = skip_to_option_part(s); } - } - /* 'listchars' */ - else if (varp == &p_lcs) { - errmsg = set_chars_option(varp); - } - /* 'fillchars' */ - else if (varp == &p_fcs) { - errmsg = set_chars_option(varp); + } else if (varp == &curwin->w_p_lcs) { // 'listchars' + errmsg = set_chars_option(curwin, varp); + } else if (varp == &curwin->w_p_fcs) { // 'fillchars' + errmsg = set_chars_option(curwin, varp); } /* 'cedit' */ else if (varp == &p_cedit) { @@ -3394,53 +3399,55 @@ skip: /// Handle setting 'listchars' or 'fillchars'. /// Assume monocell characters /// -/// @param varp either &p_lcs ('listchars') or &p_fcs ('fillchar') +/// @param varp either &curwin->w_p_lcs or &curwin->w_p_fcs /// @return error message, NULL if it's OK. -static char_u *set_chars_option(char_u **varp) +static char_u *set_chars_option(win_T *wp, char_u **varp) { int round, i, len, entries; - char_u *p, *s; - int c1, c2 = 0; - struct charstab { + char_u *p, *s; + int c1 = 0, c2 = 0, c3 = 0; + + struct chars_tab { int *cp; ///< char value char *name; ///< char id int def; ///< default value }; - static struct charstab filltab[] = { - { &fill_stl, "stl" , ' ' }, - { &fill_stlnc, "stlnc", ' ' }, - { &fill_vert, "vert" , 9474 }, // │ - { &fill_fold, "fold" , 183 }, // · - { &fill_diff, "diff" , '-' }, - { &fill_msgsep, "msgsep", ' ' }, - { &fill_eob, "eob", '~' }, + struct chars_tab *tab; + + struct chars_tab fcs_tab[] = { + { &wp->w_p_fcs_chars.stl, "stl", ' ' }, + { &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' }, + { &wp->w_p_fcs_chars.vert, "vert", 9474 }, // │ + { &wp->w_p_fcs_chars.fold, "fold", 183 }, // · + { &wp->w_p_fcs_chars.diff, "diff", '-' }, + { &wp->w_p_fcs_chars.msgsep, "msgsep", ' ' }, + { &wp->w_p_fcs_chars.eob, "eob", '~' }, }; - static struct charstab lcstab[] = { - { &lcs_eol, "eol", NUL }, - { &lcs_ext, "extends", NUL }, - { &lcs_nbsp, "nbsp", NUL }, - { &lcs_prec, "precedes", NUL }, - { &lcs_space, "space", NUL }, - { &lcs_tab2, "tab", NUL }, - { &lcs_trail, "trail", NUL }, - { &lcs_conceal, "conceal", NUL }, + struct chars_tab lcs_tab[] = { + { &wp->w_p_lcs_chars.eol, "eol", NUL }, + { &wp->w_p_lcs_chars.ext, "extends", NUL }, + { &wp->w_p_lcs_chars.nbsp, "nbsp", NUL }, + { &wp->w_p_lcs_chars.prec, "precedes", NUL }, + { &wp->w_p_lcs_chars.space, "space", NUL }, + { &wp->w_p_lcs_chars.tab2, "tab", NUL }, + { &wp->w_p_lcs_chars.trail, "trail", NUL }, + { &wp->w_p_lcs_chars.conceal, "conceal", NUL }, }; - struct charstab *tab; - if (varp == &p_lcs) { - tab = lcstab; - entries = ARRAY_SIZE(lcstab); + if (varp == &wp->w_p_lcs) { + tab = lcs_tab; + entries = ARRAY_SIZE(lcs_tab); } else { - tab = filltab; - entries = ARRAY_SIZE(filltab); + tab = fcs_tab; + entries = ARRAY_SIZE(fcs_tab); if (*p_ambw == 'd') { // XXX: If ambiwidth=double then "|" and "·" take 2 columns, which is // forbidden (TUI limitation?). Set old defaults. - filltab[2].def = '|'; - filltab[3].def = '-'; + fcs_tab[2].def = '|'; + fcs_tab[3].def = '-'; } else { - filltab[2].def = 9474; // │ - filltab[3].def = 183; // · + fcs_tab[2].def = 9474; // │ + fcs_tab[3].def = 183; // · } } @@ -3453,8 +3460,9 @@ static char_u *set_chars_option(char_u **varp) *(tab[i].cp) = tab[i].def; } } - if (varp == &p_lcs) { - lcs_tab1 = NUL; + if (varp == &wp->w_p_lcs) { + wp->w_p_lcs_chars.tab1 = NUL; + wp->w_p_lcs_chars.tab3 = NUL; } } p = *varp; @@ -3464,6 +3472,7 @@ static char_u *set_chars_option(char_u **varp) if (STRNCMP(p, tab[i].name, len) == 0 && p[len] == ':' && p[len + 1] != NUL) { + c1 = c2 = c3 = 0; s = p + len + 1; // TODO(bfredl): use schar_T representation and utfc_ptr2len @@ -3472,7 +3481,7 @@ static char_u *set_chars_option(char_u **varp) if (mb_char2cells(c1) > 1 || (c1len == 1 && c1 > 127)) { continue; } - if (tab[i].cp == &lcs_tab2) { + if (tab[i].cp == &wp->w_p_lcs_chars.tab2) { if (*s == NUL) { continue; } @@ -3481,15 +3490,23 @@ static char_u *set_chars_option(char_u **varp) if (mb_char2cells(c2) > 1 || (c2len == 1 && c2 > 127)) { continue; } + if (!(*s == ',' || *s == NUL)) { + int c3len = utf_ptr2len(s); + c3 = mb_cptr2char_adv((const char_u **)&s); + if (mb_char2cells(c3) > 1 || (c3len == 1 && c3 > 127)) { + continue; + } + } } if (*s == ',' || *s == NUL) { if (round) { - if (tab[i].cp == &lcs_tab2) { - lcs_tab1 = c1; - lcs_tab2 = c2; - } else if (tab[i].cp != NULL) + if (tab[i].cp == &wp->w_p_lcs_chars.tab2) { + wp->w_p_lcs_chars.tab1 = c1; + wp->w_p_lcs_chars.tab2 = c2; + wp->w_p_lcs_chars.tab3 = c3; + } else if (tab[i].cp != NULL) { *(tab[i].cp) = c1; - + } } p = s; break; @@ -5613,6 +5630,8 @@ static char_u *get_varp(vimoption_T *p) case PV_KMAP: return (char_u *)&(curbuf->b_p_keymap); case PV_SCL: return (char_u *)&(curwin->w_p_scl); case PV_WINHL: return (char_u *)&(curwin->w_p_winhl); + case PV_FCS: return (char_u *)&(curwin->w_p_fcs); + case PV_LCS: return (char_u *)&(curwin->w_p_lcs); default: IEMSG(_("E356: get_varp ERROR")); } /* always return a valid pointer to avoid a crash! */ @@ -5691,6 +5710,8 @@ void copy_winopt(winopt_T *from, winopt_T *to) to->wo_fmr = vim_strsave(from->wo_fmr); to->wo_scl = vim_strsave(from->wo_scl); to->wo_winhl = vim_strsave(from->wo_winhl); + to->wo_fcs = vim_strsave(from->wo_fcs); + to->wo_lcs = vim_strsave(from->wo_lcs); check_winopt(to); // don't want NULL pointers } @@ -5721,6 +5742,8 @@ static void check_winopt(winopt_T *wop) check_string_option(&wop->wo_cocu); check_string_option(&wop->wo_briopt); check_string_option(&wop->wo_winhl); + check_string_option(&wop->wo_fcs); + check_string_option(&wop->wo_lcs); } /* @@ -5741,12 +5764,16 @@ void clear_winopt(winopt_T *wop) clear_string_option(&wop->wo_cocu); clear_string_option(&wop->wo_briopt); clear_string_option(&wop->wo_winhl); + clear_string_option(&wop->wo_fcs); + clear_string_option(&wop->wo_lcs); } void didset_window_options(win_T *wp) { check_colorcolumn(wp); briopt_check(wp); + set_chars_option(wp, &wp->w_p_fcs); + set_chars_option(wp, &wp->w_p_lcs); parse_winhl_opt(wp); } diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 7b99b6f266..c80de4d46b 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -486,7 +486,6 @@ EXTERN long *p_linespace; // 'linespace' EXTERN char_u *p_lispwords; // 'lispwords' EXTERN long p_ls; // 'laststatus' EXTERN long p_stal; // 'showtabline' -EXTERN char_u *p_lcs; // 'listchars' EXTERN int p_lz; // 'lazyredraw' EXTERN int p_lpl; // 'loadplugins' @@ -638,7 +637,6 @@ EXTERN long p_ul; ///< 'undolevels' EXTERN long p_ur; ///< 'undoreload' EXTERN long p_uc; ///< 'updatecount' EXTERN long p_ut; ///< 'updatetime' -EXTERN char_u *p_fcs; ///< 'fillchar' EXTERN char_u *p_shada; ///< 'shada' EXTERN char_u *p_vdir; ///< 'viewdir' EXTERN char_u *p_vop; ///< 'viewoptions' @@ -814,6 +812,8 @@ enum { , WV_WRAP , WV_SCL , WV_WINHL + , WV_FCS + , WV_LCS , WV_COUNT // must be the last one }; diff --git a/src/nvim/options.lua b/src/nvim/options.lua index aba8f8b893..c3aff87bbf 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -806,11 +806,11 @@ return { }, { full_name='fillchars', abbreviation='fcs', - type='string', list='onecomma', scope={'global'}, + type='string', list='onecomma', scope={'window'}, deny_duplicates=true, vi_def=true, - redraw={'all_windows'}, - varname='p_fcs', + alloced=true, + redraw={'current_window'}, defaults={if_true={vi=''}} }, { @@ -1425,11 +1425,11 @@ return { }, { full_name='listchars', abbreviation='lcs', - type='string', list='onecomma', scope={'global'}, + type='string', list='onecomma', scope={'window'}, deny_duplicates=true, vim=true, - redraw={'all_windows'}, - varname='p_lcs', + alloced=true, + redraw={'current_window'}, defaults={if_true={vi="eol:$", vim="tab:> ,trail:-,nbsp:+"}} }, { diff --git a/src/nvim/screen.c b/src/nvim/screen.c index a17688afbc..2038fb4d2c 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1446,13 +1446,12 @@ static void win_update(win_T *wp) wp->w_botline = buf->b_ml.ml_line_count + 1; j = diff_check_fill(wp, wp->w_botline); if (j > 0 && !wp->w_botfill) { - /* - * Display filler lines at the end of the file - */ - if (char2cells(fill_diff) > 1) + // display filler lines at the end of the file + if (char2cells(wp->w_p_fcs_chars.diff) > 1) { i = '-'; - else - i = fill_diff; + } else { + i = wp->w_p_fcs_chars.diff; + } if (row + j > wp->w_grid.Rows) { j = wp->w_grid.Rows - row; } @@ -1463,8 +1462,8 @@ static void win_update(win_T *wp) wp->w_botline = lnum; // 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_grid.Rows, HLF_EOB); + // write the 'eob' character to rows that aren't part of the file. + win_draw_end(wp, wp->w_p_fcs_chars.eob, ' ', row, wp->w_grid.Rows, HLF_EOB); } if (wp->w_redr_type >= REDRAW_TOP) { @@ -1815,7 +1814,7 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T txtcol = col; /* remember where text starts */ - // 5. move the text to linebuf_char[off]. Fill up with "fill_fold". + // 5. move the text to linebuf_char[off]. Fill up with "fold". // Right-left text is put in columns 0 - number-col, normal text is put // in columns number-col - window-width. int idx; @@ -1847,7 +1846,7 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T col -= txtcol; schar_T sc; - schar_from_char(sc, fill_fold); + schar_from_char(sc, wp->w_p_fcs_chars.fold); while (col < wp->w_grid.Columns - (wp->w_p_rl ? txtcol : 0) ) { @@ -2040,30 +2039,33 @@ win_line ( bool number_only // only update the number column ) { - int c = 0; // init for GCC - long vcol = 0; // virtual column (for tabs) - long vcol_sbr = -1; // virtual column after showbreak - long vcol_prev = -1; // "vcol" of previous character - char_u *line; // current line - char_u *ptr; // current position in "line" - int row; // row in the window, excl w_winrow - ScreenGrid *grid = &wp->w_grid; // grid specfic to the window - - char_u extra[18]; /* line number and 'fdc' must fit in here */ - int n_extra = 0; /* number of extra chars */ - char_u *p_extra = NULL; /* string of extra chars, plus NUL */ - char_u *p_extra_free = NULL; /* p_extra needs to be freed */ - int c_extra = NUL; /* extra chars, all the same */ - int extra_attr = 0; /* attributes when n_extra != 0 */ - static char_u *at_end_str = (char_u *)""; /* used for p_extra when - displaying lcs_eol at end-of-line */ - int lcs_eol_one = lcs_eol; /* lcs_eol until it's been used */ - int lcs_prec_todo = lcs_prec; /* lcs_prec until it's been used */ + int c = 0; // init for GCC + long vcol = 0; // virtual column (for tabs) + long vcol_sbr = -1; // virtual column after showbreak + long vcol_prev = -1; // "vcol" of previous character + char_u *line; // current line + char_u *ptr; // current position in "line" + int row; // row in the window, excl w_winrow + ScreenGrid *grid = &wp->w_grid; // grid specfic to the window + + char_u extra[18]; // line number and 'fdc' must fit in here + int n_extra = 0; // number of extra chars + char_u *p_extra = NULL; // string of extra chars, plus NUL + char_u *p_extra_free = NULL; // p_extra needs to be freed + int c_extra = NUL; // extra chars, all the same + int c_final = NUL; // final char, mandatory if set + int extra_attr = 0; // attributes when n_extra != 0 + static char_u *at_end_str = (char_u *)""; // used for p_extra when displaying + // curwin->w_p_lcs_chars.eol at + // end-of-line + int lcs_eol_one = wp->w_p_lcs_chars.eol; // 'eol' until it's been used + int lcs_prec_todo = wp->w_p_lcs_chars.prec; // 'prec' until it's been used /* saved "extra" items for when draw_state becomes WL_LINE (again) */ int saved_n_extra = 0; char_u *saved_p_extra = NULL; int saved_c_extra = 0; + int saved_c_final = 0; int saved_char_attr = 0; int n_attr = 0; /* chars with special attr */ @@ -2430,11 +2432,11 @@ win_line ( } if (wp->w_p_list) { - if (lcs_space || lcs_trail) { + if (curwin->w_p_lcs_chars.space || wp->w_p_lcs_chars.trail) { extra_check = true; } // find start of trailing whitespace - if (lcs_trail) { + if (wp->w_p_lcs_chars.trail) { trailcol = (colnr_T)STRLEN(ptr); while (trailcol > (colnr_T)0 && ascii_iswhite(ptr[trailcol - 1])) { trailcol--; @@ -2642,6 +2644,7 @@ win_line ( /* Draw the cmdline character. */ n_extra = 1; c_extra = cmdwin_type; + c_final = NUL; char_attr = win_hl_attr(wp, HLF_AT); } } @@ -2660,6 +2663,7 @@ win_line ( p_extra_free[n_extra] = NUL; p_extra = p_extra_free; c_extra = NUL; + c_final = NUL; char_attr = win_hl_attr(wp, HLF_FC); } } @@ -2673,6 +2677,7 @@ win_line ( int text_sign; // Draw cells with the sign value or blank. c_extra = ' '; + c_final = NUL; char_attr = win_hl_attr(wp, HLF_SC); n_extra = win_signcol_width(wp); @@ -2683,6 +2688,7 @@ win_line ( int symbol_blen = (int)STRLEN(p_extra); if (p_extra != NULL) { c_extra = NUL; + c_final = NUL; // symbol(s) bytes + (filling spaces) (one byte each) n_extra = symbol_blen + (win_signcol_width(wp) - mb_string2cells(p_extra)); @@ -2734,8 +2740,11 @@ win_line ( rl_mirror(extra); p_extra = extra; c_extra = NUL; - } else + c_final = NUL; + } else { c_extra = ' '; + c_final = NUL; + } n_extra = number_width(wp) + 1; char_attr = win_hl_attr(wp, HLF_N); @@ -2790,11 +2799,14 @@ win_line ( if (draw_state == WL_SBR - 1 && n_extra == 0) { draw_state = WL_SBR; if (filler_todo > 0) { - /* Draw "deleted" diff line(s). */ - if (char2cells(fill_diff) > 1) + // draw "deleted" diff line(s) + if (char2cells(wp->w_p_fcs_chars.diff) > 1) { c_extra = '-'; - else - c_extra = fill_diff; + c_final = NUL; + } else { + c_extra = wp->w_p_fcs_chars.diff; + c_final = NUL; + } if (wp->w_p_rl) { n_extra = col + 1; } else { @@ -2806,6 +2818,7 @@ win_line ( /* Draw 'showbreak' at the start of each broken line. */ p_extra = p_sbr; c_extra = NUL; + c_final = NUL; n_extra = (int)STRLEN(p_sbr); char_attr = win_hl_attr(wp, HLF_AT); need_showbreak = false; @@ -2827,6 +2840,7 @@ win_line ( /* Continue item from end of wrapped line. */ n_extra = saved_n_extra; c_extra = saved_c_extra; + c_final = saved_c_final; p_extra = saved_p_extra; char_attr = saved_char_attr; } else { @@ -3024,20 +3038,18 @@ win_line ( } } - /* - * Get the next character to put on the screen. - */ - /* - * The "p_extra" points to the extra stuff that is inserted to - * represent special characters (non-printable stuff) and other - * things. When all characters are the same, c_extra is used. - * "p_extra" must end in a NUL to avoid mb_ptr2len() reads past - * "p_extra[n_extra]". - * For the '$' of the 'list' option, n_extra == 1, p_extra == "". - */ + // Get the next character to put on the screen. + // + // The "p_extra" points to the extra stuff that is inserted to + // represent special characters (non-printable stuff) and other + // things. When all characters are the same, c_extra is used. + // If c_final is set, it will compulsorily be used at the end. + // "p_extra" must end in a NUL to avoid mb_ptr2len() reads past + // "p_extra[n_extra]". + // For the '$' of the 'list' option, n_extra == 1, p_extra == "". if (n_extra > 0) { - if (c_extra != NUL) { - c = c_extra; + if (c_extra != NUL || (n_extra == 1 && c_final != NUL)) { + c = (n_extra == 1 && c_final != NUL) ? c_final : c_extra; mb_c = c; // doesn't handle non-utf-8 multi-byte! if (enc_utf8 && utf_char2len(c) > 1) { mb_utf8 = true; @@ -3144,6 +3156,7 @@ win_line ( mb_utf8 = (c >= 0x80); n_extra = (int)STRLEN(p_extra); c_extra = NUL; + c_final = NUL; if (area_attr == 0 && search_attr == 0) { n_attr = n_extra + 1; extra_attr = win_hl_attr(wp, HLF_8); @@ -3196,6 +3209,7 @@ win_line ( p_extra = extra; n_extra = (int)STRLEN(extra) - 1; c_extra = NUL; + c_final = NUL; c = *p_extra++; if (area_attr == 0 && search_attr == 0) { n_attr = n_extra + 1; @@ -3230,6 +3244,7 @@ win_line ( if (n_skip > 0 && mb_l > 1 && n_extra == 0) { n_extra = 1; c_extra = MB_FILLER_CHAR; + c_final = NUL; c = ' '; if (area_attr == 0 && search_attr == 0) { n_attr = n_extra + 1; @@ -3389,6 +3404,7 @@ win_line ( - vcol % (int)wp->w_buffer->b_p_ts - 1; } c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' '; + c_final = NUL; if (ascii_iswhite(c)) { if (c == TAB) /* See "Tab alignment" below. */ @@ -3399,13 +3415,14 @@ win_line ( } } - // 'list': change char 160 to lcs_nbsp and space to lcs_space. + // 'list': change char 160 to 'nbsp' and space to 'space'. if (wp->w_p_list && (((c == 160 || (mb_utf8 && (mb_c == 160 || mb_c == 0x202f))) - && lcs_nbsp) - || (c == ' ' && lcs_space && ptr - line <= trailcol))) { - c = (c == ' ') ? lcs_space : lcs_nbsp; + && curwin->w_p_lcs_chars.nbsp) + || (c == ' ' && curwin->w_p_lcs_chars.space + && ptr - line <= trailcol))) { + c = (c == ' ') ? wp->w_p_lcs_chars.space : wp->w_p_lcs_chars.nbsp; n_attr = 1; extra_attr = win_hl_attr(wp, HLF_0); saved_attr2 = char_attr; // save current attr @@ -3420,7 +3437,7 @@ win_line ( } if (trailcol != MAXCOL && ptr > line + trailcol && c == ' ') { - c = lcs_trail; + c = wp->w_p_lcs_chars.trail; n_attr = 1; extra_attr = win_hl_attr(wp, HLF_0); saved_attr2 = char_attr; // save current attr @@ -3441,7 +3458,7 @@ win_line ( if (!vim_isprintc(c)) { // when getting a character from the file, we may have to // turn it into something else on the way to putting it on the screen. - if (c == TAB && (!wp->w_p_list || lcs_tab1)) { + if (c == TAB && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) { int tab_len = 0; long vcol_adjusted = vcol; // removed showbreak length // Only adjust the tab_len, when at the first column after the @@ -3465,26 +3482,28 @@ win_line ( tab_len += vcol_off; } // boguscols before FIX_FOR_BOGUSCOLS macro from above. - if (lcs_tab1 && old_boguscols > 0 && n_extra > tab_len) { + if (wp->w_p_lcs_chars.tab1 && old_boguscols > 0 + && n_extra > tab_len) { tab_len += n_extra - tab_len; } /* if n_extra > 0, it gives the number of chars to use for * a tab, else we need to calculate the width for a tab */ - int len = (tab_len * mb_char2len(lcs_tab2)); + int len = (tab_len * mb_char2len(wp->w_p_lcs_chars.tab2)); if (n_extra > 0) { len += n_extra - tab_len; } - c = lcs_tab1; + c = wp->w_p_lcs_chars.tab1; p = xmalloc(len + 1); memset(p, ' ', len); p[len] = NUL; xfree(p_extra_free); p_extra_free = p; for (i = 0; i < tab_len; i++) { - utf_char2bytes(lcs_tab2, p); - p += mb_char2len(lcs_tab2); - n_extra += mb_char2len(lcs_tab2) - (saved_nextra > 0 ? 1: 0); + utf_char2bytes(wp->w_p_lcs_chars.tab2, p); + p += mb_char2len(wp->w_p_lcs_chars.tab2); + n_extra += mb_char2len(wp->w_p_lcs_chars.tab2) + - (saved_nextra > 0 ? 1: 0); } p_extra = p_extra_free; @@ -3509,19 +3528,23 @@ win_line ( // Make sure, the highlighting for the tab char will be // correctly set further below (effectively reverts the // FIX_FOR_BOGSUCOLS macro. - if (n_extra == tab_len + vc_saved && wp->w_p_list && lcs_tab1) { + if (n_extra == tab_len + vc_saved && wp->w_p_list + && wp->w_p_lcs_chars.tab1) { tab_len += vc_saved; } } mb_utf8 = false; // don't draw as UTF-8 if (wp->w_p_list) { - c = lcs_tab1; + c = (n_extra == 0 && wp->w_p_lcs_chars.tab3) + ? wp->w_p_lcs_chars.tab3 + : wp->w_p_lcs_chars.tab1; if (wp->w_p_lbr) { c_extra = NUL; /* using p_extra from above */ } else { - c_extra = lcs_tab2; + c_extra = wp->w_p_lcs_chars.tab2; } + c_final = wp->w_p_lcs_chars.tab3; n_attr = tab_len + 1; extra_attr = win_hl_attr(wp, HLF_0); saved_attr2 = char_attr; // save current attr @@ -3532,6 +3555,7 @@ win_line ( c = 0xc0; } } else { + c_final = NUL; c_extra = ' '; c = ' '; } @@ -3559,10 +3583,11 @@ win_line ( p_extra = at_end_str; n_extra = 1; c_extra = NUL; + c_final = NUL; } } - if (wp->w_p_list && lcs_eol > 0) { - c = lcs_eol; + if (wp->w_p_list && wp->w_p_lcs_chars.eol > 0) { + c = wp->w_p_lcs_chars.eol; } else { c = ' '; } @@ -3586,6 +3611,7 @@ win_line ( if ((dy_flags & DY_UHEX) && wp->w_p_rl) rl_mirror(p_extra); /* reverse "<12>" */ c_extra = NUL; + c_final = NUL; if (wp->w_p_lbr) { char_u *p; @@ -3632,8 +3658,8 @@ win_line ( c = match_conc; } else if (syn_get_sub_char() != NUL) { c = syn_get_sub_char(); - } else if (lcs_conceal != NUL) { - c = lcs_conceal; + } else if (wp->w_p_lcs_chars.conceal != NUL) { + c = wp->w_p_lcs_chars.conceal; } else { c = ' '; } @@ -3703,12 +3729,13 @@ win_line ( && filler_todo <= 0 && draw_state > WL_NR && c != NUL) { - c = lcs_prec; + c = wp->w_p_lcs_chars.prec; lcs_prec_todo = NUL; if (has_mbyte && (*mb_char2cells)(mb_c) > 1) { /* Double-width character being overwritten by the "precedes" * character, need to fill up half the character. */ c_extra = MB_FILLER_CHAR; + c_final = NUL; n_extra = 1; n_attr = 2; extra_attr = win_hl_attr(wp, HLF_AT); @@ -3753,7 +3780,7 @@ win_line ( cur = cur->next; } } - if (lcs_eol == lcs_eol_one + if (wp->w_p_lcs_chars.eol == lcs_eol_one && ((area_attr != 0 && vcol == fromcol && (VIsual_mode != Ctrl_V || lnum == VIsual.lnum @@ -3856,7 +3883,8 @@ win_line ( // Make sure alignment is the same regardless // if listchars=eol:X is used or not. - bool delay_virttext = lcs_eol == lcs_eol_one && eol_hl_off == 0; + bool delay_virttext = wp->w_p_lcs_chars.eol == lcs_eol_one + && eol_hl_off == 0; if (wp->w_p_cuc) { rightmost_vcol = wp->w_virtcol; @@ -3976,15 +4004,15 @@ win_line ( break; } - /* line continues beyond line end */ - if (lcs_ext + // line continues beyond line end + if (wp->w_p_lcs_chars.ext && !wp->w_p_wrap && filler_todo <= 0 && (wp->w_p_rl ? col == 0 : col == grid->Columns - 1) && (*ptr != NUL || (wp->w_p_list && lcs_eol_one > 0) || (n_extra && (c_extra != NUL || *p_extra != NUL)))) { - c = lcs_ext; + c = wp->w_p_lcs_chars.ext; char_attr = win_hl_attr(wp, HLF_AT); mb_c = c; if (enc_utf8 && utf_char2len(c) > 1) { @@ -4162,7 +4190,8 @@ win_line ( if ((wp->w_p_rl ? (col < 0) : (col >= grid->Columns)) && (*ptr != NUL || filler_todo > 0 - || (wp->w_p_list && lcs_eol != NUL && p_extra != at_end_str) + || (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL + && p_extra != at_end_str) || (n_extra != 0 && (c_extra != NUL || *p_extra != NUL))) ) { bool wrap = wp->w_p_wrap // Wrapping enabled. @@ -4222,16 +4251,19 @@ win_line ( saved_n_extra = n_extra; saved_p_extra = p_extra; saved_c_extra = c_extra; + saved_c_final = c_final; saved_char_attr = char_attr; n_extra = 0; - lcs_prec_todo = lcs_prec; - if (filler_todo <= 0) - need_showbreak = TRUE; - --filler_todo; - /* When the filler lines are actually below the last line of the - * file, don't draw the line itself, break here. */ - if (filler_todo == 0 && wp->w_botfill) + lcs_prec_todo = wp->w_p_lcs_chars.prec; + if (filler_todo <= 0) { + need_showbreak = true; + } + filler_todo--; + // When the filler lines are actually below the last line of the + // file, don't draw the line itself, break here. + if (filler_todo == 0 && wp->w_botfill) { break; + } } } /* for every character in the line */ @@ -6830,17 +6862,17 @@ static int fillchar_status(int *attr, win_T *wp) bool is_curwin = (wp == curwin); if (is_curwin) { *attr = win_hl_attr(wp, HLF_S); - fill = fill_stl; + fill = wp->w_p_fcs_chars.stl; } else { *attr = win_hl_attr(wp, HLF_SNC); - fill = fill_stlnc; + fill = wp->w_p_fcs_chars.stlnc; } /* Use fill when there is highlighting, and highlighting of current * window differs, or the fillchars differ, or this is not the * current window */ if (*attr != 0 && ((win_hl_attr(wp, HLF_S) != win_hl_attr(wp, HLF_SNC) || !is_curwin || ONE_WINDOW) - || (fill_stl != fill_stlnc))) { + || (wp->w_p_fcs_chars.stl != wp->w_p_fcs_chars.stlnc))) { return fill; } if (is_curwin) { @@ -6856,7 +6888,7 @@ static int fillchar_status(int *attr, win_T *wp) static int fillchar_vsep(win_T *wp, int *attr) { *attr = win_hl_attr(wp, HLF_C); - return fill_vert; + return wp->w_p_fcs_chars.vert; } /* @@ -6982,12 +7014,12 @@ static void win_redr_ruler(win_T *wp, int always) off = 0; } - /* In list mode virtcol needs to be recomputed */ + // In list mode virtcol needs to be recomputed colnr_T virtcol = wp->w_virtcol; - if (wp->w_p_list && lcs_tab1 == NUL) { - wp->w_p_list = FALSE; + if (wp->w_p_list && wp->w_p_lcs_chars.tab1 == NUL) { + wp->w_p_list = false; getvvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL); - wp->w_p_list = TRUE; + wp->w_p_list = true; } #define RULER_BUF_LEN 70 diff --git a/src/nvim/testdir/test_listchars.vim b/src/nvim/testdir/test_listchars.vim index 57ea7ca5a9..4899f59910 100644 --- a/src/nvim/testdir/test_listchars.vim +++ b/src/nvim/testdir/test_listchars.vim @@ -42,6 +42,38 @@ func Test_listchars() call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$'))) endfor + " tab with 3rd character. + set listchars-=tab:>- + set listchars+=tab:<=>,trail:- + let expected = [ + \ '<======>aa<====>$', + \ '..bb<==>--$', + \ '...cccc>-$', + \ 'dd........ee--<>$', + \ '-$' + \ ] + redraw! + for i in range(1, 5) + call cursor(i, 1) + call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$'))) + endfor + + set listchars-=trail:- + let expected = [ + \ '<======>aa<====>$', + \ '..bb<==>..$', + \ '...cccc>.$', + \ 'dd........ee..<>$', + \ '.$' + \ ] + redraw! + for i in range(1, 5) + call cursor(i, 1) + call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$'))) + endfor + + set listchars-=tab:<=> + set listchars+=tab:>- set listchars+=trail:< set nolist normal ggdG diff --git a/test/functional/options/fillchars_spec.lua b/test/functional/options/chars_spec.lua index 99177a11b4..1330c29e61 100644 --- a/test/functional/options/fillchars_spec.lua +++ b/test/functional/options/chars_spec.lua @@ -4,6 +4,8 @@ local clear, command = helpers.clear, helpers.command local eval = helpers.eval local eq = helpers.eq local exc_exec = helpers.exc_exec +local insert = helpers.insert +local feed = helpers.feed describe("'fillchars'", function() local screen @@ -69,5 +71,51 @@ describe("'fillchars'", function() shouldfail('eob:xy') -- two ascii chars shouldfail('eob:\255', 'eob:<ff>') -- invalid UTF-8 end) + it('is local to window', function() + clear() + screen = Screen.new(50, 5) + screen:attach() + insert("foo\nbar") + command('set laststatus=0') + command('1,2fold') + command('vsplit') + command('set fillchars=fold:x') + screen:expect([[ + ^+-- 2 lines: fooxxxxxxxx│+-- 2 lines: foo·······| + ~ │~ | + ~ │~ | + ~ │~ | + | + ]]) + end) + end) +end) + +describe("'listchars'", function() + local screen + + before_each(function() + clear() + screen = Screen.new(50, 5) + screen:attach() + end) + + after_each(function() + screen:detach() + end) + + it('is local to window', function() + feed('i<tab><tab><tab><esc>') + command('set laststatus=0') + command('set list listchars=tab:<->') + command('vsplit') + command('set listchars&') + screen:expect([[ + > > ^> │<------><------><------>| + ~ │~ | + ~ │~ | + ~ │~ | + | + ]]) end) end) diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index 8ba03953b9..7805ed3cb9 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -11,6 +11,7 @@ describe('ui/mouse/input', function() before_each(function() clear() meths.set_option('mouse', 'a') + meths.set_option('list', true) meths.set_option('listchars', 'eol:$') screen = Screen.new(25, 5) screen:attach() @@ -82,7 +83,7 @@ describe('ui/mouse/input', function() feed('<LeftMouse><0,0>') feed('<LeftRelease><0,0>') screen:expect([[ - ^t{1:esting}{3: } | + ^t{1:esting} | mouse | support and selection | {0:~ }| @@ -125,7 +126,7 @@ describe('ui/mouse/input', function() insert('this is bar') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| - this is ba^r | + this is ba^r{0:$} | {0:~ }| {0:~ }| | @@ -162,7 +163,7 @@ describe('ui/mouse/input', function() insert('this is bar') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| - this is ba^r | + this is ba^r{0:$} | {0:~ }| {0:~ }| | @@ -170,7 +171,7 @@ describe('ui/mouse/input', function() feed('<LeftMouse><11,0>') screen:expect{grid=[[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| - this is ba^r | + this is ba^r{0:$} | {0:~ }| {0:~ }| | @@ -178,7 +179,7 @@ describe('ui/mouse/input', function() feed('<LeftDrag><6,0>') screen:expect([[ {sel: + bar }{tab: + foo }{fill: }{tab:X}| - this is ba^r | + this is ba^r{0:$} | {0:~ }| {0:~ }| | @@ -192,7 +193,7 @@ describe('ui/mouse/input', function() insert('this is bar') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| - this is ba^r | + this is ba^r{0:$} | {0:~ }| {0:~ }| | @@ -222,7 +223,7 @@ describe('ui/mouse/input', function() insert('this is bar') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| - this is ba^r | + this is ba^r{0:$} | {0:~ }| {0:~ }| | @@ -260,7 +261,7 @@ describe('ui/mouse/input', function() insert('this is bar') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| - this is ba^r | + this is ba^r{0:$} | {0:~ }| {0:~ }| | @@ -268,7 +269,7 @@ describe('ui/mouse/input', function() feed('<LeftMouse><11,0>') screen:expect{grid=[[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| - this is ba^r | + this is ba^r{0:$} | {0:~ }| {0:~ }| | @@ -276,7 +277,7 @@ describe('ui/mouse/input', function() feed('<LeftDrag><11,1>') screen:expect{grid=[[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| - this is ba^r | + this is ba^r{0:$} | {0:~ }| {0:~ }| | @@ -284,7 +285,7 @@ describe('ui/mouse/input', function() feed('<LeftDrag><6,1>') screen:expect([[ {sel: + bar }{tab: + foo }{fill: }{tab:X}| - this is ba^r | + this is ba^r{0:$} | {0:~ }| {0:~ }| | @@ -298,7 +299,7 @@ describe('ui/mouse/input', function() insert('this is bar') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| - this is ba^r | + this is ba^r{0:$} | {0:~ }| {0:~ }| | @@ -347,7 +348,7 @@ describe('ui/mouse/input', function() insert('this is bar') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| - this is ba^r | + this is ba^r{0:$} | {0:~ }| {0:~ }| | @@ -370,7 +371,7 @@ describe('ui/mouse/input', function() insert('this is bar') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| - this is ba^r | + this is ba^r{0:$} | {0:~ }| {0:~ }| | @@ -393,7 +394,7 @@ describe('ui/mouse/input', function() insert('this is bar') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| - this is ba^r | + this is ba^r{0:$} | {0:~ }| {0:~ }| | @@ -401,7 +402,7 @@ describe('ui/mouse/input', function() feed('<2-LeftMouse><4,0>') screen:expect([[ {sel: Name] }{tab: + foo + bar }{fill: }{tab:X}| - ^ | + {0:^$} | {0:~ }| {0:~ }| | @@ -517,14 +518,14 @@ describe('ui/mouse/input', function() feed('<LeftDrag><2,2>') screen:expect([[ testing | - mo{1:use}{3: } | + mo{1:use} | {1:su}^pport and selection | {0:~ }| {2:-- VISUAL --} | ]]) feed('<LeftDrag><0,0>') screen:expect([[ - ^t{1:esting}{3: } | + ^t{1:esting} | {1:mou}se | support and selection | {0:~ }| @@ -555,7 +556,7 @@ describe('ui/mouse/input', function() feed('<LeftMouse><0,1>') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| - ^this is bar | + ^this is bar{0:$} | {0:~ }| {0:~ }| :tabprevious | @@ -563,7 +564,7 @@ describe('ui/mouse/input', function() feed('<LeftDrag><4,1>') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| - {vis:this}^ is bar | + {vis:this}^ is bar{0:$} | {0:~ }| {0:~ }| {sel:-- VISUAL --} | @@ -586,7 +587,7 @@ describe('ui/mouse/input', function() screen:expect([[ testing | mouse | - {1:su}^p{1:port and selection}{3: } | + {1:su}^p{1:port and selection} | {0:~ }| {2:-- VISUAL LINE --} | ]]) @@ -614,8 +615,8 @@ describe('ui/mouse/input', function() ]]) feed('<RightMouse><2,2>') screen:expect([[ - {1:testing}{3: } | - {1:mouse}{3: } | + {1:testing} | + {1:mouse} | {1:su}^pport and selection | {0:~ }| {2:-- VISUAL --} | |