這篇文章主要講解了“VSCode怎么實現一個代碼診斷插件”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“VSCode怎么實現一個代碼診斷插件”吧!

成都創新互聯自2013年創立以來,先為山南等服務建站,山南等地企業,進行企業商務咨詢服務。為山南企業網站制作PC+手機+微官網三網同步一站式服務解決您的所有建站問題。
Visual Studio Code 的編程語言功能擴展是有 Language Server 來實現的,這很好理解,畢竟檢查語言功能是耗費性能的,需要另起一個進程來作為語言服務,這就是 Language Server 語言服務器。
Language Server 是一種特殊的 Visual Studio Code 擴展,可為許多編程語言提供編輯體驗。使用語言服務器,您可以實現自動完成、錯誤檢查(診斷)、跳轉到定義以及VS Code 支持的許多其他語言功能。
既然有了服務器提供的語法檢查功能,就需要客戶端去連接語言服務器,然后和服務器進行交互,比如用戶在客戶端進行代碼編輯時,進行語言檢查。
當打開 Vue 文件時會激活插件,此時就會啟動 Language Server,當文檔發生變化時,語言服務器就會重新診斷代碼,并把診斷結果發送給客戶端。
代碼診斷的效果是出現波浪線,鼠標移上顯示提示消息,如果有快速修復,會在彈出提示的窗口下出現快速修復的按鈕
了解了代碼診斷的基本原理之后,開始動手實現,從上面的基本原理可知,我們需要實現兩大部分的功能:
客戶端與語言服務器交互
語言服務器的診斷和快速修復功能
官方文檔 提供了一個示例 - 用于純文本文件的簡單語言服務器,我們可以在這個示例的基礎上去修改。
> git clone https://github.com/microsoft/vscode-extension-samples.git > cd vscode-extension-samples/lsp-sample > npm install > npm run compile > code .
首先在 client 建立服務器
// client/src/extension.ts
export function activate(context: ExtensionContext) {
...
const clientOptions: LanguageClientOptions = {
documentSelector: [{ scheme: 'file', language: 'vue' }], // 打開 vue 文件時才激活
...
};
client = new LanguageClient(...);
client.start();
}接著在 server/src/server.ts 中,編寫于客戶端的交互邏輯,比如在客戶端文檔發生變化的時候,校驗代碼:
// 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 => {
// 文檔發生變化時,校驗文檔
validateTextDocument(change.document);
});
async function validateTextDocument(textDocument: TextDocument): Promise<void> {
...
// 拿到診斷結果
const diagnostics = getDiagnostics(textDocument, settings);
// 發給客戶端
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
}
// 提供快速修復的操作
connection.onCodeAction(provideCodeActions);
async function provideCodeActions(params: CodeActionParams): Promise<CodeAction[]> {
...
return quickfix(textDocument, params);
}在完成上面客戶端與服務端交互之后,可以注意到這兩個方法 getDiagnostics(textDocument, settings) 和 quickfix(textDocument, params)。 這兩個方法分別是為文檔提供診斷數據和快速修復的操作。
在處理客戶端傳遞過來的 Vue 代碼文本的,需要通過 vue/compiler-dom 解析成三部分 ast 格式的數據結構,分別是 template、JS、CSS, 由于現在前端代碼使用的都是 TypeScript,JS 部分沒有解析成 AST,因此需要使用 babel/parser 去解析 TypeScript 代碼生成最終的 JS 的 AST 數據結構。
const VueParser = require('@vue/compiler-dom');
// 該函數返回診斷結果客戶端
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 得到診斷結果
...analyzeScript(script, textDocument), // 解析 js 得到診斷結果
];
}
// 分析 js 語法
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',
],
});在得到代碼的語法樹之后,我們需要對每一個代碼節點進行檢查,來判斷是否符合 Code Review 的要求,因此需要遍歷語法樹來對每個節點處理。
使用深度優先搜索對 template 的 AST 進行遍歷:
function deepLoopData(
data: AstTemplateInterface[],
handler: Function,
diagnostics: Diagnostic[],
) {
function dfs(data: AstTemplateInterface[]) {
for (let i = 0; i < data.length; i++) {
handler(data[i], diagnostics); // 在這一步對代碼進行處理
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[]){
// ...對代碼節點檢查
}而對于 JS AST 遍歷,可以使用 babel/traverse 遍歷:
traverse(scriptAst, {
enter(path: any) {
...
}
}根據 ast 語法節點去判斷語法是否合規,如果不符合要求,需要在代碼處生成診斷,一個基礎的診斷對象(diagnostics)包括下面幾個屬性:
range: 診斷有問題的范圍,也就是畫波浪線的地方
severity: 嚴重性,分別有四個等級,不同等級標記的顏色不同,分別是:
Error: 1
Warning: 2
Information:3
Hint:4
message: 診斷的提示信息
source: 來源,比如說來源是 Eslint
data:攜帶數據,可以將修復好的數據放在這里,用于后面的快速修復功能
比如實現一個提示函數過長的診斷:
function isLongFunction(node: Record<string, any>) {
return (
// 如果結束位置的行 - 開始位置的行 > 80 的話,我們認為這個函數寫得太長了
node.type === 'ClassMethod' && node.loc.end.line - node.loc.start.line > 80
);
}在遍歷 AST 時如果遇到某個節點是出現函數過長的時候,就往診斷數據中添加此診斷
traverse(scriptAst, {
enter(path: any) {
const { node } = path;
if (isLongFunction(node)) {
const diagnostic: Diagnostic ={
severity: DiagnosticSeverity.Warning,
range: getPositionRange(node, scriptStart),
message: '盡可能保持一個函數的單一職責原則,單個函數不宜超過 80 行',
source: 'Code Review 指南',
}
diagnostics.push(diagnostic);
}
...
}
});文檔中所有的診斷結果會保存在 diagnostics 數組中,最后通過交互返回給客戶端。
上面那個函數過長的診斷沒辦法快速修復,如果能快速修復的話,可以將修正后的結果放在 diagnostics.data 。換個例子寫一個快速修復, 比如 Vue template 屬性排序不正確,我們需要把代碼自動修復
// attributeOrderValidator 得到判斷結果 和 修復后的代碼
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) { // 如果有修復后的代碼
// 將快速修復數據保存在 diagnostic.data
diagnostic.data = {
title: '按照 Code Review 指南的順序修復',
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) { // 如果有快速修復數據
// 添加快速修復
codeActions.push({
title: (diag.data as any)?.title,
kind: CodeActionKind.QuickFix, // 快速修復
diagnostics: [diag], // 屬于哪個診斷的操作
edit: {
changes: {
[params.textDocument.uri]: [
{
range: diag.range,
newText: (diag.data as any)?.newText, // 修復后的內容
},
],
},
},
});
}
}
});有快速修復的診斷會保存在 codeActions 中,并且返回給客戶端, 重新回看交互的代碼,在 documents.onDidChangeContent 事件中,通過 connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }) 把診斷發送給客戶端。quickfix 結果通過 connection.onCodeAction 發給客戶端。
import {
createConnection
TextDocuments,
ProposedFeatures,
...
} from 'vscode-languageserver/node';
const connection = createConnection(ProposedFeatures.all);
const documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);
documents.onDidChangeContent(change => {
...
// 拿到診斷結果
const diagnostics = getDiagnostics(textDocument, settings);
// 發給客戶端
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
});
// 提供快速修復的操作
connection.onCodeAction(provideCodeActions);
async function provideCodeActions(params: CodeActionParams): Promise<CodeAction[]> {
...
return quickfix(textDocument, params);
}感謝各位的閱讀,以上就是“VSCode怎么實現一個代碼診斷插件”的內容了,經過本文的學習后,相信大家對VSCode怎么實現一個代碼診斷插件這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創新互聯,小編將為大家推送更多相關知識點的文章,歡迎關注!
文章標題:VSCode怎么實現一個代碼診斷插件
轉載源于:http://www.yijiale78.com/article32/gcspsc.html
成都網站建設公司_創新互聯,為您提供App開發、云服務器、外貿建站、商城網站、微信公眾號、網站排名
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