原则

快捷键不能过多

快捷键不能过多,一个 ide 就有许多快捷键,再加上浏览器,终端等等,快捷键如此之多,而且快捷键与功能之间并没有关系,也就是说,你只能靠大量练习形成肌肉记忆,这并不是一个高效的学习方式。

一般来说,对于一个应用来说,记忆 20,30 个都是很多的了,最主要的不是把快捷键记住,而是你要知道你在干什么,你要干什么。

不要只死记快捷键,要理解它的意义

比如我曾经无数次搜索 Goland 的快捷键表,可是最后记忆的还是那么几个,几乎没有增加,像什么 alt+shift,实现接口的 ctrl+i ,我看见过许多次,可是我仍然无法记住。

现在看来,这种方式就是错误的,不应该去“记忆”,而是要先理解它是什么意义,然后在用的时候会想到有这么一个功能,然后再去用。

使用工具帮助记忆

自然我们会时常忘记对应的快捷键是什么,但这其实反而并不是太大的问题。为了解决这个问题,我们有两个方法。一就是写 cheat-list,要用的时候直接查,而就是使用搜索功能,直接搜索对应的快捷键。而在 Goland 中,搜索功能就是特殊的几个快捷键,如搜索 action, file, type 等。

这里扩展一下 i3 桌面,也是这么个道理,把多余的画面去掉,即可以强迫我们使用键盘,也能强迫我们使用搜索功能。无鼠标编程显然效率是灰常高的,当然也比时常要移动手去使用鼠标要舒适,还有 b 格加成。然后同时使用搜索功能也有几个好处。一是强化功能的印象,搜索时,其名字往往代表其功能,故在搜索时能表明知道自己在做什么,也能强化有这个功能的印象,许多功能并不是只有这个工具才有,这种强化思维也能迁移,会提高有意识提高效率的意识,会主动去寻找提高效率的方式,同时迁移工具时也会考虑这些。最后就是熟悉一下单词,比如我现在已经能熟练拼写 settings, plugins, keymap, background等等。。这能加大我们接触英语的可能性,因为这是我们主动的,而不是被动的。

  • 不要大幅度的修改初始快捷键

自由度过高,大幅度的修改快捷键有一些坏处,如难以迁移,从迁移环境时比较麻烦,别人难以使用,有时会有牵一发而动全身的效果,改一个快捷键可能会造成后续大量的快捷键冲突,自定义的不符合人体工程学,上手成本过高等等。我曾经在 Goland 中使用 vim 插件,给 action 自定义了大量的快捷键,而且不同模式移动光标方法也不同,我使用的键盘也是可编程键盘,然后折腾了许久,最后对这一套我自己也不熟悉,编程效率直线下贱,而代码却未敲几行,甚至我还尝试过不同的键盘布局,最后我想到有时会使用其他环境而放弃了。总之,一切皆有度。

移动光标

基础

上 下 左 右

移动到行首/尾

向上/下翻页

这几个都是非常高频的键,但是如果是标准键位,使用这几个键都有大幅度的手部移动,这是非常难受的。我使用的是 66 布局,可以硬件编程。在 Space 左边有一个 fn 键,我的设置是

fn + h/j/k/l 左/下/上/右

fn + i/o home/end

fn + ; / page up/ page down

左手大拇指按住 fn,右书按字母,这样不用移动手,习惯了后效率还是挺高的。

  • Ctrl+m

这也是一个常用的键,有时候我们的写着写着,光标就到屏幕底部了,继续写时观看很不方便,这时我们就可以使用这个键,屏幕会滑动到光标所在处,即屏幕以光标为中心。

Ctrl+左/右 移动到上/下一个单词

Ctrl+home/end 移动到文件首/尾处

Ctrl+上/下 屏幕滑动但光标不动

跳转

  • ctrl+B

这是一个非常常用的键,使用这个会跳转到变量的定义处,如我们要查看某个函数的源码实现时,对着实例使用即可

  • 插件: acejump-lite

当我们要在屏幕上跳转时,如光标在屏幕低,要跳转到屏幕顶某处,就可以使用这个插件,我自定义为 fn+m, 然后输入跳转的目标字符

  • Ctrl+e

这是一个非常常用的键

