setting vim map in function in vim script - vim

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

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)

Vim autocmd does not map properly a mapping

I want to create a plugin which maps a certain localleader mapping to a function call.
I have a ftplugin/javascript.vim file with the following content:
augroup javascript_pluginName
au!
echom "The plugin is loaded for JS"
au FileType javascript nnoremap <buffer> <localleader>j :call pluginName#pluginName#funName()<cr>
augroup END
When I open a javascript file, the message is printed, but then checking the mapped combinations shows that there is no such mapping.
I understand, that nnoremap is simply not going to map on top of other mappings. However, if I only leave the nnoremap command, without putting it in an autocmd, it would map properly.
Simply executing the call in command mode also works fine.
I looked into the vim help to see how to use autocmd, and I can't see any difference between the way I use it and what is explained there. The "Learn Vim the Hard Way" book also didn't help.
Is there something I'm missing? Should I frame the autocmd somehow differently?
I finally found out what the problem is!
Apparently, according to this article: https://vimways.org/2018/from-vimrc-to-vim/ if you put a filetype specific code into a filetype plugin, there is no need to create an autocmd for it.
The boilerplate is all made redundant by the general behaviour of vim setting the filetype on open and then running the ftplugin scripts, which are relevant for the file.
This means that in my ftplugin/javascript.vim file I only need the mapping:
nnoremap <buffer> <localleader>j :call pluginName#pluginName#funName()<cr>

Mapping the same key for Compiling/Running code of different languages from vim

I have map <F8> : w <bar> !clang -o %< % && ./%< <CR> inside my .vimrc, however I want to map F8 to run python codes as well. How is that possible? (obviously I don't want clang to run python, I want to have a condition or something to redirect what command F8 maps to based on the language that is already specified e.g. via :setf python etc.)
You're looking for filetype-specific mappings. Put the Python variant into ~/.vim/after/ftplugin/python.vim, and add the <buffer> keyword to the :map command:
nnoremap <buffer> <F8> : w <bar> !python % <CR>
This requires :filetype plugin on, but you probably already have that. Similarly, you can move your original mapping to ftplugin/c.vim, or keep that as a global fallback for all filetypes.
Additional tips
You should use :noremap; it makes the mapping immune to remapping and recursion.
Better specify the concrete modes this applies to, in this case normal mode via :n[nore]map.
Very doable. Here's an example where I have the same key mapped for tidying different types of file:
autocmd FileType perl nnoremap <buffer> <F12> mz:%!perltidy<CR>`z
autocmd FileType javascript nnoremap <buffer> <F12> :call JsBeautify()<CR>
It's been a while since I added this to my vimrc, but if memory and a quick google serves me well, this watched for the event of the fileype being perl or javascript, and then runs the nnoremap command for whichever event has occurred. I'm sure there are many other ways to accomplish it!

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.

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