久久久久久久视色,久久电影免费精品,中文亚洲欧美乱码在线观看,在线免费播放AV片

<center id="vfaef"><input id="vfaef"><table id="vfaef"></table></input></center>

    <p id="vfaef"><kbd id="vfaef"></kbd></p>

    
    
    <pre id="vfaef"><u id="vfaef"></u></pre>

      <thead id="vfaef"><input id="vfaef"></input></thead>

    1. 站長(zhǎng)資訊網(wǎng)
      最全最豐富的資訊網(wǎng)站

      VSCode插件開發(fā)實(shí)戰(zhàn):實(shí)現(xiàn)一個(gè)代碼診斷插件

      本篇文章給大家分享一個(gè)VSCode插件開發(fā)實(shí)戰(zhàn),開發(fā)一個(gè)代碼診斷插件,分析一下基本原理,并一步步實(shí)現(xiàn),希望對(duì)大家有所幫助!

      VSCode插件開發(fā)實(shí)戰(zhàn):實(shí)現(xiàn)一個(gè)代碼診斷插件

      最近,我們內(nèi)部出了一份 Code Review 指南,但是 Code Review 過(guò)程非常占時(shí)間,大家不會(huì)太仔細(xì)去 review 代碼,因此想通過(guò)一個(gè)插件讓開發(fā)者在開發(fā)階段就能感知到寫法的錯(cuò)誤,做出的效果如下圖

      VSCode插件開發(fā)實(shí)戰(zhàn):實(shí)現(xiàn)一個(gè)代碼診斷插件

      接下來(lái)將介紹如何從 0 實(shí)現(xiàn)這么一個(gè)功能。

      基本原理

      Visual Studio Code 的編程語(yǔ)言功能擴(kuò)展是有 Language Server 來(lái)實(shí)現(xiàn)的,這很好理解,畢竟檢查語(yǔ)言功能是耗費(fèi)性能的,需要另起一個(gè)進(jìn)程來(lái)作為語(yǔ)言服務(wù),這就是 Language Server 語(yǔ)言服務(wù)器?!就扑]學(xué)習(xí):《vscode入門教程》】

      Language Server 是一種特殊的 Visual Studio Code 擴(kuò)展,可為許多編程語(yǔ)言提供編輯體驗(yàn)。使用語(yǔ)言服務(wù)器,您可以實(shí)現(xiàn)自動(dòng)完成、錯(cuò)誤檢查(診斷)、跳轉(zhuǎn)到定義以及VS Code 支持的許多其他語(yǔ)言功能。

      既然有了服務(wù)器提供的語(yǔ)法檢查功能,就需要客戶端去連接語(yǔ)言服務(wù)器,然后和服務(wù)器進(jìn)行交互,比如用戶在客戶端進(jìn)行代碼編輯時(shí),進(jìn)行語(yǔ)言檢查。具體交互如下:

      VSCode插件開發(fā)實(shí)戰(zhàn):實(shí)現(xiàn)一個(gè)代碼診斷插件

      當(dāng)打開 Vue 文件時(shí)會(huì)激活插件,此時(shí)就會(huì)啟動(dòng) Language Server,當(dāng)文檔發(fā)生變化時(shí),語(yǔ)言服務(wù)器就會(huì)重新診斷代碼,并把診斷結(jié)果發(fā)送給客戶端。

      代碼診斷的效果是出現(xiàn)波浪線,鼠標(biāo)移上顯示提示消息,如果有快速修復(fù),會(huì)在彈出提示的窗口下出現(xiàn)快速修復(fù)的按鈕

      動(dòng)手實(shí)現(xiàn)

      了解了代碼診斷的基本原理之后,開始動(dòng)手實(shí)現(xiàn),從上面的基本原理可知,我們需要實(shí)現(xiàn)兩大部分的功能:

      • 客戶端與語(yǔ)言服務(wù)器交互

      • 語(yǔ)言服務(wù)器的診斷和快速修復(fù)功能

      客戶端與語(yǔ)言服務(wù)器交互

      官方文檔 提供了一個(gè)示例 – 用于純文本文件的簡(jiǎn)單語(yǔ)言服務(wù)器,我們可以在這個(gè)示例的基礎(chǔ)上去修改。

      > git clone https://github.com/microsoft/vscode-extension-samples.git > cd vscode-extension-samples/lsp-sample > npm install > npm run compile > code .

      首先在 client 建立服務(wù)器

      // client/src/extension.ts export function activate(context: ExtensionContext) {     ...     const clientOptions: LanguageClientOptions = {         documentSelector: [{ scheme: 'file', language: 'vue' }], // 打開 vue 文件時(shí)才激活         ...     };     client = new LanguageClient(...);     client.start(); }

      接著在 server/src/server.ts 中,編寫于客戶端的交互邏輯,比如在客戶端文檔發(fā)生變化的時(shí)候,校驗(yàn)代碼:

      // server/src/server.ts import {     createConnection     TextDocuments,     ProposedFeatures,     ... } from 'vscode-languageserver/node'; const connection = createConnection(ProposedFeatures.all); const documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument); documents.onDidChangeContent(change => {     // 文檔發(fā)生變化時(shí),校驗(yàn)文檔     validateTextDocument(change.document); });  async function validateTextDocument(textDocument: TextDocument): Promise<void> {     ...     // 拿到診斷結(jié)果     const diagnostics = getDiagnostics(textDocument, settings);     // 發(fā)給客戶端     connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); } // 提供快速修復(fù)的操作 connection.onCodeAction(provideCodeActions);  async function provideCodeActions(params: CodeActionParams): Promise<CodeAction[]> {     ...     return quickfix(textDocument, params); }

      在完成上面客戶端與服務(wù)端交互之后,可以注意到這兩個(gè)方法 getDiagnostics(textDocument, settings)quickfix(textDocument, params)。 這兩個(gè)方法分別是為文檔提供診斷數(shù)據(jù)和快速修復(fù)的操作。

      代碼診斷

      整體流程

      VSCode插件開發(fā)實(shí)戰(zhàn):實(shí)現(xiàn)一個(gè)代碼診斷插件

      1. 將代碼文檔轉(zhuǎn)成 AST 語(yǔ)法樹

      在處理客戶端傳遞過(guò)來(lái)的 Vue 代碼文本的,需要通過(guò) vue/compiler-dom 解析成三部分 ast 格式的數(shù)據(jù)結(jié)構(gòu),分別是 template、JS、CSS, 由于現(xiàn)在前端代碼使用的都是 TypeScript,JS 部分沒有解析成 AST,因此需要使用 babel/parser 去解析 TypeScript 代碼生成最終的 JS 的 AST 數(shù)據(jù)結(jié)構(gòu)。

      const VueParser = require('@vue/compiler-dom'); // 該函數(shù)返回診斷結(jié)果客戶端 function getDiagnostics(textDocument: TextDocument, settings: any): Diagnostic[] { 	const text = textDocument.getText(); 	const res = VueParser.parse(text); 	const [template, script] = res.children; 	return [ 		...analyzeTemplate(template), // 解析 template 得到診斷結(jié)果 		...analyzeScript(script, textDocument), // 解析 js 得到診斷結(jié)果 	]; } // 分析 js 語(yǔ)法 function analyzeScript(script: any, textDocument: TextDocument) {   const scriptAst = parser.parse(script.children[0]?.content, {     sourceType: 'module',     plugins: [       'typescript', // typescript       ['decorators', { decoratorsBeforeExport: true }], // 裝飾器       'classProperties', // ES6 class 寫法       'classPrivateProperties',     ],   });

      得到的 AST 語(yǔ)法樹結(jié)構(gòu)如下:

      Template AST

      VSCode插件開發(fā)實(shí)戰(zhàn):實(shí)現(xiàn)一個(gè)代碼診斷插件

      JS AST

      VSCode插件開發(fā)實(shí)戰(zhàn):實(shí)現(xiàn)一個(gè)代碼診斷插件

      2. 遍歷語(yǔ)法樹對(duì)代碼校驗(yàn)

      在得到代碼的語(yǔ)法樹之后,我們需要對(duì)每一個(gè)代碼節(jié)點(diǎn)進(jìn)行檢查,來(lái)判斷是否符合 Code Review 的要求,因此需要遍歷語(yǔ)法樹來(lái)對(duì)每個(gè)節(jié)點(diǎn)處理。

      使用深度優(yōu)先搜索對(duì) template 的 AST 進(jìn)行遍歷:

      function deepLoopData(   data: AstTemplateInterface[],   handler: Function,   diagnostics: Diagnostic[], ) {   function dfs(data: AstTemplateInterface[]) {     for (let i = 0; i < data.length; i++) {       handler(data[i], diagnostics); // 在這一步對(duì)代碼進(jìn)行處理       if (data[i]?.children?.length) {         dfs(data[i].children);       } else {         continue;       }     }   }   dfs(data); }  function analyzeTemplate(template: any) {   const diagnostics: Diagnostic[] = [];   deepLoopData(template.children, templateHandler, diagnostics);   return diagnostics; } function templateHandler(currData: AstTemplateInterface, diagnostics: Diagnostic[]){    // ...對(duì)代碼節(jié)點(diǎn)檢查 }

      而對(duì)于 JS AST 遍歷,可以使用 babel/traverse 遍歷:

       traverse(scriptAst, {     enter(path: any) {       ...     }  }

      3. 發(fā)現(xiàn)不合規(guī)代碼,生成診斷

      根據(jù) ast 語(yǔ)法節(jié)點(diǎn)去判斷語(yǔ)法是否合規(guī),如果不符合要求,需要在代碼處生成診斷,一個(gè)基礎(chǔ)的診斷對(duì)象(diagnostics)包括下面幾個(gè)屬性:

      • range: 診斷有問(wèn)題的范圍,也就是畫波浪線的地方

      • severity: 嚴(yán)重性,分別有四個(gè)等級(jí),不同等級(jí)標(biāo)記的顏色不同,分別是:

        • Error: 1
        • Warning: 2
        • Information:3
        • Hint:4
      • message: 診斷的提示信息

      • source: 來(lái)源,比如說(shuō)來(lái)源是 Eslint

      • data:攜帶數(shù)據(jù),可以將修復(fù)好的數(shù)據(jù)放在這里,用于后面的快速修復(fù)功能

      比如實(shí)現(xiàn)一個(gè)提示函數(shù)過(guò)長(zhǎng)的診斷:

      function isLongFunction(node: Record<string, any>) {   return (     // 如果結(jié)束位置的行 - 開始位置的行 > 80 的話,我們認(rèn)為這個(gè)函數(shù)寫得太長(zhǎng)了     node.type === 'ClassMethod' && node.loc.end.line - node.loc.start.line > 80   ); }

      在遍歷 AST 時(shí)如果遇到某個(gè)節(jié)點(diǎn)是出現(xiàn)函數(shù)過(guò)長(zhǎng)的時(shí)候,就往診斷數(shù)據(jù)中添加此診斷

      traverse(scriptAst, {     enter(path: any) {         const { node } = path;         if (isLongFunction(node)) {             const diagnostic: Diagnostic ={                 severity: DiagnosticSeverity.Warning,                 range: getPositionRange(node, scriptStart),                 message: '盡可能保持一個(gè)函數(shù)的單一職責(zé)原則,單個(gè)函數(shù)不宜超過(guò) 80 行',                 source: 'Code Review 指南',             }             diagnostics.push(diagnostic);         }         ...        } });

      文檔中所有的診斷結(jié)果會(huì)保存在 diagnostics 數(shù)組中,最后通過(guò)交互返回給客戶端。

      4. 提供快速修復(fù)

      上面那個(gè)函數(shù)過(guò)長(zhǎng)的診斷沒辦法快速修復(fù),如果能快速修復(fù)的話,可以將修正后的結(jié)果放在 diagnostics.data 。換個(gè)例子寫一個(gè)快速修復(fù), 比如 Vue template 屬性排序不正確,我們需要把代碼自動(dòng)修復(fù)

      // attributeOrderValidator 得到判斷結(jié)果 和 修復(fù)后的代碼 const {isGoodSort, newText} = attributeOrderValidator(props, currData.loc.source);     if (!isGoodSort) {       const range = {         start: {           line: props[0].loc.start.line - 1,           character: props[0].loc.start.column - 1,         },         end: {           line: props[props.length - 1].loc.end.line - 1,           character: props[props.length - 1].loc.end.column - 1,         },       }       let diagnostic: Diagnostic = genDiagnostics(         'vue template 上的屬性順序',         range       );       if (newText) { // 如果有修復(fù)后的代碼         // 將快速修復(fù)數(shù)據(jù)保存在 diagnostic.data         diagnostic.data = {           title: '按照 Code Review 指南的順序修復(fù)',           newText,         }       }       diagnostics.push(diagnostic);     }

      quickfix(textDocument, params)

      export function quickfix(   textDocument: TextDocument,   params: CodeActionParams ): CodeAction[] {   const diagnostics = params.context.diagnostics;   if (isNullOrUndefined(diagnostics) || diagnostics.length === 0) {     return [];   }   const codeActions: CodeAction[] = [];   diagnostics.forEach((diag) => {     if (diag.severity === DiagnosticSeverity.Warning) {       if (diag.data) { // 如果有快速修復(fù)數(shù)據(jù)         // 添加快速修復(fù)         codeActions.push({           title: (diag.data as any)?.title,           kind: CodeActionKind.QuickFix, // 快速修復(fù)           diagnostics: [diag], // 屬于哪個(gè)診斷的操作           edit: {             changes: {                 [params.textDocument.uri]: [                   {                     range: diag.range,                     newText: (diag.data as any)?.newText, // 修復(fù)后的內(nèi)容                   },                 ],               },            },         });     }    } });

      有快速修復(fù)的診斷會(huì)保存在 codeActions 中,并且返回給客戶端, 重新回看交互的代碼,在 documents.onDidChangeContent 事件中,通過(guò) connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }) 把診斷發(fā)送給客戶端。quickfix 結(jié)果通過(guò) connection.onCodeAction 發(fā)給客戶端。

      import {     createConnection     TextDocuments,     ProposedFeatures,     ... } from 'vscode-languageserver/node'; const connection = createConnection(ProposedFeatures.all); const documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument); documents.onDidChangeContent(change => {     ...     // 拿到診斷結(jié)果     const diagnostics = getDiagnostics(textDocument, settings);     // 發(fā)給客戶端     connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); });  // 提供快速修復(fù)的操作 connection.onCodeAction(provideCodeActions);  async function provideCodeActions(params: CodeActionParams): Promise<CodeAction[]> {     ...     return quickfix(textDocument, params); }

      總結(jié)

      實(shí)現(xiàn)一個(gè)代碼診斷的插件功能,需要兩個(gè)步驟,首先建立語(yǔ)言服務(wù)器,并且建立客戶端與語(yǔ)言服務(wù)器的交互。接著需要 服務(wù)器根據(jù)客戶端的代碼進(jìn)行校驗(yàn),把診斷結(jié)果放入 Diagnostics,快速修復(fù)結(jié)果放在 CodeActions,通過(guò)與客戶端的通信,把兩個(gè)結(jié)果返回給客戶端,客戶端即可出現(xiàn)黃色波浪線的問(wèn)題提示。

      贊(0)
      分享到: 更多 (0)
      網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)