VimL: Make function called through key receive input from Vim command line - vim

So, this may not be a clear question because of the title. But here's the actual explanation:
I have this custom function, I want this function to be able to be called from the command line, this is done (:FunctionName arg), but now I need to make this function react to a certain mapping.
So when the user presses <leader>cs it will prompt the user for the arg part, mapping this is still kind of unclear to me, but also how to achieve this functionality. This is kind of what the Surround script does, where it lets you input the old character and the new character to replace it with.
I need this for my first script that I'm making BTW, which allows you to change the file's syntax in a manner similar to Sublime Text's way.
Thanks for all your help!

The simplest way is to remove the concluding <CR> from the mapping, so that it just enters command-line mode and fills the command line with your custom command:
:nnoremap <Leader>cs :FunctionName<Space>
You can then enter the arg and trigger the command with Enter.
Alternatively, you can query for user input via input() (and single characters with getchar(); obvious, isn't it?!), like this:
function! FunctionNameWithQuery()
let arg = input('arg: ')
execute 'FunctionName' arg
endfunction
nnoremap <Leader>cs :call FunctionNameWithQuery()<CR>

Related

In vim toggle case of all characters in a text file with a single command

In Vim I need to convert all lowercase to uppercase and all uppercase to lowercase with a single command. So if my text file looks like this..
Hello World
.. it needs to be toggled to look like this..
hELLO wORLD
I know :%s/[a-z]/\U&/g will change all lowercase to uppercase and that :%s/[A-Z]/\L&/g will change all uppercase to lowercase. But how would I write that to do both at the same time?
In addition I know if my cursor is at the top of the file VG~ will toggle case everything but that's not the answer I need. Thank you.
<Esc>1GVG~
Explanation:
<Esc> — return to Normal mode; just in case we're in Insert mode or Command line
1G — jump to the 1st line
V — start Visual mode
G — jump to the last line extending selection
~ — toggle case in the selection
Or
<Esc>1Gg~G
g~<motion> — change case during motion; the motion is G (jump to last line)
Docs: http://vimdoc.sourceforge.net/htmldoc/change.html#~
Looks like you already know everything you need. ggVG~ marks all your code and toggles the case. If you want a single command you can either use:
:nnoremap <keybinding> ggVG~
or use this function, which does the same, but keeps your current position in the file:
function ToggleCase()
exec "normal! mqHmw"
exec "normal! ggVG~"
exec "normal! 'wzt`q"
endfunction
command ToggleCase silent call ToggleCase()
the first and last exec mark your position in the file and restore them, after the case toggling. See: :h marks
type :ToggleCase to use the function. Of cause you can bind this to a keybinding as well.
:nnoremap <keybinding> :ToggleCase<cr>
Since you mentioned using a single command and you mentioned some :%s/.../ substitutions, I'll offer this one:
:%normal! g~~
This will run the g~~ command to switch case of a single line, for each line of the buffer.
One more way to accomplish this, if you're ok adopting a plug-in, is to use the kana/vim-textobj-entire plug-in for a text object for the entire buffer.
As the plug-in README.md file says:
Though these are trivial operations (e.g. ggVG), text object versions are more handy, because you do not have to be conscious of the cursor position (e.g. vae).
With this plug-in installed and enabled, you can switch case of the whole buffer with:
g~ae

Convert character to code point in Vim

ga is useful to get the code point of a character. However it doesn't put the results into a buffer. Right now, to convert a character to its code point, I do this:
Press ga and try to memorize the code point.
Press a and type whatever I remember because the display has now disappeared.
Press <Esc>4hga to check again to make sure I got it right.
Unsurprisingly, I make mistakes with this method. Any suggestions?
After reading this post, it looks like it would be possible to write a function. However a solution that doesn't require a lot of customization would be preferred.
The redir command will do what you want; it redirects the output of subsequent Vim commands into a file or register. As a proof of concept, I just wrote a short function to do what you want:
function GetCodepoint()
redir #"> " Redirect command output into the unnamed register
ascii " Print ASCII value of the character under the cursor
redir END " Stop redirection
endfunction
Running call GetCodepoint() will save the output of the ga command into
the unnamed register (") but the redir command can be used to redirect the output of subsequent Vim commands into any register you like. See :help :redir for all the different ways that it can be called.
Also, the function is short enough that you could probably just run the
commands manually. It would depend on how often you’d be doing this.

Returning to Normal Mode from Insert Mode after "execute normal"

You can perform normal mode commands programmatically in Ex mode, via execute normal, e.g.
:execute "normal" "iNEWTEXT\<Esc>0"
This switches to insert mode (i), writes "NEWTEXT", escapes to normal mode (\< Esc>), then moves to the start of the line (0).
However, using a non-constant string, either a register or variable, the behavior is different. For example, suppose you have the same command above saved on a line in any file (not necessarily a vimscript file):
iNEWTEXT\<Esc>0
You can then copy the text into any register (here, z) via "zy$ and execute the register via #z. This time, though, the output is different:
NEWTEXT\<Esc>0
After entering insert mode, the Escape is no longer treated as a special character, and is instead taken literally. Alternative forms like \e don't work either. Is there a way around this?
EDIT: Using Ingo's answer, I created the the following function. Basically, the use is for having a set of normal/insert commands embedded within the text of the file, and being able to execute them. More commonly, something similar is used for running Ex commands from a line of text, but I couldn't find anything that did this exact thing for normal and insert mode.
So, you'd have text like the following in your file:
jy10j10jpO\<Esc>jEll
When on that line, you could call the function or a remap, and the commands would execute (in this example, copying and pasting 10 lines, and moving 2 columns past the first word). Ingo's alternatives are better for serious usage, namely sourcing commands from another file, having the command in the .vimrc, or a file-type specific option. Macros saved by a session would work just as well, and are more practical than having commands scattered throughout a file. In my case, I was syncing across multiple devices, and didn't want to have another file or clutter my vimrc with this very specific command, but didn't mind cluttering this specific file itself. Think of this like a portable macro.
" Execute current line as Vim normal mode commands.
nnoremap <A-y> :call EvaluateLineAsNormalModeCmd()<CR>
function! EvaluateLineAsNormalModeCmd()
let g:getCurrentLine = getline(".")
"have to :execute twice: once to get the contents of the
"register inserted into a double-quoted string, and then once for
"the :normal to evaluate the string.
execute 'execute "normal" "' . g:getCurrentLine . '"'
endfunction
EDIT2/3: Here are two functions using Christian Brabandt's answer. They work about the same but can put the user in insert mode at the end (whereas, based on my minimal information, 'i' in the other context is considered an incomplete command and not executed, and :startinsert can't be used in that situation). PS: Please don't ask me what all those single and double quotes are doing, as I can't wrap my head around it O_o
function! EvaluateLineAsNormalModeCmd()
normal! 0y$
execute ':call feedkeys("'.#".'", "t")'
endfunction
function! EvaluateLineAsNormalModeCmd()
let g:getCurrentLine = getline(".")
execute ':call feedkeys("'.g:getCurrentLine.'", "t")'
endfunction
If you really need this (the use case is dubious), you have to :execute twice: once to get the contents of the register inserted into a double-quoted string, and then once for the :normal to evaluate the string.
:execute 'execute "normal" "' . #z . '"'
PS: Please give more background; what is your final goal? When a question is only about a small technical step, it's difficult to provide a good answer. If you don't tell us why you want this, it's easy to succumb to the XY problem.
I would rather use the feedkeys() function. E.g. for your sample, this should work:
exe ':call feedkeys("'.#".'", "t")'
(If you yanked your line into the unnamed register, else adjust the register name accordingly). Note, quoting could get ugly.
To understand what is going on, this is what is done:
exe ':call feedkeys(' - First part of the feedkeys() function call
" - Start of Quote for the first argument
. - String concatenation
#" - content of the unnamed register
. - String concatenation
' - Start of second part of the feedkeys function call
" - End of Quote for the first argument
, "t")' - Second argument of feedkeys() function call
You could also do it in 2 steps like this:
exe ':let a="'. #". '"' - Also needs to quote #" correctly.
call feedkeys(a, 't')
which should be easier to understand. The exe call is only to translate the normalized key notation into literal keys.

