tutor.lua (2690B)
1 ---@class nvim.TutorMetadata 2 ---@field expect table<string, string|-1> 3 4 ---@alias nvim.TutorExtmarks table<string, string> 5 6 ---@type nvim.TutorExtmarks? 7 vim.b.tutor_extmarks = vim.b.tutor_extmarks 8 9 ---@type nvim.TutorMetadata? 10 vim.b.tutor_metadata = vim.b.tutor_metadata 11 12 local sign_text_correct = '✓' 13 local sign_text_incorrect = '✗' 14 local tutor_mark_ns = vim.api.nvim_create_namespace('nvim.tutor.mark') 15 local tutor_hl_ns = vim.api.nvim_create_namespace('nvim.tutor.hl') 16 17 local M = {} 18 19 ---@param line integer 1-based 20 local function check_line(line) 21 if vim.b.tutor_metadata and vim.b.tutor_metadata.expect and vim.b.tutor_extmarks then 22 local ctext = vim.fn.getline(line) 23 24 ---@type vim.api.keyset.get_extmark_item[] 25 local extmarks = vim 26 .iter(vim.api.nvim_buf_get_extmarks( 27 0, 28 tutor_mark_ns, 29 { line - 1, 0 }, 30 { line - 1, -1 }, -- the extmark can move to col > 0 if users insert text there 31 { details = true } 32 )) 33 :filter(function(extmark) 34 return not extmark[4].invalid 35 end) 36 :totable() 37 38 for _, extmark in ipairs(extmarks) do 39 local mark_id = extmark[1] 40 local expct = vim.b.tutor_extmarks[tostring(mark_id)] 41 local expect = vim.b.tutor_metadata.expect[expct] 42 local is_correct = expect == -1 or ctext == expect 43 44 vim.api.nvim_buf_set_extmark(0, tutor_mark_ns, line - 1, 0, { 45 id = mark_id, 46 sign_text = is_correct and sign_text_correct or sign_text_incorrect, 47 sign_hl_group = is_correct and 'tutorOK' or 'tutorX', 48 invalidate = true, 49 }) 50 end 51 end 52 end 53 54 function M.apply_marks() 55 vim.cmd [[hi! link tutorExpect Special]] 56 if vim.b.tutor_metadata and vim.b.tutor_metadata.expect then 57 vim.b.tutor_extmarks = {} 58 for expct, _ in pairs(vim.b.tutor_metadata.expect) do 59 ---@diagnostic disable-next-line: assign-type-mismatch 60 local lnum = tonumber(expct) ---@type integer 61 vim.api.nvim_buf_set_extmark(0, tutor_hl_ns, lnum - 1, 0, { 62 line_hl_group = 'tutorExpect', 63 invalidate = true, 64 }) 65 66 local mark_id = vim.api.nvim_buf_set_extmark(0, tutor_mark_ns, lnum - 1, 0, {}) 67 68 -- Cannot edit field of a Vimscript dictionary from Lua directly, see `:h lua-vim-variables` 69 ---@type nvim.TutorExtmarks 70 local tutor_extmarks = vim.b.tutor_extmarks 71 tutor_extmarks[tostring(mark_id)] = expct 72 vim.b.tutor_extmarks = tutor_extmarks 73 74 check_line(lnum) 75 end 76 end 77 end 78 79 function M.apply_marks_on_changed() 80 if vim.b.tutor_metadata and vim.b.tutor_metadata.expect and vim.b.tutor_extmarks then 81 local lnum = vim.fn.line('.') 82 check_line(lnum) 83 end 84 end 85 86 return M