日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
實(shí)例解析:如何開(kāi)發(fā) VSCode LSP 服務(wù)

從一張動(dòng)圖說(shuō)起:

創(chuàng)新互聯(lián)是一家集網(wǎng)站建設(shè),南溪企業(yè)網(wǎng)站建設(shè),南溪品牌網(wǎng)站建設(shè),網(wǎng)站定制,南溪網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營(yíng)銷,網(wǎng)絡(luò)優(yōu)化,南溪網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競(jìng)爭(zhēng)力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶成長(zhǎng)自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。

上圖應(yīng)該大家經(jīng)常使用的「錯(cuò)誤診斷」 功能,它能夠在你編寫(xiě)代碼的過(guò)程中提示,那一塊代碼存在什么類型的問(wèn)題。

這個(gè)看似高大上的功能,從插件開(kāi)發(fā)者的角度看其實(shí)特別簡(jiǎn)單,基本上就是上一篇文章《你不知道的 VSCode 代碼高亮原理》中簡(jiǎn)單介紹過(guò)的 VSCode 開(kāi)發(fā)語(yǔ)言特性的三種方案:

  • 基于 「Sematic Tokens Provider」 協(xié)議的詞法高亮
  • 基于 「Language API」 的編程式語(yǔ)法高亮
  • 基于 「Language Server Protocol」 的多進(jìn)程架構(gòu)語(yǔ)法高亮

其中, 「Language Server Protocol」 由于性能與開(kāi)發(fā)效率上的優(yōu)勢(shì)已經(jīng)逐漸成為主流實(shí)現(xiàn)方案,本文接下來(lái)會(huì)基于 LSP 展開(kāi)介紹各種語(yǔ)言特性的實(shí)現(xiàn)細(xì)節(jié),解答 LSP 的通訊模型與開(kāi)發(fā)模式。

示例代碼

本文示例均已同步到 Github,建議讀者先拉下代碼實(shí)際體驗(yàn):

 
 
 
 
  1. # 1. clone 示例代碼 
  2. git clone git@github.com:Tecvan-fe/vscode-lsp-sample.git 
  3. # 2. 安裝依賴 
  4. npm i # or yarn 
  5. # 3. 使用 vscode 打開(kāi)示例代碼 
  6. code ./vscode-lsp-sample 
  7. # 4. 在 vscode 中按下 F5 啟動(dòng)調(diào)試 

順利執(zhí)行完畢后,可以看到插件的調(diào)試窗口:

核心代碼有:

  • server/src/server.ts:LSP 服務(wù)端代碼,提供代碼補(bǔ)全、錯(cuò)誤診斷、代碼提示等常見(jiàn)語(yǔ)言功能的示例
  • client/src/extension.ts:提供一系列 LSP 參數(shù),包括 Server 的調(diào)試端口、代碼入口、通訊方式等。
  • packages.json:主要提供了語(yǔ)法插件所需要的配置信息,包括:

activationEvents:聲明插件的激活條件,代碼中的 onLanguage:plaintext 意為打開(kāi) txt 文本文件時(shí)激活

main:插件的入口文件

其中,client/src/extension.ts 與 packages.json 都比較簡(jiǎn)單,本文過(guò)多介紹,重點(diǎn)在于 server/src/server.ts 文件,接下來(lái)我們逐步拆解,解析不同語(yǔ)言特性的實(shí)現(xiàn)細(xì)節(jié)。

如何編寫(xiě) Language Server

Server 結(jié)構(gòu)解析

