aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2025-07-17 23:26:55 -0400
committerGitHub <noreply@github.com>2025-07-17 23:26:55 -0400
commitfd5d04fbffec9a4ba87add584b36abbefa21a69e (patch)
treeaa363c9fe287efa1ee0d7caab397270873d7fd23 /runtime/lua
parent995bc31eafd2b3bee2ef10b1d0eb50313b2e6d46 (diff)
parent1de2a03fc7f4cf2eb11300257c832f88422d4745 (diff)
downloadrneovim-fd5d04fbffec9a4ba87add584b36abbefa21a69e.tar.gz
rneovim-fd5d04fbffec9a4ba87add584b36abbefa21a69e.tar.bz2
rneovim-fd5d04fbffec9a4ba87add584b36abbefa21a69e.zip
Merge #34921 tutor: reimplement interactive marks as extmarks
Diffstat (limited to 'runtime/lua')
-rw-r--r--runtime/lua/nvim/tutor.lua80
1 files changed, 80 insertions, 0 deletions
diff --git a/runtime/lua/nvim/tutor.lua b/runtime/lua/nvim/tutor.lua
new file mode 100644
index 0000000000..2f7e571958
--- /dev/null
+++ b/runtime/lua/nvim/tutor.lua
@@ -0,0 +1,80 @@
+---@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 = {}
+
+---@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)
+
+ local extmarks = 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
+ {}
+ )
+ 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',
+ -- This may be a hack. By default, all extmarks only move forward, so a line cannot contain
+ -- any extmarks that were originally created for later lines.
+ priority = tonumber(expct),
+ })
+ 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 = tonumber(expct) ---@type integer
+ vim.api.nvim_buf_set_extmark(0, tutor_hl_ns, lnum - 1, 0, {
+ line_hl_group = 'tutorExpect',
+ })
+
+ 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