Run a vim formatter conditionally - vim

I have a callback setup-ed that auto-formats the code automatically after saving the buffer (depending on the file type). I'd like to avoid this behavior for very long files. Is it possible to make :w behave the same as :noa w, when the file is longer than N lines?

A direct implementation of your requirement would be by mapping :w. Through the use of :help map-expr, you can dynamically react to conditions (here: the number of lines in the buffer):
:nnoremap <expr> :w ':' . (line('$') >= 1000 ? 'noa ' : '') . 'w'
Note that there are more robust approaches for overriding built-in Ex commands. (For example cmdalias.vim - Create aliases for Vim commands.)
Recommended alternative
The advantage of the mapping is that you directly see what effect it has (though you have to remember what effects :noautocmd has here), and you can influence / override it easily.
However, it won't work with mappings or plugins that invoke :update directly. I would prefer to modify the callback setup instead. You probably have something like
:autocmd BufWritePost <buffer> call AutoFormat()
I would introduce a boolean flag that guards this:
:autocmd BufWritePost <buffer> if exists('b:AutoFormat') && b:AutoFormat | call AutoFormat() | endif
And then setup a hook that initializes it:
:autocmd BufWritePre <buffer> if ! exists('b:AutoFormat') | let b:AutoFormat = (line('$') < 1000) | endif
This way, you see whether auto-formatting is enabled (you can even put b:AutoFormat into your statusline), and you can tweak the behavior by manipulating the flag:
:let b:AutoFormat = 0 " Turn off auto-formatting even though the buffer is small.
:let b:AutoFormat = 1 " Force auto-formatting even though it's a large buffer.

Related

Vim key binding to call a function with input from user

