Tutorial for vim-go - 转载

https://www.5axxw.com/wiki/content/wo6s81#alternate-files

( 如需查看英文版本,请 点击这里 )

存档项目。无需维护。

此项目不再维护,已存档。如果需要的话,你可以自由选择,做出自己的改变。更多细节请阅读我的博客文章:从我的项目中无限期休假

感谢大家的宝贵反馈和贡献。

vim-go教程。一个关于如何安装和使用vim-go的简单教程。

目录

  1. Quick Setup
  2. Hello World
  3. Build it
  4. Cover it
  5. Edit it
  1. Understand it

Quick Setup

我们将使用vim-plug来安装vim-go。请随意使用其他插件管理器。我们将创建一个最小的~/.vimrc,并在继续的过程中添加它。

首先获取并安装vim-plugvim-go

1
2
3
curl -fLo ~/.vim/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
git clone https://github.com/fatih/vim-go.git ~/.vim/plugged/vim-go

使用以下内容创建~/.vimrc

1
2
3
4
call plug#begin()
Plug 'fatih/vim-go', { 'do': ':GoInstallBinaries' }
call plug#end()

或者打开Vim并执行:GoInstallBinaries。这是一个vim-go命令,它为您安装所有vim-go依赖项。它不下载预编译的二进制文件,而是在后台调用go get,因此二进制文件都在主机中编译(这既安全又简化了安装过程,因为我们不需要为多个平台提供二进制文件)。如果您已经有一些依赖项(例如gurugoimports),请调用:GoUpdateBinaries来更新二进制文件。

在本教程中,我们所有的例子都在GOPATH/src/github.com/fatih/vim-go-tutorial/下。请确保您在该文件夹中。这将使您更容易地遵循教程。如果已经有GOPATH设置,请执行:

1
2
go get github.com/fatih/vim-go-tutorial

或者根据需要创建文件夹。

Hello World!

从终端打开main.go文件:

1
2
vim main.go

这是一个将vim-go打印到stdout的非常基本的文件。

Run it

您可以使用:GoRun %轻松运行该文件。在引擎盖下,它为当前文件调用go run。您应该看到它打印了vim-go

对于使用:GoRun运行的整个包。

Build it

vim-go替换为Hello Gophercon。让我们编译这个文件,而不是运行它。我们有:GoBuild。如果您调用它,您应该看到以下消息:

1
2
vim-go: [build] SUCCESS

在引擎盖下面它叫go build,但它更聪明一点。它做了一些不同的事情:

  • 不创建二进制文件;您可以多次调用:GoBuild,而不会污染您的工作区。
  • 它会自动cds到源包的目录中
  • 它分析任何错误并在快速修复列表中显示它们
  • 它自动检测GOPATH并在需要时修改它(检测诸如gbGodeps、etc..)等项目
  • 如果在Vim8.0.xxx或NeoVim中使用,则运行异步

Fix it

让我们通过添加两个编译错误来介绍两个错误:

1
2
3
4
5
6
7
var b = foo()

func main() {
fmt.Println("Hello GopherCon")
a
}

保存文件并再次调用:GoBuild

这次将打开quickfix视图。要在错误之间跳转,可以使用:cnext:cprevious。让我们修复第一个错误,保存文件并再次调用:GoBuild。您将看到quickfix列表更新了一个错误。同时删除第二个错误,保存文件并再次调用:GoBuild。现在,因为不再有错误,vim-go会自动关闭quickfix窗口。

让我们稍微改进一下。Vim有一个名为autowrite的设置,如果您调用:make,它会自动写入文件的内容。vim-go也使用此设置。打开.vimrc并添加以下内容:

1
2
set autowrite

现在,当您调用:GoBuild时,您不必再保存文件了。如果我们重新引入这两个错误并调用:GoBuild,我们现在可以通过只调用:GoBuild来更快地迭代。

:GoBuild跳转到遇到的第一个错误。如果您不想跳转附加!(bang)符号::GoBuild!

在所有go命令中,例如:GoRun:GoInstall:GoTest、etc..,每当出现错误时,quickfix窗口总是会弹出。

vimrc improvements

  • 您可以添加一些快捷方式,以便在快速修复列表中的错误之间切换:
1
2
3
4
map <C-n> :cnext<CR>
map <C-m> :cprevious<CR>
nnoremap <leader>a :cclose<CR>

  • 我还使用这些快捷方式来构建和运行一个带有<leader>b<leader>r的Go程序:
1
2
3
autocmd FileType go nmap <leader>b  <Plug>(go-build)
autocmd FileType go nmap <leader>r <Plug>(go-run)

  • Vim中有两种类型的错误列表。一个叫做location list,另一个叫quickfix。不幸的是,每个列表的命令是不同的。所以:cnext只适用于quickfix列表,而location lists则必须使用:lnextvim-go中的一些命令打开一个位置列表,因为位置列表与一个窗口相关联,每个窗口都可以有一个单独的列表。这意味着您可以有多个窗口和多个位置列表,一个用于Build,一个用于Check,一个用于Tests,等等。。

但是有些人喜欢只使用quickfix。如果将以下内容添加到vimrc中,则所有列表的类型都为quickfix

1
2
let g:go_list_type = "quickfix"

Test it

让我们编写一个简单的函数并对该函数进行测试。添加以下内容:

1
2
3
4
func Bar() string {
return "bar"
}

打开一个名为main_test.go的新文件(无论您如何打开它,从Vim内部,一个单独的Vim会话,等等)。。这取决于你)。让我们使用当前缓冲区并通过:edit main_test.go从Vim打开它。

当你打开新文件时,你会注意到一些东西。文件将自动添加包声明:

1
2
package main

这是由vim-go自动完成的。它检测到文件在一个有效的包中,因此基于包名创建了一个文件(在我们的例子中,包名是main)。如果没有文件,vim-go会自动用一个简单的主包填充内容。

使用以下代码更新测试文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
"testing"
)

func TestBar(t *testing.T) {
result := Bar()
if result != "bar" {
t.Errorf("expecting bar, got %s", result)
}
}

打电话:GoTest。您将看到以下消息:

1
2
vim-go: [test] PASS

:GoTest在引擎盖下调用go test。它具有与:GoBuild相同的改进。如果有任何测试错误,将再次打开快速修复列表,您可以轻松地跳转到该列表。

另一个小的改进是您不必打开测试文件本身。你自己试试:打开main.go然后打:GoTest。您将看到测试也将为您运行。

:GoTest默认10秒后超时。这很有用,因为Vim在默认情况下不是异步的。您可以使用let g:go_test_timeout = '10s'更改超时值

我们还有两个命令,可以方便地处理测试文件。第一个是:GoTestFunc。这只测试光标下的函数。让我们将测试文件(main_test.go)的内容更改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"testing"
)

func TestFoo(t *testing.T) {
t.Error("intentional error 1")
}

func TestBar(t *testing.T) {
result := Bar()
if result != "bar" {
t.Errorf("expecting bar, got %s", result)
}
}

func TestQuz(t *testing.T) {
t.Error("intentional error 2")
}

现在,当我们调用:GoTest时,一个快速修复窗口将打开,并显示两个错误。但是,如果进入TestBar函数并调用:GoTestFunc,您将看到我们的测试通过了!如果您有很多需要时间的测试,而您只想运行某些测试,那么这非常有用。

另一个test-related命令是:GoTestCompile。测试不仅需要成功地通过,而且还必须毫无问题地进行编译。:GoTestCompile编译测试文件,就像:GoBuild一样,并在出现错误时打开快速修复程序。但这并不能运行测试。这是非常有用的,如果你有一个大的测试,你要编辑很多。调用:GoTestCompile在当前测试文件中,您应该看到以下内容:

1
2
vim-go: [test] SUCCESS

vimrc improvements

  • :GoBuild一样,我们可以添加一个映射,用组合键轻松调用:GoTest。在.vimrc中添加以下内容:
1
2
autocmd FileType go nmap <leader>t  <Plug>(go-test)

现在您可以通过<leader>t轻松测试文件

  • 让我们简化building Go文件。首先,删除前面添加的以下映射:
1
2
autocmd FileType go nmap <leader>b  <Plug>(go-build)

我们将添加一个改进的映射。为了使任何Go文件无缝,我们可以创建一个简单的Vim函数来检查Go文件的类型,并执行:GoBuild:GoTestCompile。下面是可以添加到.vimrc中的帮助函数:

1
2
3
4
5
6
7
8
9
10
11
12
" run :GoBuild or :GoTestCompile based on the go file
function! s:build_go_files()
let l:file = expand('%')
if l:file =~# '^\f\+_test\.go$'
call go#test#Test(0, 1)
elseif l:file =~# '^\f\+\.go$'
call go#cmd#Build(0)
endif
endfunction

autocmd FileType go nmap <leader>b :<C-u>call <SID>build_go_files()<CR>

现在只要你点击<leader>b,它就会生成你的Go文件,或者无缝地编译你的测试文件。

  • 默认情况下,leader快捷方式被定义为:\我已经将我的leader映射到,,因为我发现下面的设置更有用(将它放在.vimrc的开头):
1
2
let mapleader = ","

因此,通过这个设置,我们可以轻松地用,b构建任何测试和非测试文件。

Cover it