示例項(xiàng)目的 server/src/server.ts 實(shí)現(xiàn)了一個(gè)小型但完整的 Language Server 應(yīng)用,核心代碼:

 
 
 
 
  1. // 要素1: 初始化 LSP 連接對(duì)象 
  2. const connection = createConnection(ProposedFeatures.all); 
  3.  
  4. // 要素2: 創(chuàng)建文檔集合對(duì)象,用于映射到實(shí)際文檔 
  5. const documents: TextDocuments = new TextDocuments(TextDocument); 
  6.  
  7. connection.onInitialize((params: InitializeParams) => { 
  8.   // 要素3: 顯式聲明插件支持的語(yǔ)言特性 
  9.   const result: InitializeResult = { 
  10.     capabilities: { 
  11.       hoverProvider: true 
  12.     }, 
  13.   }; 
  14.   return result; 
  15. }); 
  16.  
  17. // 要素4: 將文檔集合對(duì)象關(guān)聯(lián)到連接對(duì)象 
  18. documents.listen(connection); 
  19.  
  20. // 要素5: 開(kāi)始監(jiān)聽(tīng)連接對(duì)象 
  21. connection.listen(); 

從示例代碼可以總結(jié)出 Language Server 的 5 個(gè)必要步驟:

  • 創(chuàng)建 connection 對(duì)象,用于實(shí)現(xiàn)客戶端與服務(wù)器之間的信息互通
  • 創(chuàng)建 documents 文檔集合對(duì)象,用于映射客戶端正在編輯的文件
  • 在 connection.onInitialize 事件中,顯式聲明插件支持的語(yǔ)法特性,例如上例中返回對(duì)象包含 hoverProvider: true 聲明,表示該插件能夠提供代碼懸停提示功能
  • 將 documents 關(guān)聯(lián)到 connection 對(duì)象
  • 調(diào)用 connection.listen 函數(shù),開(kāi)始監(jiān)聽(tīng)客戶端消息

上述 connection 、documents 等對(duì)象定義在 npm 包:

vscode-languageserver/nodevscode-languageserver-textdocument這是一個(gè)基本模板,主要完成了 Language Server 各種初始化操作,后續(xù)就可以使用 connection.onXXX 或 documents.onXXX 監(jiān)聽(tīng)各類交互事件,并在事件回調(diào)中返回符合 LSP 協(xié)議的結(jié)果,或者顯式調(diào)用通訊函數(shù)如 connection.sendDiagnostics 發(fā)送交互信息。

接下來(lái)我們通過(guò)幾個(gè)簡(jiǎn)單實(shí)例,分析各項(xiàng)語(yǔ)言特性的實(shí)現(xiàn)邏輯。

懸停提示

當(dāng)鼠標(biāo)停留在語(yǔ)言元素如函數(shù)、變量、符號(hào)等 token 時(shí),VSCode 會(huì)顯示 token 對(duì)應(yīng)描述與幫助信息:

要實(shí)現(xiàn)懸停提示功能,首先需要聲明插件支持 hoverProvider 特性:

 
 
 
 
  1. connection.onInitialize((params: InitializeParams) => { 
  2.   return { 
  3.     capabilities: { 
  4.       hoverProvider: true 
  5.     }, 
  6.   }; 
  7. }); 

之后,需要監(jiān)聽(tīng) connection.onHover 事件,并在事件回調(diào)中返回提示信息:

 
 
 
 
  1. connection.onHover((params: HoverParams): Promise => { 
  2.   return Promise.resolve({ 
  3.     contents: ["Hover Demo"], 
  4.   }); 
  5. }); 

OK,這就是一個(gè)很簡(jiǎn)單的語(yǔ)言特性示例了,本質(zhì)上就是監(jiān)聽(tīng)事件 + 返回結(jié)果,非常簡(jiǎn)單。

代碼格式化

代碼格式化是一個(gè)特別有用的功能,能夠幫助用戶快速、自動(dòng)完成代碼的美化處理,實(shí)現(xiàn)效果如:

實(shí)現(xiàn)懸停提示功能,首先需要聲明插件支持 documentFormattingProvider 特性:

 
 
 
 
  1.     ... 
  2.     capabilities : { 
  3.         documentFormattingProvider: true 
  4.         ... 
  5.     } 

