0%

現代的專案開發,很少在從無到有打造,大部分都是基於現有的程式之上繼續開發或維護,進入專案之後,通常第一件事就是大量閱讀程式碼理清專案的整個脈絡,才開始著手寫 code,所以好的程式碼導覽技巧將會帶你上天堂。

我將大部分的程式碼導覽情境大致拆成了三種情況:

  • 基本的跳轉:
    • #if #endif 中間的程式碼太長,想要跳轉到成對的 #if #endif
    • cursor 在 {…} block 裡, 這個 block 又臭又長,想快速跳轉 block 的開頭和結尾
    • 註解長篇大論,想快速跳到註解的開頭或結尾
  • 尋找專案下檔案 (可透過設定 path option 解決)
    • 跳轉到標頭檔
    • 跳轉到指定檔案
  • 使用 source code tagging system 快速挑轉到 function 定義及特定 symbol 等等:
    專案相當大的時候,source code tagging system 相當好用,可以快速找到定位,也不會花掉太多的資源,比起 LSP 的跳轉定義,是較輕量的選擇。

本篇依據上述三種情境,一一講述。

Read more »

看下圖,如果想將 <expr> 改成 <leader> 你會怎麼做呢?

通常我看過的幾種方式如下:

  1. 使用 j 向下到 97 行,在使用 l 到 target 並且開始編輯,這也是最糟的
  2. target 的行數看起來離 window 中央行很近, 使用 M 跳到 window 中央行,再使用 h j k l 到 target
  3. 使用 :set number 列出行號, 按下 97G 到達 97 行, fe ; ; ;
    註: f{char} 找到此行的下一個字母、 ; 為重複上一個 f 命令
  4. 使用 / 搜尋 expr,在使用 n n n n 到 target
  5. 使用 set mouse=n,用滑鼠點 target
    Read more »

y 是在 vim 裡最常使用的使用的 operator 之一,使用 y operator 將會執行 yank 動作,但它無法像 c (change) 和 d (delete) operator 使用之後馬上就知道對文件編輯了什麼。yank 動作是沒有反饋的,按了 yiw 複製 word 之後,你也無法確定是不是按對了,還要使用 :reg 檢視,頗麻煩的。

解決方案:在使用 yank operator 之後,高亮複製區域短暫的時間,就能確認複製的區域有沒有誤。以下圖例為,在 23 行按下 yy, 在 24 行的 return 上按下 yiw

Read more »

vim 高效率編輯的背後,operator, motion, text object 的配合功不可沒。

operator 是編輯的動作, text object 就是文字區塊,motion 是跳轉到文件點,透過三者的按鍵配合,就能達到所想及所得的編輯方式。

如果你有看過別人使用 vim ,通常會有這種想法:怎麼隨便按幾個鍵就刪這裡刪那裡複製這裡複製那裡,速度快到看不懂在幹嘛,而且還不會出錯,因為只要想好要做什麼,按鍵按下去就對了,這也是為什麼會高效率。說了一堆,反正就是 耍潮必備

本篇將會提到:

  • operator, text object, motion 的配合用法

  • 內建 text object 的不足之處

  • 擴充 text object 的 plugin

  • 淺談 nvim-treesitter

    Read more »

前言

回想一下,上一次使用 vim 寫 code 到一個段落要編譯時,你是怎麼做的。

我看到大多數人的方式,就是將 terminal 切成兩塊,一塊寫 code,一塊編譯,若編譯有錯誤,再移動到寫 code 區塊更改。
如果使用此方法,當專案有規模時,編譯錯誤一百條,看了編譯錯誤在哪之後,再將編譯錯誤的位置,從專案裡找出來,反覆來回 100 次,直到修正完畢。
本文以 C 語言專案為例 (當然不是只有 C 語言才能這樣子幹,可看 Q&A),聊聊強大的 vim 在這種情境下有什麼好的解決方案。

Read more »

寫程式的時候常常遇到一種情況,假設你是團隊的新人,需要常常參照別人的程式,看看團隊的 coding style 等等,可能需要參照 A file 的第 10 行,B file 第 8 行,C file 第 999 行。

另一種情境則是在寫測試的時候會和實作交互參照,而測試程式往往和實作程式會拆開不同的檔案,所以就必須要一直跳轉。

