Detect filetype from within VIM plugin? - vim

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.

Related

How to create a if estatement inside a function to check file type in vimscript?

I need to check the file type I have opened in Vim to do one thing or another inside a function.
This is what my vimrc looks like:
function! MyFunction()
autocmd filetype typescript <C-V>%
autocmd filetype go <C-V>%
endfunction
nnoremap <Leadar>f :call MyFunction()<CR>
Next to <C-V>% will be more instructions, but for now this is what I'm testing.
My function has to detect the file I have openend, according to its type do one thing or another. I'm doing this inside a function because in the near future I will move this to a separete plugin, but for now it is my vimrc file.
Another thing I've tried already and I know it works is this
autocmd filetype typescript nnoremap <Leader>f <C-V>% DoTypescriptThings
autocmd filetype go nnoremap <Leader>f <C-V>% DoGolangThings
If I move this lines outside of a function body I works. But this way I couldn't change the <Leader> KEY easily if I make this a plugin. That's why I moved it to a function.
How can I make my function detect my types so my function works?
If I can understand clearly, you actually want to pull out the filetype checking from auto-command so that remapping the main key (<LEADER>f) becomes easy.
You can do this by using &filetype.
Your function will look something like this:
function! MyFunction()
if &filetype ==# 'typescript'
autocmd filetype typescript <C-V>%
" Now you dont need autocmds here to be precise;
" But you may consider some other autocmd-events in future
" Until then a simple :execute or :execute "normal!..." should be sufficient instead
elseif &filetype ==# 'go'
autocmd filetype go <C-V>%
endif
endfunction
nnoremap <Leadar>f :call MyFunction()<CR>
Now considering the fact that autocmd also does the same thing (checking filetype and applying a mapping) so I guess your main motivation is not to use autocmd but to be able to remap main key (<LEADER>f) easily.
So in conclusion, I would suggest to not use autocmd here and go with a function definition only so that one key can rule your function call. (Of course, unless you decide to use some other autocmd-events too)

setting vim map in function in vim script