让我们深入到测试的世界。测试真的很重要。Go有一种非常好的方式来显示源代码的覆盖率。vim-go可以很容易地看到代码覆盖率,而不必以非常优雅的方式离开Vim。

让我们首先将main_test.go文件改回:

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
"testing"
)

func TestBar(t *testing.T) {
result := Bar()
if result != "bar" {
t.Errorf("expecting bar, got %s", result)
}
}

main.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

func Bar() string {
return "bar"
}

func Foo() string {
return "foo"
}

func Qux(v string) string {
if v == "foo" {
return Foo()
}

if v == "bar" {
return Bar()
}

return "INVALID"
}

现在让我们打电话给:GoCoverage。在引擎盖下面,这叫做go test -coverprofile tempfile。它解析概要文件中的行,然后动态更改源代码的语法以反映覆盖率。如您所见,因为我们只对Bar()函数进行了测试,这是唯一一个绿色的函数。

要清除语法突出显示,可以调用:GoCoverageClear。让我们添加一个测试用例,看看覆盖率是如何变化的。将以下内容添加到main_test.go

1
2
3
4
5
6
7
8
9
10
11
12
func TestQuz(t *testing.T) {
result := Qux("bar")
if result != "bar" {
t.Errorf("expecting bar, got %s", result)
}

result = Qux("qux")
if result != "INVALID" {
t.Errorf("expecting INVALID, got %s", result)
}
}

如果我们再次调用:GoCoverage,您将看到Quz函数现在也经过了测试,并且覆盖范围更广。再次调用:GoCoverageClear清除语法高亮显示。

因为调用:GoCoverage:GoCoverageClear经常一起使用,所以有另一个命令可以方便地调用和清除结果。您也可以使用:GoCoverageToggle。它充当一个开关并显示覆盖范围,当再次调用时,它将清除覆盖范围。如何使用它们取决于您的工作流程。

最后,如果您不喜欢vim-go’s内部视图,也可以调用:GoCoverageBrowser。它使用go tool cover创建一个HTML页面,然后在默认浏览器中打开它。有些人更喜欢这个。

使用:GoCoverageXXX命令不会创建任何类型的临时文件,也不会污染您的工作流。所以你不必每次都处理删除不需要的文件。

vimrc improvements

.vimrc中添加以下内容:

1
2
autocmd FileType go nmap <Leader>c <Plug>(go-coverage-toggle)

有了这个,你可以很容易地用<leader>c调用:GoCoverageToggle

Edit it

Imports

让我们从一个main.go文件开始:

1
2
3
4
5
6
7
8
package main

import "fmt"

func main() {
fmt.Println("gopher" )
}

让我们从我们已经知道的事情开始吧。如果我们保存文件,你会看到它会自动格式化。默认情况下,它是启用的,但是如果需要,可以使用let g:go_fmt_autosave = 0禁用它(不确定为什么要禁用:)。我们还可以选择提供:GoFmt命令,它在引擎盖下运行gofmt

让我们用大写字母打印"gopher"字符串。为此,我们将使用strings包。要更改定义:

1
2
fmt.Println(strings.ToUpper("gopher"))

当你建立它时,你会得到一个错误,当然:

1
2
main.go|8| undefined: strings in strings.ToUpper

您将看到我们得到一个错误,因为strings包没有导入。vim-go有两个命令可以方便地操作导入声明。

我们可以很容易地去编辑文件,但是我们将使用Vim命令:GoImport。此命令将给定的包添加到导入路径。{via::GoImport strings}运行。您将看到正在添加strings包。这个命令最棒的地方是它还支持完成。所以你只需输入:GoImport s然后点击tab。

我们还需要:GoImportAs:GoDrop来编辑导入路径。:GoImportAs:GoImport相同,但它允许更改包名。例如:GoImportAs str strings,将使用包名str.导入strings

最后,:GoDrop可以很容易地从导入声明中删除任何导入路径。:GoDrop strings将从导入声明中删除它。

当然,操纵导入路径是2010年的事了。我们有更好的工具来处理这个案子。如果你还没听说过,那就叫goimportsgoimportsgofmt的替代品。你有两种使用方法。第一种(也是推荐的)方法是告诉vim-go在保存文件时使用它:

1
2
let g:go_fmt_command = "goimports"

现在每当您保存文件时,goimports将自动格式化并重写导入声明。有些人不喜欢goimports,因为它在非常大的代码基上可能很慢。在本例中,我们还有:GoImports命令(注意末尾的s)。这样,您就可以显式地调用goimports

Text objects

让我们展示更多编辑技巧。我们可以使用两个文本对象来更改函数。它们是ifafif表示内部函数,它允许您选择函数外壳的内容。将main.go文件更改为:

1
2
3
4
5
6
7
8
9
10
11
12
package main

import "fmt"

func main() {
fmt.Println(1)
fmt.Println(2)
fmt.Println(3)
fmt.Println(4)
fmt.Println(5)
}

将光标放在func关键字上,现在在normal模式下执行以下操作,然后看看会发生什么:

1
2
dif

您将看到函数体被移除。因为我们使用了d运算符。使用u撤消更改。最棒的是,光标可以是从func关键字开始到右大括号}的任意位置。它使用引擎盖下的刀具运动。我为vim-go显式地编写了motion来支持这样的特性。它具有很强的感知能力,因此它的性能非常好。你可能会问什么?将main.go改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

func Bar() string {
fmt.Println("calling bar")

foo := func() string {
return "foo"
}

return foo()
}

以前我们使用regexp-based文本对象,这会导致问题。例如,在这个例子中,将光标放在匿名函数func关键字上,并以normal模式执行dif。您将看到只有匿名函数的主体被删除。

到目前为止,我们只使用了d运算符(delete)。但这取决于你。例如,您可以通过vif选择它,或者使用yif来拖动(复制)。

我们还有af,意思是a function。此文本对象包括整个函数声明。将main.go更改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import "fmt"

// bar returns a the string "foo" even though it's named as "bar". It's an
// example to be used with vim-go's tutorial to show the 'if' and 'af' text
// objects.
func bar() string {
fmt.Println("calling bar")

foo := func() string {
return "foo"
}

return foo()
}

所以这是一件伟大的事情。由于motion,我们对每个语法节点都有充分的了解。将光标放在func关键字的上方或下方或上方的任何位置(无所谓)。如果现在执行vaf,您将看到函数声明和doc注释都被选中了!例如,您可以用daf删除整个函数,您将看到注释也消失了。继续将光标放在注释的顶部,执行vif,然后执行vaf。您将看到它选择了函数体,即使光标在函数之外,或者它也选择了函数注释。

这真的很强大,这一切都要归功于我们从let g:go_textobj_include_function_doc = 1motion学到的知识。如果不希望注释成为函数声明的一部分,可以使用以下方法轻松禁用它:

1
2
let g:go_textobj_include_function_doc = 0

如果您有兴趣了解关于motion的更多信息,请查看我写的博客文章:将Go类型视为vim中的对象

(可选问题:不看go/ast包,doc注释是否是函数声明的一部分?)

结构拆分和联接

有一个很好的插件可以让你拆分或连接Go结构。它实际上不是一个Go插件,但它支持Go结构。要启用它,请将plug定义之间的plugin指令添加到vimrc中,然后在vim编辑器中执行:source ~/.vimrc并运行:PlugInstall。例子:

1
2
3
4
5
call plug#begin()
Plug 'fatih/vim-go'
Plug 'AndrewRadev/splitjoin.vim'
call plug#end()

安装插件后,将main.go文件更改为:

1
2
3
4
5
6
7
8
9
10
11
12
package main

type Foo struct {
Name string
Ports []int
Enabled bool
}

func main() {
foo := Foo{Name: "gopher", Ports: []int{80, 443}, Enabled: true}
}

将光标放在与结构表达式相同的行上。现在输入gS。这将split将结构表达式分成多行。你甚至可以逆转它。如果光标仍在foo变量上,请在normal模式下执行gJ。您将看到字段定义都已联接。

这不使用任何AST-aware工具,因此例如,如果您在字段顶部键入gJ,您将看到只有两个字段被联接。

Snippets

Vim-go支持两个流行的snippet插件。Ultisnips和neosnippet。默认情况下,如果您安装了Ultisnips,它就可以工作了。让我们先安装ultisnips。在vimrc中的plug指令之间添加它,然后在vim编辑器中执行:source ~/.vimrc,然后运行:PlugInstall。例子:

1
2
3
4
5
call plug#begin()
Plug 'fatih/vim-go'
Plug 'SirVer/ultisnips'
call plug#end()

有许多有用的片段。要查看完整列表,请查看我们当前的片段:https://github.com/fatih/vim-go/blob/master/gosnippets/UltiSnips/go.snippets

UltiSnips和YouCompleteMe可能在[tab]按钮上发生冲突

让我展示一下我用得最多的一些片段。将main.go内容更改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import "encoding/json"

type foo struct {
Message string
Ports []int
ServerName string
}

func newFoo() (*foo, error) {
return &foo{
Message: "foo loves bar",
Ports: []int{80},
ServerName: "Foo",
}, nil
}

func main() {
res, err := newFoo()

out, err := json.Marshal(res)
}

