Is it possible to fix options in vim? - vim

Following up on How to ensure that formatoptions *never* contains `r` or `o`?, which was poorly phrased.
Is it possible in vim to assign options in such a way that they cannot be modified by plugins or startup scripts. For example, something like:
set const final opt=value
in .vimrc would be great. I imagine this would be an error if opt is already constant, causing vim to refuse to open the buffer it is initializing when it encounters this, or possibly even exiting.
Does such a feature currently exist?

Not exactly. There are both :const and :lockvar, but they are for variables only. An option is a very different thing for VimScript.
However, you can fake this functionality with an auto-command. For example,
augroup ForbidSetOptions | au!
autocmd OptionSet formatoptions
\ if &filetype ==# 'foo'
\ echom 'You are not allowed to "set fo" for "foo" files!'
\ set formatoptions=jcroql
\ endif
augroup end
Note that we don't fall into an infinite recursion, because an auto-command is not triggered from another auto-command by default.
But note that this event is intentionally NOT triggered on startup. So you probably should consult a relevant plugin documentation instead to setup the options the way you want. Or write your own ~/.vim/after/plugin/... (or ~/.vim/after/ftplugin/...) script to undo such undesirable settings automatically.

Related

Is that correct `vim -S session` will load `.vimrc` again?

I found out all the settings in .vimrc actually are stored in session file. If that is the case, why vim -S session load .vimrc again? Why does vim design like this?
I have some commands in .vimrc and when I use vim -S, it causes problems because those commands should only be run once, not twice.
Thanks a lot.
My problematic vimrc block in mksession:
let g:netrw_banner = 0
let g:netrw_liststyle = 3
let g:netrw_browse_split = 4
let g:netrw_altv = 1
let g:netrw_winsize = 25
augroup ProjectDrawer
autocmd!
autocmd VimEnter * :Vexplore
autocmd TabNew * call feedkeys(":Vexplore\<CR>", 'n')
augroup END
What a session stores
What gets stored in a saved session is controlled by the :help 'sessionoptions' option. It's defaults are very conservative, aimed at not losing any ad-hoc mappings or changed options.
However, most users place their settings in the ~/.vimrc, use plugins and filetype plugins, and generally do not make up mappings or change settings on the fly. If that kind of workflow also applies to you, I'd suggest to remove the following values:
:set sessionoptions-=globals
:set sessionoptions-=localoptions
:set sessionoptions-=options
You may also want to remove buffers and resize, too. That change not just reduces the size of the saved session, it also prevents that an old session overrides any configuration that you've changed in your ~/.vimrc in the meantime.
vimrc reload
So, your ~/.vimrc isn't directly reloaded, but (with default 'sessionoptions'), a lot of options and variables are restored.
By using :augroup followed by :autocmd!, you've also avoided a frequent beginner's mistake: Without that, the autocmds would be redefined on each reload of your ~/.vimrc. The :autocmd! prevents that, and the :augroup makes that apply just to your own group.
sessions and special (plugin) buffers
A session should store all currently open buffers. Regular buffers contain file contents, and these can easily be reloaded when a session is opened again. Unfortunately, plugins (mis-)use buffers for their user interfaces (e.g. a sidebar, as by the netrw plugin), and Vim's session handling is not clever enough to distinguish between the two. But those plugin buffers have no corresponding file; instead, the plugin used command to directly build and modify its contents, often with the help of plugin variables that - see above - either are or aren't saved and restored.
Plugins would have to explicitly handle session restoration; I don't remember any plugin that does this, though. So often, you'll see an "empty" window restored, and the plugin may or may not recognize or reuse that. (And I think that's the problem you're asking about.)
The general recommendation is to close any plugin buffers before saving a session, either manually, or by writing a custom :Mksession wrapper that automates this for you. On session reload, your configuration may then create a fresh, functional plugin window again. (Or you have to trigger that manually.)
Not all the settings from the vimrc are stored in the session file (see :h mksession). The settings you see are specified in the :h sessionoptions option.
You can load a session without loading the vimrc by using vim -u NORC -S; however, you'll quickly see you're missing your desired baseline settings.
Which commands should only be run once? There are specific ways to prevent commands from running twice unnecessarily when sourcing your vimrc.
For instance, if the command that should only be run once is an autocmd you should use an augroup like so:
augroup vimrc
autocmd! " Remove all vimrc autocommands
au BufNewFile,BufRead *.html so <sfile>:h/html.vim
augroup END

How can I get rid of this "Auto Commands" message from vim?

If I have this line in my vimrc
au VimLeave %bdelete
Then whenever vim starts it says
--- Auto-Commands ---
Press ENTER or type command to continue
I have that line there to empty the buffers from gvim, because new gvim instances have massive :ls output from previous runs. Notably, gvim doesn't actually produce this prompt.
Obviously I can set this instance up to only occur during gvim startup and not console vim, but I'd like to understand this rather than avoid it. Mostly I'm confused that VimLeave seems to cause things to happen on startup.
TIA
Altreus
The problem is that this is an incomplete :autocmd definition, so Vim attempts to list all VimLeave autocommands defined for the pattern %bdelete. You need to specify the any file pattern to make it work:
au VimLeave * %bdelete
Also, check whether you have % in your 'viminfo' option; that one enables the saving and restoring of the buffer list you're complaining about. The f option of file marks may also result in buffers being restored; you could try :set viminfo+=f0.
New Vim instances don't inherit the buffer list of the previous instance unless you add % to the viminfo option.
Setting that option to a sane value will remove the need for your broken fix. Reading the documentation before adding options to your config will prevent you from similar issues.
See :help 'viminfo'.

