Understanding sil exe in vimscript - vim

Can someone explain to me what sil exe ":sb " . current_buffer does in a vimscript. I'm trying to learn how to use it, but finding documentation on vimscript seems nearly impossible.

That's a combination of several commands, and all of them are abbreviated.
The commands being used there are :silent, :execute, and :sbuffer. You
can get information on those commands by using :help :silent from within vim,
and the same for the other commands.
The . operator is also being used to do string concatenation.
First the . operator is used to concatenate :sb with the value of the
current_buffer variable. The resulting string is evaluated as if you'd typed
it by the :exe portion, which will supresss messages because it's part of the
:sil command.
So, the effect is as if you'd typed :sb 4, from normal mode (using 4 there as
the value of current_buffer). This would split the current window and use the
new window to edit buffer number 4.

Related

Vim normal! sending literal <Space> string instead of whitespace character

I'm not sure what I'm doing wrong here.
:normal! I###<Space><Esc>
The end result I want when I execute this command over a word is this:
### word
But instead I get this:
###<Space>word
Vim's :normal is a nightmare with anything moderately complicated. I don't remember why, but this is the easiest way to get what you want. You need to escape the opening < in a string passed to execute.
execute "normal! I###\<Space>\<Esc>"
Edit: Vim doesn't really explain why, but :help normal
An alternative is to use |:execute|, which uses an
expression as argument. This allows the use of
printable characters to represent special characters.
Lets first explain the vim command:
:normal! run the rest of the commands as normal mode
I enter insert mode from the beginning of the line <shift> + i
###<Space><Esc> - type out the characters ###<Space><Esc> literally since we are in insert mode (notice that if you just enter insert mode, and type ###<space><esc>, you'll get just that literally.)
When this command finishes, you'll notice how it's still in normal mode. This means that your command can just omit the <space><esc> and enter the space literally:
:norm! I###
(There is an extra space after the last hash)

vim cmap lhs only at the beginning

I have a mapping
:cnoremap ch call ShowHistoryMatching
The problem is that the ch characters expand to the right sentence in any case they are typed, no matter if at the beginning or later in the cmap input.
The problem is when I try to search for words in vim using / or ? e.g. for
/cache - it will be expanded using the mapping above.
How can I set the mapping ch to be extended only when it occurs at the beginning of the command?
cmap's are notoriously tricky because they often execute in the wrong context. Some better alternatives:
Use a normal mapping e.g. nnoremap <leader>ch :call ShowHistoryMatching()<cr>
Create a command e.g. command Ch call ShowHistoryMatching()
Use a the cmdalias.vim plugin
Use a more clever abbreviation as described in vim change :x function to delete buffer instead of save & quit post. Similar technique to cmdailias.vim.
Personally I would just create a new command.
You can use the getcmdpos() function to determine if you're at the beginning of the line or somewhere else. This technique can replace a built-in command using an abbreviation or you can adapt it for use in a mapping, possibly with an <expr> mapping.

Show comments for specific mappings in .vimrc

I know that I can use :nmap, :vmap, :imap commands to display all the mapping for those 3 modes. However, I would like to display comments that I have for a single mapping.
So suppose I have the following entry in my vimrc:
" Execute current line in bash
nmap <F9> :exec '!'.getline('.')<CR>
This could also look like this:
nmap <F9> :exec '!'.getline('.')<CR> " Execute current line in bash
I would like to have a command that for this mapping that would look something like this:
<F9> Execute current line in bash
As this probably can only be done using custom function, where do I start? How do I parse .vimrc to strip only key mappings and their corresponding comments (I have only done some very basic Vim scripts)?
Do I understand correctly from :help map-comments that I should avoid inline comments in .vimrc?
It's very hard to "embed" comments into a mapping without affecting its functionality. And this would only help with our own mappings, not the ones from plugins.
Rather, I'd suggest to learn from (well-written) plugins, which provide :help targets for their (default) key mappings. So, to document your mapping(s), create a help file ~/.vim/doc/mymappings.txt with:
*<F9>*
<F9> Execute current line in bash
After :helptags ~/.vim/doc, you will be able to look up the comments via :h <F9>. By using the help file, you can use syntax highlighting, longer multi-line comments, and even examples, so I think that'a better and more straightforward solution.
Personally, I wouldn't have much use for such a feature for two reasons: the first one, is that I always have a vim session with a note taking buffer that contains all the tips/commands not yet in my muscle memory. Secondly, you can list all the maps using :map, and it's always a great exercise for your brain in learning vim to literally read vim commands.
That being said…
As this probably can only be done using custom function, where do I start?
…well I guess you could imagine using a function with a prototype such as:
AddMap(op, key, cmd, comment)
used like:
call AddMap("nmap", "<F9>", ":exec '!'.getline('.')<CR>", "Execute current line in shell")
which implementation would add key and comment to an array, so that you could list the array to get your own mappings declared. Using a function like ListMap().
let map_list = []
function! AddMap(op, key, cmd, comment)
" add to map list
insert(map_list, [op, key, comment])
" execute map assign
exec op.' '.key.' '.cmd
endfunction
function! ListMap()
for it in map_list
echo it[0] . '\t' . it[1] . '\t' . it[2]
endfor
endfunction
Of course, one could elaborate on the ListMap() function implementation to generate a more "proper" help file that you could open in a buffer. But that's an idea of how you could do an implementation for a feature close to what you're looking for.
N.B.: this snippet is mostly an idea of how I'd tackle this problem, and it's a one shot write that I did not actually try.
How do I parse .vimrc to strip only key mappings and their corresponding comments (I have only done some very basic Vim scripts)?
I don't think that would be a good solution, I think the better way to achieve your goal is my suggestion. Simpler and more explicit, though it won't show off all mappings. But you don't have all your mappings in your .vimrc.
Do I understand correctly from :help map-comments that I should avoid inline comments in .vimrc?
To be more precise you shall not use inline comments after maps, because they will be interpreted part of the mapping.
how to get mapping created by plugins?
Besides, using :map, you just don't. You look at the sources/documenation of your plugins and find out about them. There's no such thing as a reverse lookup from runtime to declaration point.

