> **本文回答**：多步骤交互命令（如 PLINE）建议把 `step / state` 当字段，`async main()` 写一个 `while (this.step > 0) { switch(this.step) {...} }` 循环，每步是一个 `async stepN_xxx()` 方法；用 `keywords` 接收 `C(close) / U(undo)` 这类关键字；`getPoint` 空输入回车/空格返回 `InputStatusEnum.None`（5000），据此判"完成"（`EnterOrSpace` 只由 `getEntity` 返回）。

## 一、PLINE 风格的状态机模板

```js
class MyPlineCommand {
  constructor() {
    this.step = 1;
    this.points = [];
    this.isClosed = false;
  }

  async main() {
    Engine.undoManager.start_undoMark('绘制多段线');
    try {
      while (this.step > 0) {
        switch (this.step) {
          case 1: await this.step1_getFirstPoint(); break;
          case 2: await this.step2_getNextPoints(); break;
          case 3: this.step3_finish();              break;
        }
      }
    } finally {
      Engine.undoManager.end_undoMark();
      Engine.clearPreview();
    }
  }

  async step1_getFirstPoint() {
    const opt = new PointInputOptions('指定起点:');
    const r = await Engine.getPoint(opt);
    if (r.status === InputStatusEnum.OK) {
      this.points.push(r.value);
      this.step = 2;
    } else {
      this.step = 0;
    }
  }

  async step2_getNextPoints() {
    const lastPt = this.points[this.points.length - 1];
    const opt = new PointInputOptions('指定下一点 [闭合(C)/撤销(U)] <完成>:');
    opt.keywords = ['C', 'U'];
    opt.useBasePoint = true;
    opt.basePoint = lastPt;
    opt.callback = (canvasPt) => {
      const wp = Engine.canvasToWcs(canvasPt);
      this.drawPreview(wp);
    };

    const r = await Engine.getPoint(opt);

    if (r.status === InputStatusEnum.OK) {
      this.points.push(r.value);
    } else if (r.stringResult === 'C') {
      if (this.points.length >= 3) { this.isClosed = true; this.step = 3; }
      else writeMessage('<br/>至少需要3个点才能闭合');
    } else if (r.stringResult === 'U') {
      if (this.points.length > 1) { this.points.pop(); }
      else this.step = 1;
    } else if (r.status === InputStatusEnum.None) {   // 空输入回车/空格 = 完成（getPoint 不返回 EnterOrSpace）
      this.step = 3;
    } else {
      this.step = 0;
    }
  }

  step3_finish() {
    if (this.points.length >= 2) {
      const pl = new PolylineEnt();
      for (const pt of this.points) pl.addVertex([pt.x, pt.y]);
      pl.isClosed = this.isClosed;
      pl.setDefaults();
      Engine.addEntities(pl);
    }
    this.step = 0;
  }

  drawPreview(currentPt) {
    if (this.points.length === 0) return;
    const pl = new PolylineEnt();
    for (const pt of this.points) pl.addVertex([pt.x, pt.y]);
    pl.addVertex([currentPt.x, currentPt.y]);
    pl.setDefaults();
    Engine.clearPreview();
    Engine.drawPreviewEntity(pl);
  }
}
```

MCP 参考：`vjcad/example/command/04statemachine.js`。

## 二、状态机关键点

1. **退出靠 `step = 0`**：把 0 作为 sentinel，`while (this.step > 0)` 自然退出。
2. **关键字判定靠 `result.stringResult`**：`PointInputOptions.keywords = ['C', 'U']` 配合 `result.status === InputStatusEnum.Keyword`（或部分实现里直接用 `result.stringResult`）。
3. **回车 / 空格判定**：`getPoint` 空输入回车/空格返回 `InputStatusEnum.None`（5000），判"完成"写 `r.status === InputStatusEnum.None`（内置 LINE 命令同此写法）；`EnterOrSpace`（3）只由 `getEntity` 返回，拿它判 getPoint 的回车永远不成立（值见 [input/get-string-number](../input/get-string-number.md) 的 `InputStatusEnum` 表）。Esc 返回 `InputStatusEnum.Cancel`，不抛异常。
4. **预览必须每次清**：`Engine.clearPreview()` + `Engine.drawPreviewEntity(...)`，命令结束 `finally` 再 clear 一次。

## 三、文档里的另一种范式（按状态字段）

```js
class MultiStepCommand {
  state = 'START';
  async main() {
    while (this.state !== 'END') {
      switch (this.state) {
        case 'START':   await this.handleStart();   break;
        case 'SELECT':  await this.handleSelect();  break;
        case 'CONFIRM': await this.handleConfirm(); break;
      }
    }
  }
}
```

MCP 参考：`vjcad/doc/command/index.md`。

## 反例

- 在 `step2` 里循环取点但忘记 `points.length === 0` 判断，导致 `lastPt = undefined`。
- 关键字大小写没统一：`opt.keywords = ['C']` 但比对时写 `r.stringResult === 'c'`，会一直走不到。
- 用 `while(true)` 死循环但没在 `step = 0` 时 `break` / `return`。

## 参考（官方文档 MCP）

- 文档：`vjcad/doc/command/index.md`
- 示例：`vjcad/example/command/04statemachine.js`、`vjcad/example/input/06getkeyword.js`
