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

代码格式化插件 format.nvim

2024-12-25
2025-09-09
Eric Wong

前因

最近,我在 Reddit 上面分享了一个新的代码格式化的插件 format.nvim, 回复大多是是在问为什么要做这个插件以及跟现有插件的区别。

在 SpaceVim v2.3.0 之前,一直使用的代码格式化插件是 neoformat, 这个插件是使用 Vim 脚本写的,同时支持 Neovim 和 Vim。 但是美中不足的地方是这个插件执行格式化命令是调用 Vim 的 system() 函数,当命令执行消耗时间很长时,就会卡住界面无法进行下一步操作。 我也尝试过给 neoformat 增加异步支持,但是最终还是决定重新开发一个插件, 主要市考虑到以下几个原因:

  • Lua 尤其是 luajit 速度相较于 Vim 脚本要快很多,相关比较文章可以查看我前面写的使用 Lua 重写 SpaceVim 内置插件Vim9Script 与 Lua 的速度比较
  • 做一个格式化插件的框架,尽可能减少默认的 formatter,提供接口自行定义。
  • 不需要太过复杂的功能,仅仅是异步执行指定命令并更新 Neovim 缓冲区。

其实 neoformat 的插件代码实现逻辑还是非常好的,因此我也是参考了 neoformat 的实现逻辑, 使用 Lua 来实现了这个异步代码格式化的插件 format.nvim。 这个插件使用了 SpaceVim 的 job api 可以异步执行格式化命令,使得 Neovim 的操作更加顺畅。

插件的安装

可以使用任意插件管理器,比如nvim-plug来进行安装:

require('plug').add({
  {
    'wsdjeg/format.nvim',
    cmds = { 'Format' },
    depends = {
      { 'wsdjeg/job.nvim' },
      { 'wsdjeg/notify.nvim' },
    },
  },
})

对于 SpaceVim 用户来说,只需要在启用 format 模块的时候,指定格式化方法为 format.nvim 即可,配置如下:

[[layers]]
  name = 'format'
  format_method = 'format.nvim'

自定义 formatter

以 Lua 语言为例,设置使用 stylua 命令进行格式化:

require('format').setup({
    custom_formatters = {
        lua = {
            exe = 'stylua',
            args = { '-' },
            stdin = true,
        },
    },
})

插件的使用

  1. 格式化整个文件

format.nvim 只提供了一个命令 :Fromat, 执行该命令时就可以根据当前文件的文件类型选择对应的 formatter 对整个 Buffer 进行格式化。

  1. 选中区域进行格式化

:Format 命令支持指定区域进行格式化,因此可以在 Neovim 中选中几行代码进行格式化。

  1. 指定文件类型

通常执行 :Format 命令时,会读取当前 Buffer 的 &filetype 选项,但是如果需要指定其他文件类型,比如 java,可以使用如下格式执行命令:

:Format! <filetype>
  1. 指定格式化工具

如果一个文件类型有多个 formatters,可以在执行改命令时指定一个 formatter 的名字,比如 :Format prettier

  1. 同时指定文件类型、格式化工具,比如选中 markdown 内一段代码块进行格式化:
:Format! <filetype> <formatter>

Markdown 代码块格式化

前面提到 :Format 命令支持指定区域格式,这里需要借助一个插件 context_filetype.vim。 通过这个插件获取到代码块的区域和文件类型,传给 Format 命令。

以下示例使用 <Leader>f 格式化光标所在的代码块:

function! s:format_code_block() abort
  let cf = context_filetype#get()
  if cf.filetype !=# 'markdown'
    let command = printf('%s,%sFormat! %s', cf.range[0][0], cf.range[1][0], cf.filetype)
    exe command
  endif
endfunction
nnoremap <silent> <Leader>f <cmd><sid>format_code_block()<cr>

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


延生阅读

分享到:

评论