当我们执行Vim Ex命令时,它会保存在命令行历史记录中,我们可以通过打开q:
的命令行窗口来访问它。
但是,有时候,我希望能够永久删除其中一些。例如,如果我执行命令:a
,则命令行窗口中的所有后续命令都将被识别为语法组vimInsert
的元素,该组将链接到突出显示组String
,在我的colorscheme中用青色着色。
要修复语法突出显示,我可以尝试从命令行窗口中点击:a
删除dd
命令,但只有在打开窗口时才会删除该条目。一旦我关闭它,并在以后重新打开它,:a
命令就会恢复。为了永久删除,我尝试在vimrc
中添加autocmd:
augroup my_cmdline_window
au!
au CmdWinLeave * call s:update_history()
augroup END
fu! s:update_history() abort
let new_hist = getline(1, '$')
call histdel(':')
for i in new_hist
call histadd(':', i)
endfor
endfu
它调用一个函数,该函数在关闭之前通过从当前窗口读取命令历史记录来更新命令历史记录。它在当前Vim会话运行时有效,但是一旦我退出它然后开始一个新的,所有删除的条目都回来了。
我认为删除不会在会话中持续存在的原因是因为当我退出会话时,Vim会将当前命令行历史记录与~/.viminfo
中存储的历史记录合并。
此文件中保存的内容由'viminfo'
选项控制。我'viminfo'
的值是:
viminfo='100,<50,s10,h
它不包含:
参数,该参数设置要保存的命令行历史记录中的最大项目数。这意味着Vim应该使用'history'
选项。我的'history'
的默认值为1000
。
因此,为了强制Vim使用当前会话之一覆盖~/.viminfo
内存储的命令行历史记录,我尝试在autocmd中使用:wviminfo
命令:
augroup my_cmdline_window
au!
au CmdWinLeave * call s:update_history()
augroup END
fu! s:update_history() abort
let new_hist = getline(1, '$')
call histdel(':')
for i in new_hist
call histadd(':', i)
endfor
wviminfo!
endfu
我根据:wviminfo
添加了:h :wviminfo
的爆炸声:
When [!] is used, the old information is not read first,
only the internal info is written.
它似乎有效,因为使用此autocmd,删除命令行窗口中的条目会在会话中持续存在。但是,它似乎也会删除最近编辑过的文件的所有更改列表,因为在启动新的Vim会话后,所有更改列表都是空的。
由于:h 'viminfo
解释了当您在'
中包含'viminfo'
项时,更改列表和跳转列表也存储在~/.viminfo
中,我怀疑跳转列表和使用:wviminfo!
后,标记也会丢失。
有没有办法避免丢失更改列表/跳转列表/标记,同时仍然从命令行历史记录中删除任意条目?
编辑:
我想出了这个:
augroup my_cmdline_window
au!
au CmdWinEnter * let s:old_cmdline_hist = getline(1, line('$')-1)
au CmdWinLeave * call s:update_history()
augroup END
fu! s:update_history() abort
let hist = filter(getline(1, '$'), 'v:val !~# "^\\s*$"')
call histdel(':')
for i in hist
call histadd(':', i)
endfor
let viminfo = expand('~/.viminfo')
if !filereadable(viminfo)
return
endif
let info = readfile(viminfo)
let deleted_entries = filter(copy(s:old_cmdline_hist), 'index(hist, v:val) == -1')
call map(deleted_entries, 'index(info, ":".v:val)')
call sort(filter(deleted_entries, 'v:val >= 0'))
if empty(deleted_entries)
return
endif
for entry in reverse(deleted_entries)
call remove(info, entry, entry + 1)
endfor
call writefile(info, viminfo, 'b')
endfu
以下是代码的作用:
当您进入命令行窗口时,第一个autocmd会捕获s:old_cmdline_hist
内的命令行历史记录。
当您离开时,第二个autocmd会更新当前会话的历史记录,并尝试从~/.viminfo
中删除已删除的条目。
let hist = filter(getline(1, '$'), 'v:val !~# "^\\s*$"')
call histdel(':')
for i in hist
call histadd(':', i)
endfor
此块仅更新当前会话的历史记录。
let viminfo = expand('~/.viminfo')
if !filereadable(viminfo)
return
endif
这个检查~/.viminfo
是否可读。如果不是,则该功能停止。
let info = readfile(viminfo)
let deleted_entries = filter(copy(s:old_cmdline_hist), 'index(hist, v:val) == -1')
此块获取列表~/.viminfo
内的info
内容(1项= 1行)。它还通过调用filter()
获取已删除的条目。后者删除所有不满足条件index(hist, v:val) == -1
的条目。如果filter()
操作的旧历史记录的条目不在新历史记录中,则此表达式为真。
call map(deleted_entries, 'index(info, ":".v:val)')
此行将已删除的文本条目转换为~/.viminfo
内的行地址。
call sort(filter(deleted_entries, 'v:val >= 0'))
if empty(deleted_entries)
return
endif
此块对行地址进行排序,并删除非正数的行,以防在上一步中找不到其中一些地址(index()
返回-1
)。它还检查此时是否仍有行地址,如果没有,则该功能再次停止。
for entry in reverse(deleted_entries)
call remove(info, entry, entry + 1)
endfor
此块从info
中删除相关行。它删除了2行(entry
和entry + 1
),因为对于~/.viminfo
中保存的每个命令行,还有第2行似乎是时间戳。此外,它以相反的顺序删除,不必更新要删除的行的地址(每次删除行时,下一行的地址减1)。
call writefile(info, viminfo, 'b')
此行用新内容覆盖~/.viminfo
,新内容应与以前相同,不在命令行窗口中删除行。
我不会将其作为答案发布,因为我不确定它是否可靠并且没有任何副作用。在测试代码之前,我对~/.viminfo
进行了备份。
EDIT2:
我认为它不能删除包含文字回车的行。可能有其他特殊字符导致类似问题(如空字符^@
)。
答案 0 :(得分:0)
历史记录以纯文本格式存储在.viminfo
文件中。因此,您只需打开.viminfo
并删除该错误行即可。
或者,您可以通过histdel()函数清除历史记录,例如搜索:
:call histdel('/')
您可以按数字或正则表达式删除某些历史记录条目。参见:help histdel
。