How to use Ctrl-R_= with :execute

I'm trying to get an expression on a variable expanded on a :execute command. I've guessed this could be achieved by using Ctrl-R_=, but it is not clear how the special characters should be inserted. None of the following worked:
exec 'echo ^R=1+1^M'
exec "echo <ctrl-r>=1+1<cr>"
The purpose is set a global variable used as an option in a plugin to select how to show the results. It is used on an :execute command, and works fine for 'vsplit' or 'split'. But the choice between vertical or horizontal split sometimes depends on the window layout. In order to do this without adding extra complexity to the plugin I've thought of something like the following:
let var = '<ctrl-r>=(winwidth(0) > 160 ? "vsplit" : "split")<cr>'
Edit
Currently the plugin has something like the following:
exec 'pluginCommands' . g:splitCmd . ' morePluginCommands'
The g:splitCmd is a plugin option, which works for when set with "split", "vsplit", "tabe", etc. My intent is to change this fixed behavior, setting g:splitCmd in such a way that it represents an expression on the execute above, instead of a fixed string.
Now that I'm understanding the issue better, I think a dynamic re-evaluation inside the config var is impossible if the variable's value is inserted in an :execute g:pluginconf . 'split' statement. To achieve that, you'd need another nested :execute, or switch to command-line mode via :normal! :...; both approaches will fail on the appended . 'split', because you can't add quoting around that.
The way I would solve this is by prepending a :help :map-expr to the plugin's mapping; change
:nmap <Leader>x <Plug>Plugin
to
:nnoremap <expr> <SID>(PluginInterceptor) PluginInterceptor()
:nmap <Leader>x <SID>(PluginInterceptor)<Plug>Plugin
Now, you're get called before the mapping is executed, and can influence the plugin config there:
fun! PluginInterceptor()
let g:plugconf = winwidth(0) > 160 ? "vsplit" : "split"
return ''
endfun
If modifying the plugin mapping is for some reason difficult, you could also trigger the PluginInterceptor() function via :autocmd; for this particular case e.g. on WinEnter events.
With :execute, you already have a way to evaluate expressions; just move them out of the static strings to get them evaluated:
exec 'echo ' . 1+1
The <C-R> only works in command-line mode (and insert mode), so only within a :cnoremap ... command (or with :normal). (And even there, you can use :map <expr>, which often gives you simpler code.)
I think that what you want is simply
:let var = (winwidth(0) > 160) ? "vsplit" : "split"
It seems to me like
exec 'pluginCommands' . eval(g:splitCmd) . ' morePluginCommands'
should work just fine, and is a simple solution to this problem.

Calling into a shared object on a key-sequence from vi/Vim

I have a shared library (in binary form; I have the source) that I use to invert/cycle between symbols in source files in Visual Studio.
I would like to be able to use the same functionality in vi and/or Vim.
Specifically, what I'd like to do is the following scenario:
the cursor is on a word
I press a key-sequence, e.g. CTRL-I
vi/Vim works out the whole of the word I'm on
vi/Vim invokes my shared library, passing the word, and receiving the invert/cycle replacement
vi/Wim replaces the original word with the new word
I don't have any clue if/how to get vi/Vim to do this, and I'm not having any luck searching.
Any advice gratefully received ...
Try
inoremap <C-i> <esc>"hciw<C-R>=libcall('path/to/your.dll', 'func', #h)<CR>
What it does:
create map for insert mode <Ctrl+i>
<esc> switch to normal mode "hciw move word under cursor into the register h
<C-r>= insert into cursor position result of the next expression
libcall(...) calls function in the run-time library.
#h is the value of the 'h' register.
In case you want to use simple binary that can be ran from the command line you can use
inoremap <C-i> <esc>"hciw<C-R>=substitute(system('mybin --word='.#h), "\n", '', 'g')<CR>
I don't know how to call a shared library from normal vim scripts, but if you can create a python wrapper for your library and you're using vim version greateri than 7, you might do it calling a python script within vim.
First of all, check if you have python support enabled: type
:version
inside vim to list the available features; if it has python support, you should see a '+python' somewhere (a '-python' otherwise). If you do not have python enabled, you may refer to this post to compile vim with python support.
Then, you could could map a key to call a python function with the word currently under the cursor:
python << EOF
import vim
import MySharedLibraryPythonBinding
def MyFunction():
# get word under cursor
x = vim.eval('expand ("<cword>")')
# get replacement
MySharedLibraryPythonBinding.GetReplacement(x)
# replace contents (you'll need some work here...)
vim.current.line = "add something sensible here..."
EOF
nmap <F3> :py MyFunction( expand("<cword>") )<CR>
This is not of course a fully working solution, but I hope it will put you on the good route.
There are many ways to do it (:h expand(), ...). One of them is the following)
:nnoremap triggerkeysequence ciw<c-r>=libcall('path/to/your.dll', 'your_function',#")<cr>
BTW: <c-i> is <tab>, are you sure you want to override the action on this key?

Resources