之后,監(jiān)聽(tīng) onDocumentFormatting 事件:

 
 
 
 
  1. connection.onDocumentFormatting( 
  2.   (params: DocumentFormattingParams): Promise => { 
  3.     const { textDocument } = params; 
  4.     const doc = documents.get(textDocument.uri)!; 
  5.     const text = doc.getText(); 
  6.     const pattern = /\b[A-Z]{3,}\b/g; 
  7.     let match; 
  8.     const res = []; 
  9.     // 查找連續(xù)大寫(xiě)字符串 
  10.     while ((match = pattern.exec(text))) { 
  11.       res.push({ 
  12.         range: { 
  13.           start: doc.positionAt(match.index), 
  14.           end: doc.positionAt(match.index + match[0].length), 
  15.         }, 
  16.         // 將大寫(xiě)字符串替換為 駝峰風(fēng)格 
  17.         newText: match[0].replace(/(?<=[A-Z])[A-Z]+/, (r) => r.toLowerCase()), 
  18.       }); 
  19.     } 
  20.  
  21.     return Promise.resolve(res); 
  22.   } 
  23. ); 

示例代碼中,回調(diào)函數(shù)主要實(shí)現(xiàn)將連續(xù)大寫(xiě)字符串格式化為駝峰字符串,效果如圖:

函數(shù)簽名函數(shù)簽名特性在用戶輸入函數(shù)調(diào)用語(yǔ)法時(shí)觸發(fā),此時(shí) VSCode 會(huì)根據(jù) Language Server 返回的內(nèi)容,顯示該函數(shù)的幫助信息。

實(shí)現(xiàn)函數(shù)簽名功能,需要首先聲明插件支持 documentFormattingProvider 特性:

 
 
 
 
  1.     ... 
  2.     capabilities : { 
  3.         signatureHelpProvider: { 
  4.             triggerCharacters: ["("], 
  5.         } 
  6.         ... 
  7.     } 

之后,監(jiān)聽(tīng) onSignatureHelp 事件:

 
 
 
 
  1. connection.onSignatureHelp( 
  2.   (params: SignatureHelpParams): Promise => { 
  3.     return Promise.resolve({ 
  4.       signatures: [ 
  5.         { 
  6.           label: "Signature Demo", 
  7.           documentation: "幫助文檔", 
  8.           parameters: [ 
  9.             { 
  10.               label: "@p1 first param", 
  11.               documentation: "參數(shù)說(shuō)明", 
  12.             }, 
  13.           ], 
  14.         }, 
  15.       ], 
  16.       activeSignature: 0, 
  17.       activeParameter: 0, 
  18.     }); 
  19.   } 
  20. ); 

實(shí)現(xiàn)效果:

錯(cuò)誤提示

注意,錯(cuò)誤提示的實(shí)現(xiàn)邏輯與上述事件 + 響應(yīng)的模式有一點(diǎn)點(diǎn)不同:

  • 首先不需要通過(guò)capabilities 做額外聲明;
  • 監(jiān)聽(tīng)的是 documents.onDidChangeContent 事件,而不是 connection 對(duì)象上的事件
  • 不是在事件回調(diào)中用 return 語(yǔ)句返回錯(cuò)誤信息,而是調(diào)用 connection.sendDiagnostics 發(fā)送錯(cuò)誤消息

完整示例:

 
 
 
 
  1. // 增量錯(cuò)誤診斷 
  2. documents.onDidChangeContent((change) => { 
  3.   const textDocument = change.document; 
  4.  
  5.   // The validator creates diagnostics for all uppercase words length 2 and more 
  6.   const text = textDocument.getText(); 
  7.   const pattern = /\b[A-Z]{2,}\b/g; 
  8.   let m: RegExpExecArray | null; 
  9.  
  10.   let problems = 0; 
  11.   const diagnostics: Diagnostic[] = []; 
  12.   while ((m = pattern.exec(text))) { 
  13.     problems++; 
  14.     const diagnostic: Diagnostic = { 
  15.       severity: DiagnosticSeverity.Warning, 
  16.       range: { 
  17.         start: textDocument.positionAt(m.index), 
  18.         end: textDocument.positionAt(m.index + m[0].length), 
  19.       }, 
  20.       message: `${m[0]} is all uppercase.`, 
  21.       source: "Diagnostics Demo", 
  22.     }; 
  23.     diagnostics.push(diagnostic); 
  24.   } 
  25.  
  26.   // Send the computed diagnostics to VSCode. 
  27.   connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); 
  28. }); 

