Skip to content

Commit ce50e0e

Browse files
authored
feat(tty): add display erase commands (#23)
1 parent 032c1eb commit ce50e0e

9 files changed

Lines changed: 158 additions & 5 deletions

File tree

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ Use `Tty` when an operation needs a real terminal handle:
3434
- dynamic color queries: `Tty::query_default_foreground_color`,
3535
`Tty::query_default_background_color`, and `Tty::query_cursor_color`
3636
- terminal events: `Tty::read_event`
37-
- output commands: cursor movement/visibility, line erase, scroll margins,
38-
reverse index, alternate screen, bracketed paste mode, SGR mouse tracking,
39-
focus tracking, DEC auto wrap mode, foreground/background colors, text
40-
attributes, and style reset
37+
- output commands: cursor movement/visibility, line/display/scrollback erase,
38+
scroll margins, reverse index, alternate screen, bracketed paste mode, SGR
39+
mouse tracking, focus tracking, DEC auto wrap mode, foreground/background
40+
colors, text attributes, and style reset
4141

4242
Raw file and stdio byte I/O should use `moonbitlang/async/fs` and
4343
`moonbitlang/async/stdio` directly. The root package does not wrap them as

docs/plan.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ This board tracks implementation direction for `moonbit-community/tty`. Use
1818
| TTY-2 | done | termios state and raw mode | `state.mbt`, `state.c`, `examples/raw` | callers can capture, make raw, restore, and manually validate raw input | `moon check`, `moon test`, `examples/raw` manual run |
1919
| VT-1 | done | cursor movement and visibility sequences | `vt/cursor.mbt`, `vt/cursor_test.mbt`, `examples/cursor` | cursor helpers emit expected bytes and demo can draw a bounded region | `moon test vt`, `examples/cursor` manual run |
2020
| VT-2 | done | line erase and alternate screen helpers | `vt/erase.mbt`, `vt/screen.mbt`, `examples/input`, `examples/cursor` | demos can redraw one-line input and restore screen state | `moon test vt`, demo manual run |
21+
| VT-6 | done | display and scrollback erase commands | `docs/plans/2026-06-01-erase-display.md`, `internal/vt/erase.mbt`, root package | root `Tty` can emit `CSI 2 J` and `CSI 3 J` without screen state | `moon fmt`, `moon test internal/vt`, `moon test .`, `moon check`, `moon info`, `git diff --check` |
2122
| IN-1 | done | input event reader and common key sequences | `docs/plans/2026-05-20-input-event-reader.md` | `EventReader` decodes common keys while preserving unknown sequences | `moon fmt`, `moon check`, `moon test input`, `moon check examples/input`, `moon info`, manual `examples/input` run |
2223
| IN-2 | done | UTF-8 text decoding via core utf8 | `docs/plans/2026-05-20-utf8-input-decoding.md`, `input/` | non-ASCII text is decoded with `moonbitlang/core/encoding/utf8` without a local utf8 package | `moon fmt`, `moon check`, `moon test input`, `moon info` |
2324
| IN-5 | done | event reader buffer window | `docs/plans/2026-05-20-event-reader-buffer-window.md`, `input/` | `EventReader` uses one dynamic ordered byte window for current-event bytes and unread bytes | `moon fmt`, `moon check`, `moon test input`, `moon info` |
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Display Erase Commands
2+
3+
## Goal
4+
5+
Add root `Tty` command helpers for `CSI 2 J` and `CSI 3 J`, while keeping VT
6+
byte construction internal and avoiding any terminal screen or scrollback state
7+
model.
8+
9+
## Status
10+
11+
Done.
12+
13+
## Context And Decisions
14+
15+
- `CSI 2 J` is Erase in Display (ED) mode 2. It clears the visible display.
16+
- `CSI 3 J` is the common xterm extension for clearing saved lines or
17+
scrollback.
18+
- Avoid the name `erase_display_all` because it can imply that `CSI 2 J` also
19+
clears scrollback.
20+
- Use the shorter approved root method names:
21+
- `erase_display`
22+
- `erase_scrollback`
23+
- These are output-side commands only. Unsupported terminals may ignore
24+
`CSI 3 J`; no support query is added.
25+
26+
## References Or Standards
27+
28+
- ECMA-48 Erase in Display (ED), `CSI Ps J`.
29+
- xterm-compatible `CSI 3 J` saved-lines erase extension.
30+
31+
## Target Files
32+
33+
- `internal/vt/erase.mbt`
34+
- `internal/vt/erase_test.mbt`
35+
- `style.mbt`
36+
- `tty_wbtest.mbt`
37+
- `README.md`
38+
- `docs/plan.md`
39+
- generated `.mbti` files from `moon info`
40+
41+
## Public API Changes
42+
43+
Root package additions:
44+
45+
- `Tty::erase_display(Self) -> Unit`
46+
- `Tty::erase_scrollback(Self) -> Unit`
47+
48+
Internal VT package additions:
49+
50+
- `erase_display : Bytes`
51+
- `erase_scrollback : Bytes`
52+
53+
No new public type, parser state, input event, platform backend, dependency, or
54+
capability query is proposed.
55+
56+
## Invariants
57+
58+
- `Tty` remains the public output-command surface.
59+
- `internal/vt` remains byte-sequence-only and does not own output streams.
60+
- The package does not remember, infer, or expose terminal display state.
61+
- The package does not model or manage terminal scrollback.
62+
63+
## Acceptance Criteria
64+
65+
- Internal VT helpers emit `CSI 2 J` and `CSI 3 J`.
66+
- Root command helpers write the same bytes through `Tty`.
67+
- Generated `.mbti` diffs contain only the intended API additions.
68+
- README mentions display and scrollback erase as output commands.
69+
70+
## Validation Commands
71+
72+
- `moon fmt`
73+
- `moon test internal/vt`
74+
- `moon test .`
75+
- `moon check`
76+
- `moon info`
77+
- review generated `.mbti` diff
78+
- `git diff --check`
79+
80+
## Public API Audit
81+
82+
- Root `.mbti` adds only `Tty::erase_display` and
83+
`Tty::erase_scrollback`.
84+
- Internal VT `.mbti` adds only the `erase_display` and `erase_scrollback`
85+
byte constants used by root command methods.
86+
- No public type, mutable field, parser state, input event, platform handle,
87+
backend selection, dependency, or capability query was added.
88+
- Generated `.mbti` files were reviewed after `moon info`.
89+
90+
## Result Notes
91+
92+
- Added fixed ED byte constants for `CSI 2 J` and `CSI 3 J`.
93+
- Added root write-through helpers on `Tty`.
94+
- Added internal VT byte tests and extended the root pipe-backed terminal
95+
command test to cover both new methods.
96+
- Updated README output-command wording and the execution board.
97+
- Real terminal clear behavior needs a terminal emulator with observable screen
98+
state; pipe and PTY tests only verify byte traffic. No terminal-emulator
99+
integration test was added for this task.
100+
101+
## Validation Results
102+
103+
- `moon fmt` passed.
104+
- `moon test internal/vt` passed: 18 tests.
105+
- `moon test .` passed: 24 tests.
106+
- `moon test` passed: 146 tests.
107+
- `moon check` passed.
108+
- `moon info` passed and regenerated intended `.mbti` entries.
109+
- generated `.mbti` diff reviewed.
110+
- `git diff --check` passed.
111+
112+
## Open Questions
113+
114+
- None.

internal/vt/erase.mbt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,15 @@ pub let erase_line_left : Bytes = b"\x1b[1K"
1515
///
1616
/// Erases the entire current line.
1717
pub let erase_line_all : Bytes = b"\x1b[2K"
18+
19+
///|
20+
/// Erase in Display (ED), ECMA-48.
21+
///
22+
/// Erases the visible display.
23+
pub let erase_display : Bytes = b"\x1b[2J"
24+
25+
///|
26+
/// Erase in Display (ED), xterm extension.
27+
///
28+
/// Erases saved lines in the terminal scrollback.
29+
pub let erase_scrollback : Bytes = b"\x1b[3J"

internal/vt/erase_test.mbt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,9 @@ test "erase line sequences" {
44
@debug.assert_eq(@vt.erase_line_left, b"\x1b[1K")
55
@debug.assert_eq(@vt.erase_line_all, b"\x1b[2K")
66
}
7+
8+
///|
9+
test "erase display sequences" {
10+
@debug.assert_eq(@vt.erase_display, b"\x1b[2J")
11+
@debug.assert_eq(@vt.erase_scrollback, b"\x1b[3J")
12+
}

internal/vt/pkg.generated.mbti

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,16 @@ pub let enable_sgr_mouse : Bytes
5050

5151
pub let enter_alt_screen : Bytes
5252

53+
pub let erase_display : Bytes
54+
5355
pub let erase_line_all : Bytes
5456

5557
pub let erase_line_left : Bytes
5658

5759
pub let erase_line_right : Bytes
5860

61+
pub let erase_scrollback : Bytes
62+
5963
pub let hide_cursor : Bytes
6064

6165
pub let italic : Bytes

pkg.generated.mbti

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ pub async fn Tty::enable_focus_tracking(Self) -> Unit
6363
pub async fn Tty::enable_mouse(Self, MouseTrackingMode) -> Unit
6464
pub async fn Tty::enter_alt_screen(Self) -> Unit
6565
pub fn Tty::enter_raw_mode(Self) -> State raise @os_error.OSError
66+
pub async fn Tty::erase_display(Self) -> Unit
6667
pub async fn Tty::erase_line_all(Self) -> Unit
68+
pub async fn Tty::erase_scrollback(Self) -> Unit
6769
pub fn Tty::get_state(Self) -> State raise @os_error.OSError
6870
pub async fn Tty::hide_cursor(Self) -> Unit
6971
pub async fn Tty::italic(Self) -> Unit

style.mbt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,18 @@ pub async fn Tty::erase_line_all(self : Self) -> Unit {
446446
self.write(@vt.erase_line_all)
447447
}
448448

449+
///|
450+
/// Erase the visible display (ED 2), ECMA-48.
451+
pub async fn Tty::erase_display(self : Self) -> Unit {
452+
self.write(@vt.erase_display)
453+
}
454+
455+
///|
456+
/// Erase saved lines in the terminal scrollback (ED 3), xterm extension.
457+
pub async fn Tty::erase_scrollback(self : Self) -> Unit {
458+
self.write(@vt.erase_scrollback)
459+
}
460+
449461
///|
450462
/// Set Top and Bottom Margins (DECSTBM).
451463
pub async fn Tty::set_top_bottom_margins(

tty_wbtest.mbt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ async test "pipe tty terminal commands write sequences" {
6363
tty.cursor_up(2)
6464
tty.cursor_forward(3)
6565
tty.erase_line_all()
66+
tty.erase_display()
67+
tty.erase_scrollback()
6668
tty.set_top_bottom_margins(1, 5)
6769
tty.reverse_index()
6870
tty.reset_top_bottom_margins()
@@ -85,7 +87,7 @@ async test "pipe tty terminal commands write sequences" {
8587
})
8688
inspect(
8789
output,
88-
content="\u{1B}[?1049h\u{1B}[?2004h\u{1B}[?25l\u{1B}[3;4H\u{1B}[2A\u{1B}[3C\u{1B}[2K\u{1B}[1;5r\u{1B}M\u{1B}[r\u{1B}[31m\u{1B}[39m\u{1B}[48;5;42m\u{1B}[49m\u{1B}[1m\u{1B}[22m\u{1B}[3m\u{1B}[23m\u{1B}[4m\u{1B}[24m\u{1B}[7m\u{1B}[27m\u{1B}[0m\u{1B}[?25h\u{1B}[?2004l\u{1B}[?1049l",
90+
content="\u{1B}[?1049h\u{1B}[?2004h\u{1B}[?25l\u{1B}[3;4H\u{1B}[2A\u{1B}[3C\u{1B}[2K\u{1B}[2J\u{1B}[3J\u{1B}[1;5r\u{1B}M\u{1B}[r\u{1B}[31m\u{1B}[39m\u{1B}[48;5;42m\u{1B}[49m\u{1B}[1m\u{1B}[22m\u{1B}[3m\u{1B}[23m\u{1B}[4m\u{1B}[24m\u{1B}[7m\u{1B}[27m\u{1B}[0m\u{1B}[?25h\u{1B}[?2004l\u{1B}[?1049l",
8991
)
9092
}
9193

0 commit comments

Comments
 (0)