Skip to content

Commit 5c91924

Browse files
author
SqlRush
committed
Parse CSI erase field area actions
1 parent edf6eb2 commit 5c91924

5 files changed

Lines changed: 19 additions & 0 deletions

File tree

docs/cc-100-roadmap.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,8 @@ M7 补充:terminal CSI parser 现在识别带 intermediate space 的 `CSI Ps S
228228

229229
M7 补充:terminal CSI parser 现在把 DEC selective erase `CSI ? Ps J` / `CSI ? Ps K` 标记为 selective display/line erase,和普通 ED/EL 区分开。
230230

231+
M7 补充:terminal CSI parser 现在把 ECMA `CSI Ps N` / `CSI Ps O` 解析成 erase-in-field / erase-in-area action,覆盖 to-end/to-start/all 三种 region。
232+
231233
M7 补充:terminal CSI parser 现在把 REP repeat-preceding-character (`CSI b`/`CSI 4b`) 解析成 edit action,visible-text/snapshot pipeline 和 ANSI message wrapping/trim 会按重复次数展开前一个可重复 grapheme。
232234

233235
M7 补充:terminal CSI parser 现在按 ANSI 默认参数解析 scroll-region (`CSI r`/`CSI ;10r`),缺失 top 默认为 1,缺失 bottom 保持为 0 表示 reset/full-height,避免把 reset 误判为单行区域。

docs/claude-code-go-rewrite-plan.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ test/parity/ # golden tests against TS/official behavior
293293
- 本轮补充:terminal CSI parser 现在把带参数的 `CSI Pl;Pr s` 解析成 left/right horizontal margin region action,同时保留无参数 `CSI s` save-cursor 语义,和 DEC `?69h/l` margin mode 闭环。
294294
- 本轮补充:terminal CSI parser 现在识别带 intermediate space 的 `CSI Ps SP @` / `CSI Ps SP A` scroll-left/right 序列,避免误解析成 insert-characters 或 cursor-up。
295295
- 本轮补充:terminal CSI parser 现在把 DEC selective erase `CSI ? Ps J` / `CSI ? Ps K` 标记为 selective display/line erase,和普通 ED/EL 区分开。
296+
- 本轮补充:terminal CSI parser 现在把 ECMA `CSI Ps N` / `CSI Ps O` 解析成 erase-in-field / erase-in-area action,覆盖 to-end/to-start/all 三种 region。
296297
- 本轮补充:terminal CSI parser 把 REP repeat-preceding-character (`CSI b`) 归入 edit action,visible-text/snapshot pipeline 和 ANSI message wrapping/trim 会按重复次数展开前一个可重复 grapheme。
297298
- 本轮补充:terminal CSI parser 按 ANSI 默认参数解析 scroll-region (`CSI r`/`CSI ;10r`),缺失 top 默认为 1,缺失 bottom 保持 0 表示 reset/full-height,避免 reset scroll region 被误判为单行区域。
298299
- 本轮补充:terminal CSI parser 把 DECSTR soft reset (`CSI !p`) 归入 reset action,并在 terminal parser 中清理 SGR/link 状态。

docs/first-second-parity-audit.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ M7 progress now includes:
247247
- `internal/tui`: terminal CSI parsing now distinguishes parameterized `CSI Pl;Pr s` left/right horizontal margin regions from bare `CSI s` save-cursor semantics.
248248
- `internal/tui`: terminal CSI parsing now recognizes intermediate-space `CSI Ps SP @` / `CSI Ps SP A` scroll-left/right sequences instead of misclassifying them as insert-characters or cursor-up.
249249
- `internal/tui`: terminal CSI parsing now marks DEC selective erase `CSI ? Ps J` / `CSI ? Ps K` separately from ordinary ED/EL erase actions.
250+
- `internal/tui`: terminal CSI parsing now exposes ECMA `CSI Ps N` / `CSI Ps O` erase-in-field and erase-in-area actions with the same to-end/to-start/all regions used by other erase operations.
250251
- `internal/tui`: keybinding config, keymap resolution, and interaction script named-key input now accept terminal aliases for `ctrl-h`/`ctrl-i`/`ctrl-j`/`ctrl-m`, `ctrl-[`, and `ctrl-?`, including `control-*` and compact/camel variants.
251252
- `internal/tui`: keybinding config now accepts page navigation aliases such as `pgup`, `pg-up`, `prior`, `pgdn`, `pg-down`, and `next`.
252253
- `internal/tui`: keybinding config and named-key script input now accept DOM-style arrow key aliases such as `arrowLeft`, `arrowRight`, `arrowUp`, `arrowDown`, and modifier-arrow variants.

internal/tui/terminal_csi.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ const (
3838
CSICommandEraseLine byte = 'K'
3939
CSICommandInsertLines byte = 'L'
4040
CSICommandDeleteLines byte = 'M'
41+
CSICommandEraseField byte = 'N'
42+
CSICommandEraseArea byte = 'O'
4143
CSICommandDeleteCharacters byte = 'P'
4244
CSICommandEraseCharacters byte = 'X'
4345
CSICommandBackwardTab byte = 'Z'
@@ -167,6 +169,8 @@ type CSIEraseActionType string
167169
const (
168170
CSIEraseActionDisplay CSIEraseActionType = "display"
169171
CSIEraseActionLine CSIEraseActionType = "line"
172+
CSIEraseActionField CSIEraseActionType = "field"
173+
CSIEraseActionArea CSIEraseActionType = "area"
170174
CSIEraseActionChars CSIEraseActionType = "chars"
171175
)
172176

@@ -412,6 +416,10 @@ func ParseCSISequence(sequence string) (CSIAction, bool) {
412416
return CSIAction{Type: CSIActionErase, Erase: CSIEraseAction{Type: CSIEraseActionDisplay, Region: csiEraseDisplayRegion(csiParamDefault(params, 0, 0)), Selective: privateMode == '?'}}, true
413417
case CSICommandEraseLine:
414418
return CSIAction{Type: CSIActionErase, Erase: CSIEraseAction{Type: CSIEraseActionLine, Region: csiEraseLineRegion(csiParamDefault(params, 0, 0)), Selective: privateMode == '?'}}, true
419+
case CSICommandEraseField:
420+
return CSIAction{Type: CSIActionErase, Erase: CSIEraseAction{Type: CSIEraseActionField, Region: csiEraseLineRegion(csiParamDefault(params, 0, 0))}}, true
421+
case CSICommandEraseArea:
422+
return CSIAction{Type: CSIActionErase, Erase: CSIEraseAction{Type: CSIEraseActionArea, Region: csiEraseLineRegion(csiParamDefault(params, 0, 0))}}, true
415423
case CSICommandEraseCharacters:
416424
return CSIAction{Type: CSIActionErase, Erase: CSIEraseAction{Type: CSIEraseActionChars, Count: p0}}, true
417425
case CSICommandInsertCharacters:

internal/tui/tui_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3759,6 +3759,8 @@ func TestParseCSISequenceActions(t *testing.T) {
37593759
{seq: CSISequence("K"), want: CSIEraseAction{Type: CSIEraseActionLine, Region: CSIEraseToEnd}},
37603760
{seq: CSISequence("?K"), want: CSIEraseAction{Type: CSIEraseActionLine, Region: CSIEraseToEnd, Selective: true}},
37613761
{seq: CSISequence(2, "K"), want: CSIEraseAction{Type: CSIEraseActionLine, Region: CSIEraseAll}},
3762+
{seq: CSISequence("N"), want: CSIEraseAction{Type: CSIEraseActionField, Region: CSIEraseToEnd}},
3763+
{seq: CSISequence(1, "O"), want: CSIEraseAction{Type: CSIEraseActionArea, Region: CSIEraseToStart}},
37623764
{seq: CSISequence(6, "X"), want: CSIEraseAction{Type: CSIEraseActionChars, Count: 6}},
37633765
}
37643766
for _, tc := range eraseCases {
@@ -3930,6 +3932,11 @@ func TestParseCSISequenceActions(t *testing.T) {
39303932
t.Fatalf("terminal parser selective erase actions = %#v", actions)
39313933
}
39323934

3935+
actions = parser.Feed(CSISequence(2, "O"))
3936+
if len(actions) != 1 || actions[0].Type != TerminalActionErase || actions[0].Erase.Type != CSIEraseActionArea || actions[0].Erase.Region != CSIEraseAll {
3937+
t.Fatalf("terminal parser area erase actions = %#v", actions)
3938+
}
3939+
39333940
actions = parser.Feed(CSISequence(5, 40, "s"))
39343941
if len(actions) != 1 || actions[0].Type != TerminalActionScroll || actions[0].Scroll.Type != CSIScrollActionSetHorizontalRegion || actions[0].Scroll.Left != 5 || actions[0].Scroll.Right != 40 {
39353942
t.Fatalf("terminal parser horizontal region actions = %#v", actions)

0 commit comments

Comments
 (0)