当我们执行Vim-Ex命令时,它会被保存在命令行历史记录中,我们可以通过用q:
打开命令行窗口来访问它。
然而,有时,我希望能够永久删除其中的一些。例如,如果我执行命令:a
,命令行窗口中的所有后续命令都将被识别为语法组vimInsert
的元素,该语法组链接到高亮显示组String
,该高亮显示组在我的配色方案中用青色着色。
为了修复语法突出显示的问题,我可以尝试从命令行窗口中删除: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
加了一个bang,根据: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')
该块获取列表info
内的~/.viminfo
的内容(1项=1行)。它还获取通过调用filter()
删除的条目。后者删除所有不满足条件index(hist, v:val) == -1
的条目。如果filter()
操作的旧历史的条目不在新历史内,则此表达式为true。
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
中的每个命令行,还有一行似乎是时间戳。此外,它按相反的顺序删除,不必更新要删除的行的地址(每次删除一行,下一行的地址都会减少一)。
call writefile(info, viminfo, 'b')
这一行用新内容覆盖~/.viminfo
,新内容应该和以前一样,没有在命令行窗口中删除行。
我不把它作为答案发布,因为我不确定它是否可靠,也没有任何副作用。在测试代码之前,我备份了~/.viminfo
。
第2版:
我认为它无法删除包含回车的行。可能存在导致类似问题的其他特殊字符(如空字符^@
)。
历史记录以纯文本形式存储在.viminfo
文件中。所以你可以打开你的.viminfo
并删除那个bug行。
或者,您可以通过histdel()函数清除历史记录,例如,对于搜索:
:call histdel('/')
您可以通过数字或正则表达式删除某些历史记录条目。参见:help histdel
。