refactor(tutor): reimplement interactive marks as extmark in Lua

Problem:
From https://matrix.to/#/!cylwlNXSwagQmZSkzs:matrix.org/$Ofj-TFIsEMbp0O9OhE8xuZSNi-nhRLtZTOgs6JRLNrs?via=matrix.org&via=gitter.im&via=mozilla.org

In lesson 2.6, users are asked to remove the second, forth and fifth
lines with `dd` command, then they are asked to undo twice to make the
text go back to original state. But after that, the mark ✗ appears
again, which confuses the user because they think they do something
wrong. This is a limitation with the current implementation, which is
based on line number only.

Solution:
Reimplement interactive marks as extmarks in Lua. This also make the
feature less fragile, as users can remove, add some arbitrary lines
without breaking the interactive marks.

Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
This commit is contained in:
brianhuster
2025-07-13 23:44:39 +07:00
parent 444a8b3ec6
commit 1255a8d88d
8 changed files with 254 additions and 63 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<