Skip to content

Commit

Permalink
fix(Mode): textchanged events (#34)
Browse files Browse the repository at this point in the history
* ref(Mode): arbitrary number of per-instance autocmds

* ref(Mode): check cursor events using loop

* fix(Mode): handle textchanged events

* docs(examples): test CursorMoved, TextChanged events
  • Loading branch information
Iron-E authored Mar 15, 2024
1 parent 8366119 commit 9fa22b0
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 32 deletions.
19 changes: 16 additions & 3 deletions examples/lua/keymaps.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,31 @@ local fooModeKeymaps =
j = 'norm j',
k = 'norm k',
l = 'norm l',

G = function(self)
local count = self.count:get()
vim.api.nvim_command('norm! ' .. count .. 'G')
end,

d = 'delete',
e = 'edit foo',
o = 'norm o',
p = 'bp',

zf = 'split',
zfc = 'q',
zff = split_twice,
zfo = 'vsplit',
e = 'edit foo',
p = 'bp',
o = 'norm o',
}

-- show that events work as expected
local id = vim.api.nvim_create_autocmd(
{ 'CursorMoved', 'CursorMovedI', 'TextChanged', 'TextChangedI', 'TextChangedP', 'TextChangedT' },
{ callback = function(ev) vim.notify(vim.inspect(ev)) end }
)

-- enter the mode using the keymaps
libmodal.mode.enter('FOO', fooModeKeymaps)

-- remove setup
vim.api.nvim_del_autocmd(id)
139 changes: 110 additions & 29 deletions lua/libmodal/Mode.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ local utils = require 'libmodal.utils' --- @type libmodal.utils
--- @alias CursorPosition {[1]: integer, [2]: integer} see `nvim_win_get_cursor`

--- @class libmodal.Mode
--- @field private autocmds integer[]
--- @field private changedtick integer
--- @field private cursor CursorPosition
--- @field private flush_input_timer unknown
--- @field private help? libmodal.utils.Help
Expand All @@ -16,24 +18,69 @@ local utils = require 'libmodal.utils' --- @type libmodal.utils
--- @field private name string
--- @field private popups libmodal.collections.Stack
--- @field private supress_exit boolean
--- @field private virtual_cursor_autocmd integer
--- @field public count libmodal.utils.Var[integer]
--- @field public exit libmodal.utils.Var[boolean]
--- @field public timeouts? libmodal.utils.Var[boolean]
local Mode = utils.classes.new()

