Using an argument of a function in normal mode in Vim? - function

I have a Vimscript function defined like this:
function Cs(a, b)
normal a:a|"cylr a:b|x"cP
endfunction
However, the intended action (do some crazy stuff with the arguments a and b in normal mode) doesn't work, instead it takes the first "a" as "append" and writes the rest of the line to the file.
How can I use arguments on a "normal" statement in Vimscript? I found no way to do this.

You need to build up a string with the parameters in and execute it with the :exec statement.
e.g. something like this:
function Cs(a, b)
exec "normal " a ":" a "|\"cylr " a ":" b "|x\"cP"
endfunction

Related

tcl scripts, struggling with [...] and [expr ...]

I can't understand how assignments and use of variables work in Tcl.
Namely:
If I do something like
set a 5
set b 10
and I do
set c [$a + $b]
Following what internet says:
You obtain the results of a command by placing the command in square
brackets ([]). This is the functional equivalent of the back single
quote (`) in sh programming, or using the return value of a function
in C.
So my statement should set c to 15, right?
If yes, what's the difference with
set c [expr $a + $b]
?
If no, what does that statement do?
Tcl's a really strict language at its core; it always follows the rules. For your case, we can therefore analyse it like this:
set c [$a + $b]
That's three words, set (i.e., the standard “write to a variable” command), c, and what we get from evaluating the contents of the brackets in [$a + $b]. That in turn is a script formed by a single command invocation with another three words, the contents of the a variable (5), +, and the contents of the b variable (10). That the values look like numbers is irrelevant: the rules are the same in all cases.
Since you probably haven't got a command called 5, that will give you an error. On the other hand, if you did this beforehand:
proc 5 {x y} {
return "flarblegarble fleek"
}
then your script would “work”, writing some (clearly defined) utter nonsense words into the c variable. If you want to evaluate a somewhat mathematical expression, you use the expr command; that's it's one job in life, to concatenate all its arguments (with a space between them) and evaluate the result as an expression using the documented little expression language that it understands.
You virtually always want to put braces around the expression, FWIW.
There are other ways to make what you wrote do what you expect, but don't do them. They're slow. OTOH, if you are willing to put the + first, you can make stuff go fast with minimum interference:
# Get extra commands available for Lisp-like math...
namespace path ::tcl::mathop
set c [+ $a $b]
If you're not a fan of Lisp-style prefix math, use expr. It's what most Tcl programmers do, after all.
set c [$a + $b]
Running the above command, you will get invalid command name "5" error message.
For mathematical operations, we should rely on expr only as Tcl treats everything as string.
set c [expr $a + $b]
In this case, the value of a and b is passed and addition is performed.
Here, it is always safe and recommended to brace the expressions as,
set c [expr {$a+$b}]
To avoid any possible surprises in the evaluation.
Update 1 :
In Tcl, everything is based on commands. It can a user-defined proc or existing built-in commands such as lindex. Using a bare-word of string will trigger a command call. Similarly, usage of [ and ] will also trigger the same.
In your case, $a replaced with the value of the variable a and since they are enclosed within square brackets, it triggers command call and since there is no command with the name 5, you are getting the error.

How to execute a normal mode command in a vim function?

I am writing a vim function to insert some text in a c++ file, please see the following function:
function! InsertDebugInfo()
let i = line('.')
call append(i+1, '#ifdef DEBUG')
call append(i+2, 'std::cout << "" << std::endl;')
call append(i+3, '#endif')
call append(i+4, '')
call cursor(i+3, 0)
endfunction
In normal mode, I use == to re-indent one code line. My question is
how to call == in the above function. Furthermore, how to execute the
command such as 2f" which move the cursor to the second ".
To indent, you can just use
normal ==
To find also you can use
normal 2f"
or even shorter
norm <whatever you do in normal mode>
Now you might be getting what I'm trying to say.
If not, read documentation :h normal.
Try this in your function:
execute 'normal 2f"'

How to concatenate several lines in vim

I am using a the vim-screen plugin that enable me to write scripts , start an interpreter in the same window and send lines the the interpreter. Problem is that the interpreter do not accept statements written on several lines.
exemple:
This will work f:{[x] y:y+1; Z:y+1; :Z; };
But this won't
f:{[x] y:y+1;
Z:y+1;
:Z;
};
How can I write a vim function that I could call to reshape the lines in order to be sent to the interpreter?
EDIT:
I had no success in making this function, I would like to create a function that would, from a input like this (that would be visually selected)
F:{[a;b;r]
//ccc1
aaa1;
aaa2;
//ccc2
aaa3;
};
output something like this F:{[a;b;r] aaa1; aaa2; aaa3; };
So I created a bounty
If you want to actually modify the buffer, J / :join do that. If you just want to join the lines that are sent to the interpreter (but keep them split in the buffer), you can retrieve the selected lines with getline(), and then join() them. Here's an example command:
:command! -range Invoke echomsg join(getline(<line1>,<line2>), '')
Edit
Based on that, you can "massage" the List of lines returned by getline(). E.g. to ignore the commented lines:
:command! -range Invoke echomsg join(filter(getline(<line1>,<line2>), 'v:val !~# "^\\s*//"'), '')
Additionally strip leading whitespace (this becomes unwieldy in a single line; better use a function now):
:command! -range Invoke echomsg join(map(filter(getline(<line1>,<line2>), 'v:val !~# "^\\s*//"'), 'substitute(v:val, "^\\s\\+", " ", "g")'), '')
Standard continuation character in vimscript scripts is backslash in the beginning of the next line. So, this
f:{[x] y:y+1;
\ Z:y+1;
\ :Z;
\ };
should work.

Vim function to copy a code function to clipboard

I want to have keyboard shortcut in Vim to copy a whole function from a Powershell file to the Windows clipboard. Here is the command for it:
1) va{Vok"*y - visual mode, select {} block, visual line mode, go to selection top, include header line, yank to Windows clipboard.
But it would work only for functions without an inner {} block. Here is a valid workaround for it:
2) va{a{a{a{a{a{a{Vok"*y - the same as (1), but selecting {} block is done multiple times - would work for code blocks that have 7 inner {} braces.
But the thing is - the (1) command works fine when called from a vim function, but (2) misbehaves and selects wrong code block when called from a vim function:
function! CopyCodeBlockToClipboard ()
let cursor_pos = getpos('.')
execute "normal" 'va{a{a{a{a{a{a{Vok"*y'
call setpos('.', cursor_pos)
endfunction
" Copy code block to clipboard
map <C-q> :call CopyCodeBlockToClipboard()<CR>
What am I doing wrong here in the CopyCodeBlockToClipboard?
The (2) command works as expected when executed directly in vim.
UPDATE:
I've noticed that:
if there are more a{ then the included blocks in the function
then vim wouldn't execute V
Looks like vim handles errors differently here. Extra a{ produces some error and regular command execution just ignores it. But execution from withing a function via :normal fails and wouldn't call V (or probably any command that follows the error).
Any workaround for this?
Try this function
function! CopyCodeBlockToClipboard()
let cursor_pos = getpos('.')
let i = 1
let done = 0
while !done
call setpos('.', cursor_pos)
execute "normal" 'v' . i . 'aBVok"*y'
if mode() =~ "^[vV]"
let done = 1
else
let i = i + 1
endif
endwhile
execute "normal \<ESC>"
call setpos('.', cursor_pos)
endfunction
This preforms a execute command to select blocks until it fails to select a block larger block. ([count]aB selects [count] blocks) It seems when the selection fails we end up in visual mode. So we can use mode() to check this.
When this function exits you should be in normal mode and the cursor should be restored to where you started. And the function will be in the * register.
This macro should come close to what you want to achieve:
?Function<CR> jump to first Function before the cursor position
v enter visual mode
/{<CR> extend it to next {
% extend it to the closing }
"*y yank into the system clipboard

octave history command - variable as filename

i want to write little helper functions that stores and loads the octave session.
function restoreSession(filename)
history -r strcat('./states/',filename,'.history');
load("-binary", strcat('./states/',filename,'.data'))
endfunction
function saveSession(filename)
history -w strcat('./states/',filename,'.history');
save("-binary", strcat('./states/',filename,'.data'))
endfunction
The save/load command works well.
My Problem is that the history command seems not to evaulate the argument.
it prodces the following error:
syntax error
>>> history -r strcat('./states/',filename,'.history');
^
I already tried to use a temporary var for the path but in this case it only interprets the variable name as filename and complains about the missing file.
Does anybody has an idea how to solve this?
Use history with the function syntax instead of a command.
history ("-r", strcat ("./states/", filename, ".history"));
All commands are actually functions. The command syntax (when you don't use parentheses) is available to all functions, it just happens that for some it looks more natural. When you omit the parentheses, all the arguments are interpreted as strings, even variable names. If you want to do something fancier, call them as functions.