Hlang从零开始编写一个vscode代码提示插件

2023年 8月 22日 25.8k 0

前言

通过一段时间的努力,Hlang是开发完毕了,但是我们还需要为它提供一个IDE。那么从零开发显然是不可取的,这会大大加大开发难度。但是我们可以基于vscode这个神奇的物质。所以我们只需要编写一个插件就可以愉快玩耍了。

那么在这个章节,我们将实现,插件的编写,发布的话,打包之后注册账号就行。

okey,废话不多说,发车了。(PS:这里面的话还是有几个图标上的bug,懒得搞,能搞,但是不想调了,心累)

直接先来看到效果:

这个的话,是在测试模式下
在这里插入图片描述

之后的话,我们还可以发布到市场上:
(插件图标,目标文件图标都有问题,麻了)
在这里插入图片描述

Hlang适配

这里的话,为了适应这个插件,所以的话,把这个Hlang上面做了一个适配。原来主要是在终端上面显示,现在的话,要适配这个。当然我们先前是在终端交互的时候才能执行文件,现在的话改了,当然也还可以在终端执行文件。通过run_file函数。

这里的更新的话,就不多说了。直接看到仓库:
地址:gitee.com/Huterox/hla…
在这里插入图片描述

实现

okey,现在我们直接创建项目:
这里我们使用这个:

npm install -g yo generator-code

就用这个创建项目。

然后安装好之后,

yo code 

就会弹出提示,按照提示创建即可。我这里创建好了是这样的:
在这里插入图片描述

语法提示

okey,那么在这里的话,我们先实现这个语法提示。
首先我们这里的话有一个入口,叫做这个:
在这里插入图片描述
然后主要实现里面的:
在这里插入图片描述
还有:
在这里插入图片描述
我这里一共是实现了两个个部分,第一个是语法提示,第二个是运行。高亮的话不是这里实现的(虽然也可以)

package.json配置

那么这里的话,我们先来看到这个package.json文件,因为里面很多配置都是在这里的。这里没搞好跑不起来。
那么这里面的话,先直接看到全部的文件:

{
  "name": "hlang",
  "displayName": "Hlang",
  "publisher": "Hlang",
  "description": "",
  "version": "0.0.1",
  "engines": {
    "vscode": "^1.54.0"
  },
  "categories": [
    "Other"
  ],
  "activationEvents": [

  ],
  "main": "./out/extension.js",
  "contributes": {
    "commands": [
      {
        "command": "hlang.code",
        "title": "Hlang"
      }
    ],
    "languages": [{
      "id": "hlang",
      "aliases": ["Hlang-Supports", "hlang"],
      "extensions": [".lang"],
      "configuration": "./.vscode/language-configuration.json"
    }],
    "grammars": [
      {
        "language": "hlang",
        "scopeName": "source.hlang",
        "path": "./syntaxes/hlang.tmLanguage.json"
      }
    ],
    "iconThemes": [
      {
        "id": "hlang-icons",
        "label": "Hlang Icons",
        "path": "./icons/icon.png"
      }
    ],
    "files.associations": {
      "*.lang": "hlang"
    }
  },
  "scripts": {
    "vscode:prepublish": "npm run compile",
    "compile": "tsc -p ./",
    "watch": "tsc -watch -p ./",
    "pretest": "npm run compile && npm run lint",
    "lint": "eslint src --ext ts",
    "test": "node ./out/test/runTest.js"
  },
  "devDependencies": {
    "@types/vscode": "^1.54.0",
    "@types/glob": "^8.1.0",
    "@types/mocha": "^10.0.1",
    "@types/node": "20.2.5",
    "@typescript-eslint/eslint-plugin": "^5.59.8",
    "@typescript-eslint/parser": "^5.59.8",
    "eslint": "^8.41.0",
    "glob": "^8.1.0",
    "mocha": "^10.2.0",
    "typescript": "^5.1.3",
    "@vscode/test-electron": "^2.3.2"
  }
}

  • "name": "hlang":插件的名称。
  • "displayName": "Hlang":插件在 Visual Studio Code 中显示的名称。
  • "publisher": "Hlang":插件的发布者名称。
  • "description": "":插件的描述信息。
  • "version": "0.0.1":插件的版本号。
  • "engines": {"vscode": "^1.54.0"}:指定插件兼容的 Visual Studio Code 版本范围。
  • "categories": ["Other"]:指定插件所属的分类。
  • "activationEvents": []:指定插件的激活事件。
  • "main": "./out/extension.js":指定插件的入口文件路径。
  • "contributes":插件的贡献信息,包括命令、语言支持、语法定义、图标主题和文件关联等。
    • "commands":定义插件的命令。
    • "languages":定义插件支持的语言。
    • "grammars":定义插件的语法规则。
    • "iconThemes":定义插件的图标主题。
    • "files.associations":定义插件的文件关联。
  • "scripts":定义插件的脚本命令,比如编译、测试和代码检查等。
  • "devDependencies":定义插件的开发依赖项。

