Reputation: 1739
For my LaTeX editing in vim, I recorded a handful of useful macros and wrapped them into functions/command. I have one, that change the type of a Latex environment e.g., when I have:
\begin{itemize}
\item First
\item Second
\end{itemize}
I just enter :ChangeEnv enumerate
with the cursor somewhere in the environment to change from itemize to enumerate.
The code in my ftplugin/tex.vim looks like this:
function! ChangeEnv(newenv)
let l:save = @e
let @e = a:newenv
let l:line = getline('.')
" Fake change to restore undo
normal ix
normal x
if match(l:line, '\\begin{') != -1
normal mz_%f{lci}e'zf{l.`z:delma z
else
normal my?\\begin{^M_mz%f{lci}^Re^['zf{l.`y:delma yz
endif
let @e = l:save
endfunction
command -nargs=1 ChangeEnv :silent call ChangeEnv(<f-args>)
The first part (after if match(...
) intended if the cursor is on the \begin{...}
part of the environment works perfectly so far, I can make the change and undo it, cursor stays where it should.
The second part, intended for inside the environment, also works great, but when the change is undone, the cursor jumps to the first charachter of the \begin line.
The normal ix
and normal x
part is intended to ensure the cursor position is restored after the und (I have this from here: Restor Cursor Position)
My question is, why doesn't it work for the second macro? Is there some error?
To spare you deconstructing the macro, this are the steps:
my
- set y
mark at current position?\\begin{^M
- Search backward for the beginning of the environment and jump there_mz
- go to first character of that line and set the z
mark%
- Jump to the matching \end{...
of the environment (This is part of the matchit vim plugin, delivered with vim but not active per default).f{l
- Jump forward to {
and one character rightci}
- Change inner {...}^Re^[
- Insert the content of the e
register, where the new environment name is stored and return to normal mode'z'
- Jump to the line beginning of the z
mark (The \begin{...
)f{l.
- Forward to {
, one step right and repeat the last change`y
- Jump to the y
mark, the initial position:delma yz
- Delete the y
and z
markThe undo behavior is not a deal breaker, nevertheless, I'd at least like to know why it behaves that way.
Thank you in advance.
Upvotes: 2
Views: 369
Reputation: 172520
Usually, when you make multiple changes, each one is undone separately. But inside a function, everything is lumped together as one single change.
Your intention of doing this dummy change at the beginning (when the cursor hasn't yet been moved) is that the cursor returns to that point after undo.
However, I think as Vim treats the whole set of changes done within ChangeEnv()
as one big modification, it returns to the beginning of the range of changed lines (mark '[
), as it does when undoing built-in commands. Which change command was executed "first" doesn't matter, all that matters is the range of lines that got changed. As your second branches searches backwards to the \begin
and makes a change there, that's where the cursor will be after undo. I don't think there's a way around this; undo cannot be influenced by scripts, as this could potentially introduce inconsistencies.
What you could do is setting a mark after doing the changes (at the end of your function: :normal! m'
), so that you can quickly jump back there (via ``
or <C-o>
).
Upvotes: 3