Maison  >  Article  >  outils de développement  >  Pratique de développement de plug-in VSCode : implémentation d'un plug-in de diagnostic de code

Pratique de développement de plug-in VSCode : implémentation d'un plug-in de diagnostic de code

青灯夜游
青灯夜游avant
2022-02-16 20:01:184069parcourir

Cet article partagera avec vous un développement pratique de plug-in VSCode, développera un plug-in de diagnostic de code, analysera les principes de base et le mettra en œuvre étape par étape. J'espère qu'il sera utile à tout le monde !

Pratique de développement de plug-in VSCode : implémentation d'un plug-in de diagnostic de code

Récemment, nous avons publié un guide de révision du code en interne. Cependant, le processus de révision du code prend beaucoup de temps et les gens ne révisent pas le code trop attentivement. Nous souhaitons donc utiliser un plug-in pour permettre. aux développeurs de comprendre la méthode d'écriture pendant la phase de développement, l'effet est comme indiqué ci-dessous

Kapture 2022-02-10 at 15.36.49.gif

Ensuite, nous présenterons comment implémenter une telle fonction à partir de 0.

Principe de base

L'extension des fonctions du langage de programmation de Visual Studio Code est implémentée par Language Server. Après tout, la vérification des fonctions de langage consomme des performances et nécessite un autre processus pour servir de service de langage. . [Apprentissage recommandé : "Tutoriel d'introduction à vscode"]

Language Server est une extension spéciale de Visual Studio Code qui offre une expérience d'édition pour de nombreux langages de programmation. À l'aide d'un serveur de langage, vous pouvez implémenter la saisie semi-automatique, la vérification des erreurs (diagnostics), passer à la définition et de nombreuses autres fonctionnalités de langage prises en charge par VS Code.

Étant donné qu'il existe une fonction de vérification de la syntaxe fournie par le serveur, le client doit se connecter au serveur de langue puis interagir avec le serveur. Par exemple, l'utilisateur effectue une vérification de la langue lors de la modification du code sur le client. L'interaction spécifique est la suivante :

Pratique de développement de plug-in VSCode : implémentation dun plug-in de diagnostic de code

Lorsque le fichier Vue est ouvert, le plug-in sera activé et le serveur de langue sera démarré. Lorsque le document change, le serveur de langue re-diagnostiquera le code et. envoyer les résultats du diagnostic au client.

L'effet du diagnostic de code est une ligne ondulée et un message d'invite s'affiche lorsque la souris est déplacée vers le haut. S'il existe une solution rapide, un bouton de solution rapide apparaîtra sous la fenêtre d'invite contextuelle

Mains. -sur la mise en œuvre

Comprendre les principes de base du diagnostic de code Après cela, nous avons commencé à l'implémenter. À partir des principes de base ci-dessus, nous pouvons savoir que nous devons implémenter deux fonctions principales :

  • Interaction entre le client et le serveur de langage

    .
  • Fonction de diagnostic et de réparation rapide du serveur de langue

Client Le client interagit avec le serveur de langue

La documentation officielle fournit un exemple - un serveur de langue simple pour les fichiers de texte brut, que nous pouvons modifier en fonction de cet exemple.

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

Créez d'abord un serveur dans le client

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

Puis dans server/src/server.ts, écrivez la logique interactive sur le client, par exemple lorsque le document client change, vérifiez le code :

// 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);
}

Complete After le client interagit avec le serveur ci-dessus, vous pouvez remarquer ces deux méthodes getDiagnostics(textDocument, settings)quickfix(textDocument, params). Ces deux méthodes visent respectivement à fournir des données de diagnostic et des opérations de réparation rapide pour le document.

Diagnostic du code

Processus global

Pratique de développement de plug-in VSCode : implémentation dun plug-in de diagnostic de code

1 Convertir le document de code en un arbre de syntaxe AST

Lors du traitement du texte du code Vue transmis par le client, il doit être analysé dans vue/compilateur. -dom Les trois parties de la structure de données au format ast sont le modèle, JS et CSS. Étant donné que le code frontal utilise actuellement TypeScript, la partie JS n'est pas analysée dans AST, donc babel/parser doit être utilisé pour analyser le TypeScript. code pour générer la structure finale des données JS AST.

const VueParser = require(&#39;@vue/compiler-dom&#39;);
// 该函数返回诊断结果客户端
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: &#39;module&#39;,
    plugins: [
      &#39;typescript&#39;, // typescript
      [&#39;decorators&#39;, { decoratorsBeforeExport: true }], // 装饰器
      &#39;classProperties&#39;, // ES6 class 写法
      &#39;classPrivateProperties&#39;,
    ],
  });

La structure de l'arborescence de syntaxe AST obtenue est la suivante :

Modèle AST

Pratique de développement de plug-in VSCode : implémentation dun plug-in de diagnostic de code

JS AST

Pratique de développement de plug-in VSCode : implémentation dun plug-in de diagnostic de code

2. code dont nous avons besoin. Chaque nœud de code est vérifié pour déterminer s'il répond aux exigences de Code Review, il est donc nécessaire de parcourir l'arborescence syntaxique pour traiter chaque nœud.

Utilisez la recherche en profondeur d'abord pour parcourir l'AST du modèle :

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[]){
   // ...对代码节点检查
}

Et pour la traversée JS AST, vous pouvez utiliser la traversée babel/traverse :

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

3. 发现不合规代码,生成诊断

根据 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 === &#39;ClassMethod&#39; && 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: &#39;尽可能保持一个函数的单一职责原则,单个函数不宜超过 80 行&#39;,
                source: &#39;Code Review 指南&#39;,
            }
            diagnostics.push(diagnostic);
        }
        ...   
    }
});

文档中所有的诊断结果会保存在 diagnostics 数组中,最后通过交互返回给客户端。

4. 提供快速修复

上面那个函数过长的诊断没办法快速修复,如果能快速修复的话,可以将修正后的结果放在 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(
        &#39;vue template 上的属性顺序&#39;,
        range
      );
      if (newText) { // 如果有修复后的代码
        // 将快速修复数据保存在 diagnostic.data
        diagnostic.data = {
          title: &#39;按照 Code Review 指南的顺序修复&#39;,
          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 &#39;vscode-languageserver/node&#39;;
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);
}

总结

实现一个代码诊断的插件功能,需要两个步骤,首先建立语言服务器,并且建立客户端与语言服务器的交互。接着需要 服务器根据客户端的代码进行校验,把诊断结果放入 Diagnostics,快速修复结果放在 CodeActions,通过与客户端的通信,把两个结果返回给客户端,客户端即可出现黄色波浪线的问题提示。

更多关于VSCode的相关知识,请访问:vscode教程!!

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer