vim key sequence as function argument - function

I want to execute the command "yiw:s/\<<C-r>"\>/<C-r>"/g<Left><Left>" by key sequence.
So I make a mapping
nnoremap <F7> yiw:s/\<<C-r>"\>/<C-r>"/g<Left><Left>
This mapping copy the word under cursor, then the string :s/\<">/"/g" (where " are substituted by the copied word) appears in the command line and the cursor in the command line is at the end of replacement statement.
I also want to save cursor position before this command and restore after.
function! SafeCommand(cmd)
let line = line('.')
let col = col('.')
// execute cmd here
call cursor( line, col )
endfunction
How to do that?

Normally, you'd just put the entire (complex) command in a function, and invoke that function from the :nnoremap. But that doesn't work for incomplete commands, like the template :substitute that your mapping represents. For that, you need to include the save / restore parts into the command-line (though that's ugly):
:fun! Save()
let s:line = line('.')
let s:col = col('.')
:endfun
:fun! Restore()
call cursor( s:line, s:col )
:endfun
:nnoremap <F7> yiw:call Save()<Bar>s/\<<C-r>"\>/<C-r>"/g<Bar>call Restore()<Left><Left><Left><Left><Left><Left><Left><Left><Left><Left><Left><Left><Left><Left><Left><Left>

Related

Moving file E93: More than one match

The following function works (it opens and moves the desired file to a new or preexisting directory). But, I receive an error message concerning line 11 of the function execute 'bwipeout '.expand(s:oldFileName). The error I receive is E93: More than one match for <the old file name>. I don't understand how this is possible because it has already successfully moved the file to the s:newFileName and the old file has been deleted. The new file's buffer doesn't have the same name as the old buffer. So, how would there be more than one match for the name of the old buffer?
command! -nargs=0 -bar MoveFile call s:functionMoveFile()
function s:functionMoveFile() abort
call dirvish#open("edit", 0)
let s:oldFileName = expand("%:p")
call inputsave()
let s:newFileName = input("Move file here: ",expand(s:oldFileName),"file")
call inputrestore()
if s:newFileName != '' && s:newFileName != s:oldFileName
execute 'sav '.fnameescape(s:newFileName)
let s:newFileDirectory = expand("%:p:h")
call delete(s:oldFileName)
execute 'Dirvish '.expand(s:newFileDirectory)
execute 'bwipeout '.expand(s:oldFileName)
endif
endfunction
I can prepend silent! to line 11 to avoid the error message. But, that isn't a proper fix. I don't know what else I should try because I am not asking vim to wipeout the new buffer and the old file's name doesn't match the new one. The command works as desired, but I would like to know what is incorrect about what I'm doing.
Thank you for your help.

How Can I Temporarily Change the Cursor Shape in a Function in Neovim

In Neovim, the cursor shape automatically changes based on the mode you are in.
I am writing a function in which I temporarily want to change what the cursor looks like in insert mode, then enter insert mode and do something, return to normal mode, and then revert to the defaults.
I tried that with this function:
function! Cache()
let oldcursor = &guicursor
let &guicursor = "i:block"
call feedkeys("a\<c-x>\<c-o>\<escape>")
let &guicursor = oldcursor
endfunction
But this didn't work.
It didn't return the &guicursor value to the oldcursor after running the line before it.
I wanted it to first run call feedkeys("\<c-x>\<c-o>\<escape>"), and then run let &guicursor = oldcursor.
But it seemed like it didn't wait for the feedkeys to finish before returning the originial value of guicursor.

Error : 'x' undefined

I got a problem with running Octave function (ODE), I've tried already present solutions for this problem but nothing is working. I've also tried by saving my filename as egzamin.m but it too not worked.
Code from octave :
function dx=egzamin(x,t)
dx=zeros(4,1);
b=0;
g=9.81;
x1=x(1);
y1=x(2);
Vx=x(3);
Vy=x(4);
dx(1)=Vx;
dx(2)=Vy;
dx(3)=-b*Vx*sqrt(Vx.^2+Vy.^2);
dx(4)=-b*Vy*sqrt(Vx.^2+Vy.^2)-g;
endfunction
N=mod(291813,100);
x1=0;
y1=0;
Vx=20+N;
Vy=20+N;
t=0:0.01:500;
sol=lsode("egzamin",[x1,y1,Vx,Vy],t);
plot(sol(:,1),sol(:,2))
The error is :
error: 'x' undefined near line 5 column 4
error: called from
egzamin at line 5 column 3
Since the file starts with function, it is not a script file,
as explained in the doc:
Unlike a function file, a script file must not begin with the keyword
function
Add any statement (even dummy like 1;) before the function line to get a script file.
# dummy statement to get a script file instead of a function file
1;
function dx=egzamin(x,t)
g = 9.81;
Vx = x(3);
Vy = x(4);
dx = [Vx, Vy, 0, -g];
endfunction
N=mod(291813,100);
x1=0;
y1=0;
Vx=20+N;
Vy=20+N;
t=0:0.01:500;
sol=lsode("egzamin",[x1,y1,Vx,Vy],t);
plot(sol(:,1),sol(:,2))
A very clear explanation of what's going on is given here.
You need to save the function (thus from function to endfunction and naught else) as egzamin.m, and then execute the rest of the code in a script or at the command line. Alternatively, provided Octave does that the same as what MATLAB does nowadays, first put your script (N=(..) to plot()) and then the function.
This is necessary since you are defining your function first, so it doesn't have any inputs yet, as you don't define them until later. The function needs to have its inputs defined before it executes, hence you need to save your function separately.
You can of course save your "script" bit, thus everything which is currently below your function declaration, as a function as well, simply don't give it in- and outputs, or, set all the input parameters here as well. (Which I wouldn't do as it's the same as your
egzamin then.) e.g.
function []=MyFunc()
N=mod(291813,100);
x1=0;
y1=0;
Vx=20+N;
Vy=20+N;
t=0:0.01:500;
sol=lsode("egzamin",[x1,y1,Vx,Vy],t);
plot(sol(:,1),sol(:,2))
endfunction