当我们要跳转到最近的文件时,可以使用这个键,然后移动到目标文件

当要跳转到某个文件时,也可以输入这个键,先使用这个键,然后输入文件名即可,这个类似使用 Ctrl+shift+N 查找文件

当我们要进入一些弹窗时,如要进入 run 窗口, terminal 窗口,enven log 窗口时,或者一些插件的窗口,如 translate 插件的 word book (生词本) 窗口,可以使用这个键再搜索名字

  • Ctrl+Shift+A

这也是一个非常常用的键,我们可以用这个键做许多事,这个键用来使用 action,action 是 goland 中许多操作都有的一个东西。

如我们要打开 设置,就可以使用键后,输入 settings 即可

如我们要打开 插件,输入 plugins, 要把主菜单关掉,可以输入 main ,第一个就是,或者我们要删除文件,可以对着文件输入 delete, 要新建文件,可以输入 go,要新建项目,可以输入 project ,要 运行代码,可以输入 run 等等,总之这个键可以代替大多数需要移动鼠标才能做的,要达到无鼠标编程,这个键需要熟练使用。

  • Ctrl+ 23

在编程中,我们经常会遇到 error,waring 等,我们想要检查文件有没有这些时,就可以使用这个键,在 goland 中原来的键是 F2/shift+F2, 即查询下/上一个 error,但是我键盘布局不方便,所以设置了一下

想提高程序健壮性,即检查 error, waing 等时,就可以使用这个键,在 goland 中一般 error 是红色下划线,警告是黄色下划线,还有其他的提示等,总之在提示处有值得改进的地方

想要快速跳转到 error 处,有时候我们要修改 error 时,使用 光标移动显然有些低效,这是我们可以直接用这个键,就可以直接跳转到 error 处

  • ctrl+shfit

返回刚打开的 tab

enter 新建下一行并移动到下一行, 改变此行

ctrl+enter 拆分此行,鼠标不动,在行尾使用,可新建下一行并鼠标不动

shift+enter 新建下一行并移动到下一行(不改变此行)

ctrl+alt+enter 新建上一行并到上一行

Ctrl+N fing type

Ctrl+Shift+N 查找 file Ctrl+Alt+Shift+N 查找 func

Ctrl+[ 跳转到函数大括号开始

Ctrl+] 跳转到函数大括号结束

提示

  • Ctrl+1 显示 error
  • Ctrl+p 显示参数 parameter info
  • Ctrl+Q show documentation, 用来显示返回值比较方便

其他

  • fn+s

这三个设置为一个快捷键比较方便,我使用的是硬件编程,也可是试试 vim 插件,我还没有找到其他方法能够这样设置

Ctrl+Alt+L 格式化代码 Ctrl+Alt+O 优化导入的类和包 Ctrl+S save

  • Alt+1

打开侧边栏

  • Shift+F6

rename 所有的文件,类名,函数名,属性名都可以重命名,使用 Shift+F6 重命名,所有使用过这个名称的地方都会跟着改变

  • Ctrl+R

run

  • Ctrl+w

我们经常会修改代码,删除代码,那么就必然需要选中代码,这个快捷键就是用来选中代码的,连续使用会以此选中更多的代码。

比如我们要修改字符串

fmt.Println("Sdfmsdf")

光标在 “” 中,那么我们使用一次 Ctrl+w 就会选中 “” 中的字符,就可以直接给修改或删除了,我们可以继续按,则会以此加上双引号,以此加上括号,以此选中整个语句。

如果我们要删除某个函数,可以对着函数名或直接在函数中多按几次 Ctrl+w, 就会选中整个函数代码块了,当然直接对着函数名按的次数少一些。

如果我们按快了,也可以按 Ctrl+shift+w 来撤销上一次选中。

Ctrl+/ 行注释

Ctrl+shift+/ 快注释

Ctrl+k git add and commit Ctrk+shift+k git push

Ctrl+shift+space 智能提示

Ctrl+shift+f find in path Ctrl+shift+r replace in path

Alt+j 选中相同的变量

Ctrl+shift+c 复制当前的文件的路径

Ctrl+d 重复当前行或选中文本

Ctrl+x 剪切当前行

Ctrl+c 双击复制当前行

Ctrl+y 删除当前行