Why check did_load_filetypes in custom filetype files?

From vim wiki:
Custom filetype.vim files should always have the following structure:
if exists("did_load_filetypes")
finish
endif
augroup filetypedetect
" au! commands to set the filetype go here
augroup END
I am wondering why is the if...endif part necessary?
I think this basically means that if filetypes are already loaded, then skip loading this script. But in that case what's the point of creating a custom filetype file?
This is just an include guard, to avoid that your custom filetype is accidentally sourced multiple times.
If all goes well, this indeed looks superfluous, but with all the variations in user configuration, plugin managers, etc., it's better to be safe. And it saves Vim from re-processing the potentially large list of definitions, speeding up startup.
What happens when this is actually sourced twice?
If you use :autocmd instead of :autocmd!, all your detections would be defined twice. (Note that an initial :autocmd! cannot be used here to clear all previous definitions, because multiple filetype.vim files are involved, and each only adds to the existing ones.)

Buffer-local value of global variable?

Many Vim's plugin's settings are specified by let g:something = value. I have a need for setting buffer-local values of some variables (actually with vim-localvimrc)
I know there's a way to outwit by using a condition (Setting buffer specific variables in Vim). But not all these autocmds are under my control, since some are set by other plugins. Is there anything like setlocal for global variable?
There are many ways to do to what you actually want (strip trailing whitespace for some projects) but I don't think there is anyway to shadow a global variable with a buffer local one (which is what you asked).
One way to do this is to add
autocmd BufWritePre /path/to/project/* call StripTrailingWhitespace()
the following to your vimrc. Which will call the function StripTrailingWhitespace for any file under /path/to/project (including subdirectories). StripTailingWhitespace is defined in spf13.
You can probably generate the path to the project dynamically by looking at the path to the local vimrc file.
Another way would be to change the condition spf13 uses to call StripTrailingWhitespace
The StripTrailingWhitespace autocmd in spf13 is defined as the following. (I added newlines so I'm not sure if this will work.)
autocmd FileType c,cpp,java,go,php,javascript,python,twig,xml,yml,perl
\ autocmd BufWritePre <buffer>
\ if !exists('g:spf13_keep_trailing_whitespace') |
\ call StripTrailingWhitespace() |
\ endif
So you would change it to !exists('g:spf13_keep_trailing_whitespace') to some other condition like checking if in whitelisted projects or check a buffer local variable.
General recommendation would be to get rid of any vim distribution. It is very hard to understand what is in them when you are first starting out. It then becomes very hard to modify them. It is generally better to start from scratch and add plugins and mappings when you find something lacking. It will take longer but you will understand everything in your vim configuration.
In your case, the plugin you are using shall accept overriding global variables with buffer local variables. Indeed it can be done by changing the condition -- in lh-vim-lib there is a lh#option#get() function that'll be easy to adapt to spf13. If you think it makes sense, contact spf13 maintainers or open an issue in the project bug tracker.
Another solution would rely on defining a local_vimrc script for each project you are working on. In some projects you'll have a :let g:spf13_keep_trailing_whitespace = 1, in other you'll have a :silent! unlet g:spf13_keep_trailing_whitespace -- usually it is accomplished with a :let g:var=0, but the current condition ignores the variable value.

Can you have different localleaders for different Vim plugins?

I started using a plugin that conflicts with my existing maps, but instead of remapping all of it's maps, I just want to add a prefix. I thought I'd be able to do this with LocalLeader.
Vimdoc says:
<LocalLeader> is just like <Leader>, except that it uses
"maplocalleader" instead of "mapleader". <LocalLeader> is to be used
for mappings which are local to a buffer.
It seems that the only way to set localleader is to set a global variable (the docs don't mention this, but b:maplocalleader didn't work):
let maplocalleader = '\\'
And I don't see how I'd cleanly unset that variable (an autocmd that clears it after plugins are setup!?)
Is there a way to do this? Or is LocalLeader only to give one global prefix and one filetype-specific prefix?
Your last hunch is correct. If the plugin uses <Leader> (and it should unless it's a filetype plugin), there's no use in messing with maplocalleader.
Remapping is canonically done via <Plug> mappings, which the plugin hopefully offers. Some plugins do define a lot of similar mappings, some of those define a g:pluginname_mappingprefix (or so) variable to save you from having to remap all mappings individually. If your plugin doesn't, maybe write a suggestion / patch to the plugin author.
While #IngoKarkat solution is a prefered one, there is a hack which lets you do what you want: the SourcePre event:
autocmd SourcePre * :let maplocalleader='\\'
autocmd SourcePre plugin-name.vim :let maplocalleader='_'
. This works for <Leader> as well. There are lots of cases when this won’t work though. You can as well use SourceCmd for this job, using something like
function s:Source(newmll)
try
let oldmll=g:maplocalleader
let g:maplocalleader=a:newmll
source <amatch>
finally
let g:maplocalleader=oldmll
endtry
endfunction
let maplocalleader='\\'
autocmd SourceCmd plugin-name.vim :call s:Source('_')
in SourceCmd is the only way I see to restore maplocalleader after plugin was sourced, but SourceCmd event here won’t be launched for any file sourced inside plugin-name.vim. For some poorly written plugins (I mean, those that emit errors while sources) putting :source inside a :try block will break execution at the point where error occurs. Should not happen most of time though. You may also want to use */ftplugin/plugin-name.vim as a pattern instead of plugin-name.vim.

Resources