Files

356 lines
12 KiB
Lua

local helpers = require("helpers")
local scratch = require("scratch_comments")
local views = require("scratch_comments.model.views")
local store = require("scratch_comments.model.store")
local expect = MiniTest.expect
local function only()
return views.anchored()[1]
end
before_each(function()
helpers.reset()
end)
describe("a line comment", function()
before_each(function()
helpers.buffer("anchoring.md", { "one", "two", "the quick brown fox", "four", "five" })
vim.cmd("3Comment")
helpers.write("on the fox")
end)
it("follows its text when lines are inserted above", function()
vim.api.nvim_buf_set_lines(0, 0, 0, false, { "new a", "new b", "new c" })
expect.equality(only().start_line, 6)
expect.equality(only().end_line, 6)
expect.equality(scratch.render():match("%- (line %d+)"), "line 6")
end)
it("follows its text when lines above are deleted", function()
vim.api.nvim_buf_set_lines(0, 0, 2, false, {})
expect.equality(only().start_line, 1)
end)
it("reads its snippet live", function()
vim.api.nvim_buf_set_text(0, 2, 4, 2, 9, { "slow" })
expect.equality(only().snippet, "the slow brown fox")
end)
it("stays anchored when part of the line is edited", function()
vim.api.nvim_win_set_cursor(0, { 3, 4 })
vim.cmd("normal! ciwslow")
expect.equality(#views.anchored(), 1)
end)
it("stays anchored when the whole line is rewritten", function()
vim.api.nvim_win_set_cursor(0, { 3, 0 })
vim.cmd("normal! ccthe slow fox")
expect.equality(#views.orphans(), 0)
end)
end)
describe("a range comment", function()
before_each(function()
helpers.buffer("anchoring.md", { "a", "b", "c", "d", "e" })
vim.cmd("2,4Comment")
helpers.write("on b through d")
end)
it("covers its lines", function()
expect.equality(only().start_line, 2)
expect.equality(only().end_line, 4)
end)
it("grows when lines are inserted inside it", function()
vim.api.nvim_buf_set_lines(0, 2, 2, false, { "inside" })
expect.equality(only().start_line, 2)
expect.equality(only().end_line, 5)
end)
it("shrinks when its last line is deleted, without taking in the next line", function()
vim.api.nvim_buf_set_lines(0, 3, 4, false, {})
expect.equality(only().end_line, 3)
expect.equality(only().snippet, "b\nc")
end)
it("stays anchored when one of its lines is rewritten", function()
vim.api.nvim_win_set_cursor(0, { 3, 0 })
vim.cmd("normal! ccrewritten")
expect.equality(#views.orphans(), 0)
end)
it("shrinks when a middle line is deleted", function()
vim.cmd("3d")
expect.equality(#views.orphans(), 0)
expect.equality(only().end_line, 3)
end)
end)
describe("an orphan", function()
before_each(function()
helpers.buffer("anchoring.md", { "one", "two", "the quick fox", "four", "five" })
vim.cmd("3Comment")
helpers.write("on the fox")
helpers.undo_break()
vim.cmd("3d")
helpers.text_changed()
end)
it("is what a comment becomes when its lines are deleted", function()
expect.equality(#views.anchored(), 0)
expect.equality(#views.orphans(), 1)
end)
it("has no sign", function()
local namespace = vim.api.nvim_get_namespaces().scratch_comments_signs
expect.equality(#vim.api.nvim_buf_get_extmarks(0, namespace, 0, -1, {}), 0)
end)
it("is exported in its own section, with its text", function()
local markdown = scratch.render()
expect.no_equality(markdown:find("## Orphaned", 1, true), nil)
expect.no_equality(markdown:find("on the fox", 1, true), nil)
end)
it("is in the list, marked", function()
vim.cmd("CommentList")
local entries = vim.fn.getqflist()
expect.equality(#entries, 1)
expect.no_equality(entries[1].text:find("[orphaned]", 1, true), nil)
vim.cmd("cclose")
end)
it("is anchored again by undo", function()
helpers.undo_break()
vim.cmd("silent undo")
expect.equality(#views.orphans(), 0)
expect.equality(only().start_line, 3)
end)
it("can be deleted from the comment list", function()
vim.cmd.CommentList()
vim.cmd.CommentDelete()
expect.equality(#views.orphans(), 0)
end)
end)
describe("a file's comments", function()
before_each(function()
vim.cmd.edit("tests/fixtures/lifecycle.txt")
vim.cmd("Comment")
helpers.write("on the first line")
end)
it("survive :e!", function()
vim.cmd("edit!")
expect.equality(#views.anchored(), 1)
end)
it("survive :bd and reattach when reopened", function()
vim.cmd("bdelete")
expect.equality(#store.all(), 1)
expect.equality(store.all()[1].state, "parked")
expect.equality(views.anchored()[1].comment, "on the first line")
vim.cmd.edit("tests/fixtures/lifecycle.txt")
vim.wait(0)
expect.equality(store.all()[1].state, nil)
expect.equality(views.anchored()[1].start_line, 1)
end)
it("retains a comment through :bunload and :bwipeout", function()
local source = vim.api.nvim_get_current_buf()
vim.cmd.enew()
vim.cmd("bunload tests/fixtures/lifecycle.txt")
expect.equality(store.all()[1].state, "parked")
local ns = vim.api.nvim_get_namespaces().scratch_comments
expect.equality(#vim.api.nvim_buf_get_extmarks(source, ns, 0, -1, {}), 0)
expect.equality(#views.anchored(), 1)
vim.cmd("bwipeout! tests/fixtures/lifecycle.txt")
expect.equality(#store.all(), 1)
expect.equality(store.all()[1].state, "parked")
end)
it("keeps a wiped diff-style buffer until the same URI returns", function()
local url = "codediff:////tmp/repo///HEAD/review.lua"
vim.cmd.enew()
vim.api.nvim_buf_set_name(0, url)
vim.api.nvim_buf_set_lines(0, 0, -1, false, { "one", "two" })
vim.bo.bufhidden = "wipe"
vim.cmd("2Comment")
helpers.write("virtual note")
local old = vim.api.nvim_get_current_buf()
vim.cmd("enew!")
expect.equality(vim.api.nvim_buf_is_valid(old), false)
expect.equality(store.all()[#store.all()].state, "parked")
vim.api.nvim_buf_set_name(0, url)
vim.api.nvim_buf_set_lines(0, 0, -1, false, { "one", "two" })
vim.api.nvim_exec_autocmds("BufEnter", { buffer = 0 })
vim.wait(0)
expect.equality(store.all()[#store.all()].state, nil)
expect.equality(helpers.sign_at(2), "│")
expect.equality(store.all()[#store.all()].comment, "virtual note")
expect.equality(#views.anchored(), 2)
end)
it("makes a reattached URI comment an orphan when its line is deleted", function()
local url = "review-test:///edited-after-attach.lua"
vim.cmd.enew()
vim.api.nvim_buf_set_name(0, url)
vim.api.nvim_buf_set_lines(0, 0, -1, false, { "one", "two" })
vim.bo.bufhidden = "wipe"
vim.cmd("2Comment")
helpers.write("on two")
vim.cmd("enew!")
vim.api.nvim_buf_set_name(0, url)
vim.api.nvim_buf_set_lines(0, 0, -1, false, { "one", "two" })
vim.api.nvim_exec_autocmds("BufEnter", { buffer = 0 })
vim.wait(0)
expect.equality(store.all()[#store.all()].state, nil)
local source_win = vim.api.nvim_get_current_win()
vim.cmd.CommentList()
vim.api.nvim_win_call(source_win, function()
vim.api.nvim_buf_set_lines(0, 1, 2, false, {})
end)
vim.wait(0)
expect.equality(#views.orphans(), 1)
local items = vim.fn.getqflist()
expect.equality(items[#items].text, "[orphaned] on two")
end)
it("uses the latest live URI range after a provider replaces its lines", function()
local url = "review-test:///live-range.lua"
vim.cmd.enew()
vim.api.nvim_buf_set_name(0, url)
vim.api.nvim_buf_set_lines(0, 0, -1, false, { "one", "two" })
vim.bo.bufhidden = "wipe"
vim.cmd("2Comment")
helpers.write("on two")
vim.cmd("enew!")
vim.api.nvim_buf_set_name(0, url)
vim.api.nvim_buf_set_lines(0, 0, -1, false, { "one", "two" })
vim.api.nvim_exec_autocmds("BufEnter", { buffer = 0 })
vim.wait(0)
local comment = store.all()[#store.all()]
expect.equality(comment.state, nil)
vim.api.nvim_buf_set_text(0, 1, 1, 1, 2, { "E" })
vim.wait(0)
expect.equality(comment.snippet, "tEo")
vim.api.nvim_buf_set_lines(0, 0, 0, false, { "leading" })
vim.wait(0)
expect.equality(comment.last_location.start_line, 3)
vim.api.nvim_buf_set_lines(0, 0, -1, false, { "leading", "one", "tEo" })
vim.wait(0)
expect.equality(comment.state, nil)
expect.equality(views.in_buffer(vim.api.nvim_get_current_buf())[1].start_line, 3)
end)
it("keeps a URI anchor through batched movement and provider replacement", function()
local url = "review-test:///batched.lua"
vim.cmd.enew()
vim.api.nvim_buf_set_name(0, url)
vim.api.nvim_buf_set_lines(0, 0, -1, false, { "one", "two" })
vim.bo.bufhidden = "wipe"
vim.cmd("2Comment")
helpers.write("on two")
vim.cmd("enew!")
vim.api.nvim_buf_set_name(0, url)
vim.api.nvim_buf_set_lines(0, 0, -1, false, { "one", "two" })
vim.api.nvim_exec_autocmds("BufEnter", { buffer = 0 })
vim.wait(0)
local comment = store.all()[#store.all()]
vim.api.nvim_buf_set_lines(0, 0, 0, false, { "first" })
vim.wait(0)
vim.api.nvim_buf_set_lines(0, 0, 0, false, { "second" })
vim.api.nvim_buf_set_lines(0, 0, -1, false, { "second", "first", "one", "two" })
vim.wait(0)
expect.equality(comment.state, nil)
expect.equality(comment.last_location.start_line, 4)
expect.equality(views.in_buffer(vim.api.nvim_get_current_buf())[1].start_line, 4)
end)
it("parks a moved range at its current location and reattaches there", function()
local url = "review-test:///moved.lua"
vim.cmd.enew()
vim.api.nvim_buf_set_name(0, url)
vim.api.nvim_buf_set_lines(0, 0, -1, false, { "one", "two" })
vim.bo.bufhidden = "wipe"
vim.cmd("2Comment")
helpers.write("on two")
vim.api.nvim_buf_set_lines(0, 0, 0, false, { "leading" })
local id = store.all()[#store.all()].id
expect.equality(
vim.iter(views.anchored()):find(function(view)
return view.id == id
end).start_line,
3
)
vim.cmd("enew!")
local comment = store.all()[#store.all()]
expect.equality(comment.state, "parked")
expect.equality(comment.last_location.start_line, 3)
expect.equality(comment.snippet, "two")
vim.api.nvim_buf_set_name(0, url)
vim.api.nvim_buf_set_lines(0, 0, -1, false, { "leading", "one", "two" })
vim.api.nvim_exec_autocmds("BufEnter", { buffer = 0 })
vim.wait(0)
expect.equality(comment.state, nil)
expect.equality(views.in_buffer(vim.api.nvim_get_current_buf())[1].start_line, 3)
end)
it("waits for a delayed URI read before reattaching", function()
local url = "review-test:///later.lua"
local pending
vim.api.nvim_create_autocmd("BufReadCmd", {
pattern = "review-test:///*",
once = true,
callback = function(args)
pending = args.buf
end,
})
vim.cmd.enew()
vim.api.nvim_buf_set_name(0, url)
vim.api.nvim_buf_set_lines(0, 0, -1, false, { "one", "two" })
vim.bo.bufhidden = "wipe"
vim.cmd("2Comment")
helpers.write("delayed")
vim.cmd("enew!")
expect.equality(store.all()[#store.all()].state, "parked")
vim.cmd.edit(url)
vim.wait(0)
expect.equality(store.all()[#store.all()].state, "parked")
expect.equality(pending, vim.api.nvim_get_current_buf())
vim.api.nvim_buf_set_lines(pending, 0, -1, false, { "one" })
vim.wait(0)
expect.equality(store.all()[#store.all()].state, "parked")
vim.api.nvim_buf_set_lines(pending, 1, -1, false, { "two" })
vim.wait(0)
expect.equality(store.all()[#store.all()].state, nil)
expect.equality(views.in_buffer(pending)[1].start_line, 2)
-- A provider may replace an already-matching partial result again.
vim.api.nvim_buf_set_lines(pending, 1, 2, false, { "two" })
vim.wait(0)
expect.equality(store.all()[#store.all()].state, nil)
expect.equality(views.in_buffer(pending)[1].start_line, 2)
end)
it("marks a changed saved range inactive after reopening", function()
vim.cmd("bdelete")
vim.cmd.edit("tests/fixtures/lifecycle.txt")
vim.api.nvim_buf_set_lines(0, 0, 1, false, { "different" })
vim.wait(0)
expect.equality(#views.orphans(), 1)
expect.equality(#views.anchored(), 0)
vim.cmd.CommentList()
vim.cmd.CommentDelete()
expect.equality(#store.all(), 0)
end)
it("retains an orphan when its buffer is wiped", function()
vim.cmd("1d")
helpers.text_changed()
expect.equality(#views.orphans(), 1)
vim.cmd("bwipeout!")
expect.equality(#views.orphans(), 1)
end)
end)