@@ -5,6 +5,8 @@ local utils = require 'libmodal.utils' --- @type libmodal.utils
55--- @alias CursorPosition { [1] : integer , [2] : integer } see ` nvim_win_get_cursor`
66
77--- @class libmodal.Mode
8+ --- @field private autocmds integer[]
9+ --- @field private changedtick integer
810--- @field private cursor CursorPosition
911--- @field private flush_input_timer unknown
1012--- @field private help ? libmodal.utils.Help
@@ -16,24 +18,69 @@ local utils = require 'libmodal.utils' --- @type libmodal.utils
1618--- @field private name string
1719--- @field private popups libmodal.collections.Stack
1820--- @field private supress_exit boolean
19- --- @field private virtual_cursor_autocmd integer
2021--- @field public count libmodal.utils.Var[integer]
2122--- @field public exit libmodal.utils.Var[boolean]
2223--- @field public timeouts ? libmodal.utils.Var[boolean]
2324local Mode = utils .classes .new ()
2425
25- --- Cursor events triggered by which modes
26- local CURSOR_EVENTS_BY_MODE = {
27- CursorMoved = {
28- n = true ,
29- V = true ,
30- v = true ,
31- [utils .api .replace_termcodes ' <C-v>' ] = true ,
26+ local C_v = utils .api .replace_termcodes ' <C-v>'
27+ local C_s = utils .api .replace_termcodes ' <C-s>'
28+
29+ --- Event groups organized by modes
30+ local EVENTS_BY_MODE = {
31+ --- Cursor events triggered by which modes
32+ CURSOR_MOVED = {
33+ CursorMoved = {
34+ n = true ,
35+ nt = true ,
36+ ntT = true ,
37+ s = true ,
38+ S = true ,
39+ [C_s ] = true ,
40+ v = true ,
41+ V = true ,
42+ [C_v ] = true ,
43+ vs = true ,
44+ Vs = true ,
45+ [C_v .. ' s' ] = true ,
46+ },
47+
48+ CursorMovedI = {
49+ i = true ,
50+ niI = true ,
51+ niR = true ,
52+ R = true ,
53+ Rv = true ,
54+ },
3255 },
3356
34- CursorMovedI = {
35- i = true ,
36- R = true ,
57+ TEXT_CHANGED = {
58+ TextChanged = {
59+ n = true ,
60+ nt = true ,
61+ ntT = true ,
62+ },
63+
64+ TextChangedI = {
65+ i = true ,
66+ niI = true ,
67+ niR = true ,
68+ R = true ,
69+ Rv = true ,
70+ },
71+
72+ TextChangedP = {
73+ ic = true ,
74+ ix = true ,
75+ Rc = true ,
76+ Rvc = true ,
77+ Rx = true ,
78+ Rvx = true ,
79+ },
80+
81+ TextChangedT = {
82+ t = true ,
83+ },
3784 },
3885}
3986
@@ -57,6 +104,18 @@ local ZERO = string.byte(0)
57104--- Byte for 9
58105local NINE = string.byte (9 )
59106
107+ --- Execute events depending on current mode
108+ --- @param events_by_mode { [string] : { [string] : true }}
109+ local function execute_event_by_mode (events_by_mode )
110+ local mode = vim .api .nvim_get_mode ().mode
111+ for event , modes in pairs (events_by_mode ) do
112+ if modes [mode ] then
113+ vim .api .nvim_exec_autocmds (event , {})
114+ break
115+ end
116+ end
117+ end
118+
60119--- execute the `instruction`.
61120--- @private
62121--- @param instruction fun ( libmodal.Mode )| string a Lua function or Vimscript command.
@@ -70,6 +129,7 @@ function Mode:execute_instruction(instruction)
70129
71130 self .count :set (0 )
72131 self :render_virtual_cursor (0 , true )
132+ self :execute_text_changed_events ()
73133end
74134
75135--- check the user's input against the `self.instruction` mappings to see if there is anything to execute.
133193--- @private
134194function Mode :clear_virtual_cursor (bufnr )
135195 vim .api .nvim_buf_clear_namespace (bufnr , NS .CURSOR , 0 , - 1 );
196+ end
136197
198+ --- Runs CursorMoved* events, if applicable
199+ --- @param cursor CursorPosition the current cursor position
200+ function Mode :execute_cursor_moved_events (cursor )
201+ if not vim .deep_equal (self .cursor , cursor ) then
202+ execute_event_by_mode (EVENTS_BY_MODE .CURSOR_MOVED )
203+ self .cursor = cursor
204+ end
205+ end
206+
207+ --- Runs TextChanged* events, if applicable
208+ function Mode :execute_text_changed_events ()
209+ local changedtick = vim .api .nvim_buf_get_changedtick (0 )
210+ if self .changedtick ~= changedtick then
211+ execute_event_by_mode (EVENTS_BY_MODE .TEXT_CHANGED )
212+ self .changedtick = changedtick
213+ end
137214end
138215
139216--- enter this mode.
@@ -156,15 +233,26 @@ function Mode:enter()
156233
157234 do
158235 local augroup = vim .api .nvim_create_augroup (' libmodal-mode-' .. self .name , { clear = false })
159- self .virtual_cursor_autocmd = vim .api .nvim_create_autocmd (' BufLeave' , {
160- callback = function (ev )
161- local bufnr = ev .buf
162- self :clear_virtual_cursor (bufnr )
163- end ,
164- group = augroup ,
165- })
236+ self .autocmds = {
237+ vim .api .nvim_create_autocmd (' BufLeave' , {
238+ callback = function (ev )
239+ local bufnr = ev .buf
240+ self :clear_virtual_cursor (bufnr )
241+ end ,
242+ group = augroup ,
243+ }),
244+
245+ vim .api .nvim_create_autocmd (' BufEnter' , {
246+ callback = function (ev )
247+ local bufnr = ev .buf
248+ self .changedtick = vim .api .nvim_buf_get_changedtick (bufnr )
249+ end ,
250+ });
251+ }
166252 end
167253
254+ self .changedtick = vim .api .nvim_buf_get_changedtick (0 )
255+
168256 self .previous_mode_name = vim .g .libmodalActiveModeName
169257 vim .g .libmodalActiveModeName = self .name
170258
@@ -253,16 +341,7 @@ function Mode:render_virtual_cursor(winid, clear)
253341 local cursor = self :cursor_in (winid )
254342 vim .highlight .range (bufnr , NS .CURSOR , ' Cursor' , cursor , cursor , { inclusive = true })
255343
256- if not vim .deep_equal (self .cursor , cursor ) then
257- local mode = vim .api .nvim_get_mode ().mode
258- if CURSOR_EVENTS_BY_MODE .CursorMoved [mode ] then
259- vim .api .nvim_exec_autocmds (' CursorMoved' , {})
260- elseif CURSOR_EVENTS_BY_MODE .CursorMovedI [mode ] then
261- vim .api .nvim_exec_autocmds (' CursorMovedI' , {})
262- end
263-
264- self .cursor = cursor
265- end
344+ self :execute_cursor_moved_events (cursor )
266345end
267346
268347--- show the mode indicator, if it is enabled
@@ -294,7 +373,9 @@ function Mode:tear_down()
294373 self :clear_virtual_cursor (0 )
295374 vim .schedule (function () vim .opt .guicursor :remove { ' a:Cursor/lCursor' } end )
296375 vim .api .nvim_command ' highlight Cursor blend=0'
297- vim .api .nvim_del_autocmd (self .virtual_cursor_autocmd )
376+ for _ , autocmd in ipairs (self .autocmds ) do
377+ vim .api .nvim_del_autocmd (autocmd )
378+ end
298379 self .cursor = nil
299380
300381 if type (self .instruction ) == ' table' then
0 commit comments