VS Code 扩展,用于高效编辑 MCGS Web 4.0 工程配置中的 MScript 脚本。
MScript 是一种基于 JavaScript 的轻量级脚本语言,专用于配置化场景。工程数据以 JSON 存储,其中的脚本以 data:script;...;base64,... 形式内嵌。本插件在「存储格式」与「编辑格式」之间提供双向转换,并配备语法高亮、智能补全、语法校验、全局搜索/替换等能力。
- 语言标识符:
mscript(编辑器内)/json(工程文件) - VS Code 引擎:
^1.80.0
功能特性
| 能力 | 说明 |
|---|---|
| MScript 编辑器 | 基于 Monaco 的独立 Webview 编辑器,支持语法高亮、主题跟随、保存写回 |
| 智能补全 | Runtime.工具类.方法 链式补全;@路径(相对/绝对节点路径)补全 |
| 语法校验 | 实时错误/警告标注,并提供快速修复(Quick Fix) |
| CodeLens / Hover | 在 data:script 字符串上方显示「Edit MScript」入口与悬浮预览 |
| 全局搜索 / 替换 | 侧边栏搜索面板,跨整个工程树(解码后的编辑格式)检索与批量替换 |
| 多入口动态解析 | 自动识别当前文件所属的 Central 入口,无需配置入口路径 |
| 项目树缓存 | 按入口缓存树结构(LRU),加速重复打开;JSON 变更自动失效 |
工作原理
工程 JSON 中脚本以 Base64 内嵌,直接阅读与编辑困难。插件在两种格式间转换:
存储格式 (json) 编辑格式 (编辑器)
data:script;[row:TBoolean]; ⇄ let isRow = Runtime.ctx.row
base64,bGV0IGlzUm93... let layout = @../mActiveLayout
if (isRow) { ... }
打开:解码 Base64 → 转换 Runtime.core.get/set/call 为 @路径 → 格式化 → 显示。
保存:语法校验 → 反向转换(@路径 → Runtime.core.*)→ Base64 编码 → 写回原位置。
双向转换规则
| 存储格式 | 编辑格式 |
|---|---|
Runtime.core.get(`path`) |
@path |
Runtime.core.set(`path`, v) |
@path = v |
await Runtime.core.call(`path`, ...args) |
@path(...args) |
Runtime.core.iter(v) |
v |
await Runtime.Xxx.method() |
Runtime.Xxx.method() |
使用方式
1. 打开 MScript 编辑器
在任意启用的 JSON 文件(默认 *.json)中,将光标悬停在 data:script;... 字符串上:
- 字符串上方出现 Edit MScript CodeLens,点击即打开编辑器;
- 或使用悬浮提示中的编辑链接。
编辑器标题形如 MScript-<文件名>,左下角状态栏显示文件路径与脚本位置。编辑器切换主题时实时同步 VS Code 主题。
2. @路径 补全
输入 @ 后提示 [/] [./] [../] 三种基准:
/—— 以当前节点为根的绝对路径;./—— 当前节点的成员/子节点;../—— 父节点。
补全候选项来自当前脚本所属节点的项目树(动态解析入口后加载),包括节点属性、方法、事件,以及子节点(以 mName 展示,以子节点 key 补全)。节点成员定义来源于配置中的 Nodes 类型定义(见下文)。
3. Runtime 补全
- 输入
R→ 提示Runtime; - 输入
Runtime.→ 列出全部工具类; - 输入
Runtime.工具类.→ 列出该工具类的全部属性/方法(含参数签名)。
工具类定义来源于配置中的 Utils。
4. 全局搜索与替换
点击左侧活动栏 MScript 搜索 图标,或通过命令面板执行 MScript 搜索,或在 JSON 文件右键菜单选择「MScript 搜索」。
- 搜索对象为每个
data:script解码后的编辑格式明文; - 作用域:仅工程树(入口 +
mChildList递归关联)涉及的文件,不扫描整个工作区; - 支持:区分大小写
Alt+C、全字匹配Alt+W、正则Alt+R、文件过滤(如windows/*); - 替换:对命中脚本执行 decode→替换→encode→重组,仅重写真正变化的脚本;已打开的文件保持未保存(dirty),未打开的文件直接写盘。
项目入口(动态解析)
本插件无需配置入口路径。打开编辑器时按以下规则自动定位当前脚本所属入口:
- 从当前 JSON 文件所在目录向上逐级查找
index.json; - 校验其第一层
mType === 'Central'; - 第一个满足条件的即为该脚本的项目入口;查找以工作区根为上限。
多入口支持:工程可存在多棵树(多个 Central 入口),每个入口独立加载与缓存。
安全约束:仅当文档位于某个工作区根子树内时才查找;文档不在任何工作区根下时拒绝查找,避免越界读取工作区外的不可信 index.json。
缓存:项目树按入口绝对路径缓存(LRU 上限 16 棵),命中且入口文件 mtime 未变则复用,避免重复磁盘 I/O。任意 JSON 文件变更(经文件监听)或入口 mtime 变化时自动失效对应缓存。
配置
VS Code 设置(mcgscript.*)
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
mcgscript.fileTypes |
string[] |
["json"] |
启用 MScript 功能的文件类型(VS Code 语言标识符) |
mcgscript.ignoreError |
boolean |
false |
为 true 时,即使编辑器中存在错误也允许直接保存 |
mcgscript.Utils |
object |
{} |
Runtime 工具类(类型定义),用于代码补全 |
mcgscript.Nodes |
object |
{} |
节点类型定义,用于 @路径 补全 |
Utils/Nodes留空时使用内置默认值(见src/config/defaults.ts)。
项目级配置(.mscript/config.json)
在工作区根放置 .mscript/config.json 可覆盖扩展设置,优先级高于 VS Code 设置:
{
"ignoreError": true,
"options": {
"Utils": { "MyUtil": { "detail": "自定义工具类" } }, // 非空则整体替换内置定义
"Nodes": { "MyWidget": { "detail": "自定义节点", "extends": ["VObject"] } }
},
"exoptions": {
"Utils": { "MyUtil": { "detail": "追加/覆盖单个工具类" } }, // 在已解析基础上合并
"Nodes": { "MyWidget": { "detail": "追加/覆盖单个节点定义" } }
}
}
合并语义:
options—— 替换:非空则完全替代默认值/扩展设置;exoptions—— 合并:在已解析的Utils/Nodes基础上追加或覆盖具体条目;ignoreError—— 项目配置 > 扩展配置 > 默认false。
注:旧版本的
entry字段已废弃(改为动态解析),配置文件中的entry将被忽略,可安全删除。
配置文件变更(VS Code 设置或 .mscript/config.json)会被自动监听并热刷新。
MScript 语法速查
存储格式
data:script;[参数名: 参数类型, ...];base64,<Base64 编码的脚本内容>
参数类型支持:TBoolean TInt TFloat TString TArray TStruct TColor TDate TScript TAny。
编辑格式语法
允许:
- 行注释
//(不允许块注释/* */) - 变量声明与赋值:
let x = ...、x = ...,支持+=-=*=/=等运算符 - 条件:
if/else if/else(可嵌套) - 特殊循环:
for (let [key, value, index, length] of iterable) {},支持continue/break - 断点调试:
debugger - 三元表达式、字符串拼接、值比较
Runtime运行时类及其工具类/方法@路径访问(get / set / call)
禁止:
- 函数定义:
function、new Function、() => {} - 块注释
/* */ - 调用
window下的原生对象/方法
条件判断与循环控制的嵌套最多三层。
示例
// 从参数获取 isRow
let isRow = Runtime.ctx.row
// 获取父节点的 ActiveLayout(@ 路径 get)
let layout = @../mActiveLayout
if (isRow) {
// 调用方法(@ 路径 call)
let rows = layout.fnRowHeights()
for (let [key, value, indxRow, length] of rows) {
let widget = @./fnGetValue("row_" + indxRow)
widget.size.mText = value == "max-content" ? "auto" : value
}
} else {
let cols = layout.fnColumnWidths()
for (let [key, value, indxCol] of cols) {
let widget = @./fnGetValue("col_" + indxCol)
widget.size.mText = value == "max-content" ? "auto" : value
}
}
数据结构
工程以 VObject 树组织,根节点为 Central(存储于 index.json):
type VOption = {
mGsid?: string
mType: string // 节点类型:Central / VModule / MWindow / Widget / WidgetWindow ...
properties?: Record<string, any> // 节点属性;VModule 的 mBasePath / mChildList / mExtName 亦在此
linkprop?: Record<string, Record<string, MScript>> // 属性订阅(数据变更触发脚本)
onevents?: Record<string, MScript> // 内置事件脚本
uievents?: Record<string, MScript> // UI 事件脚本(click / mousedown / keydown ...)
exevents?: Record<string, MScript> // 用户自定义事件脚本
exvariants?: Record<string, { type: string; value: any; readonly?: boolean }> // 自定义变量
children?: Record<string, VOption> // 子节点(内联或独立文件)
}
节点类型层级:
| 类型 | 继承 | 职责 |
|---|---|---|
VObject |
— | 框架基类:数据存储、订阅监听、事件脚本、自定义属性/事件、子节点 |
VModule |
VObject |
模块通用加载/存储;数据型业务模块,可独立文件存储 |
Central |
VModule |
工程根节点,管控工程加载与数据订阅代理中转 |
MWindow |
VModule |
UI 窗口管理:加载、渲染、路由 |
Widget |
VObject |
UI 组件基类:渲染、属性动态更新、UI 事件绑定 |
WidgetWindow |
Widget |
窗口组件,仅作 MWindow 直接子节点:布局、生命周期事件 |
独立文件存储:VModule 的 properties.mBasePath 非空时,其子节点数据存于 <mBasePath>/<childId>.<mExtName>(默认 json)。根节点必须存于 index.json,加载时依据 mBasePath + mChildList 递归构建树形结构。
许可证
MIT