Vim: restore cursor position after undo of imap - vim

I have modified the IMAP function from vim-latex, so that an undo is possible, by changing the final return line of LookupCharacter to
return a:char . "^Gu\<bs>" . bs . IMAP_PutTextWithMovement(rhs, phs, phe)
Now to do the undo directly from insert mode, I have this mapping in my .vimrc:
imap <Undo> <Esc>ui
I use the Neo2 keyboard layout, so there's an undo key on the 4th layer.
Now this works fairly well, but the problem is that when I use it at the end of a line, the cursor is placed before the last character. To test it without vim-latex, you could do
:imap a a^Gu\<bs>test
type 'b' in the middle and at the end of a line and then execute the undo, by shortcut or by manually pressing <Esc>ui.
So is there a way to make vim jump back to the correct cursor position?
I have seen an entry in wikia which gives an idea, but I don't know how to achieve it with the imap mapping.

The cursor move is due to the indiscriminate use of i at the end of your mapping. Typically, one temporarily executes a normal mode command via
imap <Undo> <C-\><C-o>u
But in your case, as the undo modifies the buffer, this may not work under all circumstances.

Related

Move to the last character of previous line vim

I have started using vim recently and was wondering if there's any keyboard shortcut to move the the last character of previous line( line number l-1 if I am currently on line number l) ?
One of the ways is to use the up arrow to move to the same column of previous line and then use $ to move to the end of the line.
I am looking for some shortcut to do that in one command.
UPDATE:
Here's also another solution to move to the beginning of the next line / the end of the previous line without the need to set any mappings. Add these three to your .vimrc:
set whichwrap+=<,h
set whichwrap+=>,l
set whichwrap+=[,]
(Credit to: Kevin H. Lin and garyjohn)
Original Answer:
I've not memorized all Vim shortcut combinations, so there might be one shortcut for what you're asking for, but I usually define a mapping whenever I need something I don't know how to do.
For what you need, you can simply define it with this:
nnoremap <S-L> <Up><S-4>
Just add it to your ~/.vimrc(if this file doesn't exist yet, create it yourself), then restart vim. Then the next time you open up your Vim, the "Shift-L" shortcut will do the job.
You can go straight into the insert mode as well and append characters after the last character of the previous line with this rule instead:
nnoremap <S-L> <Up><S-A>
Also in case you don't understand the structure of the above rules, you can read more about it here:
Understand Vim Mappings and Create Your Own Shortcuts.
There is a corner case: If your cursor is on the first line, pressing this mapping should not move the cursor.
Therefore, we can use the <expr> mapping:
nnoremap <expr> <F6> line('.')==1?'\<F6>':'k$'
In the example above, I used <F6>, you can choose the short-cut key you like.

Doing an insert remapping for commenter

I am trying to do a remapping when I'm in insert mode to insert a comment but am having a tough time figuring out what all the keys map to. What I am trying to do is:
:inoremap leadercspace ==> escleadercspacei
Basically, if I'm in insert mode I want to get out of insert mode to insert the comment (leader+c+space) and then go back into insert mode.
What would the correct :inoremap mapping for this be? What I have right now is:
:inoremap <leader>c<space> <Esc><Leader>c<space>i
But this doesn't seem to work (at least the latter half of it -- it does seem to be executing the mapping command). Note: the plugin I'm trying to remap is:
https://github.com/preservim/nerdcommenter
[count]<leader>c<space> |NERDCommenterToggle|
Toggles the comment state of the selected line(s). If the topmost selected line is commented, all selected lines are uncommented and vice versa.
From vim doc (:help nore):
Disallow mapping of {rhs}, to avoid nested and recursive mappings
In other words, the nore part forbids mapping to be applied to the rhs (right hand side).
So in your case, the <Esc><Leader>c<space>i doesn't trigger the VimCommenter mapping for that reason.
To allow recursion, you can take off the nore:
:imap <leader>c<space> <Esc><Leader>c<space>i
My recommendation is that, instead of creating an insert-mode mapping for this purpose, just use the native Ctrl+O mapping to run a single Normal mode command from Insert mode.
Assuming your leader key is set to the default \, you can use:
Ctrl+O, \, c, Space
You'll be left in Insert mode at the end of this sequence.
The advantages of this approach over an insert mode mapping are:
You don't need any extra configuration, since Ctrl+O is a native Vim command.
This works for any Normal mode command, so you don't need to add extra mappings for other commands you might want to be able to access from Insert mode.
Adding a multi-character mapping in Insert mode starting with <Leader> means Vim will always pause and hold if you insert the leader character. In this case, it will also pause when you insert <Leader> and c. I find that avoiding this kind of mappings of otherwise printable characters is usually best.

How do I map return in insert to go to normal mode and then do the o action

I am currently using vim and I'd like to map the Return key (I'm on a mac. I believe that this is generally represented by <ENTER> in maps) to leave insert mode, and then perform the o action. I was trying to put something like this
imap <ENTER> <ESC>o
However this is not performing the desired action. Any help would be fantastic.
Edit: The desired action is that if I am typing in insert mode, each new line is a new action. So if I press undo in normal mode it just undoes the last line instead of all the lines typed while in insert mode.
I'm not sure why this isn't working. I do not know why escaping and using o to open a newline does not add to the change-list. However, lucky for us, there is a command for explicitly adding the current state of the text to the change list. That command is (in insert mode) <C-g>u. From :h i_ctrl-g_u
CTRL-G u break undo sequence, start new change *i_CTRL-G_u*
Conveniently, this command doesn't even leave insert mode! Putting it all together, the mapping you're looking for is:
:inoremap <cr> <C-g>u<cr>
Or, you could also do
:inoremap <cr> <cr><C-g>u
which will leave you with a blank line after undoing.