I am trying to do a small plugin for vim to let me browse the clear case history of a file, but I am having trouble getting started. I need help with setting up the mapping exposed to the user.
Here is the bare minimum version of what I've done
function! s:DefineLeftKeyMaps()
echom 'in defineLeft'
nnoremap <leader>k <Plug>MinimalDoSomething
endfunction
noremap <script> <Plug>MinimalDoSomething <SID>DoSomething
noremap <SID>DoSomething :call <SID>DoSomething()<CR>
function! s:DoSomething()
echom 'In DoSomething'
endfunction
command! InitCCTTSample call s:DefineLeftKeyMaps()
I need to set the final map exposed to the user in a function cause it will be used in a split where I open up different files (eventually using said mapping). Since it is a buffer local map i assume i need to reset it every time.
Edit:
The problem with the above solution was that even thou the mapping was it didn't do anything.
:map reported:
....
....
\k * <Plug>MinimalDoSomething
....
....
The reason was that I used noremap instead of map, so the subsequent mapping to the function call was ignored
Cheers
<plug>-mappings are difficult to define as buffer local mappings. Indeed the end-user cannot define them in their .vimrc, and to define them in a ftplugin, that'll mean you'll need to associate a filetype to your split buffers.
You'll need to provide a hook to the end user. I see two solutions
Provide a function where the end user will be able to register her/his preferences in their .vimrc.
" .vimrc
call your#plug#tune_mappings({'<Plug>MinimalDoSomething': '<localleader>left'})
Expose a User Event for the user to do stuff (like defining the mappings) when you trigger the event from your plugin.
" .vimrc
augroup YourPlugUser
au!
au User TuneKeyBindings let b:maplocalleader = ',,'
au User TuneKeyBindings nmap <buffer> <localleader><left> <Plug>MinimalDoSomething
aug END
" your plugin
split yourplug://foobar
doautocommand User TuneKeyBindings
if !hasmapto('<Plug>MinimalDoSomething, 'n')
nmap <buffer> <leader>k <Plug>MinimalDoSomething
endif
The reason it doesnt work is because I used nnoremap <leader>k <Plug>MinimalDoSomething rather than nmap <leader>k <Plug>MinimalDoSomething
Cheers

Run a vim formatter conditionally

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.

Switch back and forth between filetypes in VIM

Similar to how spell checking works. I have a custom syntax I wish to temporary apply to any filetype in the buffer. I figure the best way to do this is just change the filetype. But, I don't know how to get back to previous filetype without being explicit. How can I accomplish this?
You can use :filetype detect to redetect the original filetype.
Set up a function and a keyboard map to do it for you, e.g.:
function! ToggleFileType()
if g:my_file_type" == 0
let ft=MYFILETYPE
let g:my_file_type = 1
else
filetype detect
let g:my_file_type = 0
endif
endfunction
nmap <silent> ;s :call ToggleFileType()<CR>
So this way, hitting ;s (in normal mode) calls the above function, which checks which filetype is set, then toggles.

How to map keys in vim differently for different kinds of buffers

The problem i am facing is that i have mapped some keys and mouse events for seraching in vim while editing a file. But those mappings impact the functionality if the quickfix buffer.
I was wondering if it is possible to map keys depending on the buffer in which they are used.
EDIT - I am adding more info for this question
Let us consider a scenario. I want to map <C-F4> to close a buffer/window. Now this behavior could depend on a number of things.
If i am editing a buffer it should just close that buffer without changing the layout of the windows. I am using buffkil plugin for this.
It does not depend on extension of file but on the type of buffer. I saw in vim documentation that there are unlisted and listed buffer. So if it is listed buffer it should close using bufkill commands.
If it is not a listed buffer it should use <c-w>c command to close buffer and changing the window layout.
I am new at writing vim functions/scripts, can someone help me getting started on this
function KillBuffer()
if &buflisted
" bufkill command here
else
execute "normal! \<C-w>c"
endif
endfunction
noremap <C-F4> :call KillBuffer()<CR>
Put this in your .vimrc
Or, if you want to handle quickfix window as unlisted buffers (in my Vim it is listed):
function KillBuffer()
if &buflisted && !&filetype=="qf"
" bufkill command here
else
execute "normal! \<C-w>c"
endif
endfunction
noremap <C-F4> :call KillBuffer()<CR>
According to the manual, you could replace execute "normal! \<C-w>c" with simpler close! in the above scripts.
You can create filetype specific settings. First, in your vimrc file, make sure filetype plugins are enabled by adding
filet plugin on
Then make a filetype specific plugin. Under Unix create a file called ~/.vim/after/ftplugin/[file-type-name].vim and put your mapping in there. In Windows the directory is $HOME/vimfiles/after/ftplugin. The [file-type-name] is the type detected by Vim, sometimes the same as the filename extension, e.g c.vim, python.vim, etc. Vim can tell you what the type is after you open the file if you enter
:echo &ft
You can intercept certain types of files loading and assign buffer specific mappings.
au! BufRead *.ext call <SID>init_hotkeys()
function s:init_hotkeys()
nnoremap <buffer> <CR> :Action<CR>
endfunction
To map complex logic on the hotkey you can use write something like this in your vimrc, or even better - put the following to the closebuffer.vim file inside your vim plugin directory
function s:close_buffer()
if &buflisted
" your command here from the killbuf plugin
echo "Listed Buffer"
else
wincmd c
" or
" normal <c-w>c
endif
endfunction
nnoremap <C-F4> :call <SID>close_buffer()<CR>
I use this in my vimrc to insert an empty line above or below the current line using only return and shift-return (as opposed to o<Esc> or O<Esc>) without interfering with the open file behaviour you want in the quickfix list.
" Use enter to insert newlines in normal mode, but not in quickfix
function! s:insert_line(direction)
if &buftype == "quickfix"
execute "normal! \<Enter>"
else
if a:direction == 'below'
execute "normal! o\<Esc>"
else
execute "normal! O\<Esc>"
endif
endif
endfunction
nmap <Enter> :call <SID>insert_line('below')<CR>
nmap <S-Enter> :call <SID>insert_line('above')<CR>
Hopefully someone else will find this useful.

Resources