feat(lua): vim.text.indent()

Problem:
Indenting text is a common task in plugins/scripts for
presentation/formatting, yet vim has no way of doing it (especially
"dedent", and especially non-buffer text).

Solution:
Introduce `vim.text.indent()`. It sets the *exact* indentation because
that's a more difficult (and thus more useful) task than merely
"increasing the current indent" (which is somewhat easy with a `gsub()`
one-liner).
This commit is contained in:
Justin M. Keyes
2025-02-21 02:02:32 +01:00
parent f4921e2b7d
commit be1fbe38b3
31 changed files with 533 additions and 331 deletions

View File

@ -4624,6 +4624,41 @@ vim.text.hexencode({str}) *vim.text.hexencode()*
Return: ~
(`string`) Hex encoded string
vim.text.indent({size}, {text}, {opts}) *vim.text.indent()*
Sets the indent (i.e. the common leading whitespace) of non-empty lines in
`text` to `size` spaces/tabs.
Indent is calculated by number of consecutive indent chars.
• The first indented, non-empty line decides the indent char (space/tab):
• `SPC SPC TAB …` = two-space indent.
• `TAB SPC …` = one-tab indent.
• Set `opts.expandtab` to treat tabs as spaces.
To "dedent" (remove the common indent), pass `size=0`: >lua
vim.print(vim.text.indent(0, ' a\n b\n'))
<
To adjust relative-to an existing indent, call indent() twice: >lua
local indented, old_indent = vim.text.indent(0, ' a\n b\n')
indented = vim.text.indent(old_indent + 2, indented)
vim.print(indented)
<
To ignore the final, blank line when calculating the indent, use gsub()
before calling indent(): >lua
local text = ' a\n b\n '
vim.print(vim.text.indent(0, (text:gsub('\n[\t ]+\n?$', '\n'))))
<
Parameters: ~
• {size} (`integer`) Number of spaces.
• {text} (`string`) Text to indent.
• {opts} (`{ expandtab?: number }?`)
Return (multiple): ~
(`string`) Indented text.
(`integer`) Indent size before modification.
==============================================================================
Lua module: tohtml *vim.tohtml*

View File

@ -321,6 +321,7 @@ LUA
• |vim.fs.relpath()| gets relative path compared to base path.
• |vim.fs.dir()| and |vim.fs.find()| now follow symbolic links by default,
the behavior can be turn off using the new `follow` option.
• |vim.text.indent()| indents/dedents text.
OPTIONS

View File

@ -186,18 +186,13 @@ local function get_healthcheck(plugin_names)
return healthchecks
end
--- Indents lines *except* line 1 of a string if it contains newlines.
--- Indents lines *except* line 1 of a multiline string.
---
--- @param s string
--- @param columns integer
--- @return string
local function indent_after_line1(s, columns)
local lines = vim.split(s, '\n')
local indent = string.rep(' ', columns)
for i = 2, #lines do
lines[i] = indent .. lines[i]
end
return table.concat(lines, '\n')
return (vim.text.indent(columns, s):gsub('^%s+', ''))
end
--- Changes ':h clipboard' to ':help |clipboard|'.

View File

@ -50,4 +50,91 @@ function M.hexdecode(enc)
return table.concat(str), nil
end
--- Sets the indent (i.e. the common leading whitespace) of non-empty lines in `text` to `size`
--- spaces/tabs.
---
--- Indent is calculated by number of consecutive indent chars.
--- - The first indented, non-empty line decides the indent char (space/tab):
--- - `SPC SPC TAB …` = two-space indent.
--- - `TAB SPC …` = one-tab indent.
--- - Set `opts.expandtab` to treat tabs as spaces.
---
--- To "dedent" (remove the common indent), pass `size=0`:
--- ```lua
--- vim.print(vim.text.indent(0, ' a\n b\n'))
--- ```
---
--- To adjust relative-to an existing indent, call indent() twice:
--- ```lua
--- local indented, old_indent = vim.text.indent(0, ' a\n b\n')
--- indented = vim.text.indent(old_indent + 2, indented)
--- vim.print(indented)
--- ```
---
--- To ignore the final, blank line when calculating the indent, use gsub() before calling indent():
--- ```lua
--- local text = ' a\n b\n '
--- vim.print(vim.text.indent(0, (text:gsub('\n[\t ]+\n?$', '\n'))))
--- ```
---
--- @param size integer Number of spaces.
--- @param text string Text to indent.
--- @param opts? { expandtab?: number }
--- @return string # Indented text.
--- @return integer # Indent size _before_ modification.
function M.indent(size, text, opts)
vim.validate('size', size, 'number')
vim.validate('text', text, 'string')
vim.validate('opts', opts, 'table', true)
-- TODO(justinmk): `opts.prefix`, `predicate` like python https://docs.python.org/3/library/textwrap.html
opts = opts or {}
local tabspaces = opts.expandtab and (' '):rep(opts.expandtab) or nil
--- Minimum common indent shared by all lines.
local old_indent --[[@type number?]]
local prefix = tabspaces and ' ' or nil -- Indent char (space or tab).
--- Check all non-empty lines, capturing leading whitespace (if any).
--- @diagnostic disable-next-line: no-unknown
for line_ws, extra in text:gmatch('([\t ]*)([^\n]+)') do
line_ws = tabspaces and line_ws:gsub('[\t]', tabspaces) or line_ws
-- XXX: blank line will miss the last whitespace char in `line_ws`, so we need to check `extra`.
line_ws = line_ws .. (extra:match('^%s+$') or '')
if 0 == #line_ws then
-- Optimization: If any non-empty line has indent=0, there is no common indent.
old_indent = 0
break
end
prefix = prefix and prefix or line_ws:sub(1, 1)
local _, end_ = line_ws:find('^[' .. prefix .. ']+')
old_indent = math.min(old_indent or math.huge, end_ or 0)
end
-- Default to 0 if all lines are empty.
old_indent = old_indent or 0
prefix = prefix and prefix or ' '
if old_indent == size then
-- Optimization: if the indent is the same, return the text unchanged.
return text, old_indent
end
local new_indent = prefix:rep(size)
--- Replaces indentation of a line.
--- @param line string
local function replace_line(line)
-- Match the existing indent exactly; avoid over-matching any following whitespace.
local pat = prefix:rep(old_indent)
-- Expand tabs before replacing indentation.
line = not tabspaces and line
or line:gsub('^[\t ]+', function(s)
return s:gsub('\t', tabspaces)
end)
-- Text following the indent.
local line_text = line:match('^' .. pat .. '(.*)') or line
return new_indent .. line_text
end
return (text:gsub('[^\n]+', replace_line)), old_indent
end
return M

View File

@ -766,18 +766,8 @@ local function scope_more_doc(o)
end
--- @param x string
--- @return string
local function dedent(x)
local xs = split(x)
local leading_ws = xs[1]:match('^%s*') --[[@as string]]
local leading_ws_pat = '^' .. leading_ws
for i in ipairs(xs) do
local strip_pat = xs[i]:match(leading_ws_pat) and leading_ws_pat or '^%s*'
xs[i] = xs[i]:gsub(strip_pat, '')
end
return table.concat(xs, '\n')
return (vim.text.indent(0, (x:gsub('\n%s-([\n]?)$', '\n%1'))))
end
--- @return table<string,vim.option_meta>

View File

