在上周,KCL 發(fā)布了 v0.4.6 版本,這個版本在語言、工具鏈、社區(qū)集成&擴展支持等方面進行了重點更新。本文包含 KCL IDE 插件的功能特性和 LSP 的介紹、KCL LSP Server 端的設(shè)計和實現(xiàn)以及未來的規(guī)劃和期望。
功能特性
在這次更新中,我們發(fā)布了全新的 KCL VS Code 插件,并且用 Rust 重寫了 LSP 的 Server 端。我們提供了 IDE 中常用的代碼輔助功能,如高亮、跳轉(zhuǎn)、補全、Outline、懸停、錯誤提示等。
-
高亮
-
補全
-
Outline
-
懸停 & 跳轉(zhuǎn)
-
錯誤/警告提示
歡迎到 https://kcl-lang.io/docs/tools/Ide/vs-code/ 了解更多
什么是 LSP?
在這次更新中,我們基于 LSP 實現(xiàn)了以上能力。LSP 指的是 Language Server Protocol,它是由微軟在 2016 年推出的一種用于編程語言工具的協(xié)議。借用一張圖,很容易就可以理解 LSP。
LSP通過 LSP ,編輯器和 IDE 可以通過 JSON-RPC 通信協(xié)議與后端運行的語言服務(wù)器(Server 端)進行通信。語言服務(wù)器可以提供代碼分析、自動補全、語法高亮、定義跳轉(zhuǎn)等功能?;?LSP,開發(fā)者可以在不同的編輯器和 IDE 之間遷移,使得語言工具的開發(fā)從 M(語言數(shù)量) * N(編輯器/IDE數(shù)量) 降低為 M + N。
為什么用Rust重寫?
KCL 編譯器和其他工具最初由 Python 實現(xiàn),因為性能原因,我們用 Rust 語言重寫了編譯器(性能提升 40 倍!我們用 Rust 重寫了自己的項目)。在此之后,我們使用 Rust 逐步重寫了 KCL 的其他工具,如測試工具、Format 工具等。在這次更新中,我們用 Rust 重寫了 LSP Server 端,其主要考慮因素也是性能。
過去,Python 版本的 Server 端在處理一些復(fù)雜的場景(編譯文件數(shù)量超過200個)時,處理一個跳轉(zhuǎn)的請求,Server 端從接收到請求到計算結(jié)果并響應(yīng),時間長達 6 秒以上,幾乎是不可用狀態(tài)。由于 Server 端的實現(xiàn)主要基于語言編譯器前中端的詞法解析和語義分析,在我們使用 Rust 重寫以后,這部分性能分別提升了 20 和 40 倍, 帶來的顯著結(jié)果就是 Server 端的響應(yīng)時間得到了巨大提升,對于同樣的場景,響應(yīng)時間縮短至 100 毫秒左右。而對于一些簡單的場景,響應(yīng)時間只有幾毫秒,做到了用戶無感。
KCL LSP Server的設(shè)計與實現(xiàn)
KCL LSP Server 的設(shè)計如下圖所示:
主要流程可以分為幾個階段:
- 建立連接,初始化 LSP 能力。在 IDE 的 Client 端,打開特定文件(KCL的 *.k)時,IDE 會啟動本地的 kcl_language_server 二進制文件,啟動 Server 端。這個文件與 KCL 一起發(fā)布,并安裝在 KCL 的 bin 目錄下。Server 啟動后會建立 standard IO 的 Connection,并等待 Client 發(fā)送的初始化請求。Server 端接收初始化請求后會定義 Server 端信息和能力,并返回給 Client,以此完成 LSP 的初始化連接。
- 建立連接后,Server 端會啟動一個輪詢函數(shù),不斷接收來自 Client 的 LSP Message,例如 Notification(打開/關(guān)閉/變更/刪除文件等)和 Request(跳轉(zhuǎn)、懸停等),以及來自 Server 端自身的 Task。并統(tǒng)一封裝成事件(Event)交給下一步處理。
- 對于各種事件,按照以下步驟處理:
///Handlesaneventfromoneofthemanysourcesthatthelanguageserversubscribesto.
fnhandle_event(&mutself,event:Event)->anyhow::Result<()>{
//1.Processtheincomingevent
matchevent{
Event::Task(task)=>self.handle_task(task)?,
Event::Lsp(msg)=>matchmsg{
lsp_server::Request(req)=>self.on_request(req,start_time)?,
lsp_server::Notification(not)=>self.on_notification(not)?,
_=>{}
},
};
//2.Processchanges
letstate_changed:bool=self.process_vfs_changes();
//3.HandleDiagnostics
ifstate_changed{
letmutsnapshot=self.snapshot();
lettask_sender=self.task_sender.clone();
//Spawnthediagnosticsinthethreadpool
self.thread_pool.execute(move||{
handle_diagnostics(snapshot, task_sender)?;
});
}
Ok(())
}
3.1 任務(wù)分發(fā):根據(jù)任務(wù)類型,做函數(shù)分發(fā)。在子函數(shù)中,會進一步基于 Request 或 Notification 的類型繼續(xù)分發(fā)到最終的處理函數(shù)中,如處理文件變更、處理跳轉(zhuǎn)請求等。這些函數(shù)會根據(jù)基于編譯器中前端編譯出的語義模型(AST,符號表,錯誤信息等)做分析,計算生成對應(yīng)的 Response(如跳轉(zhuǎn)請求的目標(biāo)位置)。
3.2 處理變更:用戶在修改代碼或更改文件時,會發(fā)送對應(yīng)的 Notification。在前一步的處理中,會將變更保存在虛擬文件系統(tǒng)(VFS)中。Server 端會根據(jù)新的源代碼,進行重新編譯,保存新的語義模型,以供下一個請求做處理。
3.3 錯誤處理:這里的錯誤并非指 Server 端的運行錯誤,而是代碼編譯中的語法、語義錯誤,編譯警告等。Client 端并沒有對應(yīng)的請求類型來請求這些錯誤,而是由 Server 端主動發(fā)送 Diagnostics。因此,在發(fā)生變更后,同步地將錯誤信息更新至 Client 端。
遇到的問題
1.為什么需要虛擬文件系統(tǒng)?
在最初的設(shè)計中,并沒有考慮使用虛擬文件系統(tǒng)。我們每次從文件系統(tǒng)中獲取源代碼,進行編譯和分析。對于一些“靜態(tài)”的任務(wù),如跳轉(zhuǎn),可以在變更代碼后保存到文件系統(tǒng),然后再進行跳轉(zhuǎn)的操作。配合到 VS Code 的自動保存功能,體驗上并沒有明顯的差距。但對于代碼補全這一功能,IDE 中輸入的補全trigger(如 “.”)會觸發(fā)文件變更的通知和代碼補全的請求,但對應(yīng)的代碼還未保存到文件系統(tǒng)中,編譯后的語義模型無法做對應(yīng)的分析。因此,我們借助 Rust Analyzer 對應(yīng)的 vfs 的create,在 Server 端引入了虛擬文件系統(tǒng),將編譯的入口從文件路徑變?yōu)榱?source code。Client 端輸入代碼后,文件變更的通知會先更新虛擬文件系統(tǒng),重新編譯文件,生成新的語義模型,然后再處理補全請求。
2. 如何處理不完整的代碼?
我們遇到的另一個比較大的問題是如何處理不完整的代碼。同樣的,對于跳轉(zhuǎn)這類“靜態(tài)”的任務(wù),可以假定代碼是完整、正確的。但對于補全操作,如以下代碼,希望在輸入.后,補全字符串的函數(shù)。對于編譯流程,第二行實際上是不完整的代碼,無法編譯出正常的 AST 樹。
s:str="hellokcl"
len=s.
為此,我們在 KCL 的編譯中實現(xiàn)了語法和語義上的多種錯誤恢復(fù),保證編譯過程始終能產(chǎn)生完整的 AST 和符號表。在這個例子中,我們新增了一個表示空的 AST 節(jié)點作為占位符,使得第二行能夠生成完整的 AST。在處理補全的請求時,會根據(jù) s 的類型和其他語義信息,補全函數(shù)名、schema attr 或 pkg 中定義的 schema 名。
Rust Analyzer architecture:
Architecture Invariant: parsing never fails, the parser produces
(T, Vec
rather than) Result
.
總結(jié)與展望
KCL 的 IDE 插件目前已經(jīng)實現(xiàn)高亮、跳轉(zhuǎn)、補全、Outline、懸停、錯誤提示等功能。這些功能提升了 KCL 用戶的開發(fā)效率。然而,作為一款 IDE 插件,它的功能還不夠完整。在未來的開發(fā)中,我們會繼續(xù)完善,未來的工作有以下幾個方向:
- 更多的語言能力:提供更多的功能,如代碼重構(gòu),錯誤的quick fix,代碼 fmt等,進一步完善功能,提升開發(fā)效率
- 更多的 IDE 接入:目前,KCL 雖然提供了 LSP,只接入了 VS Code,未來會基于 LSP 的能力為 KCL 用戶提供更多選擇。
- AI 能力的集成:目前,ChatGPT 風(fēng)靡全網(wǎng),各行各業(yè)都在關(guān)注。我們也在探索 AI×KCL 的結(jié)合,提供更智能的研發(fā)體驗。總之,我們會繼續(xù)完善和優(yōu)化 KCL 的 IDE 插件,讓它更加成熟和實用。為KCL用戶帶來更加方便和高效的開發(fā)體驗。
審核編輯 :李倩
-
編程語言
+關(guān)注
關(guān)注
10文章
1945瀏覽量
34736 -
LSP
+關(guān)注
關(guān)注
0文章
13瀏覽量
9784 -
KCL
+關(guān)注
關(guān)注
0文章
8瀏覽量
4070
原文標(biāo)題:Rust 重寫的 LSP:KCL IDE 插件的功能介紹與設(shè)計解析
文章出處:【微信號:Rust語言中文社區(qū),微信公眾號:Rust語言中文社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論