neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

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