让我们把光标放在newFoo()表达式之后。如果错误是non-nil,让我们在这里惊慌失措。在insert模式下输入errp,然后点击tab。您将看到它将被展开并将光标放在“panic()`”函数中:

1
2
3
4
5
6
if err != nil {
panic( )
^
cursor position
}

err填充恐慌,然后转到json.Marshal语句。做同样的事情。

现在让我们打印变量out。由于变量打印非常流行,因此我们有几个片段:

1
2
3
4
5
fn -> fmt.Println()
ff -> fmt.Printf()
ln -> log.Println()
lf -> log.Printf()

这里fflf是特殊的。它们还动态地将变量名复制到格式字符串中。你自己试试吧。将光标移到main函数的末尾,输入ff,然后点击tab。展开代码段后,可以开始键入。输入string(out),您将看到格式字符串和变量参数都将用您键入的相同字符串填充。

这对于快速打印用于调试的变量非常方便。使用:GoRun运行文件,您将看到以下输出:

1
2
string(out) = {"Message":"foo loves bar","Ports":[80],"ServerName":"Foo"}

伟大的。现在让我展示最后一个我认为非常有用的片段。正如您在输出中看到的,字段MessagePorts以大写字符开头。为了解决这个问题,我们可以在struct字段中添加一个json标记。vim-go使添加字段标记变得非常容易。将光标移到字段中Message字符串行的末尾:

1
2
3
4
5
type foo struct {
Message string .
^ put your cursor here
}

insert模式下,输入json并点击tab。您将看到它将自动扩展为有效的字段标记。字段名将自动转换为小写,并放在那里。现在应该可以看到以下内容:

1
2
3
4
type foo struct {
Message string `json:"message"`
}

真是太神奇了。但我们可以做得更好!继续为ServerName字段创建一个代码段扩展。您将看到它被转换为server_name。太棒了对吧?

1
2
3
4
5
6
type foo struct {
Message string `json:"message"`
Ports []int
ServerName string `json:"server_name"`
}

vimrc improvements

  • 别忘了把gofmt改成goimports
1
2
let g:go_fmt_command = "goimports"

  • 当您保存文件时,gofmt在解析文件期间显示任何错误。如果有任何解析错误,它会在快速修复列表中显示它们。这是默认启用的。有些人不喜欢。要禁用它,请添加:
1
2
let g:go_fmt_fail_silently = 1

  • 您可以在转换时更改应应用的大小写。默认情况下,vim-go使用snake_case。但如果您愿意,也可以使用camelCase。例如,如果要将默认值更改为大小写,请使用以下设置:
1
2
let g:go_addtags_transform = "camelcase"

Beautify it

默认情况下,我们只启用了有限的语法高亮显示。主要有两个原因。首先,人们不喜欢太多的颜色,因为它会让人分心。第二个原因是它对Vim的性能影响很大。我们需要显式地启用它。首先将以下设置添加到.vimrc

1
2
let g:go_highlight_types = 1

这突出显示了barfoo

1
2
3
4
5
6
type foo struct{
quz string
}

type bar interface{}

添加以下内容:

1
2
let g:go_highlight_fields = 1

将突出显示下面的quz

1
2
3
4
5
6
7
type foo struct{
quz string
}

f := foo{quz:"QUZ"}
f.quz # quz here will be highlighted

如果我们添加以下内容:

1
2
let g:go_highlight_functions = 1

我们现在还在声明中突出显示函数和方法名。Foomain现在将突出显示,但是Println不是因为这是一个调用:

1
2
3
4
5
6
func (t *T) Foo() {}

func main() {
fmt.Println("vim-go")
}

如果还想突出显示函数和方法调用,请添加以下内容:

1
2
let g:go_highlight_function_calls = 1

现在,Println也将突出显示:

1
2
3
4
5
6
func (t *T) Foo() {}

func main() {
fmt.Println("vim-go")
}

如果添加let g:go_highlight_operators = 1,它将突出显示以下运算符,例如:

1
2
3
4
5
6
- + % < > ! & | ^ * =
-= += %= <= >= != &= |= ^= *= ==
<< >> &^
<<= >>= &^=
:= && || <- ++ --

如果添加let g:go_highlight_extra_types = 1,以下额外类型也将突出显示:

1
2
3
4
5
bytes.(Buffer)
io.(Reader|ReadSeeker|ReadWriter|ReadCloser|ReadWriteCloser|Writer|WriteCloser|Seeker)
reflect.(Kind|Type|Value)
unsafe.Pointer

让我们继续了解更多有用的亮点。构建标签呢?不查看go/build文档就不容易实现它。让我们首先添加以下内容:let g:go_highlight_build_constraints = 1并将main.go文件更改为:

1
2
3
// build linux
package main

你会看到它是灰色的,所以它是无效的。将+添加到build单词并再次保存:

1
2
3
// +build linux
package main

你知道为什么吗?如果您阅读go/build包,您将看到以下内容隐藏在文档中:

… 前面只有空行和其他行注释。

让我们再次更改内容并将其保存到:

1
2
3
4
// +build linux

package main

您将看到它以有效的方式自动高亮显示它。真的很棒。如果您将linux更改为某个内容,您将看到它还检查有效的官方标记(例如darwinraceignore等)

另一个类似的特性是突出显示Go指令//go:generate。如果将let g:go_highlight_generate_tags = 1放入vimrc,它将突出显示使用go generate命令处理的有效指令。

我们有更多的亮点设置,这些只是一个偷窥。如需更多信息,请通过:help go-settings查看设置

vimrc improvements

  • 有些人不喜欢标签的显示方式。默认情况下,Vim为单个选项卡显示8个空格。然而,如何在Vim中表示取决于我们。以下内容将更改为将单个选项卡显示为4个空格:
1
2
autocmd BufNewFile,BufRead *.go setlocal noexpandtab tabstop=4 shiftwidth=4

此设置不会将选项卡展开为空格。它将用4空格显示一个选项卡。它将使用4空格来表示单个缩进。

  • 很多人要我的配色方案。我用的是稍加修改的molokai。要启用它,请在Plug定义之间添加Plug指令:
1
2
3
4
5
call plug#begin()
Plug 'fatih/vim-go'
Plug 'fatih/molokai'
call plug#end()

同时添加以下内容,以启用原始配色方案和256色版本的molokai:

1
2
3
4
let g:rehash256 = 1
let g:molokai_original = 1
colorscheme molokai

然后重启Vim并调用:source ~/.vimrc,然后调用:PlugInstall。这将拉插件并为您安装。安装插件后,您需要重新启动Vim。

Check it

从前面的示例中,您看到我们有许多命令,当出现问题时,这些命令将显示quickfix窗口。例如,:GoBuild显示编译输出中的错误(如果有)。或者例如:GoFmt显示当前文件格式化时的解析错误。

我们有许多其他命令,允许我们调用然后收集错误、警告或建议。

例如:GoLint。在幕后,它调用golint,这是一个建议更改以使Go代码更具惯用性的命令。还有一个:GoVet,它在引擎盖下调用go vet。有许多其他工具可以检查某些东西。为了让它更简单,有人决定创建一个调用所有这些跳棋的工具。这个工具叫做gometalinter。vim-go通过命令:GoMetaLinter支持它。那么它有什么作用呢?

如果您只是调用:GoMetaLinter来获取给定的Go源代码。默认情况下,它将同时运行go vetgolinterrcheckgometalinter收集所有输出并将其规范化为通用格式。因此,如果您调用:GoMetaLinter,vim-go将在快速修复列表中显示所有这些跳棋的结果。然后,您可以轻松地在lint、vet和errcheck结果之间切换。此默认设置如下:

1
2
let g:go_metalinter_enabled = ['vet', 'golint', 'errcheck']

还有许多其他工具,您可以轻松地自定义此列表。如果您调用:GoMetaLinter,它将自动使用上面的列表。

因为:GoMetaLinter通常很快,vim-go也可以在每次保存文件时调用它(就像:GoFmt)。要启用它,您需要将以下内容添加到.vimrc:

1
2
let g:go_metalinter_autosave = 1

最棒的是,autosave的跳棋与:GoMetaLinter不同。这很好,因为您可以自定义它,所以在保存文件时只调用快速检查程序,但如果您调用:GoMetaLinter,则调用其他检查程序。下面的设置允许您为autosave特性定制方格。

1
2
let g:go_metalinter_autosave_enabled = ['vet', 'golint']

如您所见,默认情况下启用vetgolint。最后,为了防止:GoMetaLinter运行太长时间,我们有一个设置,在给定的超时之后取消它。默认为5 seconds,但可以通过以下设置进行更改:

1
2
let g:go_metalinter_deadline = "5s"

Navigate it

到目前为止,我们只跳过了main.gomain_test.go两个文件。如果在同一个目录中只有两个文件,那么切换非常容易。但是如果项目随着时间的推移变得越来越大呢?或者如果文件本身太大,以至于您很难导航它呢?

Alternate files

vim-go有几种改进导航的方法。首先让我展示一下如何在Go源代码和它的测试文件之间快速跳转。

假设您有一个foo.go及其等价的测试文件foo_test.go。如果您有前面示例中的main.go及其测试文件,您也可以打开它。打开后,只需执行以下Vim命令:

1
2
:GoAlternate

您将看到您立即切换到main_test.go。如果您再次执行它,它将切换到main.go:GoAlternate起到切换的作用,如果您有一个包含许多测试文件的包,则非常有用。这个想法非常类似于plugina.vim命令名。这个插件在.c.h文件之间跳跃。在我们的例子中,:GoAlternate用于在测试和non-test文件之间切换。

转到定义

最常用的特性之一是go to definition。从一开始,vim-go有一个:GoDef命令,该命令跳转到任何标识符的声明。让我们首先创建一个main.go文件来显示它的实际操作。使用以下内容创建它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

type T struct {
Foo string
}

func main() {
t := T{
Foo: "foo",
}

fmt.Printf("t = %+v\n", t)
}

现在我们有几种跳到声明的方法。例如,如果您将光标放在T表达式的顶部,紧跟在main函数之后并调用:GoDef,它将跳转到类型声明。

如果您将光标放在t变量声明的顶部,紧跟在main函数之后并调用:GoDef,您将看到不会发生任何事情。因为没有地方可去,但是如果您向下滚动几行并将光标放在fmt.Printf()中使用的t变量并调用:GoDef,您将看到它跳转到变量声明。

:GoDef不仅适用于本地范围,而且在全局范围内(跨GOPATH)工作。例如,如果您将光标放在Printf()函数的顶部并调用:GoDef,它将直接跳转到fmt包。因为这是如此频繁地使用,vim-go也覆盖了内置的Vim快捷方式gdctrl-]。因此,您可以轻松地使用gdctrl-]来代替:GoDef

一旦我们跳转到一个声明,我们可能还想回到以前的位置。默认情况下,Vim快捷键ctrl-o会跳转到上一个光标位置。当它运行得很好时,它会很好地工作,但是如果您在Go声明之间导航,就不够好了。例如,如果您跳转到:GoDef的文件,然后向下滚动到底部,然后可能到顶部,ctrl-o也会记住这些位置。因此,如果你想在调用:GoDef时跳回上一个位置,你必须多次点击ctrl-o。这真的很烦人。

不过,我们不需要使用这个快捷方式,因为vim-go为您提供了更好的实现。有一个命令:GoDefPop正是这样做的。vim-go为使用:GoDef访问的所有位置保留一个内部堆栈列表。这意味着您可以通过:GoDefPop轻松地跳回原来的位置,即使您在文件中向下/向上滚动也可以。因为这也被使用了很多次,所以我们有一个快捷方式ctrl-t,它在引擎盖下调用:GoDefPop。所以回顾一下:

  • 使用ctrl-]gd跳转到本地或全局定义
  • 使用ctrl-t跳回上一个位置

让我们继续问另一个问题,假设你跳得太远,只想回到你开始的地方?如前所述,vim-go保存通过:GoDef调用的所有位置的历史记录。有一个命令显示了所有这些,它名为:GoDefStack。如果您调用它,您将看到一个带有旧位置列表的自定义窗口。只需导航到所需的位置,然后按enter键。最后随时调用:GoDefStackClear清除堆栈列表。

在函数之间移动

从前面的例子中,我们看到:GoDef如果您知道您想跳转到哪里,那么:GoDef是很好的。但是如果你不知道你的下一个目的地呢?或者你只知道一个函数的名字?

在我们的Edit it部分中,我提到了一个名为motion的工具,它是一个专门为vim-go定制的工具。motion还有其他功能。motion解析Go包,因此对所有声明都有很好的理解。我们可以利用这个特性在声明之间跳转。有两个命令,在安装某个插件之前是不可用的。命令包括:

1
2
3
:GoDecls
:GoDeclsDir

首先,让我们通过安装必要的插件来启用这两个命令。这个插件叫做ctrlp。Long-timeVim用户已经安装了它。要安装它,请在plug指令之间添加以下行,然后在vim编辑器中执行:source ~/.vimrc,并调用:PlugInstall来安装它:

1
2
Plug 'ctrlpvim/ctrlp.vim'

安装后,请使用以下main.go内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import "fmt"

type T struct {
Foo string
}

func main() {
t := T{
Foo: "foo",
}

fmt.Printf("t = %+v\n", t)
}

func Bar() string {
return "bar"
}

func BarFoo() string {
return "bar_foo"
}

以及一个main_test.go文件,其中包含以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import (
"testing"
)

type files interface{}

func TestBar(t *testing.T) {
result := Bar()
if result != "bar" {
t.Errorf("expecting bar, got %s", result)
}
}

func TestQuz(t *testing.T) {
result := Qux("bar")
if result != "bar" {
t.Errorf("expecting bar, got %s", result)
}

result = Qux("qux")
if result != "INVALID" {
t.Errorf("expecting INVALID, got %s", result)
}
}

打开main.go并调用:GoDecls。您将看到:GoDecls为您显示了所有类型和函数声明。如果您输入ma,您将看到ctrlp为您过滤列表。如果你点击enter,它将自动跳转到它。模糊搜索功能与motion的AST功能相结合,给我们带来了一个非常简单但功能强大的特性。

例如,调用:GoDecls并写入foo。您将看到它将为您过滤BarFoo。Go解析器速度非常快,可以很好地处理包含数百个声明的大型文件。

有时仅仅在当前文件中搜索是不够的。一个Go包可以有多个文件(例如测试)。类型声明可以在一个文件中,而特定于一组功能的某些函数可以在另一个文件中。这就是:GoDeclsDir有用的地方。它解析给定文件的整个目录,并列出给定目录(但不是子目录)中文件的所有声明。

打电话:GoDeclsDir。这次您将看到它还包括来自main_test.go文件的声明。如果您输入Bar,您将看到BarTestBar函数。如果您只想获得所有类型和函数声明的概述,并跳转到它们,这真是太棒了。

让我们继续问一个问题。如果你只想转到下一个或上一个函数呢?如果当前函数体很长,则很可能看不到函数名。或者在当前函数和其他函数之间还有其他声明。

Vim已经有了像w表示单词或b表示向后单词的运动操作符。但是如果我们可以加上行动计划呢?例如函数声明?

vim-go提供(重写)两个运动对象在函数之间移动。这些是:

1
2
3
]] -> jump to next function
[[ -> jump to previous function

默认情况下,Vim有这些快捷方式。但这些都适用于C源代码和大括号之间的跳转。我们可以做得更好。就像我们前面的例子一样,motion是在引擎盖下用于这个操作的

打开main.go并移动到文件的顶部。在normal模式下,输入]],然后看看会发生什么。您将看到您跳转到main()函数。另一个]]将跳转到Bar(),如果你点击[[,它将跳回main()函数。

]][[也接受counts。例如,如果您再次移动到顶部并点击3]],您将看到它将跳转到源文件中的第三个函数。接下来,因为这些都是有效的运动,你也可以对它应用操作符!

如果您将文件移到顶部并点击d]],您将看到它在下一个函数之前删除了任何内容。例如,一个有用的用法是输入v]],然后再次点击]]来选择下一个函数,直到完成选择为止。

.vimrc improvements

  • 我们可以改进它来控制它如何打开备用文件。在.vimrc中添加以下内容:
1
2
3
4
5
autocmd Filetype go command! -bang A call go#alternate#Switch(<bang>0, 'edit')
autocmd Filetype go command! -bang AV call go#alternate#Switch(<bang>0, 'vsplit')
autocmd Filetype go command! -bang AS call go#alternate#Switch(<bang>0, 'split')
autocmd Filetype go command! -bang AT call go#alternate#Switch(<bang>0, 'tabe')

这将添加新的命令,称为:A:AV:AS:AT。这里:A的工作方式与:GoAlternate相同,它用备用文件替换当前缓冲区。:AV将使用备用文件打开一个新的垂直拆分。:AS将在新的拆分视图中打开备用文件,在新选项卡中打开:AT。这些命令的效率非常高,这取决于您如何使用它们,所以我认为拥有它们很有用。

  • “go to definition”命令系列非常强大,但使用起来却很简单。默认情况下,它使用工具guru(以前是oracle)。guru有很好的可预测性记录。它适用于点导入、供应商化导入和许多其他non-obvious标识符。但有时对于某些查询来说,它非常慢。以前vim-go使用的是godef,它在解决查询方面非常快。在最新版本中,可以很容易地使用或切换:GoDef的底层工具。要将其更改回godef,请使用以下设置:
1
2
let g:go_def_mode = 'godef'

  • 当前默认情况下,:GoDecls:GoDeclsDir显示类型和函数声明。这可以使用g:go_decls_includes设置进行自定义。默认情况下,它的形式是:
1
2
let g:go_decls_includes = "func,type"

如果只想显示函数声明,请将其更改为:

1
2
let g:go_decls_includes = "func"

Understand it

编写/编辑/更改代码通常只有在我们首先了解代码在做什么时才能做。vim-go有几种方法可以使您更容易地理解代码的全部内容。

Documentation lookup

让我们从基础知识开始。Go文档非常well-written,并且高度集成到goast中。如果只编写一些注释,解析器可以轻松地解析它并与AST中的任何节点关联。所以这意味着我们可以很容易地以相反的顺序找到文档。如果您有一个AST节点,那么您可以轻松地从该节点读取它!

我们有一个名为:GoDoc的命令,它显示与光标下标识符相关的任何文档。让我们将main.go的内容更改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

func main() {
fmt.Println("vim-go")
fmt.Println(sayHi())
fmt.Println(sayYoo())
}

// sayHi() returns the string "hi"
func sayHi() string {
return "hi"
}

func sayYoo() string {
return "yoo"
}

将光标放在Println函数的顶部,紧跟main函数,然后调用:GoDoc。您将看到它vim-go自动打开一个草稿窗口,为您显示文档:

1
2
3
4
5
6
7
8
9
import "fmt"

func Println(a ...interface{}) (n int, err error)

Println formats using the default formats for its operands and writes to
standard output. Spaces are always added between operands and a newline is
appended. It returns the number of bytes written and any write error
encountered.

它显示导入路径、函数签名,最后是标识符的doc注释。最初vim-go使用的是普通的go doc,但它有一些缺点,例如不能基于字节标识符进行解析。go doc非常适合终端使用,但是很难集成到编辑器中。幸运的是,我们有一个非常有用的工具gogetdoc,它解析并检索底层节点的AST节点,并输出相关的doc注释。

这就是:GoDoc适用于任何类型的标识符的原因。如果您将光标放在sayHi()下并调用:GoDoc,您将看到它也显示了它。如果你把它放在sayYoo()下,你会看到它只输出no documentation作为AST节点,没有doc注释。

与其他特性一样,我们重写默认的普通快捷方式K,以便它调用:GoDoc,而不是man(或其他东西)。很容易找到文档,只需在正常模式下点击K

:GoDoc只显示给定标识符的文档。但是它不是一个文档浏览器,如果你想浏览文档,有一个third-party插件来完成它:go-explorer。在vim-go中包含了一个开放的bug。

Identifier resolution

有时您想知道函数接受或返回的是什么。或者光标下的标识符是什么。像这样的问题很常见,我们有命令来回答。

使用相同的main.go文件,检查Println函数并调用:GoInfo。您将看到函数签名正在状态行中打印。这真的很好的看到它在做什么,因为你不必跳到定义和检查签名是什么。

但是每次打:GoInfo都很乏味。我们可以做一些改进来更快地调用它。一如既往,加快速度的一种方法是添加快捷方式:

1
2
autocmd FileType go nmap <Leader>i <Plug>(go-info)

现在只需按一下<leader>i,就可以轻松地调用:GoInfo。但仍有改进的余地。vim-go支持在移动光标时自动显示信息。要启用它,请在.vimrc中添加以下内容:

1
2
let g:go_auto_type_info = 1

现在,只要将光标移到有效标识符上,就会看到状态行自动更新。默认情况下,它每更新一次800ms。这是一个vim设置,可以用updatetime设置进行更改。要将其更改为100ms,请将以下内容添加到.vimrc

1
2
set updatetime=100

Identifier highlighting

有时我们只想快速看到所有匹配的标识符。例如变量、函数等。。假设您有以下Go代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

func main() {
fmt.Println("vim-go")
err := sayHi()
if err != nil {
panic(err)
}
}

// sayHi() returns the string "hi"
func sayHi() error {
fmt.Println("hi")
return nil
}

如果您将光标放在err上并调用:GoSameIds,您将看到所有的err变量都会高亮显示。将光标放在sayHi()函数调用上,您将看到sayHi()函数标识符都高亮显示。要清除它们,请致电:GoSameIdsClear

如果我们不必每次都手动调用它,这会更有用。vim-go可以自动突出显示匹配的标识符。在vimrc中添加以下内容:

1
2
let g:go_auto_sameids = 1

重新启动vim之后,您将看到不再需要手动调用:GoSameIds。匹配的标识符变量现在会自动为您高亮显示。

依赖项和文件

如您所知,一个包可以由多个依赖项和文件组成。即使目录中有许多文件,也只有正确包含package子句的文件才是包的一部分。

要查看组成包的文件,可以调用以下命令:

1
2
:GoFiles

将输出(my$GOPATH设置为~/Code/Go):

1
2
['/Users/fatih/Code/go/src/github.com/fatih/vim-go-tutorial/main.go']

如果你有其他文件,这些也会列出。请注意,此命令仅用于列出属于构建一部分的Go文件。将不列出测试文件。

为了显示文件的依赖关系,可以调用:GoDeps。如果你叫它,你会看到:

1
2
3
4
['errors', 'fmt', 'internal/race', 'io', 'math', 'os', 'reflect', 'runtime',
'runtime/internal/atomic', 'runtime/internal/sys', 'strconv', 'sync',
'sync/atomic ', 'syscall', 'time', 'unicode/utf8', 'unsafe']

Guru

前一个特性是在引擎盖下使用guru工具。让我们来谈谈古鲁。那么什么是古鲁?Guru是一个用于导航和理解Go代码的编辑器集成工具。有一本用户手册显示了所有的特性:https://golang.org/s/using-guru

让我们使用手册中的相同示例来展示我们集成到vim-go中的一些功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import (
"fmt"
"log"
"net/http"
)

func main() {
h := make(handler)
go counter(h)
if err := http.ListenAndServe(":8000", h); err != nil {
log.Print(err)
}
}

func counter(ch chan<- int) {
for n := 0; ; n++ {
ch <- n
}
}

type handler chan int

func (h handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-type", "text/plain")
fmt.Fprintf(w, "%s: you are visitor #%d", req.URL, <-h)
}

将光标放在handler上,然后调用:GoReferrers。这将调用referrers模式guru,它查找对所选标识符的引用,扫描工作区内所有必需的包。结果将是一个位置列表。

guru的模式之一也是describe模式。它就像:GoInfo,但它更高级一些(它提供了更多信息)。例如,它显示类型的方法集(如果有)。如果选中,则显示包的声明。

让我们继续使用相同的main.go文件。将光标放在URL字段或req.URL(在ServeHTTP函数内)的顶部。打电话给:GoDescribe。您将看到一个包含以下内容的位置列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
main.go|27 col 48| reference to field URL *net/url.URL
/usr/local/go/src/net/http/request.go|91 col 2| defined here
main.go|27 col 48| Methods:
/usr/local/go/src/net/url/url.go|587 col 15| method (*URL) EscapedPath() string
/usr/local/go/src/net/url/url.go|844 col 15| method (*URL) IsAbs() bool
/usr/local/go/src/net/url/url.go|851 col 15| method (*URL) Parse(ref string) (*URL, error)
/usr/local/go/src/net/url/url.go|897 col 15| method (*URL) Query() Values
/usr/local/go/src/net/url/url.go|904 col 15| method (*URL) RequestURI() string
/usr/local/go/src/net/url/url.go|865 col 15| method (*URL) ResolveReference(ref *URL) *URL
/usr/local/go/src/net/url/url.go|662 col 15| method (*URL) String() string
main.go|27 col 48| Fields:
/usr/local/go/src/net/url/url.go|310 col 2| Scheme string
/usr/local/go/src/net/url/url.go|311 col 2| Opaque string
/usr/local/go/src/net/url/url.go|312 col 2| User *Userinfo
/usr/local/go/src/net/url/url.go|313 col 2| Host string
/usr/local/go/src/net/url/url.go|314 col 2| Path string
/usr/local/go/src/net/url/url.go|315 col 2| RawPath string
/usr/local/go/src/net/url/url.go|316 col 2| RawQuery string
/usr/local/go/src/net/url/url.go|317 col 2| Fragment string

您将看到,我们可以看到字段的定义、方法集和URL结构的字段。这是一个非常有用的命令,如果您需要它并想理解周围的代码,它就在那里。尝试通过在其他各种标识符上调用:GoDescribe来测试输出是什么。

被问得最多的问题之一是如何知道一个类型正在实现的接口。假设您有一个类型和一个由多个方法组成的方法集。您想知道它可能实现哪个接口。guru的模式implement就是这样做的,它有助于找到一个类型实现的接口。

只需继续前一个main.go文件。将光标放在handler标识符上main()函数之后。Call:GoImplements您将看到一个位置列表,其中包含以下内容:

1
2
3
main.go|23 col 6| chan type handler
/usr/local/go/src/net/http/server.go|57 col 6| implements net/http.Handler

第一行是我们选择的类型,第二行是它实现的接口。因为一个类型可以实现许多接口,所以它是一个位置列表。

guru模式中可能有帮助的是whicherrs。如你所知,错误只是价值观。所以它们可以被编程,因此可以代表任何类型。看看guru手册上说的:

whichers模式报告可能出现在类型error值中的一组可能的常量、全局变量和具体类型。在处理错误时,这些信息可能很有用,以确保所有重要的案件都得到处理。

那么我们如何使用它呢?很简单。我们仍然使用相同的main.go文件。将光标放在从http.ListenAndServe返回的err标识符的顶部。调用:GoWhicherrs,您将看到以下输出:

1
2
3
4
5
6
main.go|12 col 6| this error may contain these constants:
/usr/local/go/src/syscall/zerrors_darwin_amd64.go|1171 col 2| syscall.EINVAL
main.go|12 col 6| this error may contain these dynamic types:
/usr/local/go/src/syscall/syscall_unix.go|100 col 6| syscall.Errno
/usr/local/go/src/net/net.go|380 col 6| *net.OpError

您将看到err值可能是syscall.EINVAL常量,也可能是动态类型syscall.Errno*net.OpError。如您所见,这在实现定制逻辑以不同方式处理错误时非常有用。注意,这个查询需要设置guruscope。稍后我们将介绍scope是什么,以及如何动态地更改它。

让我们继续使用相同的main.go文件。Go以其并发原语(如channels)而闻名。跟踪值如何在通道之间发送有时会很困难。为了更好地理解它,我们有peers模式guru。此查询显示通道操作数上可能的发送/接收集(发送或接收操作)。

将光标移到以下表达式并选择整行:

1
2
ch <- n

打电话给:GoChannelPeers。您将看到一个包含以下内容的位置列表窗口:

1
2
3
4
5
main.go|19 col 6| This channel of type chan<- int may be:
main.go|10 col 11| allocated here
main.go|19 col 6| sent to, here
main.go|27 col 53| received from, here

如您所见,您可以看到通道的分配,它从何处发送和接收。因为这使用指针分析,所以必须定义一个范围。

让我们看看函数调用和目标是如何相关的。这次创建以下文件。main.go的内容应为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"fmt"

"github.com/fatih/vim-go-tutorial/example"
)

func main() {
Hello(example.GopherCon)
Hello(example.Kenya)
}

func Hello(fn func() string) {
fmt.Println("Hello " + fn())
}

文件应该在example/example.go下:

1
2
3
4
5
6
7
8
9
10
package example

func GopherCon() string {
return "GopherCon"
}

func Kenya() string {
return "Kenya"
}

所以跳转到main.go中的Hello函数,并将光标放在名为fn()的函数调用的顶部。执行:GoCallees。此命令显示所选函数调用的可能调用目标。如您所见,它将向我们展示example函数中的函数声明。这些函数是被调用者,因为它们是由名为fn()的函数调用调用的。

再次跳回main.go,这次将光标放在函数声明Hello()上。如果我们想看到这个函数的调用者呢?执行:GoCallers

您应该看到输出:

1
2
3
main.go| 10 col 7 static function call from github.com/fatih/vim-go-tutorial.Main
main.go| 11 col 7 static function call from github.com/fatih/vim-go-tutorial.Main

最后还有callstack模式,它显示从调用图根到包含选择的函数的任意路径。

将光标放回Hello()函数内的fn()函数调用。选择函数并调用:GoCallstack。输出应如下(简化形式):

1
2
3
4
main.go| 15 col 26 Found a call path from root to (...)Hello
main.go| 14 col 5 (...)Hello
main.go| 10 col 7 (...)main

它从15行开始,然后到14行,然后在10行结束。这是从根(从main()开始)到我们选择的函数(在我们的例子中是fn())的图

对于大多数guru命令,您不需要定义任何范围。什么是scope?以下摘录直接摘自guru手册:

指针分析范围:有些查询涉及指针分析,这是一种回答“这个指针可能指向什么”形式的问题的技术?”. 对工作区中的所有包运行指针分析通常开销太大,因此这些查询需要一个称为scope的附加配置参数,它决定要分析的包集。将作用域设置为当前正在使用的应用程序(或applications—a客户机和服务器的集合)。指针分析是whole-program分析,因此范围内唯一重要的包是主包和测试包。

作用域通常被指定为comma-separated包集,或者像github.com/my/dir/…这样的通配符子树;请参阅编辑器的特定文档,了解如何设置和更改范围。

vim-go自动尝试智能化,并为您将当前包导入路径设置为scope。如果命令需要一个作用域,那么大部分都可以覆盖。大多数情况下,这已经足够了,但是对于某些查询,您可能需要更改范围设置。为了便于动态更改scope,请使用一个名为:GoGuruScope的特定设置

如果您调用它,它将返回一个错误:guru scope is not set。让我们显式地将其更改为`github.com/fatih/vim-go-tutorial“范围:

1
2
:GoGuruScope github.com/fatih/vim-go-tutorial

您应该看到以下消息:

1
2
guru scope changed to: github.com/fatih/vim-go-tutorial

如果不带任何参数运行:GoGuruScope,它将输出以下内容

1
2
current guru scope: github.com/fatih/vim-go-tutorial

要选择整个GOPATH,可以使用...参数:

1
2
:GoGuruScope ...

您还可以定义多个包和子目录。以下示例选择github.comgolang.org/x/tools包下的所有包:

1
2
:GoGuruScope github.com/... golang.org/x/tools

您可以通过在包前面加上-(负号)来排除包。以下示例选择encoding下而不是encoding/xml下的所有包:

1
2
:GoGuruScope encoding/... -encoding/xml

要清除范围,只需传递一个空字符串:

1
2
:GoGuruScope ""

如果您正在一个项目中工作,您必须将范围始终设置为相同的值,并且您不希望每次启动Vim时都调用:GoGuruScope,那么您还可以通过向vimrc添加一个设置来定义一个永久作用域。该值必须是字符串类型的列表。以下是上述命令中的一些示例:

1
2
3
4
5
let g:go_guru_scope = ["github.com/fatih/vim-go-tutorial"]
let g:go_guru_scope = ["..."]
let g:go_guru_scope = ["github.com/...", "golang.org/x/tools"]
let g:go_guru_scope = ["encoding/...", "-encoding/xml"]