There might be a simple solution for this. I couldn't find any solution to it (I might be searching with wrong context)
Here is my requirement.
I wrote the below key mapping in vimrc. It should print the line "Hello user_name." n times, where n and user_name are the user input once the key is pressed.
autocmd FileType ruby nnoremap <expr> <C-h> :call FuncPrnt(<-syntax to pass input from user->)
function! FuncPrnt(count, uname)
let c=a:count
let i=0
while i<c
call append(line("."), "Hello ".a:uname.".")
let i+=1
endwhile
endfunction
On Pressing the key user enters 3 and 'Ironman'. The output would be like
Hello Ironman.
Hello Ironman.
Hello Ironman.
Thanks in advance
A simpler approach is to just use a non-<expr> mapping to prepare the Ex command without executing it:
nnoremap <C-h> :call FuncPrnt(,"")<left><left><left><left>
If you associate that with a filetype (such as "ruby" in your case), make sure you create a local mapping using <buffer>. Otherwise the mapping will be global and will work on every buffer and not only those with Ruby source files.
If you use an autocmd in your vimrc, make sure you wrap it in an augroup to prevent getting duplicated commands if you reload your vimrc.
Alternatively, you can add filetype mappings for Ruby in a file ~/.vim/ftplugin/ruby.vim which gets automatically loaded by Vim whenever a file of type Ruby is loaded. (That way you don't need to use explicit autocmds, Vim will take care of those details on your behalf.)
Got the answer. Just in case someone else is searching for similar solution
autocmd FileType ruby nnoremap <expr> <C-h> input("", ":call FuncPrnt(,\"\")<left><left><left><left>")
will print the command and waits for the user to edit. Once the user edits and enters it is executed
Thinking outside of the box a little bit (questioning the premise), perhaps the best in this case is to define a user-defined command instead of a mapping.
You can define one here with:
command! -buffer -bar -count=1 -nargs=1 FuncPrnt
\ call FuncPrnt(<count>, <q-args>)
That way you can use it with:
:3FuncPrnt Ironman
If you omit the count, 1 will be used. (You can pick a different default as the argument to -count=N.)
You can use Tab completion for FuncPrnt, so perhaps :3Fu<Tab> or even :3F<Tab> might be enough to complete the command.
This might end up being quicker or more convenient to type than <C-H>3<right><right>, since it doesn't involve moving your hand to the arrow keys.

detect, if ctrlp is opened/active

I like the relativenumbers, but they turn out to be slow if I move through files with very long lines. So I added
autocmd CursorMoved,CursorMovedI * if &relativenumber | set norelativenumber | endif
autocmd CursorHold,CursorHoldI * set relativenumber
set updatetime=500
to my vimrc. It disables relative numbers during cursor movement and enables them afterwards again. However, this also enables them in ctrlp. Especially since ctrlp disables number, this causes columns to jump as soon as I cursor. Is there a way to detect, if I'm currently in the curlp file browser?
The CtrlP scratch buffer probably has a distinct name (if bufname('') =~ 'ctrlp')) or filetype (if &filetype ==# 'ctrlp') that you can use in a condition on a similar autocmd BufEnter.
(Note: I don't use the plugin, so the above names are just for illustration.)

Vim: disable word wrapping for specific lines

I am using :set textwidth=80 to make the Vim editor do hard wrapping automatically. However, sometimes for certain lines within a file (e.g. tables in LaTeX), I do not want Vim to do any hard wrapping automatically. Is there a way to mark certain lines to disable hard wrapping in Vim? Or automatically :set textwidth=0 just for specified lines?
There's nothing out-of-the-box, but you can build a solution with an :autocmd <buffer> on the CursorMoved,CursorMovedI events. On each move of the cursor, you have to check whether you're currently in one of those "certain lines", and modify the local 'textwidth' option accordingly:
autocmd CursorMoved,CursorMovedI <buffer> if IsSpecialLine() | setlocal textwidth=0 | else | setlocal textwidth=80 | endif
Put this into ~/.vim/after/ftplugin/tex.vim. (This requires that you have :filetype plugin on; use of the after directory allows you to override any default filetype settings done by $VIMRUNTIME/ftplugin/tex.vim.) Alternatively, you could define an :autocmd FileType tex autocmd ... directly in your ~/.vimrc, but this tends to become unwieldy once you have many customizations.
For the IsSpecialLine() function, you probably need to match a regular expression on the current line (getline('.') =~# "..."). If you can identify the "certain lines" through syntax highlighting, my OnSyntaxChange plugin can do all the work for you.
I've tried Ingo Karkat answer. Although it does work very well and does what the OP asks, I find it distracting (if I have long tables with row hundreds of characters long, there are a lot of shifts up and down when passing over tables) and can slow down a lot vim on big files (the textwidth and wrap are changed for the whole file, so running the autocmd for every cursor movement can be costly).
So I propose a static solution based on the idea that hopefully you have to modify a table as few times as possible. I've added the following to my ftplugin/tex.vim file:
" By default the text is
let s:textwidth = 90
let &l:textwidth=s:textwidth
" Toggle between "textwidth and wrap" and "textwidth=0 and nowrap".
" When editing a table, can be useful to have all the '&' aligned (using e.g.
" ':Tabularize /&') but without line brakes and wraps. Besides it's very
" annoying when line brakes "happen" while editing.
" As hopefully tables must be edited only from time to time, one can toggle
" wrap and textwidth by hand.
function! ToggleTwWrap() "{{{
" if textwidth and wrap is used, then disable them
if &textwidth > 0
let &l:textwidth=0
setlocal nowrap
else " otherwise re-enable them
let &l:textwidth=s:textwidth
setlocal wrap
endif
endfunction
So now if I want to edit manually a table I simply do
:call ToggleTwWrap()
to disable wrapping and textwidth and then again when I'm done with the table.
And of course you can create a command or a map
You can certainly set it for specific file types, but I don't think you can change those settings (or any, really) for individual lines.
Ingo Karat's answer works but setting textwidth on every single cursor move is too slow. This adapted version will only call setlocal textwidth= if the text width is actually going to change. This speeds things up considerably:
autocmd CursorMoved,CursorMovedI <buffer> if IsSpecialLine() | if &textwidth != 0 | setlocal textwidth=0 | endif | else | if &textwidth != 80 | setlocal textwidth=80 | endif | endif

Vim: Resolve ambiguity of key mappings in a specific buffer to avoid timeout

I use plugin "Buffet", and there's local-to-buffer mapping "d" to delete buffer under cursor.
I also use plugun Surround, and there's global mapping "ds" that means "delete surround".
So, when i press "d" in the Buffet's window, Vim waits for a second before execute mapping "d". I know about &timeoutlen, but i don't want to change it. So that I want to resolve ambiguity of key mappings for "d" in the Buffet's window to avoid timeout to delete a buffer.
To resolve the problem, I want to unmap in Buffet window all the mappings that start with "d", but except Buffet's own mappings. How can i do that?
P.S. I have read about maparg() and mapcheck(), but they seem not to be what i need, unfortunately.
It seems like i found the solution myself:
au BufEnter buflisttempbuffer* nunmap ds
au BufLeave buflisttempbuffer* nmap ds <Plug>Dsurround
I hoped that there's more universal approach (to remove really all mappings starting from "d"), but at this moment i failed to find it.
Even if i found out how to get all these mappings, unfortunately i can't do unmap <buffer> ds, because ds is a global mapping. I'm sure that i should be able to disable global mapping for some buffer, though. Vim is great but not perfect.
Well, it works for me now.
Now that the question has been "rephrased", this solution is no longer relevant, but I'll post it anyway since I spent a few minutes on it.
Here's a function that grabs the output of map <letter> and extracts the individual maps. Then it unmaps them all.
function! Unmap(leader)
redir => maps
sil exe "map " . a:leader
redir END
let maps_list = split(strtrans(maps),'\^#')
if len(maps_list) > 1
for this in maps_list
let mapn = matchstr(this,"^\\w\\s*\\zsd\\w*\\>")
exe "unmap " . mapn
endfor
endif
endfunction
Example usage: call Unmap("d"). This will remove all mappings that begin with d, leaving only Vim's defaults.
Disclaimer: this has not been rigorously tested. In particular I don't know how portable the \^# character is, but that's how it looks on my (Win32) machine.
The easiest way to do it is:
:e /WHERE/YOU/HAD/INSTALLED/buffet.vim
:%s:map <buffer> <silent> d:"&:
:wq
$ vim # Restart Vim to take effect...
Generally you can't unmap based on a pattern.
If you want to use another key (e.g. with <leader>, just change this line in the plugin:
map <buffer> <silent> d :call <sid>deletebuffer(0)<cr>
This question is rather old, but if you're still interested, you might want to give Bufstop a try.
This issue is handled by the plugin, you can press the d key to delete a buffer, and you won't get any timeout if you installed other plugins which have global mappings.
A cheap trick that worked for me was to make the timeoutlen so short it becomes pretty much instantaneous. As long as you don't use multiple key mappings yourself, that will cover all plugins in one shot.
We don't want that setting to stay however, so we remove it every time we leave the buffer.
Add this so that it runs inside your custom buffer:
augroup no_map_chords
autocmd!
autocmd BufEnter <buffer> let g:bak_timeoutlen = &timeoutlen | set timeoutlen=1
autocmd BufLeave <buffer> let &timeoutlen = g:bak_timeoutlen | unlet g:bak_timeoutlen
augroup END
A similar technique could be used for a specific file type, or other such "global" settings.
Buffet is a very young plugin, I don't think it's used by as many people as Command-T or NERDTree so you may not receive lots of answers. Its author has been very responsive on the numerous threads he created there about it you should contact him directly or create an issue on Buffet's github.

Detect filetype from within VIM plugin?

I have a vim plugin that defines a bunch of key mappings.
I'm trying to figure out how I can change the defininition of the key mapping based on the filetype.
For example:
If the file is a *.py: then map the key to X
If the file is a *.php: then map the key to Y
Thanks!
Yes. One way would be to use autocmd to call a custom function that sets your maps. It would look roughly like the following (could have mangled the syntax, so this isn't really copy & pastable):
augroup foo
autocmd!
autocmd FileType python call MyPythonSettings()
augroup end
function !MyPythonSettings()
set noai
" set mappings...
endfunction
When specific commands/abbreviation/mappings needs to be defined, I always split my plugin into several files:
the core functions that go into autoload plugin(s)
the global mappings/commands/abbreviations that go into a "plain" plugin
the filetype specific stuff that go into ftplugins.
Useless example:
The autoload plugin
" autoload/lh/ThePlugin.vim
let g:multby = 42
function lh#ThePlugin#fn(arg)
return a:arg * g:multby
endfunction
function lh#ThePlugin#inc_mult()
let g:multby += 1
endfunction
The "plain" plugin
" plugin/ThePlugin.vim
if exist('g:ThePlugin_loaded') | finish | endif
let g:ThePlugin_loaded = '1.0.0'
nnoremap £ :call lh#ThePlugin#inc_mult()
One ftplugin
" ftplugin/cpp/cpp_ThePlugin.vim
if exist('b:ThePlugin_loaded') | finish | endif
let b:ThePlugin_loaded = '1.0.0'
inoremap <buffer> µ <c-r>=lh#ThePlugin#fn(12)<cr>
PS: note the use of <buffer> in order to not pollute other filetypes with mappings that make no sense, nor override previously defined (and specific) mappings that do make sense.
The FileType event gets fired when the user opens a file to edit. This, however requires that the user has enabled :filetype on in her vimrc (or manually), whether for those specific extensions or globally.
If the user is editing a new file, you won't get that until they first save the buffer or do :setf autohotkey :setf sql etc.

Resources