Skip to content

Commit 581857e

Browse files
feat: add OnBinary and OnWriteParsed events to Terminal public API
Expose OnBinary (forwarded from CoreService) and OnWriteParsed (fires after each Write/WriteString call) on the Terminal type, matching the upstream CoreTerminal public API. Fixes #36 Co-authored-by: Ona <no-reply@ona.com>
1 parent 572f9ae commit 581857e

2 files changed

Lines changed: 97 additions & 0 deletions

File tree

terminal.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ type Terminal struct {
4848
OnResizeEmitter EventEmitter[BufferResizeEvent]
4949
OnScrollEmitter EventEmitter[int]
5050
OnRenderEmitter EventEmitter[RowRange]
51+
OnWriteParsedEmitter EventEmitter[struct{}]
5152
OnRequestColorSchemeQueryEmitter EventEmitter[struct{}]
5253
OnRequestWindowsOptionsReportEmitter EventEmitter[WindowsOptionsReportType]
5354
}
@@ -105,12 +106,14 @@ func New(opts ...Option) *Terminal {
105106
// Write writes data to the terminal, implementing io.Writer.
106107
func (t *Terminal) Write(p []byte) (n int, err error) {
107108
t.inputHandler.Parse(p)
109+
t.OnWriteParsedEmitter.Fire(struct{}{})
108110
return len(p), nil
109111
}
110112

111113
// WriteString writes a string to the terminal.
112114
func (t *Terminal) WriteString(s string) {
113115
t.inputHandler.ParseString(s)
116+
t.OnWriteParsedEmitter.Fire(struct{}{})
114117
}
115118

116119
// Resize changes the terminal dimensions.
@@ -213,6 +216,17 @@ func (t *Terminal) OnData(fn func(string)) Disposable {
213216
return t.coreService.OnDataEmitter.Event(fn)
214217
}
215218

219+
// OnBinary subscribes to binary data events from the terminal.
220+
func (t *Terminal) OnBinary(fn func(string)) Disposable {
221+
return t.coreService.OnBinaryEmitter.Event(func(data string) { fn(data) })
222+
}
223+
224+
// OnWriteParsed subscribes to write-parsed events.
225+
// Fires after each Write/WriteString call completes parsing.
226+
func (t *Terminal) OnWriteParsed(fn func()) Disposable {
227+
return t.OnWriteParsedEmitter.Event(func(struct{}) { fn() })
228+
}
229+
216230
// OnBell registers a callback for bell events.
217231
func (t *Terminal) OnBell(fn func()) Disposable {
218232
return t.OnBellEmitter.Event(func(struct{}) { fn() })
@@ -412,6 +426,7 @@ func (t *Terminal) Dispose() {
412426
t.OnResizeEmitter.Dispose()
413427
t.OnScrollEmitter.Dispose()
414428
t.OnRenderEmitter.Dispose()
429+
t.OnWriteParsedEmitter.Dispose()
415430
t.OnRequestColorSchemeQueryEmitter.Dispose()
416431
t.OnRequestWindowsOptionsReportEmitter.Dispose()
417432
}

terminal_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,5 +1262,87 @@ func TestTerminalOnRequestColorSchemeQueryDispose(t *testing.T) {
12621262
}
12631263
}
12641264

1265+
func TestTerminalOnBinary(t *testing.T) {
1266+
t.Parallel()
1267+
term := newTestTerminal(80, 24)
1268+
var got []string
1269+
term.OnBinary(func(data string) { got = append(got, data) })
1270+
1271+
// Trigger a binary event via the core service (the plumbing path).
1272+
term.coreService.TriggerBinaryEvent("\x1b[2J")
1273+
1274+
if len(got) != 1 {
1275+
t.Fatalf("expected 1 OnBinary event, got %d", len(got))
1276+
}
1277+
if got[0] != "\x1b[2J" {
1278+
t.Errorf("OnBinary data = %q, want %q", got[0], "\x1b[2J")
1279+
}
1280+
}
1281+
1282+
func TestTerminalOnBinaryDispose(t *testing.T) {
1283+
t.Parallel()
1284+
term := newTestTerminal(80, 24)
1285+
count := 0
1286+
d := term.OnBinary(func(string) { count++ })
1287+
1288+
term.coreService.TriggerBinaryEvent("data1")
1289+
if count != 1 {
1290+
t.Fatalf("expected 1 fire before dispose, got %d", count)
1291+
}
1292+
1293+
d.Dispose()
1294+
term.coreService.TriggerBinaryEvent("data2")
1295+
if count != 1 {
1296+
t.Errorf("OnBinary fired after Dispose: count = %d, want 1", count)
1297+
}
1298+
}
1299+
1300+
func TestTerminalOnWriteParsed(t *testing.T) {
1301+
t.Parallel()
1302+
term := newTestTerminal(80, 24)
1303+
count := 0
1304+
term.OnWriteParsed(func() { count++ })
1305+
1306+
term.WriteString("hello")
1307+
if count != 1 {
1308+
t.Fatalf("expected 1 OnWriteParsed after WriteString, got %d", count)
1309+
}
1310+
1311+
term.Write([]byte("world"))
1312+
if count != 2 {
1313+
t.Fatalf("expected 2 OnWriteParsed after Write, got %d", count)
1314+
}
1315+
}
1316+
1317+
func TestTerminalOnWriteParsedDispose(t *testing.T) {
1318+
t.Parallel()
1319+
term := newTestTerminal(80, 24)
1320+
count := 0
1321+
d := term.OnWriteParsed(func() { count++ })
1322+
1323+
term.WriteString("hello")
1324+
if count != 1 {
1325+
t.Fatalf("expected 1 fire before dispose, got %d", count)
1326+
}
1327+
1328+
d.Dispose()
1329+
term.WriteString("world")
1330+
if count != 1 {
1331+
t.Errorf("OnWriteParsed fired after Dispose: count = %d, want 1", count)
1332+
}
1333+
}
12651334

1335+
func TestTerminalOnWriteParsedMultipleWrites(t *testing.T) {
1336+
t.Parallel()
1337+
term := newTestTerminal(80, 24)
1338+
count := 0
1339+
term.OnWriteParsed(func() { count++ })
12661340

1341+
// Each write should fire exactly once.
1342+
for i := range 5 {
1343+
term.WriteString(fmt.Sprintf("line %d\r\n", i))
1344+
}
1345+
if count != 5 {
1346+
t.Errorf("expected 5 OnWriteParsed fires, got %d", count)
1347+
}
1348+
}

0 commit comments

Comments
 (0)