Reason
then executing it 40000 times with 40000@a. This is very slow. I must be doing something wrong, because while it does work it takes something like 60-90 seconds.
None of the other answers targeted the major cause. Slow macro running is mainly affected by plugins, pasting, not by screen redrawing.
filetype
syntax and lazyredraw
only have a very, very, very minimal impact. The correct solution should be finding out and disabling those plugins which slows down your editing in insert mode.
Macro is just replaying actions stored in the register onto the selected region. The environment of running the macro is the same as the env you record the macro. Plugin may introduce additional cost during editing. Commonly it's not a problem. But multiplying the cost with 40,000 amplifies the impact dramatically.
Here's the impact factor on macro running I got:
system clipboard >> plugins >> filetype, syntax
Solution
System Clipboard
Avoid accessing system clipboard in the macro.
Accessing external system clipboard +
, *
introduces additional cost, when compared with accessing internal registers. It may even freeze the macro replaying forever (run macro on 6000 lines in my test).
imap and event
Disable plugins which affects the editing speed
- Those do insert mapping, e.g.
imap
<CR>
<Tab>
...
jiangmiao/auto-pairs
Raimondi/delimitMate
- ...
:noautocmd :norm @q
to disable events temporarily during macro running.
Or set eventignore=all
before macro running, and set it back after.
- ~~Those register hooks related with editing~~ (Seems not needed)
- Related hooks
CursorMoved(I)
, CursorHold(I)
InsertCharPre
, InsertEnter
, InsertLeave(Pre)
neoclide/coc.nvim
brglng/vim-im-select
chrisbra/Colorizer
- ...
Note:
- Not all plugins registering functions on above hooks make a big differentce
on macro running speed. Some of them only have a trivial, negligible impact on
the editing speed. You have to find them by replaying macro with and without
the plugin and comparing the time cost.
- Insert mode mapping from
auto-pairs
couldn't be disabled completely,
which is one of reasons I switched to delimitMate
.
- To use
:noautocmd
on selection. The range should be put before norm
: :noa '<,'>norm! @q
Here's a toggle function I made to disable above plugins before running macro.
Use <F3>m
to disables plugins before recording a macro. After running the macro,
<F3>m
again to reset the states of these plugins.
(I group all my toggles under <F3>
, m
stands for macro. You can adjust the mapping for your need.)
function! <SID>ToggleMacro(...)
" Optimize macro running by disable some plugins.
if get(g:, '_macro', {}) ==# {}
let g:_macro = {'state': 1}
let g:_macro.auto_pairs = get(b:, 'autopairs_enabled')
if g:_macro.auto_pairs
let b:autopairs_enabled = 0
endif
let g:_macro.delimit_mate = get(b:, 'delimitMate_enabled')
if g:_macro.delimit_mate
DelimitMateOff
endif
let g:_macro.eventignore = &eventignore
set eventignore=all
else
let g:_macro.state = 0
let b:autopairs_enabled = g:_macro.auto_pairs
if g:_macro.delimit_mate
DelimitMateOn
endif
let &eventignore = g:_macro.eventignore
endif
if g:_macro.state
echo 'Macro boost: On'
else
echo 'Macro boost: Off'
unlet g:_macro
endif
endfunction
nnoremap <F3>m :call <SID>ToggleMacro()<CR>
command! -nargs=? ToggleMacro call <SID>ToggleMacro(<f-args>)
Ref
:h autocmd
, :h noautocmd
, :h eventignore
- Thank Christian Brabandt for mentioning
:noautocmd
, eventignore