Perform operation on entire buffer, without changing the cursor position

Consider a scenario, when you are editing a C file in vim. & you have used a mix of spaces & tabss. & you need to convert all of them to spaces.
There is a utility, called expand, which performs this task intelligently & it's a preferred way than s/\t/<4 spaces>/g. We can use this command in vim using :%!expand -t4. Typically, I map a function key, say F11 for this.
Similarly, we can use any other command in vim.
The problem is when you run any such operation on entire buffer, the cursor position changes & can be irritating at times.
Hence, the question is, how to perform an operation on entire buffer, without changing cursor position?
The cursor position can be restored by using the latest jump mark:
``
If you also want to maintain the exact window view, use winsaveview() / winrestview().
The anwolib - Yet another vim library has a handy :KeepView command for that:
:KeepView %!expand -t4
For such a case, we can use marks (e.g. mA). The mapping would be:
:nmap <F11> mZ:%!expand -t4<CR>`Z
However, there is still a catch. The screen scroll position may change, when operating on entire buffer.
This can be handled by using 2 marks as below:
:nmap <F11> mZHmY:%!expand -t4<CR>'Yzt`Z
Explanation:
Mark current position. (mZ)
Go to top of screen. (H)
Mark this line. (mY)
Run your filter. (:%!expand -t4)
Go to line Y. ('Y)
Make it top of screen. (zt)
Go to your mark. (`Z)
Now, when you press the mapped key, F11, the filter runs on the buffer & the cursor remains at its proper location.
you can just:
:your expand cmd|norm! ``
you can map it to <F11> if you like. but better use nnoremap
I just saw you mentioned in your question:
Similarly, we can use any other command in vim
If you want to do that, the reliable solution would be wrap the "any other command" in a function, and before/after the main logic, save/restore the cursor position. Because "any other command" could change the (back tick) mark, even the marks you defined.

Opposite of newline in vim

In vim, is there a command to delete the newline, and all empty space behind the cursor?
Say I stand in the middle of a text in insert mode and press Enter, what command would the reverse what I just did?
A) An example:
"some code{ in here }"
B) After pressing Enter:
"some code{
in here }"
Now pressing backspace will delete one space of the indentation. I would rather have it delete all indentation, and jump back to A.
Can this be done in a command or by doing some remapping to the backspace key?
It's tragic how unknown the J command is. It joins lines in normal mode.
In insert mode, you can press <C-U> twice; first, it'll delete the indent before the cursor, then it'll join with the previous line. Note that this requires
:set backspace=indent,eol,start
did you try J (uppercase) ? it will give exactly what you want.
"some code{ cursor on this line, pressJ
in here }"
You can do ᴇꜱᴄ, K, Shift+J.
K jumps up to the previous line and Shift+J joins the two lines.
However, with properly configured indentation and syntax, a backspace doesn’t just delete a space, it deletes the full previous indentation block.
One easy way is up one line, to end of that line and just delete. As long as you still are in insert mode it will do the same thing as J when deleting at the last position - like most other editors. For me that is the quickest alternative because I'm used to it from other editors.
That is: ↑, End, Delete (when still in insert mode)
One quick alternative (the VIM-way) is (when still in insert mode):
↑, Ctrl+o, J (when still in insert mode)
(Ctrl+o is used in insert mode to enter one normal mode command.)
It's also possible to use a remapping of the backspace key:
inoremap <expr> <bs> getline('.')[:col('.')-2]=~'^\s\+$' ? "<c-u><c-u>" : "<bs>"
Note that this mapping completely overrides the normal behavior the backspace key. This will only be useful when you don't intend to use its normal behavior. This is not recommended if you can easily access the other options (c-u or J)
However, (as far as I know) there's no way to distinguish between manually added leading white spaces and auto indent. If you use noexpandtab, you can edit the regex to only match tabs.
This also does not work in some modes of auto-indent (for example, in block comment in C, vim automatically start a new line starts with *)

Resources