vim keep cursor position when counting matches

I have a function to count and return the number of matches of some text:
function! count_matches()
redir => matches_cnt
silent! %s/\[\d*\]//gn
redir END
return split(matches_cnt)[0]
endfunction
I created a map to insert the return value of count_matches() at the current position:
noremap <C-A> Go[foo<C-R>=count_matches()<CR>]
However the cursor jumps to the beginning of the line after executing the silent %s/[\d*]//gn command. So when I press control+a vim inserts "[foo", then the function is being executed, the search command resets the cursor position and the return value is inserted at the beginning of the line resulting in "1][foo" instead of "[foo1]".
Can I somehow prevent count from changing the cursor position, or reset the cursor position after counting the matches?
The script also leads to an error, if the pattern is not found. How can I get the function to return 1 without an error for zero matches?
Even better then just to save the cursor position, is to save the complete viewport. (But that only works, if you do not change the window layout)
See :help winsaveview()
let wsv = winsaveview()
MoveTheCursorAround
call winrestview(wsv)
In your particular case, I would take another approach:
inoremap <expr> <f3> len(split(join(getline(1,'$'),"\n"), '\[\d\+\]',1))
Which takes the whole text, and splits it on the pattern \[\d\+\] and then counts how many elements there are. Or if you like to add some text:
inoremap <expr> <f3> '['.len(split(join(getline(1,'$'),"\n"), '\[\d\+\]',1)).']'
This will add the [ in front and ] after the number. Adjust the mapping key and text to your personal taste. (Note, you do not need the winsaveview() function, cause the cursor won't move).
It is perhaps not such a good idea to use that function on a multi MB text size. ;)
This is the same function, reworked to return 1 when there's no match:
function! count_matches()
redir => matches_cnt
try
silent! %s/\[\d*\]//gn
catch
echo 1
endtry
redir END
return split(matches_cnt)[0]
endfunction
See :help getpos()
let save_cursor = getpos(".")
MoveTheCursorAround
call setpos('.', save_cursor)
My solution:
function! CountWithCursorKeep(...)
let currentCursor = getcurpos()
let pattern = expand('<cword>')
if a:0 > 0 | let pattern = a:1 | endif
execute(':%s#' . pattern . '##gn')
call setpos('.', currentCursor)
endfunction
nmap ,ns :call CountWithCursorKeep(<C-R>/)<cr>
nmap ,nw :call CountWithCursorKeep(expand('<cword>'))<cr>
nmap ,nW :call CountWithCursorKeep(expand('<cWORD>'))<cr>
command! -nargs=? -bar -complete=tag CountMatch call CountWithCursorKeep(<f-args>)
You can use :CountMatch pattern to get how many times pattern occurs in current file.

Returning insert mode cursor to same place after function call

I've been trying to write a bit of Vimscript to call a function when I insert the same character twice, in my particular case I wanted that if you inserted semi colon twice for it to actually move the semi colon to the end of the line.
command! Semi call Semi()
inoremap ; <C-O>:Semi2<CR>
function! Semi()
let x = getpos(".")
" If we are in the last column..
if col(".")+1 == col("$")
let insert_semi = getline(".") . ";"
call setline(".", insert_semi)
let x[2] += 1
call setpos(".", x)
return
endif
let char = getline(".")[x[2] - 2]
if char == ";"
" if prev char was a semicolon also, remove and append to the end
else
" insert semicolon normally...
endif
endfunction
The problem I am having is when calling this function on the last column, you have to exit insert mode to call this function the cursor will go into normal mode and move the cursor to the last column. Is there any way to tell whether the cursor was appending to the end of the line or inserting before the last column and when function call is finished return it to the same position?
I am well aware that I could use an insert mapping on ;; however I dislike this behaviour, where Vim goes into a waiting for next key mode and does not display what you have written. This issue is not only to do with my problem listed but a more general problem which also occurs in the first column.
If your function does not use insert mode to append the ';' -- e.g. by pasting from a buffer -- you can use the gicommand to return to the place, where you exited insert mode.
I would advise against using i_CTRL-O, it triggers InsertLeave and InsertEnter events, which may affect other plugins. I would use :inoremap <expr> ; here. See :help :map-expr. Inside that expression (i.e. your function), record the current cursor position and compare it with the last recorded one. If it's next to it, return the keys to undo the inserts and redo at the end (<BS><BS><End>;), else just return ;.
You don't need a function for that:
inoremap ;; ;;<Esc>h"_xxm`$p``a
or cleaner:
inoremap ;; <Esc>m`A;<Esc>``a