最后,vim-go会在使用:GoGuruScope时自动完成软件包。所以当你试图写github.com/fatih/vim-go-tutorial只需输入gi并点击tab,你会发现它会扩展到github.com

另一个需要注意的设置是构建标记(也称为构建约束)。例如,下面是您在Go源代码中放置的构建标记:

1
2
// +build linux darwin

有时源代码中可能有自定义标记,例如:

1
2
// +build mycustomtag

在这种情况下,guru将失败,因为底层的go/build包将无法构建该包。因此,所有guru相关的命令都将失败(即使:GoDef在使用guru时也是如此)。幸运的是,guru有一个-tags标志,允许我们传递自定义标记。为了方便vim-go用户,我们有一个:GoBuildTags

对于示例,只需调用以下命令:

1
2
:GoBuildTags mycustomtag

这将把这个标记传递给guru,从现在起它将按预期工作。就像:GoGuruScope,你可以用以下方法清除它:

1
2
:GoBuildTags ""

最后,如果您愿意,可以使用以下设置使其永久化:

1
2
let g:go_build_tags = "mycustomtag"

Refactor it

Rename identifiers

重命名标识符是最常见的任务之一。但这也是一件需要小心处理的事情,以免破坏其他包裹。同样,仅仅使用sed这样的工具有时是没有用的,因为您希望能够感知AST的重命名,所以它只应该重命名属于AST的标识符(它不应该重命名其他非Go文件中的标识符,比如构建脚本)

