├── README.md ├── ext └── vim-deferred.gif └── plugin └── deferred.vim /README.md: -------------------------------------------------------------------------------- 1 | # deferred.vim 2 | 3 | Defer the execution of Vim commands. 4 | 5 | ![vim-deferred](https://github.com/kevinushey/vim-deferred/blob/17d6dc08d4377fd730c986bc5ed3f4c069de77b5/ext/vim-deferred.gif) 6 | 7 | This plugin was primarily made so I could source some 8 | particularly heavily Vim scripts 'lazily', so that Vim 9 | itself could open and render a little bit faster. 10 | 11 | # Wait, What? 12 | 13 | This plugin provides a means for delaying the execution of 14 | a command until certain `autocmd` events are fired. This 15 | means you can attach one-time executions to events like: 16 | 17 | - The user types `:`, 18 | - The user enters insert mode, 19 | - The user opens a new file 20 | 21 | And so on. The main utility gained relative to basic 22 | `autocmd`s is that this plugin goes out of its way to 23 | ensure each command is executed only once, rather than 24 | tying its execution to every event. 25 | 26 | # Usage and Motivation 27 | 28 | This pluging was basically developed to provide a means 29 | for lazily sourcing a Vim script. Here's how I'm using it 30 | right now, to lazily load some supplementary Vim scripts 31 | which take just a little bit too long to load at startup: 32 | 33 | ```viml 34 | autocmd VimEnter * Defer 35 | \ source ~/.vim/startup/spf13.vim | 36 | \ source ~/.vim/startup/global.vim | 37 | \ redraw 38 | ``` 39 | 40 | Note that the only reason I `autocmd VimEnter` that is 41 | because the `Defer` command (exported by this package) 42 | doesn't seem to be available at that time. It could be 43 | because I am not properly loading it or because I need to 44 | move the folder from `plugin/` to `autoloads/`. 45 | 46 | # License 47 | 48 | Copyright (c) Kevin Ushey. Distributed under the same 49 | terms as Vim itself. See `:help license` for more details. 50 | -------------------------------------------------------------------------------- /ext/vim-deferred.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinushey/vim-deferred/47bfeda0907801ca63a795b9e5e4fea1e9e92b2b/ext/vim-deferred.gif -------------------------------------------------------------------------------- /plugin/deferred.vim: -------------------------------------------------------------------------------- 1 | " Location: plugin/deferred.vim 2 | " Author: Kevin Ushey 3 | " Version: 0.1 4 | " License: Same as Vim itself. See :help license 5 | 6 | if exists('g:loaded_deferred') || &cp 7 | finish 8 | endif 9 | let g:loaded_deferred = 1 10 | 11 | " The set of autocmd events that will serve as default 12 | " triggers for the 'Defer' command. 13 | if !exists('g:DeferredEvents') 14 | let g:DeferredEvents = ['CursorHold', 'CursorHoldI', 'OnNormalModeColon'] 15 | endif 16 | 17 | let s:DeferredCount = 0 18 | let s:DeferredCommands = [] 19 | 20 | function! s:Verbose(...) 21 | 22 | if a:0 > 0 23 | let Level = a:1 24 | else 25 | let Level = 0 26 | endif 27 | 28 | return exists('g:verbose') && g:verbose > Level 29 | 30 | endfunction 31 | 32 | " TODO: Populate this list dynamically. 33 | 34 | " Autocmd Events {{{ 35 | 36 | let s:InternalAutocmdEvents = [ 37 | \ 'BufAdd', 38 | \ 'BufCreate', 39 | \ 'BufDelete', 40 | \ 'BufEnter', 41 | \ 'BufFilePost', 42 | \ 'BufFilePre', 43 | \ 'BufHidden', 44 | \ 'BufLeave', 45 | \ 'BufNew', 46 | \ 'BufNewFile', 47 | \ 'BufRead', 48 | \ 'BufReadPost', 49 | \ 'BufReadCmd', 50 | \ 'BufReadPre', 51 | \ 'BufUnload', 52 | \ 'BufWinEnter', 53 | \ 'BufWinLeave', 54 | \ 'BufWipeout', 55 | \ 'BufWrite', 56 | \ 'BufWriteCmd', 57 | \ 'BufWritePost', 58 | \ 'CmdUndefined', 59 | \ 'CmdwinEnter', 60 | \ 'CmdwinLeave', 61 | \ 'ColorScheme', 62 | \ 'CompleteDone', 63 | \ 'CursorHold', 64 | \ 'CursorHoldI', 65 | \ 'CursorMoved', 66 | \ 'CursorMovedI', 67 | \ 'EncodingChanged', 68 | \ 'FileAppendCmd', 69 | \ 'FileAppendPost', 70 | \ 'FileAppendPre', 71 | \ 'FileChangedRO', 72 | \ 'FileChangedShell', 73 | \ 'FileChangedShellPost', 74 | \ 'FileEncoding', 75 | \ 'FileReadCmd', 76 | \ 'FileReadPost', 77 | \ 'FileReadPre', 78 | \ 'FileType', 79 | \ 'FileWriteCmd', 80 | \ 'FileWritePost', 81 | \ 'FileWritePre', 82 | \ 'FilterReadPost', 83 | \ 'FilterReadPre', 84 | \ 'FilterWritePost', 85 | \ 'FilterWritePre', 86 | \ 'FocusGained', 87 | \ 'FocusLost', 88 | \ 'FuncUndefined', 89 | \ 'GUIEnter', 90 | \ 'GUIFailed', 91 | \ 'InsertChange', 92 | \ 'InsertCharPre', 93 | \ 'InsertEnter', 94 | \ 'InsertLeave', 95 | \ 'MenuPopup', 96 | \ 'QuickFixCmdPre', 97 | \ 'QuickFixCmdPost', 98 | \ 'QuitPre', 99 | \ 'RemoteReply', 100 | \ 'SessionLoadPost', 101 | \ 'ShellCmdPost', 102 | \ 'ShellFilterPost', 103 | \ 'SourcePre', 104 | \ 'SourceCmd', 105 | \ 'SpellFileMissing', 106 | \ 'StdinReadPost', 107 | \ 'StdinReadPre', 108 | \ 'SwapExists', 109 | \ 'Syntax', 110 | \ 'TabEnter', 111 | \ 'TabLeave', 112 | \ 'TermChanged', 113 | \ 'TermResponse', 114 | \ 'TextChanged', 115 | \ 'TextChangedI', 116 | \ 'User', 117 | \ 'UserGettingBored', 118 | \ 'VimEnter', 119 | \ 'VimLeave', 120 | \ 'VimLeavePre', 121 | \ 'VimResized', 122 | \ 'WinEnter', 123 | \ 'WinLeave' 124 | \ ] 125 | 126 | " }}} 127 | 128 | function! s:SplitOnFirstSpace(string) 129 | let FirstSpaceIdx = stridx(a:string, ' ') 130 | let First = strpart(a:string, 0, FirstSpaceIdx) 131 | let Rest = strpart(a:string, FirstSpaceIdx + 1) 132 | return [First, Rest] 133 | endfunction 134 | 135 | function! DeferredExecuteOnce(quoted) 136 | 137 | let Splat = s:SplitOnFirstSpace(a:quoted) 138 | 139 | let ID = Splat[0] 140 | let Command = Splat[1] 141 | 142 | let VariableName = 'g:deferred_' . ID 143 | if exists(VariableName) 144 | return 145 | endif 146 | 147 | execute Command 148 | execute "let " . VariableName . " = 1" 149 | 150 | endfunction 151 | 152 | function! DeferredFunction(call) 153 | let Command = ':call ' . call 154 | Defer Command 155 | endfunction 156 | 157 | function! s:DeferCommand(events, command) 158 | 159 | let CommandID = s:DeferredCount 160 | 161 | for Event in a:events 162 | 163 | let GroupID = s:DeferredCount 164 | let s:DeferredCount += 1 165 | 166 | " TODO: Construct these in a neater way. There's a 167 | " bunch of ugliness because user events and 168 | " internal events are specified in a different 169 | " way. 170 | if count(s:InternalAutocmdEvents, Event) 171 | let MaybeUser = '' 172 | let EventString = Event . " * " 173 | else 174 | let MaybeUser = "User" 175 | let EventString = "User " . Event . " " 176 | endif 177 | 178 | let GroupName = "Deferred_" . GroupID 179 | let AutoCommand = EventString . 180 | \ 'DeferredExecuteOnce ' . CommandID . ' ' . a:command 181 | 182 | let DeleteCommand = 'autocmd! ' . GroupName . ' ' . MaybeUser . " " . Event 183 | 184 | if s:Verbose() 185 | echomsg "Deferring command: " . AutoCommand 186 | echomsg "Killing with: " . DeleteCommand 187 | endif 188 | 189 | execute "augroup " . GroupName 190 | execute " autocmd!" 191 | execute " autocmd " . AutoCommand . " | " . DeleteCommand 192 | execute "augroup end" 193 | 194 | endfor 195 | 196 | endfunction 197 | 198 | command! -nargs=* DeferredExecuteOnce 199 | \ call DeferredExecuteOnce() 200 | 201 | function! s:DeferUntilCommand(quoted) 202 | let splat = s:SplitOnFirstSpace(a:quoted) 203 | return s:DeferCommandImpl(split(splat[0], ','), splat[1]) 204 | endfunction 205 | 206 | " Example: 207 | " 208 | " Defer :echo 'Hello!' 209 | " 210 | " The command will be called after an event in the 211 | " 'g:DeferredEvents' set is triggered. 212 | command! -nargs=* Defer 213 | \ call DeferCommand(g:DeferredEvents, ) 214 | 215 | " Example: 216 | " 217 | " DeferUntil BufEnter :echo 'Hello!' 218 | " 219 | " The command will be called after the autocmd event 220 | " specified as the first 'argument' to the command is 221 | " entered. The set of arguments should be specified as a 222 | " comma-delimited string with no spaces. 223 | command! -nargs=* DeferUntil 224 | \ call DeferUntilCommand() 225 | 226 | " Allow ':' to trigger deferred events. 227 | function! DeferColon() 228 | doautocmd User OnNormalModeColon 229 | return ":" 230 | endfunction 231 | 232 | if strlen(maparg(':', 'n')) == 0 233 | nnoremap : DeferColon() 234 | endif 235 | 236 | 237 | --------------------------------------------------------------------------------