【发布时间】:2015-10-11 10:28:55
【问题描述】:
编辑:
我已经简化了函数并澄清了问题。
页面下方仍可找到原始问题。交叉发布到 vim_dev 邮件列表: https://groups.google.com/forum/#!topic/vim_dev/_Rz3uVXbwsQ
已作为错误报告给 Neovim:
https://github.com/neovim/neovim/issues/6276
为什么以下两个例子中光标的位置不同:
-
[CORRECT CURSOR POSITION] 替换的结果加入到缓冲区中的先前更改(添加第 3 行),光标位置正确恢复到缓冲区中的第二行。
normal ggiline one is full of aaaa set undolevels=10 " splits the change into separate undo blocks normal Goline two is full of bbbb set undolevels=10 normal Goline three is full of cccc set undolevels=10 undojoin keepjumps %s/aaaa/zzzz/ normal u -
[INCORRECT CURSOR POSITION] 替换的结果加入到缓冲区中的先前更改(添加第 4 行),光标位置不正确恢复到缓冲区中的第一行(应该是第 3 行)。
normal ggiline one is bull of aaaa set undolevels=10 " splits the change into separate undo blocks normal Goline two is full of bbbb set undolevels=10 normal Goline three is full of cccc set undolevels=10 normal Goline four is full of aaaa's again set undolevels=10 undojoin keepjumps %s/aaaa/zzzz/ normal u
原始问题
我的 VIM 设置方式,将缓冲区保存到文件会触发自定义 StripTrailingSpaces() 函数(附在问题的末尾):
autocmd BufWritePre,FileWritePre,FileAppendPre,FilterWritePre <buffer>
\ :keepjumps call StripTrailingSpaces(0)
在看到Restore the cursor position after undoing text change made by a script 之后,我想到了将 StripTrailingSpaces() 函数所做的更改从撤消历史记录中排除,方法是将函数创建的撤消记录合并到缓冲区中先前更改的末尾。
这样,在撤消更改时,函数似乎根本没有创建自己的撤消记录。
为了验证我的想法,我使用了一个简单的测试用例:创建一个干净的缓冲区并手动输入以下命令,或者将以下块保存为文件并通过以下方式获取它:
vim +"source <saved-filename-here>"
normal ggiline one is full of aaaa
set undolevels=10 " splits the change into separate undo blocks
normal Goline two is full of bbbb
set undolevels=10
normal Goline three is full of cccc
set undolevels=10
undojoin
keepjumps %s/aaaa/zzzz/
normal u
如您所见,撤消缓冲区中的最后一次更改,即创建第三行后,光标正确返回到文件中的第二行。
由于我的测试成功,我在我的 StripTrailingSpaces() 中实现了一个几乎相同的 undojoin。但是,当我在函数运行后撤消最后一次更改时,光标将返回到文件中最顶部的更改。这通常是一个剥离的空间,不是我undojoin-ed 更改的位置。
谁能想到为什么会这样?更好的是,任何人都可以提出解决方案吗?
function! StripTrailingSpaces(number_of_allowed_spaces)
" Match all trailing spaces in a file
let l:regex = [
\ '\^\zs\s\{1,\}\$',
\ '\S\s\{' . a:number_of_allowed_spaces . '\}\zs\s\{1,\}\$',
\ ]
" Join trailing spaces regex into a single, non-magic string
let l:regex_str = '\V\(' . join(l:regex, '\|') . '\)'
" Save current window state
let l:last_search=@/
let l:winview = winsaveview()
try
" Append the comming change onto the end of the previous change
" NOTE: Fails if previous change doesn't exist
undojoin
catch
endtry
" Substitute all trailing spaces
if v:version > 704 || v:version == 704 && has('patch155')
execute 'keepjumps keeppatterns %s/' . l:regex_str . '//e'
else
execute 'keepjumps %s/' . l:regex_str . '//e'
call histdel('search', -1)
endif
" Restore current window state
call winrestview(l:winview)
let @/=l:last_search
endfunction
【问题讨论】:
-
对不起,这75行和
:%s/\s*$/有什么区别? -
@steffen:嗯……它有 74 行和 2866 个字符……它还具有描述性 cmets,保留您的搜索历史和上次搜索字符串,不会更改您的
''、'.和 @ 987654335@ 标记,不添加新的jumplist和changelist记录,保留您的视图和光标位置,并且应该创建更流畅的撤消体验。 (虽然最后一点是主观的,这也是这个问题在这里的原因。) -
在进行更改之前会记住光标位置,然后在撤消更改后恢复。
标签: vim undo cursor-position neovim