Merge #34921 tutor: reimplement interactive marks as extmarks

This commit is contained in:
Justin M. Keyes
2025-07-17 23:26:55 -04:00
committed by GitHub
8 changed files with 250 additions and 117 deletions

View File

@@ -77,46 +77,6 @@ function! tutor#TutorFolds()
endif
endfunction
" Marks: {{{1
function! tutor#ApplyMarks()
hi! link tutorExpect Special
if exists('b:tutor_metadata') && has_key(b:tutor_metadata, 'expect')
let b:tutor_sign_id = 1
for expct in keys(b:tutor_metadata['expect'])
let lnum = eval(expct)
call matchaddpos('tutorExpect', [lnum])
call tutor#CheckLine(lnum)
endfor
endif
endfunction
function! tutor#ApplyMarksOnChanged()
if exists('b:tutor_metadata') && has_key(b:tutor_metadata, 'expect')
let lnum = line('.')
if index(keys(b:tutor_metadata['expect']), string(lnum)) > -1
call tutor#CheckLine(lnum)
endif
endif
endfunction
function! tutor#CheckLine(line)
if exists('b:tutor_metadata') && has_key(b:tutor_metadata, 'expect')
let bufn = bufnr('%')
let ctext = getline(a:line)
let signs = sign_getplaced(bufn, {'lnum': a:line})[0].signs
if !empty(signs)
call sign_unplace('', {'id': signs[0].id})
endif
if b:tutor_metadata['expect'][string(a:line)] == -1 || ctext ==# b:tutor_metadata['expect'][string(a:line)]
exe "sign place ".b:tutor_sign_id." line=".a:line." name=tutorok buffer=".bufn
else
exe "sign place ".b:tutor_sign_id." line=".a:line." name=tutorbad buffer=".bufn
endif
let b:tutor_sign_id+=1
endif
endfunction
" Tutor Cmd: {{{1
function! s:Locale()
@@ -243,9 +203,9 @@ function! tutor#EnableInteractive(enable)
setlocal buftype=nofile
setlocal concealcursor+=inv
setlocal conceallevel=2
call tutor#ApplyMarks()
lua require('nvim.tutor').apply_marks()
augroup tutor_interactive
autocmd! TextChanged,TextChangedI <buffer> call tutor#ApplyMarksOnChanged()
autocmd! TextChanged,TextChangedI <buffer> lua require('nvim.tutor').apply_marks_on_changed()
augroup END
else
setlocal buftype<

View File

@@ -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

View File

@@ -304,7 +304,7 @@ it would be easier to simply type two d's to delete a line.
3. Now move to the fourth line.
4. Type `2dd`{normal} to delete two lines, then press `u`{normal} twice to undo all three lines.
4. Type `2dd`{normal} to delete two lines.
1) Roses are red,
2) Mud is fun,

View File

@@ -12,11 +12,11 @@
"273": -1,
"292": "This line of words is cleaned up.",
"309": "1) Roses are red,",
"310": "3) Violets are blue,",
"311": "6) Sugar is sweet",
"312": "7) And so are you.",
"313": "7) And so are you.",
"314": "7) And so are you.",
"310": "",
"311": "3) Violets are blue,",
"312": "",
"313": "",
"314": "6) Sugar is sweet",
"315": "7) And so are you.",
"335": "Fix the errors on this line and replace them with undo.",
"381": -1,

View File

@@ -11,13 +11,13 @@
"233": "誰かがこの行の最後を2度タイプしました。",
"272": -1,
"291": "この行の単語は綺麗になった。",
"308": -1,
"309": -1,
"310": -1,
"311": -1,
"312": -1,
"313": -1,
"314": -1,
"308": "1) 薔薇は赤く",
"309": "",
"310": "3) 菫は青く",
"311": "",
"312": "",
"313": "6) 砂糖は甘く",
"314": "7) そして貴方も",
"335": "この行の間違いを修正し、後でそれらの修正を取り消します。",
"381": -1,
"382": -1,

View File

@@ -287,7 +287,7 @@ This ABC DE line FGHI JK LMN OP of words is Q RS TUV cleaned up.
3. 现在移动到第 4 行。
4. 输入 `2dd`{normal} 来删除两行,然后按两次 `u`{normal} 来恢复这三行
4. 输入 `2dd`{normal} 来删除两行。
1) Roses are red,
2) Mud is fun,

View File

@@ -14,11 +14,11 @@
"259": -1,
"276": "This line of words is cleaned up.",
"292": "1) Roses are red,",
"293": "3) Violets are blue,",
"294": "6) Sugar is sweet",
"295": "7) And so are you.",
"296": "7) And so are you.",
"297": "7) And so are you.",
"293": "",
"294": "3) Violets are blue,",
"295": "",
"296": "",
"297": "6) Sugar is sweet",
"298": "7) And so are you.",
"318": "Fix the errors on this line and replace them with undo.",
"319": "Fix the errors on this line and replace them with undo.",