aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/options.txt22
-rw-r--r--src/nvim/globals.h1
-rw-r--r--src/nvim/message.c3
-rw-r--r--src/nvim/option.c1
-rw-r--r--src/nvim/screen.c10
-rw-r--r--src/nvim/version.c10
-rw-r--r--test/functional/legacy/listchars_spec.lua96
7 files changed, 129 insertions, 14 deletions
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 3ef3e689a6..23284f5852 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -4440,31 +4440,35 @@ A jump table for the options with a short description can be found at |Q_op|.
{not in Vi}
Strings to use in 'list' mode and for the |:list| command. It is a
comma separated list of string settings.
- *lcs-eol*
+ *lcs-eol*
eol:c Character to show at the end of each line. When
omitted, there is no extra character at the end of the
line.
- *lcs-tab*
+ *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.
- *lcs-trail*
+ *lcs-space*
+ space:c Character to show for a space. When omitted, spaces
+ are left blank.
+ *lcs-trail*
trail:c Character to show for trailing spaces. When omitted,
- trailing spaces are blank.
- *lcs-extends*
+ trailing spaces are blank. Overrides the "space"
+ setting for trailing spaces.
+ *lcs-extends*
extends:c Character to show in the last column, when 'wrap' is
off and the line continues beyond the right of the
screen.
- *lcs-precedes*
+ *lcs-precedes*
precedes:c Character to show in the first column, when 'wrap'
is off and there is text preceding the character
visible in the first column.
- *lcs-conceal*
+ *lcs-conceal*
conceal:c Character to show in place of concealed text, when
'conceallevel' is set to 1.
- *lcs-nbsp*
+ *lcs-nbsp*
nbsp:c Character to show for a non-breakable space (character
0xA0, 160). Left blank when omitted.
@@ -4477,7 +4481,7 @@ A jump table for the options with a short description can be found at |Q_op|.
:set lcs=tab:>-,eol:<,nbsp:%
:set lcs=extends:>,precedes:<
< The "NonText" highlighting will be used for "eol", "extends" and
- "precedes". "SpecialKey" for "nbsp", "tab" and "trail".
+ "precedes". "SpecialKey" for "nbsp", "space", "tab" and "trail".
|hl-NonText| |hl-SpecialKey|
*'lpl'* *'nolpl'* *'loadplugins'* *'noloadplugins'*
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index a8c97c800d..832377f488 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -998,6 +998,7 @@ 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);
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 52b023dc5e..5efa9f6549 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -1450,6 +1450,9 @@ void msg_prt_line(char_u *s, int list)
} else if (c == ' ' && trail != NULL && s > trail) {
c = lcs_trail;
attr = hl_attr(HLF_8);
+ } else if (c == ' ' && list && lcs_space != NUL) {
+ c = lcs_space;
+ attr = hl_attr(HLF_8);
}
}
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 12118cdebc..7c28064ea0 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -4502,6 +4502,7 @@ static char_u *set_chars_option(char_u **varp)
{&lcs_ext, "extends"},
{&lcs_nbsp, "nbsp"},
{&lcs_prec, "precedes"},
+ {&lcs_space, "space"},
{&lcs_tab2, "tab"},
{&lcs_trail, "trail"},
{&lcs_conceal, "conceal"},
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index c9a2e147dc..a680599f9b 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -3165,11 +3165,11 @@ win_line (
}
++ptr;
- /* 'list' : change char 160 to lcs_nbsp. */
- if (wp->w_p_list && (c == 160
- || (mb_utf8 && mb_c == 160)
- ) && lcs_nbsp) {
- c = lcs_nbsp;
+ // 'list': change char 160 to lcs_nbsp and space to lcs_space.
+ if (wp->w_p_list
+ && (((c == 160 || (mb_utf8 && mb_c == 160)) && lcs_nbsp)
+ || (c == ' ' && lcs_space && ptr <= line + trailcol))) {
+ c = (c == ' ') ? lcs_space : lcs_nbsp;
if (area_attr == 0 && search_attr == 0) {
n_attr = 1;
extra_attr = hl_attr(HLF_8);
diff --git a/src/nvim/version.c b/src/nvim/version.c
index d1ba6e473e..a6a86f09d1 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -67,6 +67,16 @@ static char *features[] = {
// clang-format off
static int included_patches[] = {
+ 712,
+ 711,
+ 710,
+ //709,
+ //708,
+ //707,
+ //706,
+ //705,
+ //704,
+ //703,
702,
//701,
//700,
diff --git a/test/functional/legacy/listchars_spec.lua b/test/functional/legacy/listchars_spec.lua
new file mode 100644
index 0000000000..e6c64daed7
--- /dev/null
+++ b/test/functional/legacy/listchars_spec.lua
@@ -0,0 +1,96 @@
+-- Tests for 'listchars' display with 'list' and :list.
+
+local helpers = require('test.functional.helpers')
+local feed, insert, source = helpers.feed, helpers.insert, helpers.source
+local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect
+
+describe("'listchars'", function()
+ before_each(clear)
+
+ it("works with 'list'", function()
+ source([[
+ function GetScreenCharsForLine(lnum)
+ return join(map(range(1, virtcol('$')), 'nr2char(screenchar(a:lnum, v:val))'), '')
+ endfunction
+ nnoremap <expr> GG ":call add(g:lines, GetScreenCharsForLine(".screenrow()."))\<CR>"
+ ]])
+
+ insert([[
+ start:
+ aa
+ bb
+ cccc
+ dd ee
+ ]])
+
+ execute('let g:lines = []')
+
+ -- Set up 'listchars', switch on 'list', and use the "GG" mapping to record
+ -- what the buffer lines look like.
+ execute('set listchars+=tab:>-,space:.,trail:<')
+ execute('set list')
+ execute('/^start:/')
+ execute('normal! jzt')
+ feed('GG<cr>')
+ feed('GG<cr>')
+ feed('GG<cr>')
+ feed('GG<cr>')
+ feed('GGH')
+
+ -- Repeat without displaying "trail" spaces.
+ execute('set listchars-=trail:<')
+ feed('GG<cr>')
+ feed('GG<cr>')
+ feed('GG<cr>')
+ feed('GG<cr>')
+ feed('GG')
+
+ -- Delete the buffer contents and :put the collected lines.
+ execute('%d')
+ execute('put =g:lines', '1d')
+
+ -- Assert buffer contents.
+ expect([[
+ >-------aa>-----$
+ ..bb>---<<$
+ ...cccc><$
+ dd........ee<<>-$
+ <$
+ >-------aa>-----$
+ ..bb>---..$
+ ...cccc>.$
+ dd........ee..>-$
+ .$]])
+ end)
+
+ it('works with :list', function()
+ insert([[
+ start:
+ fff
+ gg
+ h
+ iii ]])
+
+ -- Set up 'listchars', switch 'list' *off* (:list must show the 'listchars'
+ -- even when 'list' is off), then run :list and collect the output.
+ execute('set listchars+=tab:>-,space:.,trail:<')
+ execute('set nolist')
+ execute('/^start:/')
+ execute('redir! => g:lines')
+ execute('+1,$list')
+ execute('redir END')
+
+ -- Delete the buffer contents and :put the collected lines.
+ execute('%d')
+ execute('put =g:lines', '1d')
+
+ -- Assert buffer contents.
+ expect([[
+
+
+ ..fff>--<<$
+ >-------gg>-----$
+ .....h>-$
+ iii<<<<><<$]])
+ end)
+end)