编写提示

然后编写提示,这里的话,我们主要是要实现这个东西:
在这里插入图片描述
然后的话,我们要把这个注入进去:
在这里插入图片描述
下面这个是入口的完整代码:


import * as vscode from 'vscode';
import { HlangCompletionProvider } from './languages/hlangCompletionProvider';
export function activate(context: vscode.ExtensionContext) {


	console.log('Congratulations, your extension "hlang" is now active!');


	let disposable = vscode.commands.registerCommand('hlang.code', () => {

		vscode.window.showInformationMessage('欢迎使用Hlang!');
	});

	let provider = vscode.languages.registerCompletionItemProvider(
		{ scheme: 'file', pattern: '**/*.lang' }, 
		new HlangCompletionProvider(), '.');


	
	//运行
	let start =  vscode.debug.onDidStartDebugSession((debugSession) => {
		const activeEditor = vscode.window.activeTextEditor;
		if (activeEditor) {
		  const fileName = activeEditor.document.fileName;
		  const filePath = activeEditor.document.uri.fsPath;
	
		  if (!fileName.endsWith('.lang')) {
			vscode.window.showErrorMessage('不支持当前文件。');
			return;
		  }
	
		  const terminal = vscode.window.createTerminal();
		  terminal.sendText(`Hlang.exe --f ${filePath}`);
		  terminal.show();
		} else {
		  vscode.window.showInformationMessage('No active editor found.');
		}
	  });

	context.subscriptions.push(provider);
	context.subscriptions.push(disposable);
	context.subscriptions.push(start);
}


// This method is called when your extension is deactivated
export function deactivate() { }

所以实现提示的话,任务就是,把关键词搞进去,其他的Vscode会自动帮我匹配。

import * as vscode from 'vscode';

const KEYWORDS = [
    'var',
    '设',
    'and',
    '且',
    'or',
    '或',
    'not',
    '否',
    'if',
    '如果',
    'elif',
    '再者',
    'else',
    '不然',
    'for',
    '遍历',
    'to',
    '到',
    'step',
    '步长',
    'while',
    '循环',
    'fun',
    '函数',
    'then',
    '就',
    'end',
    '结束',
    'return',
    '返回',
    'continue',
    '继续',
    'break',
    '终止'
];

const BUILTIN_FUNCTIONS = {
    'print()': '输出函数:在终端输出内容,例:print("Hello")',
    'print_ret()': '与print()函数功能类似,但是具备返回值',
    'run_file()': '执行脚本文件,例:run_file("hello.lang")',
    'input()': '输入函数,获取用户在终端的输入,得到的结果为字符串类型',
    'input_int()': '输入函数,获取用户在终端的输入,得到的结果为整数类型',
    'clear()': '清空终端输出',
    'is_num()': '检查是否为数字',
    'is_str()': '检查是否为字符串',
    'is_list()': '检查是否为列表',
    'is_fun()': '检查是否为函数',
    'append()': '向列表末尾添加元素',
    'pop()': '从列表末尾弹出元素',
    'extend()': '将一个列表的元素添加到另一个列表',
    'len()': '获取列表的长度'
};