這段邏輯診斷代碼中是否存在連續(xù)大寫(xiě)字符串,通過(guò) sendDiagnostics 發(fā)送相應(yīng)的錯(cuò)誤信息,實(shí)現(xiàn)效果:

如何識(shí)別事件與響應(yīng)體

上述示例,我有意忽略大多數(shù)實(shí)現(xiàn)細(xì)節(jié),更關(guān)注實(shí)現(xiàn)語(yǔ)言特性的基本框架和輸入輸出。授人以魚(yú)不如授人以漁,所以接下來(lái)我們花一點(diǎn)點(diǎn)時(shí)間了解從哪里獲取這些接口、參數(shù)、響應(yīng)體的信息。有兩個(gè)非常重要的鏈接:

  • https://zjsms.com/egWtqPj/ , VSCode 官網(wǎng)關(guān)于可編程語(yǔ)言特性的說(shuō)明文檔
  • https://zjsms.com/egWVTPg/ ,LSP 協(xié)議官網(wǎng)

這兩個(gè)網(wǎng)頁(yè)提供了 VSCode 所支持的所有語(yǔ)言特性的詳細(xì)介紹,可以在這里找到你想要實(shí)現(xiàn)的特性的概念性描述,例如對(duì)于代碼補(bǔ)齊:

嗯,有點(diǎn)復(fù)雜且太過(guò) detail,不過(guò)還是很有必要耐心了解下,讓你對(duì)即將要做的事情有一個(gè)高層概念上的理解。

此外,如果你選擇使用 TS 編寫(xiě) LSP,事情會(huì)變得更簡(jiǎn)單。vscode-languageserver 包提供了非常完善的 Typescript 類型定義,我們完全可以借助 ts + VSCode 的代碼提示找到需要使用的監(jiān)聽(tīng)函數(shù):

之后,根據(jù)函數(shù)簽名找到參數(shù)、結(jié)果的類型定義:

之后,就可以根據(jù)類型定義,有針對(duì)性地處理參數(shù),返回對(duì)應(yīng)結(jié)構(gòu)的數(shù)據(jù)。

深入理解 LSP

看完示例后,我們?cè)俜催^(guò)頭來(lái)看看 LSP。LSP —— Language Server Protocol 本質(zhì)上是一種基于 JSON-RPC 的進(jìn)程間通訊協(xié)議,LSP 本身包含兩大塊內(nèi)容:

  • 定義 client 與 server 之間的通訊模型,也就是誰(shuí)、在什么時(shí)候、以什么方式向?qū)Ψ桨l(fā)送什么格式的信息,接收方又以什么方式返回響應(yīng)信息
  • 定義通訊信息體,也就是以什么格式、什么字段、什么樣的值表達(dá)信息狀態(tài)

作為類比,HTTP 協(xié)議專門(mén)用于描述網(wǎng)絡(luò)節(jié)點(diǎn)間如何傳輸、理解超媒體文檔的網(wǎng)絡(luò)通訊協(xié)議;而 LSP 協(xié)議則專門(mén)用于描述 IDE 中,用戶行為與響應(yīng)之間的通訊方式與信息結(jié)構(gòu)。

總結(jié)一下,LSP 架構(gòu)的工作流程如下:

  • 編輯器如 VSCode 跟蹤、計(jì)算、管理用戶行為模型,在發(fā)生某些特定的行為序列時(shí),以 LSP 協(xié)議規(guī)定的通訊方式向 Language Server 發(fā)送動(dòng)作與上下文參數(shù)
  • Language Server 根據(jù)這些參數(shù)異步地返回響應(yīng)信息
  • 編輯器再根據(jù)響應(yīng)信息處理交互反饋