有一个为您重命名的工具,名为gorenamevim-go使用:GoRename命令在引擎盖下使用gorename。让我们将main.go更改为以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import "fmt"

type Server struct {
name string
}

func main() {
s := Server{name: "Alper"}
fmt.Println(s.name) // print the server name
}

func name() string {
return "Zeynep"
}

将光标放在Server结构中name字段的顶部,然后调用:GoRename bar。您将看到所有name引用都被重命名为bar。最终内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import "fmt"

type Server struct {
bar string
}

func main() {
s := Server{bar: "Alper"}
fmt.Println(s.bar) // print the server name
}

func name() string {
return "Zeynep"
}

如您所见,只有必要的标识符被重命名,但是函数name或注释中的字符串没有被重命名。更好的是:GoRename搜索GOPATH下的所有包,并重命名依赖于该标识符的所有标识符。这是一个非常强大的工具。

Extract function

让我们来看另一个例子。将main.go文件更改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import "fmt"

func main() {
msg := "Greetings\nfrom\nTurkey\n"

var count int
for i := 0; i < len(msg); i++ {
if msg[i] == '\n' {
count++
}
}

fmt.Println(count)
}

这是一个基本示例,它只计算msg变量中的换行数。如果您运行它,您将看到它输出3

假设我们想在其他地方重用换行计数逻辑。让我们重构它。在这些情况下,大师可以用freevars模式帮助我们。freevars模式显示在给定选择中被引用但未定义的变量。

让我们选择visual模式下的片段:

1
2
3
4
5
6
7
var count int
for i := 0; i < len(msg); i++ {
if msg[i] == '\n' {
count++
}
}

选择后,请致电:GoFreevars。它应该是:'<,'>GoFreevars的形式。结果又是一个快速修复列表,它包含了所有自由变量的变量。在我们的例子中,它是一个单一变量,结果是:

1
2
var msg string

那么这有多有用呢?这一小块信息足以将其重构为一个独立的函数。创建包含以下内容的新函数:

1
2
3
4
5
6
7
8
9
10
func countLines(msg string) int {
var count int
for i := 0; i < len(msg); i++ {
if msg[i] == '\n' {
count++
}
}
return count
}

您将看到内容是我们先前选择的代码。函数的输入是:GoFreevars,自由变量的结果。我们只决定归还什么(如果有的话)。在我们的情况下,我们返回计数。我们的main.go将采用以下形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import "fmt"

func main() {
msg := "Greetings\nfrom\nTurkey\n"

count := countLines(msg)
fmt.Println(count)
}

func countLines(msg string) int {
var count int
for i := 0; i < len(msg); i++ {
if msg[i] == '\n' {
count++
}
}
return count
}

这就是重构一段代码的方法。:GoFreevars也可以用来理解代码的复杂性。只需运行它,看看有多少变量与之相关。

Generate it

