在没有插件的情况下在 vim 上运行 gofmt

hid*_*atz 4 vim go gofmt

我想在 vim 上保存时运行 gofmt 而不安装任何插件。这是我尝试过的(灵感来自https://gist.github.com/tbrisbout/a91ac3419440cde40c5f54dc32c94427):

function! GoFmt()
  let file = expand('%')
  silent execute "!gofmt -w " . file
  edit!
endfunction

command! GoFmt call GoFmt()
augroup go_autocmd
  autocmd BufWritePost *.go GoFmt
augroup END
Run Code Online (Sandbox Code Playgroud)

这仅在没有格式错误时有效。但是,如果代码包含错误,它会在屏幕底部显示错误消息,并且(看起来)它以文本形式出现在缓冲区上,因此整个代码都会被破坏。有没有一种简单的方法可以在 vim 上处理此类工作?

rom*_*inl 5

Gofmt 既是代码格式化程序又是linter,这一设计决策使其与 Vim 集成变得更加困难。在实践中,这意味着事情可以朝三种方向发展:

\n
    \n
  • 你的代码是完美的,什么也没有发生,
  • \n
  • 您的代码有格式问题,已重新格式化,
  • \n
  • 您的代码有语法问题,会输出错误列表。
  • \n
\n

例如,我们可以天真地尝试通过以下方式使用gofmtfor和friendsgq:help \'formatprg\' but the chances of eventually overwritting our code with crap like:

\n
<standard input>:4:2: expected statement, found \'.\'\n<standard input>:5:3: expected \'}\', found \'EOF\'\n
Run Code Online (Sandbox Code Playgroud)\n

太高了。就像您的情况一样,我们可以u撤消,但这并不有趣。我想我们将不得不解决gofmt糟糕的设计。

\n

第一步:切换到:help BufWritePre. 我们已经看到gofmtcan handler stdin,它允许我们格式化缓冲区和文件。这很方便,因为在写入文件后对其进行格式化会无缘无故地再次写入该文件,并迫使我们将其重新加载到 Vim\xe2\x80\xa6 中,而所有这些似乎都是浪费。:help BufWritePost最好保留那些不影响 Vim 状态的东西。

\n
function! GoFmt()\n  echomsg "hello"\nendfunction\n\ncommand! GoFmt call GoFmt()\n\naugroup go_autocmd\n  autocmd BufWritePre *.go GoFmt\naugroup END\n
Run Code Online (Sandbox Code Playgroud)\n

第二步:过滤整个缓冲区gofmt。

\n
function! GoFmt()\n  silent %!gofmt\nendfunction\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  • 最好的情况:没有任何反应或缓冲区被格式化内容覆盖。
  • \n
  • 最坏的情况:整个缓冲区被错误报告替换。
  • \n
\n

Third step: "handle" the worst case scenario with a basic undo. If the external command returns an error, we can get it via :help v:shell_error and do what needs to be done.

\n
function! GoFmt()\n  silent %!gofmt\n  if v:shell_error > 0\n    silent undo\n  endif\nendfunction\n
Run Code Online (Sandbox Code Playgroud)\n

Fourth step: try to keep the cursor in place.

\n
function! GoFmt()\n  let saved_view = winsaveview()\n  silent %!gofmt\n  if v:shell_error > 0\n    silent undo\n  endif\n  call winrestview(saved_view)\nendfunction\n
Run Code Online (Sandbox Code Playgroud)\n

See :help winsaveview() and :help winrestview().

\n

Fifth step: if applicable, create a quickfix list with the errors reported by gofmt. :help getline() gives us all the lines of the buffer\xe2\x80\x94therefore all the errors\xe2\x80\x94in a list of which we modify each item so that the file name is the current file name instead of the useless <standard input>. We give that list to :help :cexpr to create a quickfix list before undoing the filter.

\n
function! GoFmt()\n  let saved_view = winsaveview()\n  silent %!gofmt\n  if v:shell_error > 0\n    cexpr getline(1, \'$\')->map({ idx, val -> val->substitute(\'<standard input>\', expand(\'%\'), \'\') })\n    silent undo\n  endif\n  call winrestview(saved_view)\nendfunction\n
Run Code Online (Sandbox Code Playgroud)\n

This step has a bit of a "draw the rest of the * howl" vibe but it really is just a simple :help substitute() in a simple :help map(). For the { foo, bar -> baz } syntax, see :help lambda.

\n

Sixth and last step: open the quickfix window if there are any valid errors, with :help :cwindow.

\n
function! GoFmt()\n  let saved_view = winsaveview()\n  silent %!gofmt\n  if v:shell_error > 0\n    cexpr getline(1, \'$\')->map({ idx, val -> val->substitute(\'<standard input>\', expand(\'%\'), \'\') })\n    silent undo\n    cwindow\n  endif\n  call winrestview(saved_view)\nendfunction\n
Run Code Online (Sandbox Code Playgroud)\n

戈夫姆特

\n