簡(jiǎn)單說(shuō),編輯器負(fù)責(zé)與用戶直接交互, Language Server 負(fù)責(zé)在背后默默計(jì)算如何響應(yīng)用戶的交互動(dòng)作,兩者以進(jìn)程粒度分離、解耦,在 LSP 協(xié)議框架下各司其職又協(xié)作共生。就好像我們通常開(kāi)發(fā)的 Web 應(yīng)用中,前端負(fù)責(zé)與用戶交互,服務(wù)端負(fù)責(zé)管理諸如權(quán)限、業(yè)務(wù)數(shù)據(jù)、業(yè)務(wù)狀態(tài)流轉(zhuǎn)等不可見(jiàn)的部分。

目前,LSP 協(xié)議已經(jīng)發(fā)展到 3.16 版本,覆蓋大多數(shù)語(yǔ)言特性,包括:

  • 代碼補(bǔ)全
  • 代碼高亮
  • 定義跳轉(zhuǎn)
  • 類型推斷
  • 錯(cuò)誤檢測(cè)
  • 等等

得益于 LSP 清晰的設(shè)計(jì),這些語(yǔ)言特性的開(kāi)發(fā)套路都很相似,學(xué)習(xí)曲線很平滑,開(kāi)發(fā)的時(shí)候基本上只需要關(guān)心監(jiān)聽(tīng)那個(gè)函數(shù),返回什么格式的結(jié)構(gòu),可以說(shuō)掌握上述幾個(gè)示例之后就可以很簡(jiǎn)單地上手了。

過(guò)去,IDE 對(duì)語(yǔ)言特性的支持是集成在 IDE 或者以同構(gòu)插件形式實(shí)現(xiàn)的,在 VSCode 中這種同構(gòu)擴(kuò)展能力以 「Language API」 或 「Sematic Tokens Provider」 接口方式提供,這兩種方式在上一篇文章《你不知道的 VSCode 代碼高亮原理》都有過(guò)介紹了,雖然架構(gòu)上比較簡(jiǎn)單,容易理解,但有一些明顯硬傷:

插件開(kāi)發(fā)者必須復(fù)用 VSCode 本身的開(kāi)發(fā)語(yǔ)言、環(huán)境,例如 Python 語(yǔ)言插件就必須用 JavaScript 寫(xiě)

同一個(gè)編程語(yǔ)言需要為不同 IDE 重復(fù)開(kāi)發(fā)相似的擴(kuò)展插件,重復(fù)投入

LSP 最大的優(yōu)勢(shì)就是將 IDE 客戶端與實(shí)際計(jì)算交互特性的服務(wù)端隔離開(kāi)來(lái),同一個(gè) Language Service 可以重復(fù)應(yīng)用在多個(gè)不同 Language Client 中。

此外,LSP 協(xié)議下客戶端、服務(wù)器分別在各自進(jìn)程運(yùn)行,在性能上也會(huì)有正向收益:

確保 UI 進(jìn)程不卡頓

  • Node 環(huán)境下,充分利用多核 CPU 能力

  • 由于不再限定 Language Server 的技術(shù)棧,開(kāi)發(fā)者可以選擇更高性能的語(yǔ)言,例如 Go
  • 總的來(lái)說(shuō),就是很強(qiáng)。

總結(jié)

本文介紹了 VSCode 下,開(kāi)發(fā)一款基于 LSP 的語(yǔ)言插件所需要具備的最最基本的技能,實(shí)際開(kāi)發(fā)的時(shí)候通常還會(huì)混合另一種技術(shù):嵌入式語(yǔ)法 —— Embedded Languages Server ,實(shí)現(xiàn)復(fù)雜的多語(yǔ)言復(fù)合支持,如果有人感興趣,我們下周可以聊聊。


網(wǎng)站名稱:實(shí)例解析:如何開(kāi)發(fā) VSCode LSP 服務(wù)
文章源于:http://m.5511xx.com/article/dpgijps.html