local script = debug.getinfo(1, "S").source:sub(2) local root = script:match("^(.*)/tests/plugin_conformance%.lua$") or "." vim.opt.runtimepath:prepend(root) local domain = require("clever_tee.domain") local assertions = dofile(root .. "/tests/assertions.lua") local same = assertions.same local truthy = assertions.truthy vim.g.clever_tee_mark_cursor = 0 vim.g.clever_tee_mark_char = 0 vim.g.clever_tee_mark_direct = 0 vim.g.clever_tee_hide_cursor_on_cmdline = 0 vim.g.clever_tee_clean_labels_eagerly = 1 vim.g.clever_tee_highlight_timeout_ms = 0 vim.cmd.runtime("plugin/clever_tee.lua") local clever_tee = require("clever_tee") local checks = 0 local function check_same(expected, actual, message) same(expected, actual, message) checks = checks + 1 end local function check_truthy(value, message) truthy(value, message) checks = checks + 1 end local function feed(keys) vim.api.nvim_feedkeys(vim.keycode(keys), "xt", false) vim.cmd("redraw") vim.wait(10) end local function setup(lines, column) feed("") clever_tee._diagnostic_full_reset() vim.g.clever_tee_mark_char = 0 vim.g.clever_tee_highlight_timeout_ms = 0 vim.api.nvim_buf_set_lines(0, 0, -1, true, lines) vim.api.nvim_win_set_cursor(0, { 1, (column or 1) - 1 }) end local visual_cases = { { prefix = "v", mode = "v", adjusted = true }, { prefix = "V", mode = "V", adjusted = true }, { prefix = "", mode = string.char(0x16), adjusted = false }, } for _, selection in ipairs({ "inclusive", "exclusive" }) do vim.o.selection = selection for _, case in ipairs(visual_cases) do setup({ "axz", "second" }) feed(case.prefix .. "fx") local expected_column = selection == "exclusive" and case.adjusted and 2 or 1 check_same(expected_column, vim.api.nvim_win_get_cursor(0)[2], selection .. case.prefix) check_same(case.mode, vim.api.nvim_get_mode().mode, selection .. case.prefix) end end vim.o.selection = "inclusive" local select_cases = { { prefix = "gh", mode = "s" }, { prefix = "gH", mode = "S" }, { prefix = "g", mode = string.char(0x13) }, } for _, case in ipairs(select_cases) do setup({ "axz", "second" }) feed(case.prefix) check_same(case.mode, vim.api.nvim_get_mode().mode, case.prefix) vim.api.nvim_feedkeys("x", "n", false) local outcome = clever_tee.StartFindForward() check_same(domain.ActionKind.MOVEMENT, outcome.kind, case.prefix) check_same(1, vim.api.nvim_win_get_cursor(0)[2], case.prefix) check_same(case.mode, vim.api.nvim_get_mode().mode, case.prefix) end local source = "hoge fuge piye poye" local operator_cases = { { keys = "dfe", column = 1, text = " fuge piye poye", cursor = 0 }, { keys = "dte", column = 1, text = "e fuge piye poye", cursor = 0 }, { keys = "dFe", column = 19, text = "hoge fuge piye", cursor = 13 }, { keys = "dTe", column = 19, text = "hoge fuge piyee", cursor = 14 }, } for _, selection in ipairs({ "inclusive", "exclusive" }) do vim.o.selection = selection for _, case in ipairs(operator_cases) do setup({ source }, case.column) feed(case.keys) check_same(case.text, vim.api.nvim_get_current_line(), selection .. case.keys) check_same(case.cursor, vim.api.nvim_win_get_cursor(0)[2], selection .. case.keys) check_same(selection, vim.o.selection, selection .. case.keys) end end vim.o.selection = "inclusive" local multibyte = "\227\129\130" setup({ "x" .. multibyte .. "x" .. multibyte }) feed("f" .. multibyte) check_same(1, vim.api.nvim_win_get_cursor(0)[2], "first multibyte target") feed("f") check_same(5, vim.api.nvim_win_get_cursor(0)[2], "second multibyte target") feed("F") check_same(1, vim.api.nvim_win_get_cursor(0)[2], "backward multibyte target") setup({ "abc" }) feed("f") check_same(0, vim.api.nvim_win_get_cursor(0)[2], "encoded special key") check_same( domain.TargetKind.SPECIAL_KEY.value, clever_tee.state():get_previous_target("n").kind.value, "encoded special key target kind" ) setup({ "1a 2a 3a 4a 5a" }) vim.fn.setreg("q", "fax") feed("5@q") check_same("1 2 3 4 5", vim.api.nvim_get_current_line(), "macro acquisitions") local saved_foldmethod = vim.wo.foldmethod local saved_foldopen = vim.o.foldopen setup({ "one", "two", "xax", "four", "five" }) vim.wo.foldmethod = "manual" vim.cmd("1,5fold") vim.cmd("2,4fold") vim.cmd("normal! zM") vim.o.foldopen = "hor" vim.api.nvim_win_set_cursor(0, { 3, 0 }) check_truthy(vim.fn.foldclosed(3) ~= -1, "nested fold starts closed") feed("fa") check_same(-1, vim.fn.foldclosed(3), "nested folds opened") check_same(1, vim.api.nvim_win_get_cursor(0)[2], "fold motion endpoint") vim.cmd("normal! zE") vim.wo.foldmethod = saved_foldmethod vim.o.foldopen = saved_foldopen local function setup_feedback(timeout) setup({ "ahahah" }) vim.g.clever_tee_mark_char = 1 vim.g.clever_tee_highlight_timeout_ms = timeout or 0 feed("fh") local state = clever_tee.state():to_table() check_same(1, #state.target_overlays, "target overlay created") check_same(1, #state.finalizers, "finalizer created") return state end setup_feedback() vim.api.nvim_win_set_cursor(0, { 1, 2 }) vim.api.nvim_exec_autocmds("CursorMoved", { buffer = 0 }) local finalized = clever_tee.state():to_table() check_same(0, #finalized.target_overlays, "CursorMoved overlay cleanup") check_same(0, #finalized.finalizers, "CursorMoved finalizer cleanup") check_same(nil, finalized.contexts.n.previous_landing, "CursorMoved landing cleanup") setup_feedback() vim.api.nvim_exec_autocmds("InsertEnter", { buffer = 0 }) finalized = clever_tee.state():to_table() check_same(0, #finalized.target_overlays, "InsertEnter overlay cleanup") check_same(nil, finalized.contexts.n.previous_landing, "InsertEnter landing cleanup") setup_feedback() vim.api.nvim_exec_autocmds("TextChanged", { buffer = 0 }) finalized = clever_tee.state():to_table() check_same(0, #finalized.target_overlays, "TextChanged overlay cleanup") check_same(nil, finalized.contexts.n.previous_landing, "TextChanged landing cleanup") for _, event in ipairs({ "WinEnter", "WinLeave", "CmdwinLeave" }) do local before = setup_feedback() vim.api.nvim_exec_autocmds(event, {}) local eager = clever_tee.state():to_table() check_same(0, #eager.target_overlays, event .. " overlay cleanup") check_same(1, #eager.finalizers, event .. " finalizer retention") check_same( before.contexts.n.previous_landing.byte_column, eager.contexts.n.previous_landing.byte_column, event .. " landing retention" ) end setup_feedback(20) local timed_landing = clever_tee.state():get_previous_landing("n") vim.wait(60) local timed = clever_tee.state():to_table() check_same(0, #timed.target_overlays, "timer overlay cleanup") check_same(nil, timed.highlight_timer, "timer identity cleanup") check_same(timed_landing, clever_tee.state():get_previous_landing("n"), "timer history retention") feed("f") local restored = clever_tee.state():to_table() check_same(4, restored.contexts.n.previous_landing.byte_column, "repeat after timer cleanup") check_same(1, #restored.target_overlays, "repeat restores target overlay") check_truthy(restored.highlight_timer ~= nil, "repeat starts a replacement timer") clever_tee._diagnostic_full_reset() io.stdout:write(string.format("Phase 16 plugin conformance: %d checks passed\n", checks))