ctrl+上:Clone Caret Above,多行选择

ctrl+下:Clone Caret Below

插件

  • translate 插件

Ctrl+shift+y 取词翻译

Ctrl+shift+o 弹出翻译弹窗

  • acejump-lite

fn+m 字符跳转

代码生成

最特殊的键 alt+enter

此 action name 是 show context actions

这个键的作用可以在 Settings>Editor>Intensions>go 中查看

最常用的几个就是 struct 相关调用函数以及add comment

struct 相关

  • File all fields

使用 struct 时,补全 其所有 fileds,只限于第一层,若 fileds 有 struct ,则不 fill 子 struct, 在 使用的 struct 里使用即可

Before

type Address struct { Street string; City string }
type Person struct { Name string; Location Address }

_ = Person{}

After

type Address struct { Street string; City string }
type Person struct { Name string; Location Address }

_ = Persona{
	Name:     "",
	Location: Address{},
}
  • File all fileds recursively

使用 struct 时,补全 其所有 fileds,只限于第一层,若 fileds 有 struct ,则递归的 fill 所有 struct, 在 使用的 struct 里使用即可

Before

type Address struct { Street string; City string }
type Person struct { Name string; Location Address }

_ = Person{}

After

type Address struct { Street string; City string }
type Person struct { Name string; Location Address }

_ = Persona{
	Name: "",
	Location: Address{
		Street: "",
		City:   "",
	},
}
  • file fileds

使用 struct, 选择一个 filed 补全, 在使用的 struct 里使用即可

Before

type Address struct { Street string; City string }
type Person struct { Name string; Location Address }

_ = Person{}

After

type Address struct { Street string; City string }
type Person struct { Name string; Location Address }

_ = Persona{
	Name:     "",
}
  • Generate constructor

给 struct 生成 new 方法,在 struct 的声明处使用

Before

type Person struct {
	name string
	age int
}

After

type Person struct {
	name string
	age int
}

func NewPerson(name string, age int) *Person {
	return &Person{name: name, age: age}
} 
  • Generate getter

给 struct filed 生成 getter 方法,在 struct 声明处,对着要生成的特定 file 即可

Before

type Person struct {
	name string
}

After

type Person struct {
	name string
}

func (p *Person) Name() string {
	return p.name
}
  • Generate setter

给 struct filed 生成 setter 方法,在 struct 声明处,对着要生成的特定 file 即可

Before

type Person struct {
	name string
}

After

type Person struct {
	name string
}

func (p *Person) SetName(name string) {
	p.name = name
}
  • Generate getter and setter

给 struct filed 生成 getter 和 setter 方法,在 struct 声明处,对着要生成的特定 file 即可

Before

type Person struct {
	name string
}

After

type Person struct {
	name string
}

func (p *Person) Name() string {
	return p.name
}

func (p *Person) SetName(name string) {
	p.name = name
}
  • Implement interface

给结构体实现某接口,接口可以是自定义的,也可以是已有的。也就是给结构体生成接口含有的所有方法。这个功能有快捷键 Ctrl+i, 对着结构体的声明处使用即可

Before

package main

type A struct {}

type B interface {
	a1() int
	a2() int
	a3() int
}

After

package main

type A struct {}

func (*A) a1() int {
	panic("implement me")
}

func (*A) a2() int {
	panic("implement me")
}

func (*A) a3() int {
	panic("implement me")
}

type B interface {
	a1() int
	a2() int
	a3() int
}
  • Move field assignment to struct initialization

使用了一个 struct, 然后又写了一个赋值语句,这个命令是把两个语句合成一个语句,在 初始化语句里使用即可

Before

s := S{}
s.foo = `bar`

After

s := S{foo: `bar`}
  • Remove keys from struct literal

使用 struct 时,把 filed name 去掉,在使用 struct 时使用

Before

var _ = struct{int; string; slice []int}{string : "a", int : 2}

After

var _ = struct{int; string; slice []int}{2, "a", nil}

调用函数

  • ignore explicitly

调用函数时,有时函数会报黄,会出现这个选项,这个选项有点类似 Introduce to local variable , 不过后者是快速生成返回值,这个则是用 _ 占位符表示返回值

before

ioutil.WriteFile()

After

