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

huangapple go评论81阅读模式
英文:

Run gofmt on vim without plugin

问题

我想在保存时在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

这只在没有格式错误时起作用。然而,如果代码包含错误,它会在屏幕底部显示错误消息,并且(似乎)以文本形式出现在缓冲区中,导致整个代码被破坏。
有没有一种简单的方法来处理vim上的这种工作?

英文:

I want to run gofmt on save on vim without installing any plugin. This is what I have tried (inspired by 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

This just works when there is no format error. However, if the code contains error, it shows error message at the bottom of the screen and (it seems like) it appears on the buffer as text, so the whole code gets broken.
Is there an simple way to handle this kind of work on vim?

答案1

得分: 3

Gofmt既是一个代码格式化工具,也是一个代码检查工具,这个设计决策使得将其与Vim集成变得比必要的困难。实际上,这意味着有三种情况可能发生:

  • 你的代码完美无误,什么都不会发生。
  • 你的代码存在格式问题,它会被重新格式化。
  • 你的代码存在语法问题,会输出一系列错误。

例如,我们可以尝试通过gq等命令使用gofmt,通过:help 'formatprg'来实现,但是这样很有可能最终用垃圾覆盖我们的代码,例如:

<standard input>:4:2: expected statement, found '.'
<standard input>:5:3: expected '}', found 'EOF'

这种情况的可能性太高了。就像你的情况一样,我们可以使用u来撤销,但这并不好玩。我想我们将不得不解决gofmt的设计问题。

第一步:切换到:help BufWritePre。我们已经知道gofmt可以处理stdin,这使得我们可以格式化缓冲区以及文件。这很方便,因为在写入文件后格式化文件会无故地再次写入文件,并强制我们在Vim中重新加载它...所有这些似乎都是浪费。最好将:help BufWritePost用于不影响Vim状态的事物。

function! GoFmt()
  echomsg "hello"
endfunction

command! GoFmt call GoFmt()

augroup go_autocmd
  autocmd BufWritePre *.go GoFmt
augroup END

第二步:将整个缓冲区通过gofmt进行过滤。

function! GoFmt()
  silent %!gofmt
endfunction
  • 最好的情况:什么都不会发生,或者缓冲区被格式化的内容覆盖。
  • 最坏的情况:整个缓冲区被替换为错误报告。

第三步:通过基本的撤销来“处理”最坏的情况。如果外部命令返回错误,我们可以通过:help v:shell_error获取错误,并执行必要的操作。

function! GoFmt()
  silent %!gofmt
  if v:shell_error > 0
    silent undo
  endif
endfunction

第四步:尝试保持光标位置不变。

function! GoFmt()
  let saved_view = winsaveview()
  silent %!gofmt
  if v:shell_error > 0
    silent undo
  endif
  call winrestview(saved_view)
endfunction

参见:help winsaveview():help winrestview()

第五步:如果适用,使用gofmt报告的错误创建一个快速修复列表。:help getline()可以将缓冲区的所有行(因此所有错误)作为列表给出,我们修改每个项目,使文件名成为当前文件名,而不是无用的<standard input>。然后,我们将该列表提供给:help :cexpr在撤销过滤之前创建一个快速修复列表。

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

这一步有点像“画出其余部分的*嚎叫”感觉,但实际上只是一个简单的:help substitute()在一个简单的:help map()中。关于{ foo, bar -> baz }语法,请参见:help lambda

第六步,也是最后一步:如果有任何有效的错误,使用:help :cwindow打开快速修复窗口。

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

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

英文:

Gofmt is both a code formatter and a linter, a design decision that makes it harder than necessary to integrate it with Vim. In practice, it means that things can go three ways:

  • your code is perfect, nothing happens,
  • your code has formatting issues, it is reformatted,
  • your code has syntax issues, a list of errors is output.

For example, we could naively try to use gofmt for gq and friends by way of :help &#39;formatprg&#39; but the chances of eventually overwritting our code with crap like:

&lt;standard input&gt;:4:2: expected statement, found &#39;.&#39;
&lt;standard input&gt;:5:3: expected &#39;}&#39;, found &#39;EOF&#39;

are too high. Like in your case, we can do u to undo but that's not fun. I guess we will have to work around gofmt's bad design.

First step: switch to :help BufWritePre. We have seen that gofmt can handle stdin, which allows us to format the buffer as well as the file. That is handy because formatting the file after it was written writes the file a second time for no good reason and forces us to reload it in Vim… and all that seems wasteful. :help BufWritePost is best kept for things that don't affect Vim's state.

function! GoFmt()
  echomsg &quot;hello&quot;
endfunction

command! GoFmt call GoFmt()

augroup go_autocmd
  autocmd BufWritePre *.go GoFmt
augroup END

Second step: filter the whole buffer through gofmt.

function! GoFmt()
  silent %!gofmt
endfunction
  • Best case scenario: nothing happens or the buffer is overwritten with the formatted content.
  • Worst case scenario: the whole buffer is replaced with an error report.

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.

function! GoFmt()
  silent %!gofmt
  if v:shell_error &gt; 0
    silent undo
  endif
endfunction

Fourth step: try to keep the cursor in place.

function! GoFmt()
  let saved_view = winsaveview()
  silent %!gofmt
  if v:shell_error &gt; 0
    silent undo
  endif
  call winrestview(saved_view)
endfunction

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

Fifth step: if applicable, create a quickfix list with the errors reported by gofmt. :help getline() gives us all the lines of the buffer—therefore all the errors—in a list of which we modify each item so that the file name is the current file name instead of the useless &lt;standard input&gt;. We give that list to :help :cexpr to create a quickfix list before undoing the filter.

function! GoFmt()
  let saved_view = winsaveview()
  silent %!gofmt
  if v:shell_error &gt; 0
    cexpr getline(1, &#39;$&#39;)-&gt;map({ idx, val -&gt; val-&gt;substitute(&#39;&lt;standard input&gt;&#39;, expand(&#39;%&#39;), &#39;&#39;) })
    silent undo
  endif
  call winrestview(saved_view)
endfunction

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 -&gt; baz } syntax, see :help lambda.

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

function! GoFmt()
  let saved_view = winsaveview()
  silent %!gofmt
  if v:shell_error &gt; 0
    cexpr getline(1, &#39;$&#39;)-&gt;map({ idx, val -&gt; val-&gt;substitute(&#39;&lt;standard input&gt;&#39;, expand(&#39;%&#39;), &#39;&#39;) })
    silent undo
    cwindow
  endif
  call winrestview(saved_view)
endfunction

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

答案2

得分: 0

你可以按照以下方式更新脚本:

function! GoFmt()
  system('gofmt -e -w ' . expand('%'))
  edit!
endfunction

Gofmt 是一个自动格式化 Go 代码的工具。Gofmt 同时也是一个代码格式化工具和代码检查工具。

文档中可以了解到:

> -e 打印所有(包括错误)错误。

> -w 不将重新格式化的源代码打印到标准输出。
如果文件的格式与 gofmt 不同,将其覆盖为 gofmt 的版本。如果在覆盖过程中发生错误,将从自动备份中恢复原始文件。

如果没有错误,上述脚本将更新文件。


如果适用,可以使用 gofmt 报告的错误创建一个快速修复列表。

function! GoFmt()
  cexpr system('gofmt -e -w ' . expand('%'))
  edit!
endfunction

cexpr 用于创建快速修复列表,可以通过 vim 帮助文档 :help :cexpr 了解更多信息,system 用于运行系统命令,可以通过 vim 帮助文档 :help :system 了解更多信息,expand 将通配符和特殊关键字扩展为字符串,可以通过 vim 帮助文档 :help :expand 了解更多信息,% 是一个特殊关键字,指代当前文件。

edit! 用于重新加载文件内容。

然后,你可以使用快速修复列表快速跳转到错误位置,可以从这篇博客文章中了解更多信息。

英文:

You can update the script as below:

function! GoFmt()
  system(&#39;gofmt -e -w &#39; . expand(&#39;%&#39;))
  edit!
endfunction

Gofmt is a tool that automatically formats Go source code. Gofmt is both a code formatter and a linter.

From doc

> -e Print all (including spurious) errors.

> -w Do not print reformatted sources to standard output.
If a file's formatting is different from gofmt's, overwrite it
with gofmt's version. If an error occurred during overwriting,
the original file is restored from an automatic backup.

The above script will update the file if there are no errors


If applicable, create a quickfix list with the errors reported by gofmt.

function! GoFmt()
  cexpr system(&#39;gofmt -e -w &#39; . expand(&#39;%&#39;))
  edit!
endfunction

cexpr to create a quickfix list read more about it using vim help doc help :cexpr, system to run system command read more about it using vim help doc help :system, expand Expand wildcards and special keywords into string read more about it using vim help doc help :expand, % is a special keyword which refer to current file.

edit! is used to reload file contents.

Then you can quickly jump over the errors using quickfix list you can learn more about it from this blog post.

huangapple
  • 本文由 发表于 2022年5月6日 09:38:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/72135274.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定