--- Cursor events triggered by which modes
local CURSOR_EVENTS_BY_MODE = {
CursorMoved = {
n = true,
V = true,
v = true,
[utils.api.replace_termcodes '<C-v>'] = true,
local C_v = utils.api.replace_termcodes '<C-v>'
local C_s = utils.api.replace_termcodes '<C-s>'

--- Event groups organized by modes
local EVENTS_BY_MODE = {
--- Cursor events triggered by which modes
CURSOR_MOVED = {
CursorMoved = {
n = true,
nt = true,
ntT = true,
s = true,
S = true,
[C_s] = true,
v = true,
V = true,
[C_v] = true,
vs = true,
Vs = true,
[C_v .. 's'] = true,
},

CursorMovedI = {
i = true,
niI = true,
niR = true,
R = true,
Rv = true,
},
},

CursorMovedI = {
i = true,
R = true,
TEXT_CHANGED = {
TextChanged = {
n = true,
nt = true,
ntT = true,
},

TextChangedI = {
i = true,
niI = true,
niR = true,
R = true,
Rv = true,
},

TextChangedP = {
ic = true,
ix = true,
Rc = true,
Rvc = true,
Rx = true,
Rvx = true,
},

TextChangedT = {
t = true,
},
},
}

Expand All @@ -57,6 +104,18 @@ local ZERO = string.byte(0)
--- Byte for 9
local NINE = string.byte(9)

--- Execute events depending on current mode
--- @param events_by_mode {[string]: {[string]: true}}
local function execute_event_by_mode(events_by_mode)
local mode = vim.api.nvim_get_mode().mode
for event, modes in pairs(events_by_mode) do
if modes[mode] then
vim.api.nvim_exec_autocmds(event, {})
break
end
end
end

--- execute the `instruction`.
--- @private
--- @param instruction fun(libmodal.Mode)|string a Lua function or Vimscript command.
Expand All @@ -70,6 +129,7 @@ function Mode:execute_instruction(instruction)

self.count:set(0)
self:render_virtual_cursor(0, true)
self:execute_text_changed_events()
end

--- check the user's input against the `self.instruction` mappings to see if there is anything to execute.
Expand Down Expand Up @@ -133,7 +193,24 @@ end
--- @private
function Mode:clear_virtual_cursor(bufnr)
vim.api.nvim_buf_clear_namespace(bufnr, NS.CURSOR, 0, -1);
end

--- Runs CursorMoved* events, if applicable
--- @param cursor CursorPosition the current cursor position
function Mode:execute_cursor_moved_events(cursor)
if not vim.deep_equal(self.cursor, cursor) then
execute_event_by_mode(EVENTS_BY_MODE.CURSOR_MOVED)
self.cursor = cursor
end
end

--- Runs TextChanged* events, if applicable
function Mode:execute_text_changed_events()
local changedtick = vim.api.nvim_buf_get_changedtick(0)
if self.changedtick ~= changedtick then
execute_event_by_mode(EVENTS_BY_MODE.TEXT_CHANGED)
self.changedtick = changedtick
end
end

--- enter this mode.
Expand All @@ -156,15 +233,26 @@ function Mode:enter()

do
local augroup = vim.api.nvim_create_augroup('libmodal-mode-' .. self.name, { clear = false })
self.virtual_cursor_autocmd = vim.api.nvim_create_autocmd('BufLeave', {
callback = function(ev)
local bufnr = ev.buf
self:clear_virtual_cursor(bufnr)
end,
group = augroup,
})
self.autocmds = {
vim.api.nvim_create_autocmd('BufLeave', {
callback = function(ev)
local bufnr = ev.buf
self:clear_virtual_cursor(bufnr)
end,
group = augroup,
}),

vim.api.nvim_create_autocmd('BufEnter', {
callback = function(ev)
local bufnr = ev.buf
self.changedtick = vim.api.nvim_buf_get_changedtick(bufnr)
end,
});
}
end

self.changedtick = vim.api.nvim_buf_get_changedtick(0)

self.previous_mode_name = vim.g.libmodalActiveModeName
vim.g.libmodalActiveModeName = self.name

Expand Down Expand Up @@ -253,16 +341,7 @@ function Mode:render_virtual_cursor(winid, clear)
local cursor = self:cursor_in(winid)
vim.highlight.range(bufnr, NS.CURSOR, 'Cursor', cursor, cursor, { inclusive = true })

if not vim.deep_equal(self.cursor, cursor) then
local mode = vim.api.nvim_get_mode().mode
if CURSOR_EVENTS_BY_MODE.CursorMoved[mode] then
vim.api.nvim_exec_autocmds('CursorMoved', {})
elseif CURSOR_EVENTS_BY_MODE.CursorMovedI[mode] then
vim.api.nvim_exec_autocmds('CursorMovedI', {})
end

self.cursor = cursor
end
self:execute_cursor_moved_events(cursor)
end

--- show the mode indicator, if it is enabled
Expand Down Expand Up @@ -294,7 +373,9 @@ function Mode:tear_down()
self:clear_virtual_cursor(0)
vim.schedule(function() vim.opt.guicursor:remove { 'a:Cursor/lCursor' } end)
vim.api.nvim_command 'highlight Cursor blend=0'
vim.api.nvim_del_autocmd(self.virtual_cursor_autocmd)
for _, autocmd in ipairs(self.autocmds) do
vim.api.nvim_del_autocmd(autocmd)
end
self.cursor = nil

if type(self.instruction) == 'table' then
Expand Down

0 comments on commit 9fa22b0

Please sign in to comment.