export class HlangCompletionProvider implements vscode.CompletionItemProvider {
    provideCompletionItems(
        document: vscode.TextDocument,
        position: vscode.Position,
        token: vscode.CancellationToken,
        context: vscode.CompletionContext
    ): vscode.ProviderResult {

        
        

        // 获取当前单词的起始和结束位置
        const wordRange = document.getWordRangeAtPosition(position);
        const currentWord = wordRange ? document.getText(wordRange) : '';

        const completions: vscode.CompletionItem[] = [];

        // 根据关键字列表添加关键字提示
        KEYWORDS.forEach(keyword => {
            if (keyword.startsWith(currentWord)) {
                completions.push({
                    label: keyword,
                    kind: vscode.CompletionItemKind.Keyword,
                    insertText: keyword,
                    range: wordRange
                });
            }
        });

        // 添加内置函数提示
        Object.entries(BUILTIN_FUNCTIONS).forEach(([funcName, funcDesc]) => {
            const completionItem = new vscode.CompletionItem(funcName, vscode.CompletionItemKind.Function);
            completionItem.insertText = funcName;
            completionItem.range = wordRange;
            completionItem.documentation = new vscode.MarkdownString(funcDesc);
            completions.push(completionItem);
        });

        return completions;
    }
}

那么这里的话,语法提示就好了。

语法配置

这个提示是做好了。但是还有一些缩进,啥的没做。看到这个里:
在这里插入图片描述
所以的话,我们在这里还有个文件:
在这里插入图片描述

{
    "comments": {
      "lineComment": "#"
    },
    "brackets": [
      ["[", "]"],
      ["(", ")"]
    ],
    "autoClosingPairs": [
      { "open": "[", "close": "]" },
      { "open": "(", "close": ")" }
    ],
    "surroundingPairs": [
      { "open": "[", "close": "]" },
      { "open": "(", "close": ")" }
    ],
    "indentationRules": {
        "increaseIndentPattern": "^(.*\b(if|如果|elif|再者|else|不然)\b.*)$",
        "decreaseIndentPattern": "^\s*\}.*$"
      }
  }
  

语法高亮

之后的话,就是语法高亮。
这个没啥,直接生成。