Is it possible to do something like `:normal :` to enter command line during the execution of a vimscript function?

I am writing a vim script function. During its execution, I want to enter the command line to provide some arguments that cannot be decided in advance, for some specific commands. I want something like
:startinsert
But should goes like
:startcmd
Is it possible? Or some other ways around?
input() is what you want.
Here is an example:
let myFile = input("Choose a file: ", "", "file")
execute 'edit ' . myFile
and another one:
buffer `=input("Choose a buffer: ", "", "buffer")`
See :help input().
You can also allow your user to choose from a predefined set of options with inputlist().
There is a way around, we can use following code to mimic the command line.
exec input(prompt, text, completion)
text and completion are optional, :h input() for more
But one thing to note:
the built-in completions of vim (:h command-completion for more) indicated by the completion argument complete with entire preceding line before the cursor when you hit <tab> in input(). This may be not what you want, e.g., I just want to complete the last word instead of the entire preceding line.
To solve this problem, you have to write your own completion function, please refer to :h command-completion-custom and :h command-completion-customlist

Vim search and highlighting control from a script

I'm writing a script in which I want to control searches programmatically, and get them highlighted. The search() function results are not highlighted (I think), so using that function is not of use to me.
What I want to do is use the 'normal /' command to search for a variable, but that doesn't seem to be straightforward. I can script the command:
execute 'normal /' . my_variable . '\<CR>'
(or other variations as suggested in the vim tip here: http://vim.wikia.com/wiki/Using_normal_command_in_a_script_for_searching )
but it doesn't do anything. I can see the correct search term down in the command line after execution of the script line, but focus is in the document, the search register has not been altered, and the cursor has not done any search. (It seems as though the < CR > isn't getting entered, although no error is thrown -- and yes, I have tried using the literal ^M too.)
I can at least control the search register by doing this:
execute 'let #/ ="' . a:term .'"'
and then the obvious thing seems to be to do a:
normal n
But that 'normal n' doesn't do anything if I run it in a script. Setting the search register does work, if I manually press 'n' after the scrip terminates the search happens (and highlighting appears, since hlsearch is on). I don't even care if the cursor is positioned, I just want the register pattern to be highlighted. But various combinations of 'set hlsearch' in the script don't work either.
I know I could use 'match()', but I want to get it working with regular search highlighting, and I wonder what I'm doing wrong. It must be something simple but I'm not seeing it. Thanks for any help.
run:
let #/ = a:searchStr
from inside your function then run
normal n
from outside your function (inside it does nothing) eg.
command -nargs=* Hs call MySearch() | normal n
or you can use:
set hlsearch
instead of normal n if you don't want the cursor to move
(I cannot work out another way of doing this without having something outside the function.)
If your script is using functions, then this quote from :help function-search-undo is relevant:
The last used search pattern and the redo command "."
will not be changed by the function. This also
implies that the effect of :nohlsearch is undone
when the function returns.
Vim usually tries to reset the search pattern (and a few other things) when a function ends, often you can get around this by adding the n (next search) to the end of a mapping, or using :map <expr> and having your function return the key sequence to be executed.
On closer inspection, it seems \<CR> is not picked up inside single quotes. Try using this instead:
execute 'normal /' . my_variable . "\<CR>"

Resources