代码生成是一个热门话题。因为有很棒的std libs,比如go/ast、go/parser、go/printer等。。围棋的优势在于能够轻松地创造出伟大的发电机。

首先我们有一个:GoGenerate命令,它在引擎盖下调用go generate。它就像:GoBuild:GoTest,等等。。如果有任何错误,它也会显示它们,以便您可以轻松地修复它。

实现接口的方法存根

接口对组合非常有用。它们使代码更容易处理。创建测试也更容易,因为您可以模拟接受接口类型的函数,该接口类型具有实现测试方法的类型。

vim-go支持工具impl。impl生成实现给定接口的方法存根。让我们将main.go的内容更改为以下内容:

1
2
3
4
5
6
7
8
9
10
package main

import "fmt"

type T struct{}

func main() {
fmt.Println("vim-go")
}

将光标放在T的顶部,然后键入:GoImpl。系统将提示您编写接口。输入io.ReadWriteCloser,然后按enter键。您将看到内容更改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import "fmt"

type T struct{}

func (t *T) Read(p []byte) (n int, err error) {
panic("not implemented")
}

func (t *T) Write(p []byte) (n int, err error) {
panic("not implemented")
}

func (t *T) Close() error {
panic("not implemented")
}

func main() {
fmt.Println("vim-go")
}

你看那真是太好了。当你在一个类型上面时,你也可以只输入:GoImpl io.ReadWriteCloser,它也会这样做。

但不需要将光标放在类型的顶部。你可以从任何地方调用它。例如,执行以下操作:

1
2
:GoImpl b *B fmt.Stringer

您将看到将创建以下内容:

1
2
3
4
func (b *B) String() string {
panic("not implemented")
}

如您所见,这是非常有帮助的,特别是当您有一个带有大型方法集的大型接口时。您可以很容易地生成它,因为它使用panic(),所以编译时没有任何问题。只要把必要的部分填好就行了。

Share it

vim-go还具有通过https://play.golang.org/与他人轻松共享代码的功能。正如你所知,围棋场是一个分享小片段、练习和/或提示和技巧的完美场所。有时候你在玩弄一个想法,想和别人分享。复制代码并访问play.golang.org,然后粘贴它。`vim-go`使用`:GoPlay`命令可以使所有这些都变得更好。

首先,让我们用以下简单代码更改main.go文件:

1
2
3
4
5
6
7
8
package main

import "fmt"

func main() {
fmt.Println("vim-go")
}

现在调用:GoPlay,然后按enter键。您将看到vim-go自动上载了源代码:GoPlay,并且还打开了一个显示它的浏览器选项卡。但还有更多。代码段链接也会自动复制到剪贴板。只需将链接粘贴到某个地方。你会看到链接与正在播放的内容相同play.golang.org.

:GoPlay也接受一个范围。您可以选择一段代码并调用:GoPlay。它只会上传所选的部分。

有两个设置可以调整:GoPlay的行为。如果您不喜欢vim-go为您打开一个浏览器选项卡,您可以使用以下命令禁用它:

1
2
let g:go_play_open_browser = 0

其次,如果您的浏览器被错误检测到(我们使用的是openxdg-open),您可以通过以下方式手动设置浏览器:

1
2
let g:go_play_browser_command = "chrome"

HTML template

默认情况下,.tmpl文件启用了gohtml模板的语法高亮显示。如果要为另一个文件类型启用它,请将以下设置添加到.vimrc

1
2
au BufRead,BufNewFile *.gohtml set filetype=gohtmltmpl

Donation

本教程是我在业余时间创作的。如果你喜欢并愿意捐款,你现在可以成为一个完全的支持者,成为一个赞助人!

作为一个用户,你使vim-go成长和成熟,帮助我投资于错误修复、新文档,并改进当前和未来的功能。它是完全可选的,只是支持vim-go’s正在进行的开发的一种直接方法。谢谢!

https://www.patreon.com/fatih

TODO Commands

  • :GoPath
  • :AsmFmt

iterm2 + zsh 环境配置

1. 先安装homebrew 和 iterm2

2. 从catalina开始,zsh是所有新建用户账户的默认shell,不需要重新安装。

3. 安装 oh-my-zsh 。

https://github.com/ohmyzsh/ohmyzsh

4. 安装 两个插件:

git clone https://github.com/zsh-users/zsh-syntax-highlighting ${ZSH_CUSTOM:-/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting
git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-
/.oh-my-zsh/custom}/plugins/zsh-autosuggestions

5.编辑.zshrc文件

6. 安装主题, powerlevel10k

https://github.com/romkatv/powerlevel10k#homebrew

7.安装colorls

https://github.com/athityakumar/colorls
sudo gem install colorls

8.代码统计工具

brew install cloc

VimGo 安装

由于众所周知的原因, “go get” & “:GoInstallBinaries” 无法正常使用,经过多方查询.找到当下能用的方法

  1. set proxy
  • 通过 export GO111MODULE=on 开启 MODULE
  • export GOPROXY=https://goproxy.io
    七牛也出了个国内代理 goproxy.cn 方便国内用户更快的访问不能访问的包
  1. 在VIM下运行 GoInstallBinaries

  2. 弹出来的错误列表中挨个复制,并使用 go install 安装.

参考资料:
https://shockerli.net/post/go-get-golang-org-x-solution/

2022影单

永恒族

2022年1月15日 05:58:23

小敏家 电视剧

2022年1月19日 15:22:38

山海情 电视剧

2022年1月26日 15:22:38

乔家的儿女 电视剧

2022年2月10日

权力的游戏 第一季

2022年2月17日

权力的游戏 第二季

2022年2月28日

权力的游戏 第三季

2022年3月7日

权力的游戏 第四季

2022年3月11日

权力的游戏 第五季

2022年3月13日

权力的游戏 第六季

2022年3月15日

权力的游戏 第七季

2022年3月17日

权力的游戏 第八季

2022年3月18日

蜘蛛侠 · 英雄无归

2022年3月23日

第九区

2022年3月25日

2001太空漫游

2022年3月27日

遗落战境 Oblivion

2022年3月28日

波巴菲特之书 第一季

2022年4月2日

人世间

2022年4月30日

康熙王朝

2022年5月30日

阳光姐妹淘

2022年6月2日

大红灯笼高高挂

2022年6月12日

对决(电视剧)

2022年6月20日

奇异博士2:疯狂多元宇宙

2022年6月24日

菊次郎的夏天

2022年7月11日

扬名立万-一部电影的诞生

2022年7月16日

南海十三郎

2022年7月20日

寻秦记

2022年7月27日

JavaScript高级编程

什么是JavaScript

1.2 JavaScript实现

