在我完成 auto-conjure 和 Conjure Piglet Client 這兩個專案的期間,開發過程中,最讓我痛苦的事,莫過於缺少了跳轉定義這個功能。所以,第三個專案,我就決定來實作這個功能。而且,我考慮把它直接實做在 Conjure Fennel Client 裡,然後去對 Conjure 提交 Pull Request 。
為了釐清到底該如何做出這個功能,我讓 LLM 解釋「編輯器的跳轉定義」通常怎麼實作。而 LLM 回答:
一個語言伺服器如何實現 "jump to definition" 呢?它會執行以下步驟:
- 程式碼解析 (Parsing):語言伺服器會讀取你的 Fennel 程式碼,並將其解析成一個抽象語法樹 (Abstract Syntax Tree, AST)。這個樹狀結構準確地表示了你的程式碼的結構和語義。
- 符號分析 (Symbol Analysis):在解析過程中,伺服器會識別出所有重要的符號(symbols),例如:變數 (variables), 函數 (functions), 宏 (macros), 鍵值對 (key-value pairs)。
- 定義與引用關聯 (Definition-Reference Linking):伺服器會建立一個映射表,將每個符號的使用位置與它的定義位置關聯起來。例如,當你在程式碼中呼叫 (my-function x) 時,伺服器會找到 (defn my-function ...) 的那一行,並將它們連結起來。
- 提供給編輯器:當你在編輯器中按下 "jump to definition" 的快捷鍵時,編輯器會向語言伺服器發送一個請求,告訴它:「用戶現在在檔案 A 的第 10 行第 5 列,請告訴我這裡的符號定義在哪裡。」
- 回傳結果:語言伺服器根據它的內部映射表,回傳定義的位置(檔案、行號、列號),然後編輯器會自動跳轉到該位置。
考慮到此一功能的用戶就是我,所以軟體規格我來決定就可以了。而我決定讓功能盡可能地簡化,讓軟體早日上線。
我做了三個簡化:
它的運作方式如下:
def-str-util.search-and-jump
函數。search-and-jump
裡,它會用 Tree-sitter 來解析程式碼、並且進一步解析模組的檔案位置,最後找出要跳轉的位置。我在開發時,很多問題都會參考看看 LLM 怎麼說;而且,我喜歡把策略/執行分開問。
然而,在策略的層次,LLM 常常提供了標準解法,這並不意味著你的使用情境最適合標準解法,所以過度依賴 LLM 有時候可能反而走更遠的路。