_ = ioutil.WriteFile()
  • do not report this method/function anymore

调用函数时,有时函数会报黄,会出现这个选项, 使用这个后这个函数班会再变黄,至于为什么会变黄,我猜是 warning, 具体原因暂时未知

  • Introduce to local variable

调用函数时,迅速生成赋值返回值变量,对着调用的函数名使用

Before

func Add(a int, b int) int {
	return a + b
}
func main() {
	Add(1, 2)
}

After

func Add(a int, b int) (int, error) {
	return a + b, nil
}
func main() {
	add, err := Add(1, 2)
}
  • Rename _

调用函数后,把没有使用的赋值变量变为 _ , 对着没有使用的变量使用即可

Before

func Add(a int, b int) (int, error) {
	return a + b, nil
}
func main() {
	a, err := Add(1, 2)
}

After

func Add(a int, b int) (int, error) {
	return a + b, nil
}
func main() {
	_, err := Add(1, 2)
}
  • Insert bank identifies to left side of assginment statement

调用函数时,若有多个返回值,而赋值的变量没有返回值多,那么对着变量使用后,会自动补全返回值个数

Before

func Add(a int, b int) (int, error) {
	return a + b, nil
}
func main() {
	a := Add(1, 2)
}

After

func Add(a int, b int) (int, error) {
	return a + b, nil
}
func main() {
	a, _ := Add(1, 2)
}
  • Replace with “:”

调用函数时,若赋值的变量没有声明,那么对其使用就会直接声明后赋值

Before

func Add(a int, b int) (int, error) {
	return a + b, nil
}
func main() {
	a = Add(1, 2)
}

AAfter

func Add(a int, b int) (int, error) {
	return a + b, nil
}
func main() {
	a := Add(1, 2)
}
  • Add missing return values

函数 return 返回值时,若个数不够,那么在 return 使用会自动增加返回值

Before

func Add(a int, b int) (int, error) {
	return a + b
}

After

func Add(a int, b int) (int, error) {
	return a + b, nil
}
  • Remove 2nd result parameter from function

函数 return 返回值时,若个数不够, 或者调用函数时,赋值的变量比返回值少,使用后会把返回值减少到对应个数

Before

func Add(a int, b int) (int, error) {
	return a + b, nil
}

func main() {
	a := Add(1, 2)
}

After

func Add(a int, b int) int {
	return a + b
}

func main() {
	a := Add(1, 2)
}

或者

Before

func Add(a int, b int) (int, error) {
	return a + b
}

After

func Add(a int, b int) int {
	return a + b
}

声明变量

  • Add parens to declaration

给单个变量声明时添加括号,在变量声明处使用

Before

var a string

After

var (
	a string
)