@ -148,10 +148,6 @@ local function url_encode(s)
)
end
local function expandtabs(s)
return s:gsub('\t', (' '):rep(8)) --[[ @as string ]]
end
local function to_titlecase(s)
local text = ''
for w in vim.gsplit(s, '[ \t]+') do
@ -275,25 +271,13 @@ end
---
--- Blank lines (empty or whitespace-only) are ignored.
local function get_indent(s)
local min_indent = nil
for line in vim.gsplit(s, '\n') do
if line and not is_blank(line) then
local ws = expandtabs(line:match('^%s+') or '')
min_indent = (not min_indent or ws:len() < min_indent) and ws:len() or min_indent
end
end
return min_indent or 0
local _, indent = vim.text.indent(0, s, { expandtab = 8 })
return indent
end
--- Removes the common indent level, after expanding tabs to 8 spaces.
local function trim_indent(s)
local indent_size = get_indent(s)
local trimmed = ''
for line in vim.gsplit(s, '\n') do
line = expandtabs(line)
trimmed = ('%s%s\n'):format(trimmed, line:sub(indent_size + 1))
end
return trimmed:sub(1, -2)
return vim.text.indent(0, s, { expandtab = 8 })
end
--- Gets raw buffer text in the node's range (+/- an offset), as a newline-delimited string.

View File

