Gopls:實現
最後主要更新:2024 年 1 月 16 日
本文件提供 gopls 結構的高層概覽,以幫助新貢獻者瞭解其運作方式。本文件無意作為實現或任何關鍵元件的完整描述;關於這方面,包文件(下文連結)和程式碼中的其他註釋是更好的指南。
下圖顯示了 gopls 模組的選定元件及其根據 Go 匯入圖的關係。未顯示測試和測試基礎設施,未顯示實用程式包,也未顯示來自 x/tools 模組的包。為簡潔起見,包以其通常無歧義的最後一個段落來引用。
每個塊的高度大致對應其技術深度。有些塊寬而淺,例如 protocol,它為整個 LSP 協議聲明瞭 Go 型別。其他塊很深,例如 cache 和 golang,因為它們包含大量密集型邏輯和演算法。
從底層開始,我們將描述各個元件。
最低層定義了語言伺服器協議的請求和響應型別
-
protocol 包定義了標準協議;它主要根據 Microsoft 提供的架構定義機械生成。最重要的型別是 DocumentURI,它代表一個
file:
URL,該 URL 標識客戶端編輯器文件。它還提供了Mapper
,用於在原始碼位置使用的不同座標系之間進行對映:UTF-8、UTF-16 和 token.Pos。 -
command 包定義了 Gopls 的非標準命令,這些命令全部透過
workspace/executeCommand
擴充套件機制呼叫。這些命令通常由伺服器作為 Code Actions 或 Code Lenses 的後續操作返回;大多數客戶端不會直接構造對它們的呼叫。
下一層定義了許多重要且使用非常廣泛的資料結構
-
file 包定義了客戶端檔案的主要抽象:它的
Identity
(URI 和內容雜湊),以及它的Handle
(它還提供了檔案特定快照的版本和內容)。 -
parsego 包定義了
File
,即 Go 原始檔的已解析形式,包括其內容、語法樹和座標對映(Mapper 和 token.File)。該包執行各種型別的樹修復,以規避 Go 解析器在錯誤恢復方面的不足。 -
metadata 包定義了
Package
,這是 Go 包元資料的抽象,類似於go list -json
的輸出。元資料由 go/packages 生成,該包負責呼叫go list
。(使用者報告稱,它在某種程度上可以與用於 Bazel 的 GOPACKAGESDRIVER 一起使用,但我們不維護針對此場景的測試。)該包還提供
Graph
,即工作區的完整匯入圖;每個圖節點都是一個Package
。
settings 層定義了 gopls 配置選項的資料結構(實際上是一個大樹),以及其 JSON 編碼。
cache 層是 gopls 中最大、最複雜的元件。它涉及狀態管理、依賴分析和失效:與客戶端通訊的 Session
;客戶端開啟的 Folder
s;特定構建選項下的特定工作區樹的 View
;某個編輯操作後工作區所有檔案狀態的 Snapshot
;所有檔案的內容,無論是已儲存到磁碟的(DiskFile
)還是已編輯但未儲存的(Overlay
);記憶體中已快取的計算(如解析 go.mod 檔案或構建符號索引)的 Cache
;以及 Package
,它儲存了從 Go 語法型別檢查包的結果。
快取層依賴於各種輔助包,包括
-
filecache 包,它管理 gopls 的永續性、事務性、基於檔案的鍵/值儲存。
-
xrefs、methodsets 和 typerefs 包定義了用於構建從型別檢查派生的資訊索引的演算法,以及在檔案快取中對這些可序列化索引進行編碼和解碼的演算法。
這些包共同實現了 v0.12 重新設計所帶來的快速重啟、降低記憶體消耗和跨程序協同工作,這些內容在 “Scaling gopls for the growing Go ecosystem” 文章中有描述。
快取還定義了 gopls 的 go/analysis 驅動程式,它在工作區中執行模組化分析(類似於 go vet
)。Gopls 還包含一些不屬於 vet 的分析通道。
下一層定義了四個包,每個包用於處理特定語言的檔案:mod 用於 go.mod 檔案;work 用於 go.work 檔案;template 用於 text/template
語法的askell;以及 golang,用於 Go 本身的檔案。這個包是迄今為止最大的,提供了 gopls 的主要功能:Go 程式碼的導航、分析和重構。正如大多數使用者所設想的那樣,這個包就是 gopls。
server 包定義了 LSP 服務實現,每個 LSP 請求型別都有一個處理程式方法。每個處理程式根據檔案型別進行切換,並將請求分派給四個特定語言的包之一。
lsprpc 包將服務介面連線到我們的 jsonrpc2 伺服器。
請注意,該圖是依賴圖,是程式結構的“靜態”視角。更動態的視角將根據處理特定請求時遇到的順序來排列包;在這種檢視中,底層代表“線路”(協議和命令),上一層將容納 RPC 相關包(lsprpc 和 server),而功能(例如,golang、mod、work、template)將位於頂部。
cmd 包定義了 gopls
命令的命令列介面,gopls 的主包只是一個簡單的包裝器。它通常不帶引數執行,導致它啟動一個伺服器並無限期監聽。它還提供了一些子命令,這些子命令啟動一個伺服器,向其傳送單個請求,然後退出,從而提供對伺服器功能的傳統批處理命令訪問。這些子命令主要作為除錯輔助工具提供;但請參閱 https://golang.org.tw/issue/63693。
本文件的原始碼可以在 golang.org/x/tools/gopls/doc 下找到。