$$
完整的JavaScript实现 \left { \begin{matrix} ECMAScript(核心) \DOM(文档对象模型) \BOM(浏览器对象模型) \end{matrix} \right.

$$

  • ECMAScript: 由ECMA-262定义并提供核心功能
  • DOM:提供与网页内容 交互的方法和接口
  • BOM:提供与浏览器 交互的方法和接口.

1.2.2 DOM

Document Object Model 是一个API,用于在HTML中使用扩展的XML.DOM将整个页面抽象为一组分层节点,HTML或者XML页面的每个组成部分都是一种节点,包含不同的数据.

1
2
3
4
5
6
7
8
<html>
<head>
<title>Sample Page</title>
</head>
<body>
<p> Hello World! </p>
</body>
</html>
1
2
3
4
5
6
7
graph LR
A[html] --> B[head]
B --> C[title]
C --> D[Sample Page]
A --> E[body]
E --> G[p]
G --> H[Hello World!]

DOM 通过创建表示文档的树,让开发者可以随心所欲地控制网页的内容和结构.使用DOM API,可以轻松地删除、添加、替换、修改节点.

HTML中的JavaScript

2.1 < script >元素

八个属性:

  • asyn

    可选,表示应该立即开始下载脚本,但不能阻止其他页面动作.只针对外部脚本文件有效.

  • charset
  • crossorigin

    可选,配置相关请求的CORS(跨资源共享)设置,默认不使用CORS. =“anonymous”请求不必设置凭据标志. =“use-credentials”设置凭据标志.

  • defer

    可选.表示脚本可以延迟到文档完全被解析和现实之后再执行.只针对外部脚本文件有效.

  • integrity

    可选.允许比对接收到的资源和指定的加密签名以验证子资源完整性(SRI, Subresource Intergrity).如果接收到的资源的资源的签名与这个属性指定的签名不匹配,则页面会报错,脚本不会执行.可以用于确保CDN不会提供恶意内容.

  • language (废弃)
  • src

    可选,表示包含要执行的代码的外部文件.

  • type

    可选,代替language,表示代码块中脚本语言的内容类型.

项目重构

数据结构设计

摒弃原来的多层结构,只用一层数据结构设计,并且使用relationships 关系

account设计(兼顾账户和第二类型)

  1. accountID

  2. balance(余额)

  3. counted

  4. icon

  5. name

  6. note

  7. type

    1. 支出
    2. 收入
    3. 普通账户
    4. 信用账户
    5. 投资账户
    6. 借贷账户
  8. billingData: 账单日 credit

  9. creditLimit: 额度 credit

  10. repaymentDate: 还款日 credit

  11. rate: 百分比:利息什么的 installment , loan

  12. autoCalculate installment, loan

  13. installmentType : installment, loan

  14. destinationAccount : loan

  15. installmentTimes: 期数 loan

  16. installmentType: 年月日 loan

  17. isDEBJ: 等额本金 loan

  18. isMoneyIn: 进、出 loan

  19. person: 借贷对象 loan

  20. state: 是否结清 loan

  21. timesInterest: 单次利息 loan

  22. timesLeft: 剩余期数 loan

ios 读取csv文件

最终代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
      
.fileImporter(isPresented: $imported, allowedContentTypes: [.delimitedText], allowsMultipleSelection: false){ result in
do {

guard let fileUrl: URL = try result.get().first else {return}

if (CFURLStartAccessingSecurityScopedResource(fileUrl as CFURL)) { //不在这个if里就出错,唉
//理由:iOS的沙盒机制保护需要我们申请临时调用url的权限

guard let data = String(data: try Data(contentsOf: fileUrl), encoding: .utf8) else { return }


handleSSJdataCSV(data: data)
//done accessing the url
CFURLStopAccessingSecurityScopedResource(fileUrl as CFURL)
}
else {
print("Permission error!")
}
} catch {
// Handle failure.
print ("error reading: \(error.localizedDescription)")
}
}

//数据格式处理代码
func handleSSJdataCSV(data : String){
var csvToStruct = [SSJdata]()

//split the long string into an array of "rows " of sata. each row is a string
//detect "/n" carriage return , then split
var rows = data.components(separatedBy: "\n")

let columnCount = rows.first?.components(separatedBy: ",").count
//remove the header rows
rows.removeFirst()

//loop around each row and split into columns
for row in rows{
let csvColumes = row.components(separatedBy: ",")
if csvColumes.count == columnCount{
let genericStruct = SSJdata.init( raw: csvColumes)
csvToStruct.append(genericStruct!)
}

}
print(csvToStruct)

for singleRecord in csvToStruct{
print(singleRecord.recordType)
}
//done accessing the url
}

参考文献:
https://stackoverflow.com/questions/67731694/how-do-i-save-an-imported-file-name-as-string-after-importing-file-in-swiftui

1
2
3
4
5
//不太管用
let fileUrl = try res.get()
self.fileName = fileUrl.lastPathComponent // <--- the file name you want

let fileData = try Data(contentsOf: fileUrl)

https://betterprogramming.pub/importing-and-exporting-files-in-swiftui-719086ec712
有大用
https://github.com/acwright/ImportExport
上面那个链接内容的示范工程

PT站点升级相关要求

Power User Elite User Crazy User Insane User Veteran User Extreme User Ultimate User Nexus User
学校 50G
4周
2.0
4w做种积分
100G
8周
2.5
8w做种积分
300G
15周
3.0
15w做种积分
500G
25周
3.5
25w做种积分
1TB
40周
4.0
40w做种积分
2TB
60周
4.5
60w做种积分
5TB
80周
5.0
80做种积分
10TB
100周
5.5
100w做种积分
阿童木
海带
葡萄

u2
50G
4周
0.95
120G
8周
1.55
300G
15周
2.05
500G
25周
2.55
750G
40周
3.05
1TB
60周
3.55
1.5TB
80周
4.05
3TB
100周
4.55
备胎(除分享率外同上)
欧神
1pt吧(类似)
老师
1.05 1.55 2.05 2.55 3.05 3.55 4.05 4.55
猫站(周数有所不同) 5 10 15 25 30 35 40 45
Area 50G
4周
1.05
120G
8周
3
300G
10周
3.5
750G
12周
4
1TB
20周
4.5
2TB
25周
5
5TB
30周
5.5
10TB
40周
6
铂金家 200G
4周
1.0
2w分
350G
8周
1.1
5w分
500G
15周
1.2
20w分
1TB
25周
1.3
40w分
2TB
40周
1.5
60w分
3TB
60周
1.5
80w分
4TB
80周
1.7
100w分
6TB
100周
1.8
120w分
馒头 200G
4周
2
400G
8周
3
500G
12周
4
800G
16周
5
1TB
20周
6
2TB
24周
7
2.5TB
28周
8
3TB
32周
9
52pt 50G
4周
1.05
120G
8周
1.55
300G
15周
2.05
1.5TB
25周
2.55
2.5TB
40周
3.05
3.0TB
60周
3.55
4.5TB
80周
4.05
5TB
100周
4.55
我堡
柠檬
100G
5周
2
350G
10周
2.5
500G
15周
3
1TB
20周
3.5
2TB
25周
4
4TB
30周
4.5
6TB
40周
5
8TB
52周
5.5
天空 200G
5周
2
500G
10周
2.5
1TB
15周
3
2TB
20周
3.5
4TB
25周
4
6TB
30周
4.5
8TB
45周
5
10TB
65周
5.5
杜比 120G
2周
2.0
256G
4周
2.5
512G
8周
3.0
768G
12周
3.5
1TB
16周
4.0
2TB
20周
4.5
4TB
24周
5.0
8TB
48周
5.5
ttg 50G
5周
1.1
150G
8周
2.0
250G
8周
2.0
500G
8周
2.5
750G
16周
2.5
1TB
24周
3.0
1.5TB
24周
3.5
2.5TB
24周
4.0
3.5TB
32周
5.0
家园 128G
5周
1.6
4w分
256G
5周
1.9
10w分
512G
10周
2.3
18w分
768G
10周
2.7
28w分
1TB
20周
3.2
40w分
2TB
20周
3.7
54w分
4TB
30周
3.6
70w分
8TB
30周
4.1
100w分
50G
5周
1.0
3200
150G
10周
1.5
19200
300G
15周
2.0
76800
500G
30周
2.5
256000
1TB
60周
3.5
64w
2TB
90周
4.0
128w
3TB
120周
4.5
192w
4TB
150周
5.0
256w

摘录-关于工作、欲望、生活、健康

我觉得我最大的极简,就是少受累,保养身体,珍爱自己的生命。

挑一个性价比最高的工作,每天八小时,双休,时薪约70元。我知道我同学有选择了996的,他们挣得很多,时薪大约是我的一半。

挑一个人际关系冷漠很少团建聚餐的工作。这样不是因为我不喜欢热闹,我只是不喜欢攀比。别人炫耀名牌首饰包包名车名表的时候,我就当看不见。别人抱怨自己月光欠花呗,那都与我无关。

我和我老公都是低欲望的人,能靠家长出首付,我们就是要理直气壮的啃老,也不会996去伤身体挣那几个臭钱。既然有的选,那我们就是不肯996。毕竟我爸妈当年也是靠姥姥姥爷买的房,我们将来有钱了也会接济孩子,这样想来可不就是理直气壮吗。

脱离世俗,想想自己的理想。想想自己真正喜欢做什么。同事有贷款买豪车充门面的,我俩工薪阶层工程师,上班又不远不用车,要门面干嘛,买个十几万的车,到处去玩就足够了。

人家梦龙雪糕都不要脸的不用牛奶了,什么玩意,那些外头买的东西,又贵又舍不得用好材料还好意思说为了环保。我就自己冻酸奶雪糕吃,自己拿纯牛奶做冰淇淋。还有自己蒸馒头,烤肉,打豆浆,做果冻,奶茶,柠檬红茶。真特么的香。反正下班有的是时间。

我们还要互相捏肩捶腿,好好保养。天热喝绿豆汤,天冷喝姜汤,周末自己磨个咖啡,香飘整屋。

我每天晚上都能看着老公送我的一架古筝,弹几个曲子,总觉得好幸福啊,这可比买首饰好听多了,还要啥自行车。

我们晚上常坐在阳台上,喝点小酒,看星星看月亮。周末我喜欢玩手机,听音乐,看看闲书。

我家的老人,都是五十多就不用务农或上班了,要么半退休。能够身体健康的直到90岁,天天晨练逛公园,自己做饭,还能领几十年退休金。身体好是真的好。

作者:鸿飞满西洲

链接:https://www.zhihu.com/question/313020218/answer/2077887482

来源:知乎

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

黑群晖安装QB

  1. 打开docker, 输入qbittorrent, 找到 linuxserver/qbittorrent
  2. 下载
  3. 创建qb文件夹,改everyone权限,内建两个新子文件夹,config和downloads
  4. docker映像设置中添加卷来映射文件夹
  5. 改端口,第三个tcp是webui端口,其他的也要改,默认的6881被大多数站点屏蔽。
  6. 配置环境变量
  7. 下一步。确认,运行。
  8. 登录qb界面,默认账户admin,默认密码 adminadmin
  9. 修改qb的连接中的监听端口为修改过的端口

意外情况:QB无法打开,提示错误:/usr/bin/qbittorrent-nox: error while loading shared libraries: libQt5Core.so.5: cannot open shared object file: No such file or directory
解决方法:

  1. ssh 到 群晖,然后
    docker exec -it qbittorrent4.3.3 /bin/sh
    qbittorrent4.3.3 是我容器名,参照自已的改

  2. apt update

  3. apt install binutils

  4. strip –remove-section=.note.ABI-tag /usr/lib/x86_64-linux-gnu/libQt5Core.so.5

  5. 重启容器

参考文献:
http://www.360doc.com/content/19/0601/19/27498460_839666036.shtml
https://post.smzdm.com/p/a7do76vd/
https://github.com/linuxserver/docker-qbittorrent/issues/103