execute-script
本文回答:vjcad 提供两条内置脚本命令——
EXECSTR(执行 CAD 命令脚本)和EXECJS(执行 JavaScript 代码)。代码侧有两个层级:Engine.editor.automate(scriptString)是裸命令喂行器(只认命令名行+点输入行),new vjcad.ExecuteStrCommand().executeString(script)才支持SET/$(VAR)/REPEAT等语法糖。
一、Engine.editor.automate(script):裸命令喂行器
语义(源码 UD 类 + getPoint 自动化分支):
- 主循环只执行命令名行:逐行
CommandRegistry.item(行)查找,非命令行(含SET、$(…)、垃圾文本、空行、注释)静默跳过——不报错、不执行。 - 命令别名在脚本里无效:主循环只查
CommandRegistry.item,不经CommandAliasManager解析,所以L/C/M这类别名行同样被静默跳过(交互命令行executerWithOp才解析别名)。脚本里一律写全名LINE/CIRCLE/MOVE。 - 只有
getPoint会消费脚本行:命令执行中每个点提示从scriptRowStack读下一非空行,跳过;///注释与空行;"x,y"解析为坐标,单数字+useBasePoint解析为相对偏移,命中keywords则按关键字,否则 Cancel 结束当前命令。 getString/getKeyword/getReal/getEntity不读脚本行——命令走到这类提示时会回到交互模式等待真实输入,automate()的 Promise 挂起不返回。- 顶层
getInteger/getCorner同样不吃automate:getInteger(opts)挂起后automate("42\n")只是把"42"当命令名查表跳过,promise 永挂;getCorner同理。这两类只能真实 UI 输入或派发 Escape 取消:命令进行中用await Engine.editor.awaitDispatchEscapeEvent()(派发 Escape 并等待命令结束;Editor上没有dispatchEscapeKey,那是右键菜单的私有方法);不在命令里的顶层调用,awaitDispatchEscapeEvent要么不派发、要么等不到命令结束,改为直接document.dispatchEvent(new KeyboardEvent('keydown', { code: 'Escape' }))。vjcad 没有 vjmapextinputManager.resolve*Value那样的程序化落地通道。 - 空行≠回车:空行被点输入读取循环跳过。
LINE/PLINE这类需要 Enter 收尾的命令,靠下一条命令名行被当无效点读入→Cancel 收尾(已画段会提交)。所以两条 LINE 要写成LINE p1 p2 LINE LINE p3 p4 <收尾行>形态。 - 脚本末尾不能留着待输入的命令:栈空时
getPoint落回交互等待,automate()Promise 永不 resolve(静默挂死)。
// 可直接运行:两条 CIRCLE(圆心+半径两段自动完成,无需收尾行)
await Engine.editor.automate(`CIRCLE
5000,5000
30
CIRCLE
5200,5000
40
`);
// LINE 需要"下一条命令名"收尾——这里第一条 CIRCLE 行既给 LINE Cancel 收尾、
// 第二条 CIRCLE 行才真正启动画圆
await Engine.editor.automate(`LINE
5000,5000
5100,5000
CIRCLE
CIRCLE
6000,5000
20
`);
MCP 参考:vjcad/example/command/05executescript.js(⚠️ 示例里的矩形脚本以 LINE+空行收尾——最后一条 LINE 会等不到输入而挂起,且空行并不产生回车)。
二、ExecuteStrCommand.executeString(script):带语法糖的脚本
EXECSTR 命令类(导出为 vjcad.ExecuteStrCommand)内部用 GD 解析器把脚本编译成可执行行序列,再喂给 automate——SET/$(VAR)/%VAR%/$(expr)/REPEAT n…ENDREPEAT/GOTO/:label/IF … GOTO 都在这一层生效,裸 automate 里它们只是被静默跳过的普通行。
await new vjcad.ExecuteStrCommand().executeString(`SET Y=8000
REPEAT 3
CIRCLE
9000,$(Y)
$(10*2)
ENDREPEAT
`);
// REPEAT 3 展开三次 → CIRCLE +3;$(Y)→8000、$(10*2)→20
⚠️ REPEAT 展开的是原样的行序列——块内若放 LINE 这类需收尾命令,块首的 LINE 行会沦为上一轮 LINE 的 Cancel 输入,循环只产出 1 条。块内用自完成命令(CIRCLE/RECTANG 等两点即完的)才稳。
语法表(ExecuteStrCommand 层):
| 元素 | 含义 |
|---|---|
; 或 // 开头 |
注释(解析器与 automate 两层都会跳过) |
SET VAR=value |
变量赋值(值先经 $(…)/%…% 展开,可解析成数字则存数字) |
$(VAR) / %VAR% |
引用变量 |
$(expr) |
表达式:$(100+50*2)、$(PI)、$(CLAYER)、$(CECOLOR)、$(LASTPOINT_X/Y) |
REPEAT n … ENDREPEAT |
循环展开(n 可写表达式,嵌套 REPEAT 支持) |
GOTO :label / IF cond GOTO :label |
跳转/条件跳转(:label 行定义标签) |
| 空行 | 编译为 "" 行进入 automate,再被点输入跳过——不是回车 |
二、EXECSTR:弹脚本编辑对话框
await Engine.editor.executerWithOp('EXECSTR');
会弹出脚本输入对话框,用户在里面写脚本然后执行。MCP 参考:vjcad/example/command/05executescript.js(⚠️ 示例里打印的 EXECUTESTR 是过期名,真实注册名 EXECSTR;EXECJS 同理)。
三、EXECJS:弹 JS 代码对话框,跑一段 JS
await Engine.editor.executerWithOp('EXECJS');
执行上下文(vjcad@1.0.16 源码):ExecuteJsCommand.executeCode 把 window.vjcad 的全部导出按名字注入 AsyncFunction 参数——即所有 vjcad 顶层导出都可用,如 Engine、Point2D、LineEnt、CircleEnt、ArcEnt、PolylineEnt、TextEnt、MTextEnt、writeMessage、message、regen 等。
⚠️ 官方示例 06executejs.js 自述的 doc / log / executeCommand / addEntity 四个全局在 vjcad@1.0.16 源码中并不存在(它们不是包导出);等价写法是 Engine.currentDoc、message.info(...)/writeMessage(...)、Engine.editor.executerWithOp('CMD')、Engine.addEntities(ent)。示例模板里 addEntity(circle) / executeCommand(...) 的写法在对话框里会 ReferenceError。
MCP 参考:vjcad/example/command/06executejs.js。
四、对比 automate vs executerWithOp
| 想做的 | 用 |
|---|---|
| 跑一段已有的命令脚本 | Engine.editor.automate(scriptString) |
| 跑一条命令(字符串名) | Engine.editor.executerWithOp('CMDNAME') |
| 弹给用户编辑脚本 / JS | Engine.editor.executerWithOp('EXECSTR') / 'EXECJS' |
反例
- 给
automate传含SET/$(…)/REPEAT的脚本:这些行在 automate 层全部静默跳过——$(R)喂给点输入会得到 NaN→Cancel。要用new vjcad.ExecuteStrCommand().executeString(...)。 - 脚本以"还在等输入的命令"结尾:栈空 →
getPoint落回交互等待 →automate()挂起。收尾用一条自完成命令(如CIRCLE两段即完)或让下一条命令名吃掉它。 - 脚本里写中文标点:
100,50不是合法参数(应是英文,)。 automate无法喂getString/getKeyword/getReal/getEntity类输入——命令走到这类提示会静默转交互、Promise 不返回。只能驱动纯点输入链路的命令。
参考(官方文档 MCP)
- 文档:
vjcad/doc/command/index.md - 示例:
vjcad/example/command/05executescript.js、vjcad/example/command/06executejs.js