I have some experience with vim but most of my time has been spent in neovim playing around with lua (although I'm still barely not a beginner). I missed out on the vimscript autocommand magic that I see alot of people pulling off and I'm wondering why not just write a lua function like...
function OpenTerm()
vim.cmd("bel split")
vim.cmd("terminal")
vim.cmd("setlocal nonumber norelativenumber")
end
instead of writing a an autocmd ?
You are conflating two totally unrelated things.
Your snippet (assuming it works, I don't use Neovim so I won't test it):
function OpenTerm()
vim.cmd("bel split")
vim.cmd("terminal")
vim.cmd("setlocal nonumber norelativenumber")
end
is a Lua function that is exactly equivalent to this Vimscript function:
function! OpenTerm()
bel split
terminal
setlocal nonumber norelativenumber
endfunction
and both functions have literally nothing to do with autocommands. They are completely passive and defining them only did some low-level memory-related things. For them to do anything, you have to call them.
Autocommands are a very different thing that allows you to tell Vim to perform some action when an event occurs, like this one:
autocmd VimEnter * call OpenTerm()
which, essentially, tells Vim this:
When you are done starting up, call the OpenTerm() function.
Unlike functions, which do nothing until they are called, autocommands have a real effect on your editor's state as soon as they are defined.
You can add an autocommand from a function, you can call a function from an autocommand, but you can't expect one to act as the other.
Basically, the question "Do I use an autocommand or a function?" makes no sense at all.
Related
I've recently installed the VimWiki plug-in, and am learning about Vim's plugin architecture in general (and better using directories like after/ftplugin instead of cramming everything into my .vimrc file).
I would like to call a function prior to writing wiki files, like so:
autocmd BufWrite *.wiki call CleanMarkdown()
However, vimwiki sets its own BufWrite autocommand, which updates any tables-of-contents in the wiki file. I could clobber this autocommand with my own function that calls both the CleanMarkdown() plus whatever vimwiki is doing today, but that would be brittle in the face of possible future changes in the vimwiki plugin.
Is there a standard way to add to the list of things to do for a BufWrite autocommand?
Multiplicity of autocmds
There can be many :autocmds for any event; the command is cummulative. The corresponding :autocmd! removes certain sets of commands (depending on the arguments given to it).
If you don't specify a [group], the autocmd will be defined in the global space, and there's a high risk of getting this cleared by some :autocmd!. Therefore, it is recommended to specify a [group] (especially in plugins). With this, you avoid that another (mis-behaving) plugin or customization clobbers your autocmd.
Integrating with vimwiki plugin
As the plugin already defines its own filetype, you don't need to duplicate the filetype detection logic, i.e. the *.wiki pattern. Instead, if you put your :autocmd definition in ~/.vim/after/ftplugin/vimwiki.vim, you can use the <buffer> special pattern to make this autocmd apply only to the current (VimWiki) buffer.
augroup MyVimWikiCleanup
autocmd BufWrite <buffer> call CleanMarkdown()
augroup END
Ordering
The :autocmds are executed in the order in which they were defined. By using the after directory, yours will be executed after the plugin's.
I'm working on a small vim plugin and needed to add an autocmd to call one of my functions MakeMatch whenever Insert mode is exited.
The actual setup to get the function to be called is pretty simple:
augroup Poi
autocmd!
autocmd InsertLeave * call s:MakeMatch()
augroup END
Originally I was under the impression that the function wasn't being called but then I added an echo into the definition and saw the string printed out.
The function is essentially the following:
function! s:MakeMatch()
"Iterate and create g:build_string up
"g:build_string=":match poi /\%5l\|\%6l/"
execute g:build_string
endfunction
If I was to :call s:MakeMatch() the build string will successfully execute as I had expected when leaving insert mode..
I've seen code in other plugins that uses au * exec.. without issue. I'm wondering if this is an issue with calling match during InsertLeave; I could definitely see something calling hi clear or maybe highlighting just isnt allowed during InsertLeave.
Been playing around with this with a co-worker and haven't been able to get it to run the match. We tried calling the match directly and other types of execute.. Would love to get some more info on why this may not be working.
Anyone have any ideas?
EDIT:
Here's the full code for the plugin I've wrote. It's working as expected now :D
I haven't got confirmation that this is the "correct" approach but it's the one that worked for me..
Removed the augroup and simply define an autocmd that will execute properly when vim loads my plugin's .vim file.
autocmd InsertLeave * call s:MakeMatch() was a little off the mark for a few reasons. The most obvious being that <SID> should have been used when attempting to call the function.
As an aside; #Peter Rinker: Mentioned trying to run autocmd InsertLeave * match Search /./. which works like a charm when running in :Ex mode but if you try to define the au! within the plugin/vimrc file it won't work..
I think this might have something to do with the fact that match is not an eval function but I am not sure if that is actually the case.
Ensure it ran as an Ex command. Since I had to run the command rather than have it be defined I had to change to {event} and call execute with the : like I had done to get #Peter's suggestion to work.
au! VimEnter * execute(":autocmd InsertLeave * call <SID>MakeMatch()")
The above did the trick for me but I'd definitely be interested in any other approach/more info.
I can use autocmd to make my cmd to run if file of specific file type loaded. For example for python:
autocmd FileType python make me happy with python
But is there any way to make my cmd to run if loaded file is NOT of specific type? Something like:
autocmd FileType !python make me happy without python
example above doesn't work and autocmd! will remove autocmd.
Any suggestions? Thanks.
There are several possibilities:
The easy way is to call a function and make your function check the filetype (and abort, if the filetype is python).
An alternative approach is to set a flag for python filetypes and make you function check the flag.
Use the * pattern and call your code only inside an if condition checking the filetype (something similar to this):
autocmd Filetype * if &ft!="python"|put your code here|endif
The hard way is to create a pattern, that doesn't match python. Something like this should do it:
:autocmd FileType [^p],[^p][^y],[^p][^y][^t],[^p][^y][^t][^h],[^p][^y][^t][^h][^o],[^p][^y][^t][^h][^o][^n] :put your code here
(the last part is untested and should illustrate, why usually any of the other possibilities are used).
You can make an autocmd which triggered by all (*) filetype, then call a function.
In the function, you can check the ft option (&ft) to decide what should be done for certain filetype. There you can do any matching logic with the value of &ft.
From vimdoc:
:checkt[ime] Check if any buffers were changed outside of Vim.
This checks and warns you if you would end up with two
versions of a file.
If this is called from an autocommand, a ":global"
command or is not typed the actual check is postponed
until a moment the side effects (reloading the file)
would be harmless.
How can I use it from an autocmd without delay?
EDIT
I somtimes use vim and an IDE to edit the same file, and I want to changes in one of them to be loaded into the other automatically.
Here is a possible solution:
autocmd CursorHold * checktime
function! Timer()
call feedkeys("f\e")
" K_IGNORE keycode does not work after version 7.2.025)
" there are numerous other keysequences that you can use
endfunction
But since checktime will be delayed, the effect may not be satisfactory.
As the quoted documentation explains, you can't, and for good reason (the reload might trigger other autocmds, the change might confuse following commands).
To work around this, you have to leave the current :autocmd, and re-trigger your code somehow. One idea is an :autocmd BufRead, which will fire if the buffer is actually reloaded. If you need to always retrigger, an :autocmd CursorHold (maybe with a temporarily reduced 'updatetime') can be used, or call feedkeys(":call Stage2()\<CR>") might be worth a try.
I ended up doing this:
autocmd CursorHold * call Timer()
function! Timer()
checktime
call feedkeys("f\e")
endfunction
It works quite well.
I was looking for an autocommand to make Vim do a gg=G on BufLeave and FocusLost so that it would indent the buffer I wasn't using. I didn't like the idea of doing this when I open or save a file because I figured I would get impatient of the larger files. But I think I dislike the autocommand I tried even more:
:autocmd BufLeave,FocusLost * :normal gg=G
So I was wondering if anyone had any suggestions to avoid the following problems:
The biggest problem is that I lose what line I am on.
Also it seemed like I was having to wait for the indenting to finish to begin editing another buffer. (I imagine just about any solution would at least make Vim slower in other buffers probably.)
I don't necessarily need the entire autocommand code itself, but more an idea for an autocommand that would accomplish the indenting without causing bigger issues. Hopefully the answer won't be too above my head as I am still reading through Learn Vimscript the Hard Way. I have been wanting to get involved on stackoverflow and this seemed like a fairly interesting and unlikely duplicated question to ask.
for the problem 1, there is solution: You could save the cursor position before you do re-indent (gg=G), and after that restore the position. vim has build-in getpos() and setpos(), which could help you in this case. For example, you could create a function:
function! Hook()
let p = getpos(".")
normal! gg=G
call setpos(".",p)
endfunction
and in your autocmd, you call the function:
:autocmd BufLeave,FocusLost * :call Hook()
This should make your vim re-indent and keep the original when you leave and back to a buffer.
However for the problem 2, it seems that there is no good solution with vimscript. Because vimscript doesn't support multi-threading. If the reindent will take 30s, you have to wait when the autocmd was triggered. There could be a solution to implement multi-threads by python/perl. However I haven't tested. Vim 7.4 has improved python interface, you may want to give the multi-threading in python a try.