Files
eleith 1057d372f3
ci/woodpecker/push/ci Pipeline was successful
Highlight characterwise comment ranges
2026-09-30 10:44:03 -07:00

222 lines
6.9 KiB
Lua
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
local helpers = require("helpers")
local scratch = require("scratch_comments")
local views = require("scratch_comments.model.views")
local expect = MiniTest.expect
---@param keys string a visual selection, such as "viw"
local function select(keys)
vim.cmd("normal! " .. keys .. "\27")
end
-- Only the text decorations, not the signs or tracking anchors.
local function highlights()
local namespace = vim.api.nvim_get_namespaces().scratch_comments_signs
local ranges = {}
for _, mark in ipairs(vim.api.nvim_buf_get_extmarks(0, namespace, 0, -1, { details = true })) do
local details = assert(mark[4])
if details.hl_group then
table.insert(ranges, { mark[2], mark[3], details.end_row, details.end_col, details.hl_group })
end
end
return ranges
end
local function lines_title()
return helpers.title((helpers.panes()))
end
before_each(function()
helpers.reset()
helpers.buffer("spans.md", { "the quick brown fox", "café → bar", "one two", "three four" })
end)
describe("a charwise selection", function()
before_each(function()
vim.api.nvim_win_set_cursor(0, { 1, 4 })
select("viw")
vim.cmd("'<,'>Comment")
end)
it("names its columns in the title", function()
expect.equality(lines_title(), " spans.md [line 1, col 5–9] ")
end)
it("makes a span over exactly the selected text", function()
helpers.write("on quick")
local quick = views.anchored()[1]
expect.equality(quick.snippet, "quick")
expect.equality(quick.start_col, 4)
expect.equality(quick.end_col, 9)
end)
it("highlights only the selected text and also shows a sign", function()
helpers.write("on quick")
expect.equality(highlights(), { { 0, 4, 0, 9, "ScratchCommentHighlight" } })
expect.equality(helpers.sign_at(1), "│")
end)
it("opens an existing comment on the exact selection for editing", function()
helpers.write("on quick")
select("viw")
vim.cmd("'<,'>Comment")
expect.equality(helpers.text(), "on quick")
helpers.write("edited quick")
expect.equality(#views.anchored(), 1)
expect.equality(views.anchored()[1].comment, "edited quick")
end)
it("is separate from a comment on the whole line", function()
helpers.write("on quick")
vim.cmd("Comment")
helpers.write("on the line")
vim.cmd("1Comment")
expect.equality(helpers.text(), "on the line")
vim.cmd.quit()
expect.equality(#views.anchored(), 2)
end)
end)
describe("a span", function()
local quick
before_each(function()
vim.api.nvim_win_set_cursor(0, { 1, 4 })
select("viw")
vim.cmd("'<,'>Comment")
helpers.write("on quick")
quick = views.anchored()[1]
end)
local function current()
return vim.tbl_filter(function(view)
return view.id == quick.id
end, views.anchored())[1]
end
it("follows its text", function()
vim.api.nvim_buf_set_text(0, 0, 4, 0, 4, { "very " })
expect.equality(current().snippet, "quick")
helpers.text_changed()
expect.equality(highlights(), { { 0, 9, 0, 14, "ScratchCommentHighlight" } })
end)
it("hides and restores both decorations with :CommentToggle", function()
vim.cmd("CommentToggle off")
expect.equality(highlights(), {})
expect.equality(helpers.sign_at(1), nil)
vim.cmd("CommentToggle on")
expect.equality(highlights(), { { 0, 4, 0, 9, "ScratchCommentHighlight" } })
expect.equality(helpers.sign_at(1), "│")
end)
it("removes both decorations when cleared", function()
vim.cmd("CommentClear")
expect.equality(highlights(), {})
expect.equality(helpers.sign_at(1), nil)
end)
it("removes both decorations when deleted", function()
vim.cmd("CommentList")
vim.cmd("CommentDelete")
vim.cmd("cclose")
expect.equality(highlights(), {})
expect.equality(helpers.sign_at(1), nil)
end)
it("is orphaned when its text is replaced, and restored by undo", function()
helpers.undo_break()
vim.api.nvim_buf_set_text(0, 0, 4, 0, 9, { "slow" })
expect.equality(current(), nil)
expect.equality(#views.orphans(), 1)
helpers.text_changed()
expect.equality(highlights(), {})
expect.equality(helpers.sign_at(1), nil)
helpers.undo_break()
vim.cmd("silent undo")
expect.equality(current().snippet, "quick")
helpers.text_changed()
expect.equality(highlights(), { { 0, 4, 0, 9, "ScratchCommentHighlight" } })
expect.equality(helpers.sign_at(1), "│")
end)
it("is listed in quickfix with its columns", function()
vim.cmd("CommentList")
local item = vim.fn.getqflist()[1]
expect.equality({ item.col, item.end_col }, { 5, 10 })
vim.cmd("cclose")
end)
it("exports 1-based columns to json, ending on the last byte", function()
local exported = vim.json.decode(scratch.render("json")).comments[1]
expect.equality(exported.start_col, 5)
expect.equality(exported.end_col, 9)
end)
end)
describe("a selection", function()
it("keeps a multibyte character whole", function()
vim.api.nvim_win_set_cursor(0, { 2, 0 })
select("v3l")
vim.cmd("'<,'>Comment")
helpers.write("on café")
expect.equality(views.anchored()[1].snippet, "café")
expect.equality(highlights(), { { 1, 0, 1, 5, "ScratchCommentHighlight" } })
end)
it("to $ ends at the end of the line", function()
vim.api.nvim_win_set_cursor(0, { 3, 4 })
select("v$")
vim.cmd("'<,'>Comment")
helpers.write("to the end")
expect.equality(views.anchored()[1].snippet, "two")
end)
it("can cross lines, titled with line:col", function()
vim.api.nvim_win_set_cursor(0, { 3, 4 })
select("vj")
vim.cmd("'<,'>Comment")
expect.equality(lines_title(), " spans.md [lines 3:5–4:5] ")
helpers.write("across lines")
expect.equality(views.anchored()[1].snippet, "two\nthree")
expect.equality(highlights(), { { 2, 4, 3, 5, "ScratchCommentHighlight" } })
expect.equality(helpers.sign_at(3), "╭")
expect.equality(helpers.sign_at(4), "╰")
end)
it("that is linewise makes a line comment", function()
vim.api.nvim_win_set_cursor(0, { 3, 4 })
select("V")
vim.cmd("'<,'>Comment")
expect.equality(lines_title(), " spans.md [line 3] ")
helpers.write("whole line")
expect.equality(views.anchored()[1].start_col, nil)
expect.equality(views.anchored()[1].snippet, "one two")
expect.equality(highlights(), {})
expect.equality(helpers.sign_at(3), "│")
end)
end)
describe("a long title", function()
local columns
before_each(function()
columns = vim.o.columns
vim.cmd.file(("a-very-long-name"):rep(6) .. ".md")
vim.api.nvim_win_set_cursor(0, { 1, 4 })
select("viw")
vim.o.columns = 60
vim.cmd("'<,'>Comment")
end)
after_each(function()
vim.o.columns = columns
end)
it("fits the window, cut from the left with an ellipsis, keeping the range", function()
local title = lines_title()
expect.equality(vim.api.nvim_strwidth(title), 56)
expect.equality(title:sub(1, 4), " …")
expect.equality(vim.endswith(title, "name.md [line 1, col 5–9] "), true)
end)
end)