{
  "name": "Hlang",
  "scopeName": "source.hlang",
  "fileTypes": ["hlang"],
  "patterns": [
    {
      "include": "#comments"
    },
    {
      "include": "#strings"
    },
    {
      "include": "#keywords"
    },
    {
      "include": "#functions"
    }
  ],
  "repository": {
    "comments": {
      "patterns": [
        {
          "name": "comment.line.number-sign.hlang",
          "match": "#.*$"
        }
      ]
    },
    "strings": {
      "patterns": [
        {
          "name": "string.quoted.double.hlang",
          "begin": """,
          "end": """,
          "patterns": [
            {
              "name": "constant.character.escape.hlang",
              "match": "\\(["\\]|[0-7]{1,3}|x[a-fA-F0-9]{2}|u[a-fA-F0-9]{4}|U[a-fA-F0-9]{8})"
            }
          ]
        },
        {
          "name": "string.quoted.single.hlang",
          "begin": "'",
          "end": "'",
          "patterns": [
            {
              "name": "constant.character.escape.hlang",
              "match": "\\(['\\]|[0-7]{1,3}|x[a-fA-F0-9]{2}|u[a-fA-F0-9]{4}|U[a-fA-F0-9]{8})"
            }
          ]
        }
      ]
    },
    "keywords": {
      "patterns": [
        {
          "name": "keyword.control.var.hlang",
          "match": "\b(var|设)\b"
        },
        {
          "name": "keyword.control.and.hlang",
          "match": "\b(and|且)\b"
        },
        {
          "name": "keyword.control.or.hlang",
          "match": "\b(or|或)\b"
        },
        {
          "name": "keyword.control.not.hlang",
          "match": "\b(not|否)\b"
        },
        {
          "name": "keyword.control.if.hlang",
          "match": "\b(if|如果)\b"
        },
        {
          "name": "keyword.control.elif.hlang",
          "match": "\b(elif|再者)\b"
        },
        {
          "name": "keyword.control.else.hlang",
          "match": "\b(else|不然)\b"
        },
        {
          "name": "keyword.control.for.hlang",
          "match": "\b(for|遍历)\b"
        },
        {
          "name": "keyword.control.to.hlang",
          "match": "\b(to|到)\b"
        },
        {
          "name": "keyword.control.step.hlang",
          "match": "\b(step|步长)\b"
        },
        {
          "name": "keyword.control.while.hlang",
          "match": "\b(while|循环)\b"
        },
        {
          "name": "keyword.control.fun.hlang",
          "match": "\b(fun|函数)\b"
        },
        {
          "name": "keyword.control.then.hlang",
          "match": "\b(then|就)\b"
        },
        {
          "name": "keyword.control.end.hlang",
          "match": "\b(end|结束)\b"
        },
        {
          "name": "keyword.control.return.hlang",
          "match": "\b(return|返回)\b"
        },
        {
          "name": "keyword.control.continue.hlang",
          "match": "\b(continue|继续)\b"
        },
        {
          "name": "keyword.control.break.hlang",
          "match": "\b(break|终止)\b"
        }
      ]
    },
    "functions": {
      "patterns": [
        {
          "name": "support.function.builtin.hlang",
          "match": "(print|print_ret|run_file|input|input_int|clear|is_num|is_str|is_list|is_fun|append|pop|extend|len)\s*\(",
          "captures": {
            "1": {
              "name": "variable.function.name.hlang",
              "match": "(print|print_ret|run_file|input|input_int|clear|is_num|is_str|is_list|is_fun|append|pop|extend|len)"
            },
            "2": {
              "name": "punctuation.parenthesis.begin.hlang",
              "match": "\("
            }
          }
        }
      ]
    }
  },
  "colors": {
    "comment.line.number-sign.hlang": "#808080",
    "string.quoted.double.hlang": "#BA2121",
    "string.quoted.single.hlang": "#BA2121",
    "constant.character.escape.hlang": "#800080",
    "keyword.control.var.hlang": "#FF0000",
    "keyword.control.and.hlang": "#FF0000",
    "keyword.control.or.hlang": "#FF0000",
    "keyword.control.not.hlang": "#FF0000",
    "keyword.control.if.hlang": "#0000FF",
    "keyword.control.elif.hlang": "#0000FF",
    "keyword.control.else.hlang": "#0000FF",
    "keyword.control.for.hlang": "#FFFF00",
    "keyword.control.to.hlang": "#FFFF00",
    "keyword.control.step.hlang": "#FFFF00",
    "keyword.control.while.hlang": "#FF00FF",
    "keyword.control.fun.hlang": "#00FFFF",
    "keyword.control.then.hlang": "#00FFFF",
    "keyword.control.end.hlang": "#00FFFF",
    "keyword.control.return.hlang": "#008000",
    "keyword.control.continue.hlang": "#008000",
    "keyword.control.break.hlang": "#008000",
    "support.function.builtin.hlang": "#0057ae",
    "variable.function.name.hlang": "#0057ae",
    "punctuation.parenthesis.begin.hlang": "#000000"
  }
}

实现效果就是和Python有点类似。没错就是把关键字替换了。
然后写好之后,在package.json里面配置好。

之后的话,就是图标啥的,这里的话搞了好久,没搞定,老是有问题。

执行

那么之后的话,就是执行,这个没啥:
入口文件里面(前面贴出来了)
在这里面:在这里插入图片描述
就是获取当前的文件,符号要求的,然后执行终端指令。前提是配置好了Hlang环境。

打包

之后的话,就是打包了,这个很简单。就是:

vsce package

当然没有的话,要下载,一条指令的事情。

然后的话,得到打包好的文件。然后到应用市场发布即可。

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论