@ -342,6 +342,7 @@ set(LUA_KEYMAP_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/keymap.lua)
set(LUA_LOADER_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/loader.lua)
set(LUA_OPTIONS_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/_options.lua)
set(LUA_SHARED_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/shared.lua)
set(LUA_TEXT_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/text.lua)
file(GLOB API_HEADERS CONFIGURE_DEPENDS api/*.h)
list(REMOVE_ITEM API_HEADERS ${CMAKE_CURRENT_LIST_DIR}/api/ui_events.in.h)
@ -624,6 +625,7 @@ add_custom_command(
${LUA_DEFAULTS_MODULE_SOURCE} "vim._defaults"
${LUA_OPTIONS_MODULE_SOURCE} "vim._options"
${LUA_SHARED_MODULE_SOURCE} "vim.shared"
${LUA_TEXT_MODULE_SOURCE} "vim.text"
DEPENDS
${CHAR_BLOB_GENERATOR}
${LUA_INIT_PACKAGES_MODULE_SOURCE}
@ -637,6 +639,7 @@ add_custom_command(
${LUA_DEFAULTS_MODULE_SOURCE}
${LUA_OPTIONS_MODULE_SOURCE}
${LUA_SHARED_MODULE_SOURCE}
${LUA_TEXT_MODULE_SOURCE}
VERBATIM
)

View File

@ -496,10 +496,12 @@ a]],
it("fdm=indent doesn't open folds when inserting lower foldlevel line", function()
insert([[
insert an unindented line under this line
keep the lines under this line folded
keep this line folded 1
keep this line folded 2
insert an unindented line under this line
keep the lines under this line folded
keep this line folded 1
keep this line folded 2
.
]])
command('set foldmethod=indent shiftwidth=2 noautoindent')
eq(1, fn.foldlevel(1))

View File

@ -227,7 +227,7 @@ describe(':source', function()
feed('GVkk')
feed_command(':source')
eq(' "\\ a\n \\ b', exec_lua('return _G.a'))
eq(' "\\ a\n \\ b', exec_lua('return _G.a'))
end)
it('whole buffer', function()
@ -247,7 +247,7 @@ describe(':source', function()
feed_command(':source')
eq(12, eval('g:c'))
eq(' \\ 1\n "\\ 2', exec_lua('return _G.a'))
eq(' \\ 1\n "\\ 2', exec_lua('return _G.a'))
eq(':source (no file)', api.nvim_get_var('sfile_value'))
eq(':source (no file)', api.nvim_get_var('stack_value'))
eq(':source (no file)', api.nvim_get_var('script_value'))

View File

@ -194,8 +194,7 @@ pcall(vim.cmd.edit, 'Xtest_swapredraw.lua')
{100:vim.o.foldexpr} {100:=} {101:'v:lua.vim.treesitter.foldexpr()'} |
{102:+-- 3 lines: vim.defer_fn(function()·······························································}|
{104:pcall}{100:(vim.cmd.edit,} {101:'Xtest_swapredraw.lua'}{100:)} |
|
{105:~ }|*33
{105:~ }|*34
{106:Xtest_swapredraw.lua 1,1 All}|
|
]])
@ -589,8 +588,8 @@ describe('quitting swapfile dialog on startup stops TUI properly', function()
api.nvim_chan_send(chan, 'q')
retry(nil, nil, function()
eq(
{ '', '[Process exited 1]', '' },
eval("[1, 2, '$']->map({_, lnum -> getline(lnum)->trim(' ', 2)})")
{ '[Process exited 1]' },
eval("[1, 2, '$']->map({_, lnum -> getline(lnum)->trim(' ', 2)})->filter({_, s -> !empty(trim(s))})")
)
end)
end)

View File

@ -6,6 +6,7 @@ local n = require('test.functional.testnvim')()
local clear, command, expect = n.clear, n.command, n.expect
local source, write_file = n.source, t.write_file
--- @return string
local function sixlines(text)
local result = ''
for _ = 1, 6 do
@ -16,6 +17,9 @@ end
local function diff(text, nodedent)
local fname = t.tmpname()
finally(function()
os.remove(fname)
end)
command('w! ' .. fname)
n.poke_eventloop()
local data = io.open(fname):read('*all')
@ -24,7 +28,6 @@ local function diff(text, nodedent)
else
t.eq(t.dedent(text), data)
end
os.remove(fname)
end
describe('character classes in regexp', function()
@ -38,7 +41,7 @@ describe('character classes in regexp', function()
local punct4 = '{|}~'
local ctrl2 = '\127\128\130\144\155'
local iso_text = '\166\177\188\199\211\233' -- "¦±¼ÇÓé" in utf-8
setup(function()
local function do_setup(no_dedent)
-- The original test32.in file was not in utf-8 encoding and did also
-- contain some control characters. We use lua escape sequences to write
-- them to the test file.
@ -52,8 +55,9 @@ describe('character classes in regexp', function()
.. punct4
.. ctrl2
.. iso_text
write_file('test36.in', sixlines(line))
end)
write_file('test36.in', sixlines(line), no_dedent)
end
setup(do_setup)
before_each(function()
clear()
command('e test36.in')
@ -288,16 +292,18 @@ describe('character classes in regexp', function()
ABCDEFGHIXYZ
ABCDEFGHIXYZ]])
end)
it([["\%1l^#.*" does not match on a line starting with "#". (vim-patch:7.4.1305)]], function()
source([[
pending(
[["\%1l^#.*" does not match on a line starting with "#". (vim-patch:7.4.1305)]],
function()
-- do_setup(true)
source([[
1 s/\%#=0\%1l^\t...//g
2 s/\%#=1\%2l^\t...//g
3 s/\%#=2\%3l^\t...//g
4 s/\%#=0\%4l^\t...//g
5 s/\%#=1\%5l^\t...//g
6 s/\%#=2\%6l^\t...//g]])
diff(
sixlines(
local text = sixlines(
string.sub(punct1, 1)
.. digits
.. punct2
@ -308,8 +314,9 @@ describe('character classes in regexp', function()
.. ctrl2
.. iso_text
)
)
end)
diff(text)
end
)
it('does not convert character class ranges to an incorrect class', function()
source([[
1 s/\%#=0[0-z]//g
@ -319,9 +326,9 @@ describe('character classes in regexp', function()
5 s/\%#=1[^0-z]//g
6 s/\%#=2[^0-z]//g
]])
diff(
string.rep(ctrl1 .. punct1 .. punct4 .. ctrl2 .. iso_text .. '\n', 3)
.. string.rep(digits .. punct2 .. upper .. punct3 .. lower .. '\n', 3)
)
local text = string.rep(ctrl1 .. punct1 .. punct4 .. ctrl2 .. iso_text .. '\n', 3)
.. string.rep(digits .. punct2 .. upper .. punct3 .. lower .. '\n', 3)
text = text:gsub('\t', ''):gsub('\n\t', '\n')
diff(text)
end)
end)

View File

@ -112,7 +112,8 @@ describe('Visual block mode', function()
line1
line2
line3
]])
.
]])
-- Test for Visual block insert when virtualedit=all and utf-8 encoding.
feed_command('set ve=all')
@ -123,7 +124,8 @@ describe('Visual block mode', function()
x line1
x line2
x line3
]])
.
]])
-- Test for Visual block append when virtualedit=all.
feed('012l<C-v>jjAx<ESC>')
@ -132,7 +134,8 @@ describe('Visual block mode', function()
x x line1
x x line2
x x line3
]])
.
]])
end)
it('should make a selected part uppercase', function()

View File

@ -13,9 +13,7 @@ local eval = n.eval
local eq = t.eq
local function expect_empty_buffer()
-- The space will be removed by t.dedent but is needed because dedent
-- will fail if it can not find the common indent of the given lines.
return expect(' ')
return expect('')
end
local function expect_line(line)
return eq(line, eval('getline(".")'))

View File

@ -200,6 +200,7 @@ describe('eval', function()
abcFc=]])
end)
-- luacheck: ignore 611 (Line contains only whitespace)
it('appending NL with setreg()', function()
command('so test_eval_setup.vim')
@ -222,6 +223,7 @@ describe('eval', function()
command([[call SetReg('D', "\n", 'l')]])
command([[call SetReg('E', "\n")]])
command([[call SetReg('F', "\n", 'b')]])
command("$put ='.'")
expect([[
{{{2 setreg('A', ']] .. '\000' .. [[')
@ -256,7 +258,8 @@ describe('eval', function()
F: type ]] .. "\0220; value: abcF2\000 (['abcF2', '']), expr: abcF2\000" .. [[ (['abcF2', ''])
==
=abcF2=
]])
.]])
end)
it('setting and appending list with setreg()', function()

View File

@ -62,10 +62,10 @@ describe('folding', function()
n.poke_eventloop()
screen:expect([[
dd {{{ |
ee {{{ }}} |
dd {{{ |
ee {{{ }}} |
{{{ |
ff }}} |*2
ff }}} |*2
^ |
line 2 foldlevel=2 |
1 |*2

View File

@ -62,12 +62,12 @@ describe("'listchars'", function()
..bb>---<<$
...cccc><$
dd........ee<<>-$
<$
$
>-------aa>-----$
..bb>---..$
...cccc>.$
dd........ee..>-$
.$]])
$]])
end)
it('works with :list', function()

View File

@ -153,16 +153,16 @@ describe('vim.secure', function()
feed('v')
screen:expect {
grid = [[
^let g:foobar = 42 |
{1:~ }|*2
{2:]]
^let g:foobar = 42 |
{1:~ }|*2
{2:]]
.. fn.fnamemodify(cwd, ':~')
.. pathsep
.. [[Xfile [RO]{MATCH:%s+}}|
|
{1:~ }|
{4:[No Name] }|
|
|
{1:~ }|
{4:[No Name] }|
|
]],
}

View File

@ -7,7 +7,133 @@ local eq = t.eq
describe('vim.text', function()
before_each(clear)
describe('hexencode() and hexdecode()', function()
describe('indent()', function()
it('validation', function()
t.matches('size%: expected number, got string', t.pcall_err(vim.text.indent, 'x', 'x'))
t.matches('size%: expected number, got nil', t.pcall_err(vim.text.indent, nil, 'x'))
t.matches('opts%: expected table, got string', t.pcall_err(vim.text.indent, 0, 'x', 'z'))
end)
it('basic cases', function()
-- Basic cases.
eq({ '', 0 }, { vim.text.indent(0, '') })
eq({ '', 0 }, { vim.text.indent(2, '') })
eq({ ' a', 4 }, { vim.text.indent(2, ' a') })
eq({ ' a\n b', 4 }, { vim.text.indent(2, ' a\n b') })
eq({ '\t\ta', 1 }, { vim.text.indent(2, '\ta') })
eq({ ' a\n\n', 5 }, { vim.text.indent(1, ' a\n\n') })
-- Indent 1 (tab) => 0. Starting with empty + blank lines.
eq({ '\n\naa a aa', 1 }, { vim.text.indent(0, '\n \n aa a aa') })
-- Indent 1 (tab) => 2 (tabs). Starting with empty + blank lines, 1-tab indent.
eq({ '\n\t\t\n\t\taa a aa', 1 }, { vim.text.indent(2, '\n\t\n\taa a aa') })
-- Indent 4 => 2, expandtab=false preserves tabs after the common indent.
eq(
{ ' foo\n bar\n \tbaz\n', 4 },
{ vim.text.indent(2, ' foo\n bar\n \tbaz\n') }
)
-- Indent 9 => 3, expandtab=true.
eq(
{ ' foo\n\n bar \t baz\n', 9 },
{ vim.text.indent(3, '\t foo\n\n bar \t baz\n', { expandtab = 8 }) }
)
-- Indent 9 => 8, expandtab=true.
eq(
{ ' foo\n\n bar\n', 9 },
{ vim.text.indent(8, '\t foo\n\n bar\n', { expandtab = 8 }) }
)
-- Dedent: 5 => 0.
eq({ ' foo\n\nbar\n', 5 }, { vim.text.indent(0, ' foo\n\n bar\n') })
-- Dedent: 1 => 0. Empty lines are ignored when deciding "common indent".
eq(
{ ' \n \nfoo\n\nbar\nbaz\n \n', 1 },
{ vim.text.indent(0, ' \n \n foo\n\n bar\n baz\n \n') }
)
end)
it('real-world cases', function()
-- Dedent.
eq({
[[
bufs:
nvim args: 3
lua args: {
[0] = "foo.lua"
}
]],
10,
}, {
vim.text.indent(
0,
[[
bufs:
nvim args: 3
lua args: {
[0] = "foo.lua"
}
]]
),
})
-- Indent 0 => 2.
eq({
[[
# yay
local function foo()
if true then
# yay
end
end
return
]],
0,
}, {
vim.text.indent(
2,
[[
# yay
local function foo()
if true then
# yay
end
end
return
]]
),
})
-- 1-tab indent, last line spaces < tabsize.
-- Preserves tab char immediately following the indent.
eq({ 'text\n\tmatch\nmatch\ntext\n', 1 }, {
vim.text.indent(0, (([[
text
match
match
text
]]):gsub('\n%s-([\n]?)$', '\n%1'))),
})
-- 1-tab indent, last line spaces=tabsize.
eq({ 'text\n match\nmatch\ntext\n', 6 }, {
vim.text.indent(
0,
[[
text
match
match
text
]],
{ expandtab = 6 }
),
})
end)
end)
describe('hexencode(), hexdecode()', function()
it('works', function()
local cases = {
{ 'Hello world!', '48656C6C6F20776F726C6421' },
@ -21,13 +147,13 @@ describe('vim.text', function()
end
end)
it('works with very large strings', function()
it('with very large strings', function()
local input, output = string.rep('😂', 2 ^ 16), string.rep('F09F9882', 2 ^ 16)
eq(output, vim.text.hexencode(input))
eq(input, vim.text.hexdecode(output))
end)
it('errors on invalid input', function()
it('invalid input', function()
-- Odd number of hex characters
do
local res, err = vim.text.hexdecode('ABC')

View File

@ -316,13 +316,13 @@ test text
local grid_without_inlay_hints = [[
test text |
^ |
^ |
|
]]
local grid_with_inlay_hints = [[
{1:01234}test text |
^ |
^ |
|
]]

View File

@ -609,12 +609,13 @@ function M._new_argv(...)
return args, env, io_extra
end
--- Dedents string arguments and inserts the resulting text into the current buffer.
--- @param ... string
function M.insert(...)
nvim_feed('i')
for _, v in ipairs({ ... }) do
local escaped = v:gsub('<', '<lt>')
M.feed(escaped)
M.feed(escaped) -- This also dedents :P
end
nvim_feed('<ESC>')
end
@ -812,6 +813,7 @@ function M.rmdir(path)
end
end
--- @deprecated Use `t.pcall_err()` to check failure, or `n.command()` to check success.
function M.exc_exec(cmd)
M.command(([[
try

View File

@ -786,9 +786,9 @@ describe('treesitter highlighting (C)', function()
screen:expect({
grid = [[
{26:int x = 4;} |
{26:int y = 5;} |
{26:int z = 6;} |
{26:int x = 4;} |
{26:int y = 5;} |
{26:int z = 6;} |
^ |
{1:~ }|*13
|
@ -815,7 +815,7 @@ describe('treesitter highlighting (C)', function()
screen:expect({
grid = [[
void foo(int {15:*}{25:bar}); |
void foo(int {15:*}{25:bar}); |
^ |
{1:~ }|*15
|
@ -883,8 +883,8 @@ describe('treesitter highlighting (lua)', function()
screen:expect({
grid = [[
{15:local} {25:ffi} {15:=} {16:require(}{26:'ffi'}{16:)} |
{25:ffi}{16:.}{25:cdef}{16:(}{26:"}{16:int}{26: }{16:(}{15:*}{26:fun}{16:)(int,}{26: }{16:char}{26: }{15:*}{16:);}{26:"}{16:)} |
{15:local} {25:ffi} {15:=} {16:require(}{26:'ffi'}{16:)} |
{25:ffi}{16:.}{25:cdef}{16:(}{26:"}{16:int}{26: }{16:(}{15:*}{26:fun}{16:)(int,}{26: }{16:char}{26: }{15:*}{16:);}{26:"}{16:)} |
^ |
{1:~ }|*14
|
@ -1185,7 +1185,7 @@ printf('Hello World!');
{18:```}{15:c} |
{25:printf}{16:(}{26:'Hello World!'}{16:);} |
{18:```} |
^ |
^ |
|
]],
})
@ -1271,7 +1271,7 @@ printf('Hello World!');
{8:120 }{25:printf}{16:(}{26:'Hello World!'}{16:);} |
{8:122 } |
{8:124 }{25:printf}{16:(}{26:'Hello World!'}{16:);} |
{8:126 } ^ |
{8:126 }^ |
|
]])
end)

View File

@ -82,7 +82,7 @@ describe('treesitter node API', function()
]])
exec_lua(function()
local parser = vim.treesitter.get_parser(0, 'c')
local parser = assert(vim.treesitter.get_parser(0, 'c'))
local tree = parser:parse()[1]
_G.root = tree:root()
vim.treesitter.language.inspect('c')
@ -92,7 +92,7 @@ describe('treesitter node API', function()
end
end)
exec_lua 'node = root:descendant_for_range(0, 11, 0, 16)'
exec_lua 'node = root:descendant_for_range(0, 9, 0, 14)'
eq('int x', lua_eval('node_text(node)'))
exec_lua 'node = node:next_sibling()'

View File

@ -386,8 +386,8 @@ void ui_refresh(void)
[[((primitive_type) @c-keyword (#any-of? @c-keyword "int" "float"))]]
)
eq({
{ 'c-keyword', 'primitive_type', { 2, 2, 2, 5 }, 'int' },
{ 'c-keyword', 'primitive_type', { 3, 4, 3, 7 }, 'int' },
{ 'c-keyword', 'primitive_type', { 2, 0, 2, 3 }, 'int' },
{ 'c-keyword', 'primitive_type', { 3, 2, 3, 5 }, 'int' },
}, res0)
local res1 = exec_lua(
@ -401,9 +401,9 @@ void ui_refresh(void)
]]
)
eq({
{ 'fizzbuzz-strings', 'string_literal', { 6, 15, 6, 38 }, '"number= %d FizzBuzz\\n"' },
{ 'fizzbuzz-strings', 'string_literal', { 8, 15, 8, 34 }, '"number= %d Fizz\\n"' },
{ 'fizzbuzz-strings', 'string_literal', { 10, 15, 10, 34 }, '"number= %d Buzz\\n"' },
{ 'fizzbuzz-strings', 'string_literal', { 6, 13, 6, 36 }, '"number= %d FizzBuzz\\n"' },
{ 'fizzbuzz-strings', 'string_literal', { 8, 13, 8, 32 }, '"number= %d Fizz\\n"' },
{ 'fizzbuzz-strings', 'string_literal', { 10, 13, 10, 32 }, '"number= %d Buzz\\n"' },
}, res1)
end)
@ -608,9 +608,9 @@ void ui_refresh(void)
eq(
{
{ 0, 2, 0, 8 },
{ 1, 2, 1, 8 },
{ 2, 2, 2, 8 },
{ 0, 0, 0, 6 },
{ 1, 0, 1, 6 },
{ 2, 0, 2, 6 },
},
test(
[[
@ -636,9 +636,9 @@ void ui_refresh(void)
eq(
{
{ 0, 2, 0, 7 },
{ 1, 2, 1, 8 },
{ 2, 2, 2, 7 },
{ 0, 0, 0, 5 },
{ 1, 0, 1, 6 },
{ 2, 0, 2, 5 },
},
test(
[[
@ -675,9 +675,9 @@ void ui_refresh(void)
end)
eq({
{ 0, 2, 0, 12 },
{ 1, 2, 1, 12 },
{ 2, 2, 2, 12 },
{ 0, 0, 0, 10 },
{ 1, 0, 1, 10 },
{ 2, 0, 2, 10 },
}, result)
end)

View File

@ -2041,11 +2041,11 @@ describe('float window', function()
[2:----------------------------------------]|*6
[3:----------------------------------------]|
## grid 2
neeed some dummy |
background text |
to show the effect |
of color blending |
of border shadow |
neeed some dummy |
background text |
to show the effect |
of color blending |
of border shadow |
^ |
## grid 3
|
@ -2065,11 +2065,11 @@ describe('float window', function()
}}
else
screen:expect{grid=[[
neeed some dummy |
background text |
to {1: halloj! }{23:e}ffect |
of {1: BORDAA }{24:n}ding |
of {23:b}{24:order sha}dow |
neeed some dummy |
background text |
to sh{1: halloj! }{23:f}ect |
of co{1: BORDAA }{24:i}ng |
of bo{23:r}{24:der shado}w |
^ |
|
]]}

View File

@ -502,9 +502,7 @@ describe('ext_hlstate detailed highlights', function()
local num_lines = 500
insert('first line\n')
for _ = 1, num_lines do
insert([[
line
]])
api.nvim_paste(' line\n', false, -1)
end
insert('last line')

View File

@ -281,14 +281,14 @@ describe("'inccommand' for user commands", function()
]])
feed('<Esc>')
screen:expect([[
text on line 1 |
more text on line 2 |
oh no, even more text |
will the text ever stop |
oh well |
did the text stop |
why won't it stop |
make the text stop |
text on line 1 |
more text on line 2 |
oh no, even more text |
will the text ever stop |
oh well |
did the text stop |
why won't it stop |
make the text stop |
^ |
{1:~ }|*7
|
@ -301,14 +301,14 @@ describe("'inccommand' for user commands", function()
command('set inccommand=nosplit')
feed(':Replace text cats')
screen:expect([[
{10:cats} on line 1 |
more {10:cats} on line 2 |
oh no, even more {10:cats} |
will the {10:cats} ever stop |
oh well |
did the {10:cats} stop |
why won't it stop |
make the {10:cats} stop |
{10:cats} on line 1 |
more {10:cats} on line 2 |
oh no, even more {10:cats} |
will the {10:cats} ever stop |
oh well |
did the {10:cats} stop |
why won't it stop |
make the {10:cats} stop |
|
{1:~ }|*7
:Replace text cats^ |
@ -319,21 +319,21 @@ describe("'inccommand' for user commands", function()
command('set inccommand=split')
feed(':Replace text cats')
screen:expect([[
{10:cats} on line 1 |
more {10:cats} on line 2 |
oh no, even more {10:cats} |
will the {10:cats} ever stop |
oh well |
did the {10:cats} stop |
why won't it stop |
make the {10:cats} stop |
{10:cats} on line 1 |
more {10:cats} on line 2 |
oh no, even more {10:cats} |
will the {10:cats} ever stop |
oh well |
did the {10:cats} stop |
why won't it stop |
make the {10:cats} stop |
|
{3:[No Name] [+] }|
|1| {10:cats} on line 1 |
|2| more {10:cats} on line 2 |
|3| oh no, even more {10:cats} |
|4| will the {10:cats} ever stop |
|6| did the {10:cats} stop |
|1| {10:cats} on line 1 |
|2| more {10:cats} on line 2 |
|3| oh no, even more {10:cats} |
|4| will the {10:cats} ever stop |
|6| did the {10:cats} stop |
{2:[Preview] }|
:Replace text cats^ |
]])
@ -343,14 +343,14 @@ describe("'inccommand' for user commands", function()
command('set inccommand=split')
feed(':Replace text cats<Esc>')
screen:expect([[
text on line 1 |
more text on line 2 |
oh no, even more text |
will the text ever stop |
oh well |
did the text stop |
why won't it stop |
make the text stop |
text on line 1 |
more text on line 2 |
oh no, even more text |
will the text ever stop |
oh well |
did the text stop |
why won't it stop |
make the text stop |
^ |
{1:~ }|*7
|
@ -361,14 +361,14 @@ describe("'inccommand' for user commands", function()
command('set inccommand=split')
feed(':Replace text cats<CR>')
screen:expect([[
cats on line 1 |
more cats on line 2 |
oh no, even more cats |
will the cats ever stop |
oh well |
did the cats stop |
why won't it stop |
make the cats stop |
cats on line 1 |
more cats on line 2 |
oh no, even more cats |
will the cats ever stop |
oh well |
did the cats stop |
why won't it stop |
make the cats stop |
^ |
{1:~ }|*7
:Replace text cats |
@ -379,14 +379,14 @@ describe("'inccommand' for user commands", function()
command('set inccommand=split')
feed('gg:.Replace text cats')
screen:expect([[
{10:cats} on line 1 |
more text on line 2 |
oh no, even more text |
will the text ever stop |
oh well |
did the text stop |
why won't it stop |
make the text stop |
{10:cats} on line 1 |
more text on line 2 |
oh no, even more text |
will the text ever stop |
oh well |
did the text stop |
why won't it stop |
make the text stop |
|
{1:~ }|*7
:.Replace text cats^ |
@ -432,14 +432,14 @@ describe("'inccommand' for user commands", function()
]])
feed(':C')
screen:expect([[
{10: cats on line 1} |
more cats on line 2 |
oh no, even more cats |
will the cats ever stop |
oh well |
did the cats stop |
why won't it stop |
make the cats stop |
{10:cats on line 1} |
more cats on line 2 |
oh no, even more cats |
will the cats ever stop |
oh well |
did the cats stop |
why won't it stop |
make the cats stop |
|
{1:~ }|*7
:C^ |
@ -482,42 +482,42 @@ describe("'inccommand' for user commands", function()
]])
feed(':Test a.a.a.a.')
screen:expect([[
text on line 1 |
more text on line 2 |
oh no, even more text |
will the text ever stop |
oh well |
did the text stop |
why won't it stop |
make the text stop |
text on line 1 |
more text on line 2 |
oh no, even more text |
will the text ever stop |
oh well |
did the text stop |
why won't it stop |
make the text stop |
a.a.a.a. |
{1:~ }|*7
:Test a.a.a.a.^ |
]])
feed('<C-V><Esc>u')
screen:expect([[
text on line 1 |
more text on line 2 |
oh no, even more text |
will the text ever stop |
oh well |
did the text stop |
why won't it stop |
make the text stop |
text on line 1 |
more text on line 2 |
oh no, even more text |
will the text ever stop |
oh well |
did the text stop |
why won't it stop |
make the text stop |
a.a.a. |
{1:~ }|*7
:Test a.a.a.a.{18:^[}u^ |
]])
feed('<Esc>')
screen:expect([[
text on line 1 |
more text on line 2 |
oh no, even more text |
will the text ever stop |
oh well |
did the text stop |
why won't it stop |
make the text stop |
text on line 1 |
more text on line 2 |
oh no, even more text |
will the text ever stop |
oh well |
did the text stop |
why won't it stop |
make the text stop |
^ |
{1:~ }|*7
|
@ -572,12 +572,12 @@ describe("'inccommand' for user commands", function()
screen:expect({
grid = [[
Preview |
oh no, even more text |
will the text ever stop |
oh well |
did the text stop |
why won't it stop |
make the text stop |
oh no, even more text |
will the text ever stop |
oh well |
did the text stop |
why won't it stop |
make the text stop |
|
{1:~ }|*8
:Test barb^az |
@ -611,9 +611,9 @@ describe("'inccommand' with multiple buffers", function()
command('set inccommand=nosplit')
feed(':Replace foo bar')
screen:expect([[
bar baz {10:bar} {10:bar} bar baz |
baz {10:bar} bar bar baz {10:bar} |
{10:bar} bar baz baz {10:bar} bar |
bar baz {10:bar} {10:bar} bar baz |
baz {10:bar} bar bar baz {10:bar} |
{10:bar} bar baz baz {10:bar} bar |
│ |
{1:~ }│{1:~ }|*11
{3:[No Name] [+] }{2:[No Name] [+] }|
@ -621,9 +621,9 @@ describe("'inccommand' with multiple buffers", function()
]])
feed('<CR>')
screen:expect([[
bar baz bar bar bar baz |
baz bar bar bar baz bar |
bar bar baz baz bar bar |
bar baz bar bar bar baz |
baz bar bar bar baz bar |
bar bar baz baz bar bar |
^ │ |
{1:~ }│{1:~ }|*11
{3:[No Name] [+] }{2:[No Name] [+] }|
@ -635,19 +635,19 @@ describe("'inccommand' with multiple buffers", function()
command('set inccommand=split')
feed(':Replace foo bar')
screen:expect([[
bar baz {10:bar} {10:bar} bar baz |
baz {10:bar} bar bar baz {10:bar} |
{10:bar} bar baz baz {10:bar} bar |
bar baz {10:bar} {10:bar} bar baz |
baz {10:bar} bar bar baz {10:bar} |
{10:bar} bar baz baz {10:bar} bar |
│ |
{3:[No Name] [+] }{2:[No Name] [+] }|
Buffer #1: |
|1| {10:bar} bar baz |
|2| bar baz {10:bar} |
|3| baz {10:bar} bar |
|1| {10:bar} bar baz |
|2| bar baz {10:bar} |
|3| baz {10:bar} bar |
Buffer #2: |
|1| bar baz {10:bar} |
|2| baz {10:bar} bar |
|3| {10:bar} bar baz |
|1| bar baz {10:bar} |
|2| baz {10:bar} bar |
|3| {10:bar} bar baz |
|
{1:~ }|
{2:[Preview] }|
@ -655,9 +655,9 @@ describe("'inccommand' with multiple buffers", function()
]])
feed('<CR>')
screen:expect([[
bar baz bar bar bar baz |
baz bar bar bar baz bar |
bar bar baz baz bar bar |
bar baz bar bar bar baz |
baz bar bar bar baz bar |
bar bar baz baz bar bar |
^ │ |
{1:~ }│{1:~ }|*11
{3:[No Name] [+] }{2:[No Name] [+] }|

View File

@ -95,8 +95,7 @@ describe('Diff mode screen with 3 diffs open', function()
{7: }{8: 9 }{4: BBB }│{7: }{8: 9 }{4: BBB }│{7: }{8: }{23:---------------------------}|
{7: }{8: 10 }{4: BBB }│{7: }{8: 10 }{4: BBB }│{7: }{8: }{23:---------------------------}|
{7: }{8: 11 }{4:>>>>>>> branch1 }│{7: }{8: 11 }{4:>>>>>>> branch1 }│{7: }{8: }{23:---------------------------}|
{7: }{8: 12 } │{7: }{8: 12 } │{7: }{8: 6 } |
{1:~ }│{1:~ }│{1:~ }|*2
{1:~ }│{1:~ }│{1:~ }|*3
{3:<-functional-diff-screen-1.3 [+] }{2:<est-functional-diff-screen-1.2 Xtest-functional-diff-screen-1 }|
:2,6diffget screen-1.2 |
]])
@ -114,8 +113,7 @@ describe('Diff mode screen with 3 diffs open', function()
{7: }{8: 4 }{4: }{27:BBB}{4: }│{7: }{8: 6 }{4: }{27:BBB}{4: }│{7: }{8: 4 }{4: }{27:AAA}{4: }|
{7: }{8: 5 }{4: }{27:BBB}{4: }│{7: }{8: 7 }{4: }{27:BBB}{4: }│{7: }{8: 5 }{4: }{27:AAA}{4: }|
{7: }{8: }{23:---------------------------}│{7: }{8: 8 }{22:>>>>>>> branch1 }│{7: }{8: }{23:---------------------------}|
{7: }{8: 6 } │{7: }{8: 9 } │{7: }{8: 6 } |
{1:~ }│{1:~ }│{1:~ }|*5
{1:~ }│{1:~ }│{1:~ }|*6
{2:<test-functional-diff-screen-1.3 }{3:<functional-diff-screen-1.2 [+] }{2:Xtest-functional-diff-screen-1 }|
:5,7diffget screen-1.3 |
]])
@ -136,8 +134,7 @@ describe('Diff mode screen with 3 diffs open', function()
{7: }{8: 4 } BBB │{7: }{8: 9 } BBB │{7: }{8: 8 } BBB |
{7: }{8: 5 } BBB │{7: }{8: 10 } BBB │{7: }{8: 9 } BBB |
{7: }{8: }{23:---------------------------}│{7: }{8: 11 }{4:>>>>>>> branch1 }│{7: }{8: 10 }{4:>>>>>>> branch1 }|
{7: }{8: 6 } │{7: }{8: 12 } │{7: }{8: 11 } |
{1:~ }│{1:~ }│{1:~ }|*2
{1:~ }│{1:~ }│{1:~ }|*3
{2:<test-functional-diff-screen-1.3 <est-functional-diff-screen-1.2 }{3:<st-functional-diff-screen-1 [+] }|
:5,6diffget screen-1.2 |
]])
@ -158,8 +155,7 @@ describe('Diff mode screen with 3 diffs open', function()
{7: }{8: 4 }{4: BBB }│{7: }{8: 9 }{4: BBB }│{7: }{8: }{23:---------------------------}|
{7: }{8: 5 } BBB │{7: }{8: 10 } BBB │{7: }{8: 7 } BBB |
{7: }{8: }{23:---------------------------}│{7: }{8: 11 }{22:>>>>>>> branch1 }│{7: }{8: }{23:---------------------------}|
{7: }{8: 6 } │{7: }{8: 12 } │{7: }{8: 8 } |
{1:~ }│{1:~ }│{1:~ }|*2
{1:~ }│{1:~ }│{1:~ }|*3
{2:<test-functional-diff-screen-1.3 }{3:<est-functional-diff-screen-1.2 }{2:<st-functional-diff-screen-1 [+] }|
:6,8diffput screen-1 |
]])
@ -179,8 +175,7 @@ describe('Diff mode screen with 3 diffs open', function()
{7: }{8: 4 } BBB │{7: }{8: 9 } BBB │{7: }{8: 8 } BBB |
{7: }{8: 5 } BBB │{7: }{8: 10 } BBB │{7: }{8: 9 } BBB |
{7: }{8: }{23:---------------------------}│{7: }{8: 11 }{4:>>>>>>> branch1 }│{7: }{8: 10 }{4:>>>>>>> branch1 }|
{7: }{8: 6 } │{7: }{8: 12 } │{7: }{8: 11 } |
{1:~ }│{1:~ }│{1:~ }|*2
{1:~ }│{1:~ }│{1:~ }|*3
{2:<test-functional-diff-screen-1.3 }{3:<est-functional-diff-screen-1.2 }{2:<st-functional-diff-screen-1 [+] }|
:6,11diffput screen-1 |
]])
@ -276,8 +271,7 @@ something
{7: }{8: 14 }common line │{7: }{8: 15 }common line |
{7: }{8: }{23:-------------------------------------------}│{7: }{8: 16 }{22:DEF }|
{7: }{8: 15 }something │{7: }{8: 17 }something |
{7: }{8: 16 } │{7: }{8: 18 } |
{1:~ }│{1:~ }|*6
{1:~ }│{1:~ }|*7
{3:Xtest-functional-diff-screen-1.2 [+] }{2:Xtest-functional-diff-screen-1 }|
:5,9diffget |
]])
@ -300,8 +294,7 @@ something
{7: }{8: 11 }common line │{7: }{8: 12 }common line |
{7: }{8: }{23:-------------------------------------------}│{7: }{8: 13 }{22:DEF }|
{7: }{8: 12 }something │{7: }{8: 14 }something |
{7: }{8: 13 } │{7: }{8: 15 } |
{1:~ }│{1:~ }|*3
{1:~ }│{1:~ }|*4
{2:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }|
:5,10diffget |
]])
@ -322,8 +315,7 @@ something
{7: }{8: 10 }common line │{7: }{8: 10 }common line |
{7: }{8: 11 }common line │{7: }{8: 11 }common line |
{7: }{8: 12 }something │{7: }{8: 12 }something |
{7: }{8: 13 } │{7: }{8: 13 } |
{1:~ }│{1:~ }|*5
{1:~ }│{1:~ }|*6
{2:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }|
:4,17diffget |
]])
@ -349,7 +341,7 @@ something
{7: }{8: 15 }common line │{7: }{8: 15 }common line |
{7: }{8: 16 }DEF │{7: }{8: 16 }DEF |
{7: }{8: 17 }something │{7: }{8: 17 }something |
{7: }{8: 18 } │{7: }{8: 18 } |
{1:~ }│{1:~ }|
{3:Xtest-functional-diff-screen-1.2 [+] }{2:Xtest-functional-diff-screen-1 }|
:4,12diffget |
]])
@ -376,7 +368,7 @@ something
{7: }{8: 11 }common line │{7: }{8: 15 }common line |
{7: }{8: }{23:-------------------------------------------}│{7: }{8: 16 }{22:DEF }|
{7: }{8: 12 }something │{7: }{8: 17 }something |
{7: }{8: 13 } │{7: }{8: 18 } |
{1:~ }│{1:~ }|
{3:Xtest-functional-diff-screen-1.2 [+] }{2:Xtest-functional-diff-screen-1 }|
:e |
]])
@ -403,7 +395,7 @@ something
{7: }{8: 11 }common line │{7: }{8: 15 }common line |
{7: }{8: }{23:-------------------------------------------}│{7: }{8: 16 }{22:DEF }|
{7: }{8: 12 }something │{7: }{8: 17 }something |
{7: }{8: 13 } │{7: }{8: 18 } |
{1:~ }│{1:~ }|
{3:Xtest-functional-diff-screen-1.2 [+] }{2:Xtest-functional-diff-screen-1 }|
:e |
]])
@ -430,7 +422,7 @@ something
{7: }{8: 11 }common line │{7: }{8: 15 }common line |
{7: }{8: }{23:-------------------------------------------}│{7: }{8: 16 }{22:DEF }|
{7: }{8: 12 }something │{7: }{8: 17 }something |
{7: }{8: 13 } │{7: }{8: 18 } |
{1:~ }│{1:~ }|
{3:Xtest-functional-diff-screen-1.2 [+] }{2:Xtest-functional-diff-screen-1 }|
:e |
]])
@ -457,7 +449,7 @@ something
{7: }{8: 12 }^common line │{7: }{8: 15 }common line |
{7: }{8: }{23:-------------------------------------------}│{7: }{8: 16 }{22:DEF }|
{7: }{8: 13 }something │{7: }{8: 17 }something |
{7: }{8: 14 } │{7: }{8: 18 } |
{1:~ }│{1:~ }|
{3:Xtest-functional-diff-screen-1.2 [+] }{2:Xtest-functional-diff-screen-1 }|
:e |
]])
@ -484,7 +476,7 @@ something
{7: }{8: 11 }common line │{7: }{8: 15 }common line |
{7: }{8: 12 }DEF │{7: }{8: 16 }DEF |
{7: }{8: 13 }^something │{7: }{8: 17 }something |
{7: }{8: 14 } │{7: }{8: 18 } |
{1:~ }│{1:~ }|
{3:Xtest-functional-diff-screen-1.2 [+] }{2:Xtest-functional-diff-screen-1 }|
:e |
]])
@ -511,7 +503,7 @@ something
{7: }{8: 11 }common line │{7: }{8: 15 }common line |
{7: }{8: }{23:-------------------------------------------}│{7: }{8: 16 }{22:DEF }|
{7: }{8: 12 }something │{7: }{8: 17 }something |
{7: }{8: 13 } │{7: }{8: 18 } |
{1:~ }│{1:~ }|
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 [+] }|
:e |
]])
@ -538,7 +530,7 @@ something
{7: }{8: 11 }common line │{7: }{8: 15 }common line |
{7: }{8: }{23:-------------------------------------------}│{7: }{8: 16 }{22:DEF }|
{7: }{8: 12 }something │{7: }{8: 17 }something |
{7: }{8: 13 } │{7: }{8: 18 } |
{1:~ }│{1:~ }|
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 [+] }|
:e |
]])
@ -565,7 +557,7 @@ something
{7: }{8: 11 }common line │{7: }{8: 15 }common line |
{7: }{8: }{23:-------------------------------------------}│{7: }{8: 16 }{22:DEF }|
{7: }{8: 12 }something │{7: }{8: 17 }something |
{7: }{8: 13 } │{7: }{8: 18 } |
{1:~ }│{1:~ }|
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 [+] }|
:e |
]])
@ -591,7 +583,7 @@ something
{7: }{8: 11 }^common line │{7: }{8: 14 }common line |
{7: }{8: }{23:-------------------------------------------}│{7: }{8: 15 }{22:DEF }|
{7: }{8: 12 }something │{7: }{8: 16 }something |
{7: }{8: 13 } │{7: }{8: 17 } |
{1:~ }│{1:~ }|
{1:~ }│{1:~ }|
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 [+] }|
:e |
@ -618,7 +610,7 @@ something
{7: }{8: }{23:-------------------------------------------}│{7: }{8: 14 }{22:DEF }|
{7: }{8: 11 }common line │{7: }{8: 15 }common line |
{7: }{8: 12 }^something │{7: }{8: 16 }something |
{7: }{8: 13 } │{7: }{8: 17 } |
{1:~ }│{1:~ }|
{1:~ }│{1:~ }|
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 [+] }|
:e |
@ -646,7 +638,7 @@ something
{7: }{8: 14 }common line │{7: }{8: 15 }common line |
{7: }{8: }{23:-------------------------------------------}│{7: }{8: 16 }{22:DEF }|
{7: }{8: 15 }something │{7: }{8: 17 }something |
{7: }{8: 16 } │{7: }{8: 18 } |
{1:~ }│{1:~ }|
{2:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }|
:e |
]])
@ -673,7 +665,7 @@ something
{7: }{8: 14 }common line │{7: }{8: 15 }common line |
{7: }{8: }{23:-------------------------------------------}│{7: }{8: 16 }{22:DEF }|
{7: }{8: 15 }something │{7: }{8: 17 }something |
{7: }{8: 16 } │{7: }{8: 18 } |
{1:~ }│{1:~ }|
{2:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }|
:e |
]])
@ -700,7 +692,7 @@ something
{7: }{8: 11 }common line │{7: }{8: 15 }common line |
{7: }{8: }{23:-------------------------------------------}│{7: }{8: 16 }{22:DEF }|
{7: }{8: 12 }something │{7: }{8: 17 }something |
{7: }{8: 13 } │{7: }{8: 18 } |
{1:~ }│{1:~ }|
{2:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }|
:e |
]])
@ -727,7 +719,7 @@ something
{7: }{8: 11 }common line │{7: }{8: 15 }common line |
{7: }{8: 12 }DEF │{7: }{8: 16 }DEF |
{7: }{8: 13 }something │{7: }{8: 17 }^something |
{7: }{8: 14 } │{7: }{8: 18 } |
{1:~ }│{1:~ }|
{2:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }|
:e |
]])
@ -757,8 +749,7 @@ d
{7: }{8: 2 }{4:abc d }│{7: }{8: 1 }{27:// }{4:abc d }|
{7: }{8: 3 }{4:d }│{7: }{8: 2 }{27:// }{4:d }|
{7: }{8: }{23:-------------------------------------------}│{7: }{8: 3 }{22:// d }|
{7: }{8: 4 } │{7: }{8: 4 } |
{1:~ }│{1:~ }|*13
{1:~ }│{1:~ }|*14
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
:e |
]])
@ -794,8 +785,7 @@ void testFunction () {
{7: }{8: 3 }{4: }{27:// }{4:} }│{7: }{8: 4 }{4: } }|
{7: }{8: }{23:-------------------------------------------}│{7: }{8: 5 }{22: } }|
{7: }{8: 4 }} │{7: }{8: 6 }} |
{7: }{8: 5 } │{7: }{8: 7 } |
{1:~ }│{1:~ }|*11
{1:~ }│{1:~ }|*12
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
:e |
]])
@ -834,8 +824,7 @@ void testFunction () {
{7: }{8: 6 }{22:?B }│{7: }{8: }{23:--------------------------------------------}|
{7: }{8: 7 }{22:?B }│{7: }{8: }{23:--------------------------------------------}|
{7: }{8: 8 }{22:?C }│{7: }{8: }{23:--------------------------------------------}|
{7: }{8: 9 } │{7: }{8: 4 } |
{1:~ }│{1:~ }|*9
{1:~ }│{1:~ }|*10
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
:e |
]])
@ -874,8 +863,7 @@ void testFunction () {
{7: }{8: 6 }{27:?}{4:B }│{7: }{8: 2 }{27:!}{4:B }|
{7: }{8: 7 }{27:?}{4:C }│{7: }{8: 3 }{27:!}{4:C }|
{7: }{8: 8 }{22:?C }│{7: }{8: }{23:--------------------------------------------}|
{7: }{8: 9 } │{7: }{8: 4 } |
{1:~ }│{1:~ }|*9
{1:~ }│{1:~ }|*10
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
:e |
]])
@ -1017,8 +1005,7 @@ something
{7: }{8: 9 }HIL │{7: }{8: 9 }HIL |
{7: }{8: 10 }common line │{7: }{8: 10 }common line |
{7: }{8: 11 }something │{7: }{8: 11 }something |
{7: }{8: 12 } │{7: }{8: 12 } |
{1:~ }│{1:~ }|*6
{1:~ }│{1:~ }|*7
{3:Xtest-functional-diff-screen-1.2 [+] }{2:Xtest-functional-diff-screen-1 }|
:1,19diffget |
]])

View File

@ -2318,25 +2318,26 @@ describe('builtin popupmenu', function()
command('set completeopt+=noinsert')
command('set mouse=a')
insert([[
Lorem ipsum dolor sit amet, consectetur
adipisicing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex
ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum
dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa
qui officia deserunt mollit anim id est
laborum.
Lorem ipsum dolor sit amet, consectetur
adipisicing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex
ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum
dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa
qui officia deserunt mollit anim id est
laborum.
.
]])
screen:expect([[
reprehenderit in voluptate velit esse cillum |
dolore eu fugiat nulla pariatur. Excepteur sint |
occaecat cupidatat non proident, sunt in culpa |
qui officia deserunt mollit anim id est |
laborum. |
. |
^ |
{4:[No Name] [+] }|
Lorem ipsum dolor sit amet, consectetur |

View File

@ -494,9 +494,8 @@ function Screen:expect(expected, attr_ids, ...)
local expected_rows = {} --- @type string[]
if grid then
-- Remove the last line and dedent. Note that gsub returns more then one
-- value.
grid = dedent(grid:gsub('\n[ ]+$', ''), 0)
-- Dedent (ignores last line if it is blank).
grid = dedent(grid, 0)
for row in grid:gmatch('[^\n]+') do
table.insert(expected_rows, row)
end

View File

@ -73,10 +73,10 @@ describe('search highlighting', function()
-- 'hlsearch' is enabled by default. #2859
feed('gg/text<cr>')
screen:expect([[
some {2:^text} |
more {2:text}stuff |
stupid{2:texttext}stuff |
a {2:text} word |
some {2:^text} |
more {2:text}stuff |
stupid{2:texttext}stuff |
a {2:text} word |
|
{1:~ }|
/text |
@ -85,10 +85,10 @@ describe('search highlighting', function()
-- overlapping matches not allowed
feed('3nx')
screen:expect([[
some {2:text} |
more {2:text}stuff |
stupid{2:text}^extstuff |
a {2:text} word |
some {2:text} |
more {2:text}stuff |
stupid{2:text}^extstuff |
a {2:text} word |
|
{1:~ }|
/text |
@ -96,10 +96,10 @@ describe('search highlighting', function()
feed('ggn*') -- search for entire word
screen:expect([[
some {2:text} |
more textstuff |
stupidtextextstuff |
a {2:^text} word |
some {2:text} |
more textstuff |
stupidtextextstuff |
a {2:^text} word |
|
{1:~ }|
/\<text\> |
@ -107,10 +107,10 @@ describe('search highlighting', function()
feed_command('nohlsearch')
screen:expect([[
some text |
more textstuff |
stupidtextextstuff |
a ^text word |
some text |
more textstuff |
stupidtextextstuff |
a ^text word |
|
{1:~ }|
:nohlsearch |
@ -641,7 +641,7 @@ describe('search highlighting', function()
feed_command('/ial te')
screen:expect {
grid = [[
very {5:spec^ial}{2: te}{6:xt} |
very {5:spec^ial}{2: te}{6:xt} |
|
{1:~ }|*4
{4:search hit BOTTOM, continuing at TOP} |
@ -652,7 +652,7 @@ describe('search highlighting', function()
topline = 0,
botline = 3,
curline = 0,
curcol = 11,
curcol = 9,
linecount = 2,
sum_scroll_delta = 0,
},
@ -670,7 +670,7 @@ describe('search highlighting', function()
}
command('%foldopen')
screen:expect([[
very {5:spec^ial}{2: te}{6:xt} |
very {5:spec^ial}{2: te}{6:xt} |
|
{1:~ }|*4
{4:search hit BOTTOM, continuing at TOP} |
@ -678,7 +678,7 @@ describe('search highlighting', function()
feed_command('call clearmatches()')
screen:expect([[
very spec{2:^ial te}xt |
very spec{2:^ial te}xt |
|
{1:~ }|*4
:call clearmatches() |
@ -688,7 +688,7 @@ describe('search highlighting', function()
-- nonconflicting attributes are combined
feed_command('syntax keyword MyGroup special')
screen:expect([[
very {5:spec}{7:^ial}{2: te}xt |
very {5:spec}{7:^ial}{2: te}xt |
|
{1:~ }|*4
:syntax keyword MyGroup special |

View File

@ -148,6 +148,7 @@ end
--- @param actual string
--- @return boolean
function M.matches(pat, actual)
assert(pat and pat ~= '', 'pat must be a non-empty string')
if nil ~= string.match(actual, pat) then
return true
end
@ -641,28 +642,9 @@ end
--- @param leave_indent? integer
--- @return string
function M.dedent(str, leave_indent)
-- find minimum common indent across lines
local indent --- @type string?
for line in str:gmatch('[^\n]+') do
local line_indent = line:match('^%s+') or ''
if indent == nil or #line_indent < #indent then
indent = line_indent
end
end
if not indent or #indent == 0 then
-- no minimum common indent
return str
end
local left_indent = (' '):rep(leave_indent or 0)
-- create a pattern for the indent
indent = indent:gsub('%s', '[ \t]')
-- strip it from the first line
str = str:gsub('^' .. indent, left_indent)
-- strip it from the remaining lines
str = str:gsub('[\n]' .. indent, '\n' .. left_indent)
return str
-- Last blank line often has non-matching indent, so remove it.
str = str:gsub('\n[ ]+$', '\n')
return (vim.text.indent(leave_indent or 0, str))
end
function M.intchar2lua(ch)