这里说一下 import 时的情况,在 goland 中,只 import 了一个package,要加括号的话在 import 后直接打 ( 即可,goland 会自动把其放入括号中

  • Remove parens from declaration

与 Add parens to delartion 相反,去除括号

Before

var (
	a string
)

After

var a string
  • Merge all declaration

给连续三个以上的变量声明划分为一组,空格隔开的不算,对着变量使用即可

Before

var a string

var b int
var c float32
var d bool

var e int

After

var a string

var (
	b int
	c float32
	d bool
)

var e int
  • split all declaration

与 Merge all declaration 相反,会把括号里的变量分开声明

Before

var (
	a string
	b int
	c int
)

After

var a string
var b int
var c int
  • merge declaration up

把这个变量和上一个声明的变量放在一个括号中,空格不影响

Before

var a string
var b int

After

var (
	a string
	b int
)
  • merge declaration up via comma

把这个变量和上一个声明的变量放在一行,空格不影响

Before

var a int
var b int

After

var a, b int
  • split declaration by comma

于 merge declaration up via comma 相反,把一行声明的变量变为多行声明

Before

var a, b int

After

var a int
var b int
  • convert to short var declaration

把 var 变量的声明格式变为更短的 := 格式,在 var 变量声明处使用

Before

var x = 1

After

x := 1
  • convert to var declaration

与 convert to short var declaration 相反,把 := 声明的变量变为 var 声明的格式,在 := 变量声明处使用

Before

x := 1

After

var x = 1

函数其他

  • Add comment

在声明函数的地方,对着函数名可以生成此函数的注释

Before

package foo

func Foo() {

}

After

package foo

// Foo 
func Foo() {

}
  • add format string argument

在支持格式化的函数的参数里, 即 (“”) 的引号中使用。选择一个变量后,会自动生成相应的格式化输出。

如 int 类型 a, 输入 a 后会自动生成 %d, 而无需记忆对应的意义。

Before

func log(count int) {
	fmt.Printf(`count is: `)
}

After

func log(count int) {
	fmt.Printf(`count is: %d`, count)
}
  • Expand signature types

在参数和返回值时,转换类型的写法, 在参数或返回值括号内使用即可

Before

func foo(s1, s2 string) (i1, i2 int) {
	return 0, 1
}

After

func foo(s1 string, s2 string) (i1 int, i2 int) {
	return 0, 1
}
  • Reuse signature types

与上面的 Expand signature types 相反

Before

func bar(s1 string, s2 string) (i1 int, i2 int) {
	return 0, 1
}

After

func bar(s1, s2 string) (i1, i2 int) {
	return 0, 1
}
  • Export

把私有 func, type, filed 变为公有的,在 func或 type 或 sturct 里的 filed 里使用即可

Before

func private() {}

After

func Private() {}
  • Invert if

把 if- else 语句反过来,在 操作符 处使用即可

Before

a := 1
if a >= 2 {
	fmt.Println("a >= 2")
} else {
	fmt.Println("a < 2")
}

After

a := 1
if a < 2 {
	fmt.Println("a < 2")
} else {
	fmt.Println("a >= 2")
}

单词

  • typo change to

对着拼错的单词使用,在对标识符命名,写注释什么的很方便

Before

blak

After

black

导包

  • sync dependencies of

在 import 后添加想要拉的包 path, 任何使用这个 action, 可以直接拉包,类似 go get。

  • Add dot import alias

在导的包前或使用的包名中使用,可以直接用包里的函数

Before

package main

import "fmt"

func main() {
    fmt.Println("Hello World!")
}

After

package main

import . "fmt"

func main() {
    Println("Hello World!")
}
  • Remove dot import alias

和 Add dot import alias 相反,取消其效果,导的包前或使用的包名中使用

Before

package main

import . "fmt"

func main() {
    Println("Hello World!")
}

After

package main

import "fmt"

func main() {
    fmt.Println("Hello World!")
}
  • Add import alias

这个同样在导的包前或使用的包名中使用,可以给包设置别名

Before

package main

import "fmt"

func main() {
    fmt.Println("Hello World!")
}

After

package main

import fmtAlias "fmt"

func main() {
    fmtAlias.Println("Hello World!")
}

操作表达式的改变

  • flip binary operator
  • negate expression
  • negate expression recurisively
  • negate topmostexpression
  • negate topmostexpression recurisively

这几个都是不常用的(至少我没怎么用到)

生成测试代码 Ctrl+Shift+T

此 action name 是 go to test

这个键一共有 4 个功能

Test for functions

这个是最常用的,在函数内使用即可。

这个功能是生成函数测试。

go 中的测试文件需要在原有文件名后加上 _test, 测试函数需要在函数名前加上 Test ,参数也是特殊的。

如果手动生成的话比较麻烦且容易忘记,于是可以使用此快捷键。

如:

Before

db.go 中

func Hello {
	fmt.Println("Hello World")
}

After

生成 db_test.go 文件
其中 
func TestHello(t *testing.T) {
//...
}

Test for files

生成此 files 内的所有函数的 test

Test for package

生成此 package 内的所有函数的 test

empty test file

生成空 test file, 里面没有 TestFunc

自动补全代码 Ctrl+shfit+enter

  • 自动补全右括号

有时候要把莫东西加到括号中,可以先加上左括号,然后对着此行使用,会自动补全右括号并移动到下一行

Before

func main() {
	fmt.Println ("Hello World!"
}

After

func main() {
	fmt.Println("Hello Wrold!")
}
  • 自动补全大括号

有时候大括号多了,会不小心删除右括号,这时候使用会自动生成右括号

Before

func main(){
	fmt.Println("hello World!")

After

func main() {
	fmt.Println("hello world!")
}

常用 action

  • find usage 查找方法被调用的地方