mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 09:44:31 +00:00 
			
		
		
		
	Problem: If users delete a line containing extmark, it will move to the next line, which could highlight the next line in an unwanted way. Solution: - Use `invalidate` field in `nvim_buf_set_extmark()` to prevent the extmark from moving. - Also save from "priority" hacking, since we can check if the extmark is valid in `nvim_buf_get_extmarks()` now.
		
			
				
	
	
		
			87 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			87 lines
		
	
	
		
			2.6 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 = {}
 | 
						|
 | 
						|
---@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 = tonumber(expct) ---@type integer
 | 
						|
      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
 |