Skip to content

Commit

Permalink
feat!: perf optimizations for large macros
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisgrieser committed Jul 24, 2023
1 parent 0632608 commit bf83eeb
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 24 deletions.
23 changes: 15 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ Enhance the usage of macros in Neovim.
- __Macro Breakpoints__ for easier debugging of macros. Breakpoints can also be set after the recording and are automatically ignored when triggering a macro with a count.
- __Status line components__: Particularly useful if you use `cmdheight=0` where the recording status is not visible.
- __Macro-to-Mapping__: Copy a macro in decoded form for mappings to your default register.
- __Various quality-of-life features__: notifications with macro content, the ability to cancel a recording, a command to edit macros, automatically setting [`lazyredraw`](https://neovim.io/doc/user/options.html#'lazyredraw') when using a high count, …
- __Various quality-of-life features__: notifications with macro content, the ability to cancel a recording, a command to edit macros,
- __Performance Optimizations for large macros__: When the macro is triggered with a high count, temporarily enable settings like [`lazyredraw`](https://neovim.io/doc/user/options.html#'lazyredraw') during the macro.
- Uses up-to-date nvim features like `vim.ui.input` or `vim.notify`. This means you can get confirmation notices with plugins like [nvim-notify](https://github.com/rcarriga/nvim-notify).
- Written 100% in lua. Lightweight (~300 LoC).

Expand Down Expand Up @@ -73,16 +74,22 @@ require("recorder").setup {
-- (Note that by default, nvim-notify does not show the levels trace & debug.)
logLevel = vim.log.levels.INFO,

-- If enabled, only essential or critical notifications are sent.
-- If enabled, only critical notifications are sent.
-- If you do not use a plugin like nvim-notify, set this to `true`
-- to remove otherwise annoying notifications.
-- to remove otherwise annoying messages.
lessNotifications = false,

-- When the number of counts is above this value, automatically
-- enable`lazyredraw` for the duration of the macro (see `:h lazyredraw`).
lazyredrawThreshold = 500,

-- experimental, see README
-- Performance optimzations for macros with high count. When `playMacro` is
-- triggered with a count higher than the threshold, nvim-recorder
-- temporarily changes changes some settings for the duration of the macro.
performanceOpts = {
countThreshold = 200,
lazyredraw = true, -- temporarily enable lazyredraw, see `:h lazyredraw`
noSystemClipboard = true, -- temporarily remove `+`/`*` from clipboard
}

-- [experimental] partially share keymaps with nvim-dap.
-- (See README for further explanations.)
dapSharedKeymaps = false,
}
```
Expand Down
61 changes: 45 additions & 16 deletions lua/recorder.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ local function isPlaying() return fn.reg_executing() ~= "" end
local function normal(cmdStr) vim.cmd.normal { cmdStr, bang = true } end

local macroRegs, slotIndex, logLevel, lessNotifications
local toggleKey, breakPointKey, dapSharedKeymaps, lazyredrawThreshold
local toggleKey, breakPointKey, dapSharedKeymaps
local perf = {}
local M = {}

local breakCounter = 0 -- resets break counter on plugin reload
Expand Down Expand Up @@ -69,7 +70,8 @@ local function playRecording()
local macro = getMacro(reg)
local hasBreakPoints = macro:find(vim.pesc(breakPointKey))
local countGiven = v.count ~= 0
local useLazyRedraw = v.count >= lazyredrawThreshold
local useLazyRedraw = v.count >= perf.threshold and perf.lazyredraw and not (opt.lazyredraw:get() == true)
local noSystemClipboard = v.count >= perf.threshold and perf.noSystemclipboard and (opt.clipboard:get() ~= "")

-- Guard Clause 1: Toggle Breakpoint instead of Macro
-- WARN undocumented and prone to change https://github.com/mfussenegger/nvim-dap/discussions/810#discussioncomment-4623606
Expand All @@ -85,7 +87,9 @@ local function playRecording()
-- Guard Clause 2: Recursively play macro
if isRecording() then
vim.notify(
"Playing the macro while it is recording would cause recursion problems. Aborting. (You can still use recursive macros by using `@" .. reg .. "`)",
"Playing the macro while it is recording would cause recursion problems. Aborting. (You can still use recursive macros by using `@"
.. reg
.. "`)",
level.ERROR
)
normal("q") -- end recording
Expand Down Expand Up @@ -119,9 +123,17 @@ local function playRecording()

-- Execute Macro (without breakpoints)
else
if useLazyRedraw and opt.lazyredraw:get() == false then opt.lazyredraw = true end
if useLazyRedraw then opt.lazyredraw = true end
local prevClipboardOpt
if noSystemClipboard then
opt.clipboard = ""
prevClipboardOpt = opt.clipboard:get()
end

normal(v.count1 .. "@" .. reg)

if useLazyRedraw then opt.lazyredraw = false end
if noSystemClipboard then opt.clipboard = prevClipboardOpt end
end
end

Expand Down Expand Up @@ -199,9 +211,14 @@ end
---@field mapping maps individual mappings
---@field logLevel integer log level (vim.log.levels)
---@field lessNotifications boolean plugin is less verbose, shows only essential or critical notifications
---@field lazyredrawThreshold boolean when the number of counts is above this value, `lazyredraw` will be temporarily enabled during macro execution
---@field performanceOpts perfOpts various performance options
---@field dapSharedKeymaps boolean (experimental) partially share keymaps with dap

---@class perfOpts
---@field countThreshold number if count used is higher than threshold, the following performance optimizations are applied
---@field lazyredraw boolean :h lazyredraw
---@field noSystemClipboard boolean no `*` or `+` in clipboard https://vi.stackexchange.com/a/31888

---@class maps
---@field startStopRecording string
---@field playMacro string
Expand All @@ -216,12 +233,31 @@ end
function M.setup(config)
config = config or {}
slotIndex = 1 -- initial starting slot
macroRegs = config.slots or { "a", "b" }

-- General settings
logLevel = config.logLevel or level.INFO
lessNotifications = config.lessNotifications or false
lazyredrawThreshold = config.lazyredrawThreshold or 500

-- validation of slots
-- clearing
if config.clear then
for _, reg in pairs(macroRegs) do
setMacro(reg, "")
end
end

-- performance opts
local defaultPerfOpts = {
countThreshold = 1,
lazyredraw = true,
noSystemClipboard = true,
}
if not config.performanceOpts then config.performanceOpts = defaultPerfOpts end
perf.countThreshold = config.performanceOpts.countThreshold or defaultPerfOpts.countThreshold
perf.lazyredraw = config.performanceOpts.lazyredraw or defaultPerfOpts.lazyredraw
perf.noSystemClipboard = config.performanceOpts.noSystemClipboard or defaultPerfOpts.noSystemClipboard

-- macro slots (+ validate them)
macroRegs = config.slots or { "a", "b" }
for _, reg in pairs(macroRegs) do
if not (reg:find("^%l$")) then
vim.notify(
Expand All @@ -232,7 +268,7 @@ function M.setup(config)
end
end

-- set keymaps
-- keymaps
local defaultKeymaps = {
startStopRecording = "q",
playMacro = "Q",
Expand Down Expand Up @@ -265,13 +301,6 @@ function M.setup(config)
keymap("n", breakPointKey, addBreakPoint, { desc = desc1 })
local desc2 = dapSharedKeymaps and "/ Continue/Play" or " Play Macro"
keymap("n", playKey, playRecording, { desc = desc2 })

-- clearing
if config.clear then
for _, reg in pairs(macroRegs) do
setMacro(reg, "")
end
end
end

--------------------------------------------------------------------------------
Expand Down

0 comments on commit bf83eeb

Please sign in to comment.