前言
通过一段时间的努力,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
当然没有的话,要下载,一条指令的事情。
然后的话,得到打包好的文件。然后到应用市场发布即可。