feat: IDE支持 — workspace拆分、LSP服务器、VS Code扩展

**Workspace 拆分**
- aster-core: 纯库,zero-dependency,包含 lexer/parser/ast/interpreter/error/analysis
- aster: REPL 二进制,薄封装 aster-core
- aster-lsp: LSP 语言服务器

**VS Code 扩展 (vscode-ext/)**
- TextMate 语法高亮 (.ast 文件)
- 语言配置 (注释切换、括号配对、自动缩进)
- LSP 客户端 (extension.js)

**LSP 服务端功能**
- Diagnostics: 实时显示 lex/parse 错误红色波浪线
- Completion: 关键字 + 内置函数 + 用户定义符号补全
- Hover: 悬停显示变量/函数信息
- Signature Help: 函数参数提示
- Goto Definition: Ctrl+Click 跳转到声明处
- Find References: 查找所有引用位置
- Rename: F2 重命名符号

**新增 analysis 模块**
- collect_symbols: AST 遍历收集符号
- find_declaration/find_all_references: Token 扫描定位声明和引用
This commit is contained in:
2026-06-25 00:28:13 +08:00
parent 99c9e9c9bd
commit bdffe9e3c5
65 changed files with 1709 additions and 47 deletions
+3
View File
@@ -0,0 +1,3 @@
.vscode/**
node_modules/**
.gitignore
+43
View File
@@ -0,0 +1,43 @@
const path = require('path');
const { LanguageClient, TransportKind } = require('vscode-languageclient/node');
/** @type {LanguageClient} */
let client;
/**
* @param {import('vscode').ExtensionContext} context
*/
function activate(context) {
const isWindows = process.platform === 'win32';
const serverExe = isWindows ? 'aster-lsp.exe' : 'aster-lsp';
// __dirname resolves to the real filesystem path (following the junction),
// so .. goes to the workspace root where target/ lives
const serverPath = path.join(__dirname, '..', 'target', 'debug', serverExe);
const serverOptions = {
command: serverPath,
args: [],
};
const clientOptions = {
documentSelector: [{ scheme: 'file', language: 'aster' }],
};
client = new LanguageClient(
'aster-lsp',
'Aster Language Server',
serverOptions,
clientOptions
);
client.start();
}
function deactivate() {
if (client) {
return client.stop();
}
}
module.exports = { activate, deactivate };
+26
View File
@@ -0,0 +1,26 @@
{
"comments": {
"lineComment": "//",
"blockComment": ["/*", "*/"]
},
"brackets": [
["{", "}"],
["[", "]"],
["(", ")"]
],
"autoClosingPairs": [
{ "open": "{", "close": "}" },
{ "open": "[", "close": "]" },
{ "open": "(", "close": ")" },
{ "open": "\"", "close": "\"" },
{ "open": "'", "close": "'" }
],
"surroundingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["\"", "\""],
["'", "'"]
],
"wordPattern": "[a-zA-Z_][a-zA-Z0-9_]*"
}
+96
View File
@@ -0,0 +1,96 @@
{
"name": "aster-lang",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "aster-lang",
"version": "0.1.0",
"dependencies": {
"vscode-languageclient": "^9.0.0"
},
"engines": {
"vscode": "^1.85.0"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"license": "MIT"
},
"node_modules/brace-expansion": {
"version": "2.1.1",
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.1.tgz",
"integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/minimatch": {
"version": "5.1.9",
"resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.9.tgz",
"integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=10"
}
},
"node_modules/semver": {
"version": "7.8.5",
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz",
"integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/vscode-jsonrpc": {
"version": "8.2.0",
"resolved": "https://registry.npmmirror.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
"integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==",
"license": "MIT",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/vscode-languageclient": {
"version": "9.0.1",
"resolved": "https://registry.npmmirror.com/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz",
"integrity": "sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==",
"license": "MIT",
"dependencies": {
"minimatch": "^5.1.0",
"semver": "^7.3.7",
"vscode-languageserver-protocol": "3.17.5"
},
"engines": {
"vscode": "^1.82.0"
}
},
"node_modules/vscode-languageserver-protocol": {
"version": "3.17.5",
"resolved": "https://registry.npmmirror.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz",
"integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==",
"license": "MIT",
"dependencies": {
"vscode-jsonrpc": "8.2.0",
"vscode-languageserver-types": "3.17.5"
}
},
"node_modules/vscode-languageserver-types": {
"version": "3.17.5",
"resolved": "https://registry.npmmirror.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
"integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==",
"license": "MIT"
}
}
}
+39
View File
@@ -0,0 +1,39 @@
{
"name": "aster-lang",
"displayName": "Aster Language Support",
"description": "Syntax highlighting and language support for the Aster scripting language",
"version": "0.1.0",
"publisher": "aster",
"engines": {
"vscode": "^1.85.0"
},
"categories": [
"Programming Languages"
],
"main": "./extension.js",
"contributes": {
"languages": [
{
"id": "aster",
"aliases": [
"Aster",
"aster"
],
"extensions": [
".ast"
],
"configuration": "./language-configuration.json"
}
],
"grammars": [
{
"language": "aster",
"scopeName": "source.aster",
"path": "./syntaxes/aster.tmLanguage.json"
}
]
},
"dependencies": {
"vscode-languageclient": "^9.0.0"
}
}
+156
View File
@@ -0,0 +1,156 @@
{
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
"name": "Aster",
"scopeName": "source.aster",
"fileTypes": ["ast"],
"patterns": [
{ "include": "#comments" },
{ "include": "#strings" },
{ "include": "#numbers" },
{ "include": "#function-declaration" },
{ "include": "#keywords" },
{ "include": "#constants" },
{ "include": "#builtins" },
{ "include": "#operators" },
{ "include": "#punctuation" },
{ "include": "#identifiers" }
],
"repository": {
"comments": {
"patterns": [
{
"name": "comment.block.aster",
"begin": "/\\*",
"end": "\\*/",
"patterns": [
{
"name": "comment.block.aster",
"match": "."
}
]
},
{
"name": "comment.line.double-slash.aster",
"match": "//.*$"
}
]
},
"strings": {
"patterns": [
{
"name": "string.quoted.double.aster",
"begin": "\"",
"end": "\"",
"patterns": [
{
"name": "constant.character.escape.aster",
"match": "\\\\[ntr\"\\\\]"
}
]
},
{
"name": "string.quoted.single.aster",
"begin": "'",
"end": "'",
"patterns": [
{
"name": "constant.character.escape.aster",
"match": "\\\\[ntr'\\\\]"
}
]
}
]
},
"numbers": {
"patterns": [
{
"name": "constant.numeric.float.aster",
"match": "\\b\\d+\\.\\d+\\b"
},
{
"name": "constant.numeric.integer.aster",
"match": "\\b\\d+\\b"
}
]
},
"function-declaration": {
"match": "\\b(fn)\\s+([a-zA-Z_][a-zA-Z0-9_]*)",
"captures": {
"1": { "name": "keyword.control.aster" },
"2": { "name": "entity.name.function.aster" }
}
},
"keywords": {
"patterns": [
{
"name": "keyword.control.aster",
"match": "\\b(let|const|fn|if|else|while|for|in|break|continue|return)\\b"
}
]
},
"constants": {
"match": "\\b(true|false|nil)\\b",
"name": "constant.language.aster"
},
"builtins": {
"match": "\\b(print|input|clock|len|typeof|push|pop|split|trim|substring|replace|contains|upper|lower|starts_with|ends_with|require)\\b",
"name": "support.function.builtin.aster"
},
"operators": {
"patterns": [
{
"name": "keyword.operator.assignment.compound.aster",
"match": "\\+=|-=|\\*=|/=|%="
},
{
"name": "keyword.operator.logical.aster",
"match": "&&|\\|\\||!"
},
{
"name": "keyword.operator.comparison.aster",
"match": "==|!=|<=|>="
},
{
"name": "keyword.operator.assignment.aster",
"match": "="
},
{
"name": "keyword.operator.arithmetic.aster",
"match": "[+\\-*/%]"
},
{
"name": "keyword.operator.comparison.aster",
"match": "<|>"
}
]
},
"punctuation": {
"patterns": [
{
"name": "punctuation.section.scope.aster",
"match": "[{}]"
},
{
"name": "punctuation.section.brackets.aster",
"match": "[\\[\\]]"
},
{
"name": "punctuation.section.parens.aster",
"match": "[\\(\\)]"
},
{
"name": "punctuation.separator.aster",
"match": "[,;:\\.]"
},
{
"name": "keyword.operator.ternary.question.aster",
"match": "\\?"
}
]
},
"identifiers": {
"match": "[a-zA-Z_][a-zA-Z0-9_]*",
"name": "variable.other.aster"
}
}
}