2021-03-13 22:46:45 +01:00
|
|
|
local M = {}
|
|
|
|
|
|
2021-07-04 16:12:17 -05:00
|
|
|
local assert = require "luassert"
|
|
|
|
|
local say = require "say"
|
|
|
|
|
local scan_dir = require("plenary.scandir").scan_dir
|
|
|
|
|
local Path = require "plenary.path"
|
2021-04-19 00:34:56 +02:00
|
|
|
|
|
|
|
|
local function same_indent(state, arguments)
|
|
|
|
|
local before = arguments[1]
|
|
|
|
|
local after = arguments[2]
|
|
|
|
|
|
|
|
|
|
local ok = true
|
2021-04-19 01:08:58 +02:00
|
|
|
local errors = { before = {}, after = {} }
|
2021-07-04 16:12:17 -05:00
|
|
|
for line = 1, #before do
|
2021-04-19 00:34:56 +02:00
|
|
|
if before[line] ~= after[line] then
|
2021-04-19 01:08:58 +02:00
|
|
|
-- store the actual indentation length for each line
|
2021-07-04 16:12:17 -05:00
|
|
|
errors.before[line] = #string.match(before[line], "^%s*")
|
|
|
|
|
errors.after[line] = #string.match(after[line], "^%s*")
|
2021-04-19 00:34:56 +02:00
|
|
|
ok = false
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2021-04-19 01:08:58 +02:00
|
|
|
-- we will always use only a single argument, passing the other one in fmtargs
|
|
|
|
|
arguments.fmtargs = { { errors = errors, other = after } }
|
|
|
|
|
arguments.fmtargs[2] = { errors = errors, other = after }
|
2021-04-19 00:34:56 +02:00
|
|
|
|
|
|
|
|
return ok
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function format_indent(arg, fmtargs)
|
2021-04-19 01:08:58 +02:00
|
|
|
-- find minimal width if any line is longer
|
|
|
|
|
local width = 40
|
|
|
|
|
for _, line in ipairs(fmtargs.other) do
|
|
|
|
|
width = #line > width and #line or width
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
width = width + 3
|
2021-07-04 16:12:17 -05:00
|
|
|
local header_fmt = "%8s %2s%-" .. tostring(width + 1) .. "s %s"
|
|
|
|
|
local fmt = "%8s %2s |%-" .. tostring(width) .. "s |%s"
|
2021-04-19 01:08:58 +02:00
|
|
|
|
2021-07-04 16:12:17 -05:00
|
|
|
local output = { header_fmt:format("", "", "Found:", "Expected:") }
|
2021-04-19 01:08:58 +02:00
|
|
|
|
2021-04-19 00:34:56 +02:00
|
|
|
for i, line in ipairs(arg) do
|
2021-04-19 01:08:58 +02:00
|
|
|
if fmtargs.errors.before[i] then
|
2021-07-04 16:12:17 -05:00
|
|
|
local indents = string.format("%d vs %d", fmtargs.errors.after[i], fmtargs.errors.before[i])
|
|
|
|
|
table.insert(output, fmt:format(indents, "=>", fmtargs.other[i], line))
|
2021-04-19 00:34:56 +02:00
|
|
|
else
|
2021-07-04 16:12:17 -05:00
|
|
|
table.insert(output, fmt:format("", "", fmtargs.other[i], line))
|
2021-04-19 00:34:56 +02:00
|
|
|
end
|
|
|
|
|
end
|
2021-04-19 01:08:58 +02:00
|
|
|
|
2021-07-04 16:12:17 -05:00
|
|
|
return table.concat(output, "\n")
|
2021-04-19 00:34:56 +02:00
|
|
|
end
|
|
|
|
|
|
2021-07-04 16:12:17 -05:00
|
|
|
say:set_namespace "en"
|
|
|
|
|
say:set("assertion.same_indent.positive", "Incorrect indentation\n%s")
|
|
|
|
|
say:set("assertion.same_indent.negative", "Incorrect indentation\n%s")
|
|
|
|
|
assert:register(
|
|
|
|
|
"assertion",
|
|
|
|
|
"same_indent",
|
|
|
|
|
same_indent,
|
|
|
|
|
"assertion.same_indent.positive",
|
|
|
|
|
"assert.same_indent.negative"
|
|
|
|
|
)
|
2021-04-19 00:34:56 +02:00
|
|
|
|
|
|
|
|
-- Custom assertion better suited for indentation diffs
|
|
|
|
|
local function compare_indent(before, after)
|
|
|
|
|
assert:add_formatter(format_indent)
|
|
|
|
|
assert.is.same_indent(before, after)
|
|
|
|
|
assert:remove_formatter(format_indent)
|
|
|
|
|
end
|
2021-03-13 23:56:29 +01:00
|
|
|
|
2021-04-22 23:46:30 +02:00
|
|
|
local function set_buf_indent_opts(opts)
|
2021-07-04 16:12:17 -05:00
|
|
|
local optnames = { "tabstop", "shiftwidth", "softtabstop", "expandtab", "filetype" }
|
2021-04-17 22:45:42 +02:00
|
|
|
for _, opt in ipairs(optnames) do
|
|
|
|
|
if opts[opt] ~= nil then
|
|
|
|
|
vim.bo[opt] = opts[opt]
|
2021-03-13 23:51:57 +01:00
|
|
|
end
|
2021-04-17 22:45:42 +02:00
|
|
|
end
|
2021-03-13 23:51:57 +01:00
|
|
|
end
|
|
|
|
|
|
2021-04-17 22:45:42 +02:00
|
|
|
function M.run_indent_test(file, runner, opts)
|
2021-04-23 00:41:44 +02:00
|
|
|
assert.are.same(1, vim.fn.filereadable(file), string.format('File "%s" not readable', file))
|
2021-03-13 22:46:45 +01:00
|
|
|
|
|
|
|
|
-- load reference file
|
2021-07-04 16:12:17 -05:00
|
|
|
vim.cmd(string.format("edit %s", file))
|
2021-04-17 22:45:42 +02:00
|
|
|
local before = vim.api.nvim_buf_get_lines(0, 0, -1, true)
|
2021-03-13 22:46:45 +01:00
|
|
|
|
2021-07-04 16:12:17 -05:00
|
|
|
assert.are.same("nvim_treesitter#indent()", vim.bo.indentexpr)
|
2021-04-22 23:46:30 +02:00
|
|
|
set_buf_indent_opts(opts)
|
2021-03-13 23:51:57 +01:00
|
|
|
|
2021-04-17 22:45:42 +02:00
|
|
|
-- perform the test
|
|
|
|
|
runner()
|
|
|
|
|
|
|
|
|
|
-- get file content after the test
|
|
|
|
|
local after = vim.api.nvim_buf_get_lines(0, 0, -1, true)
|
2021-03-13 22:46:45 +01:00
|
|
|
|
|
|
|
|
-- clear any changes to avoid 'No write since last change (add ! to override)'
|
2021-07-04 16:12:17 -05:00
|
|
|
vim.cmd "edit!"
|
2021-03-13 22:46:45 +01:00
|
|
|
|
2021-04-17 22:45:42 +02:00
|
|
|
return before, after
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function M.indent_whole_file(file, opts)
|
|
|
|
|
local before, after = M.run_indent_test(file, function()
|
2021-07-04 16:12:17 -05:00
|
|
|
vim.cmd "silent normal gg=G"
|
2021-04-17 22:45:42 +02:00
|
|
|
end, opts)
|
|
|
|
|
|
2021-04-19 00:34:56 +02:00
|
|
|
compare_indent(before, after)
|
2021-03-13 22:46:45 +01:00
|
|
|
end
|
|
|
|
|
|
2021-04-18 00:14:03 +02:00
|
|
|
-- Open a file, use `normal o` to insert a new line and compare results
|
|
|
|
|
-- @param file path to the initial file
|
|
|
|
|
-- @param spec a table with keys:
|
|
|
|
|
-- on_line: line on which `normal o` is executed
|
|
|
|
|
-- text: text inserted in the new line
|
|
|
|
|
-- indent: expected indent before the inserted text (string or int)
|
2021-04-22 23:46:30 +02:00
|
|
|
-- @param opts buffer options passed to set_buf_indent_opts
|
2021-04-18 00:14:03 +02:00
|
|
|
function M.indent_new_line(file, spec, opts)
|
|
|
|
|
local before, after = M.run_indent_test(file, function()
|
|
|
|
|
-- move to the line and input the new one
|
2021-07-04 16:12:17 -05:00
|
|
|
vim.cmd(string.format("normal! %dG", spec.on_line))
|
|
|
|
|
vim.cmd(string.format("normal! o%s", spec.text))
|
2021-04-18 00:14:03 +02:00
|
|
|
end, opts)
|
|
|
|
|
|
2021-07-04 16:12:17 -05:00
|
|
|
local indent = type(spec.indent) == "string" and spec.indent or string.rep(" ", spec.indent)
|
2021-04-18 00:14:03 +02:00
|
|
|
table.insert(before, spec.on_line + 1, indent .. spec.text)
|
2021-04-19 00:34:56 +02:00
|
|
|
|
|
|
|
|
compare_indent(before, after)
|
2021-04-18 00:14:03 +02:00
|
|
|
end
|
|
|
|
|
|
2021-04-23 00:41:44 +02:00
|
|
|
local Runner = {}
|
|
|
|
|
Runner.__index = Runner
|
|
|
|
|
|
|
|
|
|
-- Helper to avoid boilerplate when defining tests
|
|
|
|
|
-- @param it the "it" function that busted defines globally in spec files
|
|
|
|
|
-- @param base_dir all other paths will be resolved relative to this directory
|
|
|
|
|
-- @param buf_opts buffer options passed to set_buf_indent_opts
|
|
|
|
|
function Runner:new(it, base_dir, buf_opts)
|
|
|
|
|
local runner = {}
|
|
|
|
|
runner.it = it
|
|
|
|
|
runner.base_dir = Path:new(base_dir)
|
|
|
|
|
runner.buf_opts = buf_opts
|
|
|
|
|
return setmetatable(runner, self)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function Runner:whole_file(dirs)
|
2021-07-04 16:12:17 -05:00
|
|
|
dirs = type(dirs) == "table" and dirs or { dirs }
|
2021-04-23 00:41:44 +02:00
|
|
|
dirs = vim.tbl_map(function(dir)
|
|
|
|
|
dir = self.base_dir / Path:new(dir)
|
|
|
|
|
assert.is.same(1, vim.fn.isdirectory(dir.filename))
|
|
|
|
|
return dir.filename
|
|
|
|
|
end, dirs)
|
|
|
|
|
local files = vim.tbl_flatten(vim.tbl_map(scan_dir, dirs))
|
|
|
|
|
for _, file in ipairs(files) do
|
|
|
|
|
local relpath = Path:new(file):make_relative(self.base_dir.filename)
|
|
|
|
|
self.it(relpath, function()
|
|
|
|
|
M.indent_whole_file(file, self.buf_opts)
|
|
|
|
|
end)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function Runner:new_line(file, spec, title)
|
|
|
|
|
title = title and title or tostring(spec.on_line)
|
2021-07-04 16:12:17 -05:00
|
|
|
self.it(string.format("%s[%s]", file, title), function()
|
2021-04-23 00:41:44 +02:00
|
|
|
local path = self.base_dir / file
|
|
|
|
|
M.indent_new_line(path.filename, spec, self.buf_opts)
|
|
|
|
|
end)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
M.Runner = Runner
|
|
|
|
|
|
2021-03-13 22:46:45 +01:00
|
|
|
return M
|