mirror of
https://github.com/neovim/neovim.git
synced 2026-05-24 22:00:10 +00:00
Problem: Expected text for interactive marks is in a separate json file from the tutor file. When the tutor file is updated, line numbers (potentially many) have to be updated in the json file. This is a burden for maintenance and automatic testing. Solution: Put the expected text inline in the tutor file, marked with `[[]]`. Parse and remove the comments before opening the tutor file so extmarks can be applied.
111 lines
3.3 KiB
Lua
111 lines
3.3 KiB
Lua
---@class nvim.TutorMetadata
|
|
---@field expect table<string, string|-1>
|
|
|
|
---@alias nvim.TutorExtmarks table<string, string>
|
|
|
|
---@type nvim.TutorExtmarks?
|
|
vim.b.tutor_extmarks = vim.b.tutor_extmarks
|
|
|
|
---@type nvim.TutorMetadata?
|
|
vim.b.tutor_metadata = vim.b.tutor_metadata
|
|
|
|
local sign_text_correct = '✓'
|
|
local sign_text_incorrect = '✗'
|
|
local tutor_mark_ns = vim.api.nvim_create_namespace('nvim.tutor.mark')
|
|
local tutor_hl_ns = vim.api.nvim_create_namespace('nvim.tutor.hl')
|
|
|
|
local M = {}
|
|
|
|
-- Extract inline comments into metadata.
|
|
-- Example: "This is wrong. [[This is right.]]" => { '1' = 'This is right.'}
|
|
function M.load_metadata()
|
|
---@type table<string, string|-1>
|
|
local data = {}
|
|
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
|
|
|
|
for i, line in ipairs(lines) do
|
|
local text, expected_text = line:match('^(.-)%s%[%[(.-)%]%]$')
|
|
|
|
if text then
|
|
if expected_text == '-1' then
|
|
data[tostring(i)] = -1
|
|
else
|
|
data[tostring(i)] = expected_text
|
|
end
|
|
lines[i] = text
|
|
end
|
|
end
|
|
|
|
vim.b.tutor_metadata = { expect = data }
|
|
vim.api.nvim_buf_set_lines(0, 0, -1, false, lines)
|
|
end
|
|
|
|
---@param line integer 1-based
|
|
local function check_line(line)
|
|
if vim.b.tutor_metadata and vim.b.tutor_metadata.expect and vim.b.tutor_extmarks then
|
|
local ctext = vim.fn.getline(line)
|
|
|
|
---@type vim.api.keyset.get_extmark_item[]
|
|
local extmarks = vim
|
|
.iter(vim.api.nvim_buf_get_extmarks(
|
|
0,
|
|
tutor_mark_ns,
|
|
{ line - 1, 0 },
|
|
{ line - 1, -1 }, -- the extmark can move to col > 0 if users insert text there
|
|
{ details = true }
|
|
))
|
|
:filter(function(extmark)
|
|
return not extmark[4].invalid
|
|
end)
|
|
:totable()
|
|
|
|
for _, extmark in ipairs(extmarks) do
|
|
local mark_id = extmark[1]
|
|
local expct = vim.b.tutor_extmarks[tostring(mark_id)]
|
|
local expect = vim.b.tutor_metadata.expect[expct]
|
|
local is_correct = expect == -1 or ctext == expect
|
|
|
|
vim.api.nvim_buf_set_extmark(0, tutor_mark_ns, line - 1, 0, {
|
|
id = mark_id,
|
|
sign_text = is_correct and sign_text_correct or sign_text_incorrect,
|
|
sign_hl_group = is_correct and 'tutorOK' or 'tutorX',
|
|
invalidate = true,
|
|
})
|
|
end
|
|
end
|
|
end
|
|
|
|
function M.apply_marks()
|
|
vim.cmd [[hi! link tutorExpect Special]]
|
|
if vim.b.tutor_metadata and vim.b.tutor_metadata.expect then
|
|
vim.b.tutor_extmarks = {}
|
|
for expct, _ in pairs(vim.b.tutor_metadata.expect) do
|
|
---@diagnostic disable-next-line: assign-type-mismatch
|
|
local lnum = vim._assert_integer(expct)
|
|
vim.api.nvim_buf_set_extmark(0, tutor_hl_ns, lnum - 1, 0, {
|
|
line_hl_group = 'tutorExpect',
|
|
invalidate = true,
|
|
})
|
|
|
|
local mark_id = vim.api.nvim_buf_set_extmark(0, tutor_mark_ns, lnum - 1, 0, {})
|
|
|
|
-- Cannot edit field of a Vimscript dictionary from Lua directly, see `:h lua-vim-variables`
|
|
---@type nvim.TutorExtmarks
|
|
local tutor_extmarks = vim.b.tutor_extmarks
|
|
tutor_extmarks[tostring(mark_id)] = expct
|
|
vim.b.tutor_extmarks = tutor_extmarks
|
|
|
|
check_line(lnum)
|
|
end
|
|
end
|
|
end
|
|
|
|
function M.apply_marks_on_changed()
|
|
if vim.b.tutor_metadata and vim.b.tutor_metadata.expect and vim.b.tutor_extmarks then
|
|
local lnum = vim.fn.line('.')
|
|
check_line(lnum)
|
|
end
|
|
end
|
|
|
|
return M
|