DECTABSR —— 制表位报告(`CSI 2 $ w`)
让终端以 DCS 报告形式输出当前水平制表位。补全往返链:HTS(`esc-hts`)设置、TBC(`csi-tbc`)清除、DECTABSR 查询当前集合。
字节形式
涵盖所有常见的字符串字面量写法,方便正反查找。
\x1b[2$w\033[2$w\e[2$wESC [ 2 $ w1b 5b 32 24 77说明
DECTABSR —— *制表位报告* —— DECRQPSR(请求陈现状态报告)的一种,请终端以 DCS 形式列出当前水平制表位。它是制表位往返链的第三只脚: - 在当前列**设置**制表位:`\x1bH`(HTS,见 `esc-hts`)。 - **清除**:`\x1b[g`(当前列)/ `\x1b[3g`(全部)(TBC,见 `csi-tbc`)。 - **查询**当前集合:`\x1b[2$w`(DECTABSR —— 本条目)。 **请求形态**:`\x1b[2$w`(`2` 是 Ps 选择子 —— `1` 是 DECCIR 光标信息报告、`2` 是 DECTABSR)。`$` 中间字节 + `w` 末字节同 DECRQPSR 约定。 **回复形态(DECRPTAB)**:`\x1bP2$u<col1>/<col2>/…/<colN>\x1b\\` —— DCS 帧,体为以斜杠分隔的当前制表位列号(1 起算)。示例(xterm 80 列默认 8 列制表): ``` \x1bP2$u9/17/25/33/41/49/57/65/73\x1b\\ ``` 注意 1 列处无制表位(本即光标 home),斜杠仅分隔不终止。DCS 前缀 `\x1bP2$u` 回显请求的 `2$` 加上 `u` 表示「unsolicited / response」(类比 DECRQSS 的 `$r`)。 **往返模式**(TUI 会话前后保存 / 恢复终端制表状态): ``` 启动: 发 \x1b[2$w -> 收 \x1bP2$u9/17/25/...\x1b\\ 解析:以 '/' 分割体 -> [9, 17, 25, ...] 保存 stops_before 退出: 发 \x1b[3g # TBC 清全部 for col in stops_before: 发 \x1b[<col>G\x1bH # CHA 至 col,HTS 于 col ``` 声明 `dectabsr` 的 terminfo 条目里,`tput rs2` 做的就是这件事 —— 「重置到已知制表状态」用读 + 还原,而不是傻发 `\x1b[?5W`(DECST8C —— 设 8 列制表)。 **边界**: - 空回复(`\x1bP2$u\x1b\\`)表示一个制表位都没有 —— 发 `\x1b[?5W` 或写一串 HTS 还原默认。 - 部分模拟器会把隐含的「行尾」位(列 == 屏宽 + 1)也报出来 —— 从保存列表里剥除。 - 回复默认以 `\x1b\\`(ST 表示为 ESC + 反斜杠)结束;xterm 在 8 位 C1 模式下(见 `decscl-compat-level`)使用单字节 `\x9c`。 **覆盖度**:**xterm** = 完整(标准)。**WezTerm** / **mlterm** / **Konsole** = 完整。**Ghostty** = 部分(回但总是默认 8 列)。**Kitty** / **iTerm2** = 部分(回但某些代码路径漏掉用户设置的位)。**Alacritty** / **Linux console** / **Windows Terminal** / **cmd / ConPTY** / **macOS Terminal** / **gnome-terminal** = 无作用(完全不回 —— 探测务必加 ~100 ms 超时)。
规范出处: DEC VT510 RM (DECTABSR / DECRPTAB) / xterm-ctlseqs (CSI 2 $ w)
示例
# Probe tab stops; read DCS reply with timeout.\nprintf '\\033[2$w'\nIFS= read -rs -d '\\\\' -t 0.1 reply </dev/tty 2>/dev/null\necho \"DECTABSR: ${reply}\" # expect: \\x1bP2$u9/17/25/...import sys, re\n# Parse the slash-separated tab list from a DECRPTAB reply.\nreply = '\\x1bP2$u9/17/25/33/41/49/57/65/73\\x1b\\\\'\nm = re.search(r'\\x1bP2\\$u([\\d/]*)\\x1b\\\\', reply)\nstops = [int(c) for c in m.group(1).split('/') if c] if m else []\nprint(stops)// Save-then-restore tab stops across a TUI's runtime.\nfmt.Print(\"\\x1b[2$w\") // request DECTABSR\n// ... read DCS reply, parse to []int ...\n// On exit: TBC all, then HTS at each saved col\nfmt.Print(\"\\x1b[3g\") // TBC 3 clears all\nfor _, col := range saved {\n fmt.Printf(\"\\x1b[%dG\\x1bH\", col)\n}// Detect 'no tab stops at all' (empty body).\nfunction parseDECTABSR(buf) {\n const m = /\\x1bP2\\$u([^\\x1b]*)\\x1b\\\\/.exec(buf);\n if (!m) return null;\n return m[1].split('/').filter(s => s.length).map(Number);\n}\nconsole.log(parseDECTABSR('\\x1bP2$u\\x1b\\\\')); // [] — no tabs at all/* Skeleton: probe + parse (cbreak stdin, timed read). */\nprintf(\"\\x1b[2$w\"); fflush(stdout);\n/* read until ST (0x1b 0x5c). then strtok on '/' between \"$u\" and ST. */终端支持
- xterm
- 支持
- Linux console (fbcon)
- 不支持
- macOS Terminal.app
- 不支持
- iTerm2
- 部分
- Windows Terminal
- 不支持
- cmd.exe / ConPTY
- 不支持
- kitty
- 部分
- alacritty
- 不支持
- WezTerm
- 支持
- Ghostty
- 部分
- GNOME Terminal
- 不支持
- Konsole
- 支持
- tmux
- 不支持
- GNU screen
- 不支持
| xterm | Linux console (fbcon) | macOS Terminal.app | iTerm2 | Windows Terminal | cmd.exe / ConPTY | kitty | alacritty | WezTerm | Ghostty | GNOME Terminal | Konsole | tmux | GNU screen |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 支持 | 不支持 | 不支持 | 部分 | 不支持 | 不支持 | 部分 | 不支持 | 支持 | 部分 | 不支持 | 支持 | 不支持 | 不支持 |