Eric's Blog 时光荏苒,岁月如梭

Neovim 近期文件浏览历史记录

2025-04-13
Eric Wong

起因

Neovim 和 Vim 一样,支持使用 v:oldfiles 获取最近浏览的文件名称列表。但是 Neovim 这一变量在 Windows 系统下面存在文件名称格式不一致的问题。 比如,一些使用 v:oldfiles 的插件,如 telescope.nvim、dashboard.nvim 等,其列表内会出现一些重复的文件。

D:/hello.txt
D:\hello.txt
d:/hello.txt
d:\hello.txt

此外,阅读:h v:oldfiles 可以看到该变量虽然支持修改,但是并不会影响 shada 文件中存储的内容。 因此,在 Neovim 重启后,之前对 v:oldfiles 这一变量的修改就会无效了。

mru.nvim 的特点

为了避免上述原因,我做了一个插件 mru.nvim

  1. Windows 系统下统一的文件名格式,上述四种文件名称统一显示为 D:/hello.txt

  2. 可以预设置多个正则表达式,当文件名称匹配上时,阻止其被加入文件浏览历史记录。

    require('mru').setup({
      ignore_path_regexs = { '/.git/', '/nvim/runtime/doc/' },
    })
    

    比如,在使用一些启动屏插件(dashboard.nvim)时,会在启动时显示近期打开过的文件, 如果前面打开过一些非文本类文件,比如图片类的,这类文件可以从浏览历史中去除掉。

    require('mru').setup({
      ignore_path_regexs = { '/.git/', '.mp3$', '.png$' },
    })
    
  3. 提供了 telescope.nvim 拓展,可以使用 :Telescope mru 打开。

  4. 提供了修改 mru 文件列表的函数,Neovim 重启后任然有效。

安装与配置

使用 nvim-plug 插件管理器进行安装:

require('plug').add({
    {
        'wsdjeg/mru.nvim',
        config = function()
            require('mru').setup({
                enable_cache = true,
                mru_cache_file = vim.fn.stdpath('data') .. '/nvim-mru.json',
                ignore_path_regexs = { '/.git/' },
                enable_logger = true, -- require wsdjeg/logger.nvim
                -- sort file by last modified time or last enter time
                -- `lastmod`, `lastread` or `lastenter`, default is `lastenter`
                sort_by = 'lastenter',
            })
        end,
    },
})

LeaderF 拓展

当然,如果并未使用 telescope,也可以自行拓展,使用 require('mru').get() 获取历史记录文件名称列表。以 leaderf 为例:

function! s:nmru(...) abort
  return v:lua.require('mru').get()
endfunction

function! s:nmru_acp(line, args) abort
  exe 'e' a:line
endfunction
" function() wrapper
if v:version > 703 || v:version == 703 && has('patch1170')
  function! s:_SID() abort
    return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
  endfunction
  let s:_s = '<SNR>' . s:_SID() . '_'
  function! s:_function(fstr, ...) abort
    if a:0 > 1
      return function(substitute(a:fstr, 's:', s:_s, 'g'))
    else
      return function(a:fstr)
    endif
  endfunction
else
  function! s:_SID() abort
    return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
  endfunction
  let s:_s = '<SNR>' . s:_SID() . '_'
  function! s:_function(fstr) abort
    return function(substitute(a:fstr, 's:', s:_s, 'g'))
  endfunction
endif
let g:Lf_Extensions = {
    \ 'nmru': {
    \       'source': string(s:_function('s:nmru', 1))[10:-3],
    \       'accept': string(s:_function('s:nmru_acp', 1))[10:-3],
    \       'supports_name_only': 1,
    \       'supports_multi': 0,
    \ },
    \}

以上为 VimL 写的 LeaderF 拓展,尝试了使用 Lua 来写,可读性更简单,但是似乎不起作用:

local function mru()
  return require('mru').get()
end

local function mru_acp(line, args)
  vim.cmd('e ' .. line)
end
vim.g.Lf_Extensions = {
  nvimmru = {
    source = mru,
    accept = mru_acp,
    supports_name_only = true,
    supports_multi = false,
  },
}

版权声明:本文为原创文章,遵循 署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0)版权协议,转载请附上原文出处链接和本声明。


延生阅读

分享到:

评论