Files
neovim/test/functional/lua/text_spec.lua
Justin M. Keyes be1fbe38b3 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).
2025-02-26 23:06:22 +01:00

173 lines
4.3 KiB
Lua

local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local clear = n.clear
local eq = t.eq
describe('vim.text', function()
before_each(clear)
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' },
{ '😂', 'F09F9882' },
}
for _, v in ipairs(cases) do
local input, output = unpack(v)
eq(output, vim.text.hexencode(input))
eq(input, vim.text.hexdecode(output))
end
end)
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('invalid input', function()
-- Odd number of hex characters
do
local res, err = vim.text.hexdecode('ABC')
eq(nil, res)
eq('string must have an even number of hex characters', err)
end
-- Non-hexadecimal input
do
local res, err = vim.text.hexdecode('nothex')
eq(nil, res)
eq('string must contain only hex characters', err)
end
end)
end)
end)