使用 split 的方式縱然是一個不錯的方式,但是如果要參照的點大於三個,就將螢幕切成三份,那工程師就相當痛苦了,不是每個人都用這種電腦螢幕啊。這種 split 的方式還有另一個缺點:如果手殘將檔案關掉,就得重找。

Read more »

你確定要用 neovim 開發 golang?

現在是 vscode 稱霸天下的時代,理所當然的 (neo)vim 也常常被拿來 vscode 比較,vscode 內建整合了 git、debugger、自動補全引擎、terminal、extension manager,另外 Golang 在 vscode 上的 extension 支援相當良好: 包括跳轉到定義 (go to definition)、重新命名 (rename)、跳轉到型態定義 (go to type definition)⋯⋯ 一堆細節功能,近期由於 google golang team 接手 go 語言的 vscode extension,相信使用 vscode 開發是不錯的選擇。

(neo)vim 當然也能達到 vscode 的類似功能,但是必須花時間設定調教,在 vscode 問世之前我會推薦 (Neo)vim 給我的朋友,vscode 問世之後我就開始推薦 vscode。

本文給那些本來就在 (neo)vim 裡打滾的人,但對 go 語言設定不熟的人,
如果你是一張白紙,而且沒有被虐的癖好,我建議選擇 vscode,可以用較少的時間進入到寫程式的環節,而不是一直在花時間在設定,這篇文你也沒必要看下去。

預備動作

Must have

  • 你應該先安裝好 neovim nightly 版本,brew install neovim --HEAD
  • 你應該要知道怎麼使用 vim-plug,至少要知道 :PlugInstall
  • 你應該先把 go 語言環境安裝好

Nice to have

  • 你知道 :checkhealth:UpdateRemotePlugin
  • 你熟悉 :help 的使用方式,這裡有一篇好文:Learn to use help

開發環境基本功能

一個好用的 Editor 我認為應該包含以下幾點基本功能:

  • Auto completion - 好的自動補全引擎
  • File manager - 可以在側邊欄看到專案的結構
  • Real time linter - 在寫 code 犯錯的同時,有一些提示 (也就是 diagnostic 功能)
  • Background compile - 編譯的同時不應該卡住整個 Editor,要讓使用者能做其他事情
  • Code navigation: 包含跳轉到定義,跳轉到型態定義,或是當一個檔案寫得很長的時候,怎麼綜觀全局

以下我將會介紹我使用的 Plugin,達到我上述所提到的功能。
以下使用的皆是 Neovim nightly 版本,使用 stable 是沒有官方 LSP 支援的。

Note:neovim 是由 vim 所 fork 出來的,差異介紹請看這篇,某些 Plugin 不會同時相容於兩者,我接會標明。

Read more »

method set, interface, auto deference or reference 當這些語法綜合再一起使用時,很容易混淆,甚至寫出 bug code,這章我們就透過題目探討一下 method set 常常被大家誤會的地方。

Golang 78 語法篇 - interface Line 50 用到的概念就在這篇。

題目

請問以下程式輸出結果為何

1
2
3
4
5
6
7
8
9
type Employee struct {}

func (e Employee) DoWork() {
fmt.Println("do work")
}

func main() {
(&Employee{}).DoWork()
}

答案只有兩種方向:
1. 無法編譯
2. 印出 do work

這個程式可以編譯,並且還印出 do work 但是為什麼呢?

Read more »

Interface 可以說是 golang 的精髓之一。golang interface 特殊的語法以及概念也是工程師常常犯錯的地方。此題只是用來釐清自己的概念。

題目

請問以下程式輸出結果為何

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
package main

import "fmt"

type S struct{
val int
}

func (s S) F() {}

type IF interface {
F()
}

func InitType() S {
var s S
return s
}

func InitPointer() *S {
var s *S
return s
}
func InitEfaceType() interface{} {
var s S
return s
}

func InitEfacePointer() interface{} {
var s *S
return s
}

func InitIfaceType() IF {
var s S
return s
}

func InitIfacePointer() IF {
var s *S
return s
}

func main() {
// fmt.Println(InitType() == nil) // type mismatch
fmt.Println(InitPointer() == nil)
fmt.Println(InitEfaceType() == nil)
fmt.Println(InitEfacePointer() == nil)
fmt.Println(InitIfaceType() == nil)
fmt.Println(InitIfacePointer() == nil)
}
Read more »