Skip to content

Commit 8a85a71

Browse files
feat: add OnCursorMove, OnResize, OnScroll public event accessors
Add public methods to subscribe to cursor move, resize, and scroll events on Terminal. The emitters and internal wiring already existed; only the forwarding accessors were missing. Fixes #19 Co-authored-by: Ona <no-reply@ona.com>
1 parent 743856d commit 8a85a71

2 files changed

Lines changed: 113 additions & 0 deletions

File tree

terminal.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,21 @@ func (t *Terminal) OnLineFeed(fn func()) Disposable {
200200
return t.OnLineFeedEmitter.Event(func(struct{}) { fn() })
201201
}
202202

203+
// OnCursorMove registers a callback for cursor move events.
204+
func (t *Terminal) OnCursorMove(fn func()) Disposable {
205+
return t.OnCursorMoveEmitter.Event(func(struct{}) { fn() })
206+
}
207+
208+
// OnResize registers a callback for terminal resize events.
209+
func (t *Terminal) OnResize(fn func(BufferResizeEvent)) Disposable {
210+
return t.OnResizeEmitter.Event(fn)
211+
}
212+
213+
// OnScroll registers a callback for scroll events.
214+
func (t *Terminal) OnScroll(fn func(int)) Disposable {
215+
return t.OnScrollEmitter.Event(fn)
216+
}
217+
203218
// OnRender registers a callback fired when terminal rows are dirty.
204219
func (t *Terminal) OnRender(fn func(RowRange)) Disposable {
205220
return t.OnRenderEmitter.Event(fn)

terminal_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,104 @@ func TestTerminalRegisterApcHandlerDispose(t *testing.T) {
624624
}
625625
}
626626

627+
func TestTerminalOnCursorMove(t *testing.T) {
628+
t.Parallel()
629+
term := newTestTerminal(80, 24)
630+
moveCount := 0
631+
term.OnCursorMove(func() { moveCount++ })
632+
// CUP (cursor position) triggers a cursor move event.
633+
term.WriteString("\x1b[5;10H")
634+
if moveCount == 0 {
635+
t.Error("expected OnCursorMove to fire, got 0 events")
636+
}
637+
}
638+
639+
func TestTerminalOnCursorMoveDispose(t *testing.T) {
640+
t.Parallel()
641+
term := newTestTerminal(80, 24)
642+
count := 0
643+
d := term.OnCursorMove(func() { count++ })
644+
term.WriteString("\x1b[2;1H")
645+
if count == 0 {
646+
t.Fatal("expected OnCursorMove to fire")
647+
}
648+
first := count
649+
d.Dispose()
650+
term.WriteString("\x1b[3;1H")
651+
if count != first {
652+
t.Errorf("OnCursorMove fired after Dispose: count went from %d to %d", first, count)
653+
}
654+
}
655+
656+
func TestTerminalOnResize(t *testing.T) {
657+
t.Parallel()
658+
term := newTestTerminal(80, 24)
659+
var events []BufferResizeEvent
660+
term.OnResize(func(e BufferResizeEvent) { events = append(events, e) })
661+
term.Resize(40, 10)
662+
if len(events) == 0 {
663+
t.Fatal("expected OnResize to fire, got no events")
664+
}
665+
got := events[len(events)-1]
666+
if got.Cols != 40 || got.Rows != 10 {
667+
t.Errorf("OnResize event = {Cols:%d, Rows:%d}, want {Cols:40, Rows:10}", got.Cols, got.Rows)
668+
}
669+
}
670+
671+
func TestTerminalOnResizeDispose(t *testing.T) {
672+
t.Parallel()
673+
term := newTestTerminal(80, 24)
674+
count := 0
675+
d := term.OnResize(func(BufferResizeEvent) { count++ })
676+
term.Resize(40, 10)
677+
if count == 0 {
678+
t.Fatal("expected OnResize to fire")
679+
}
680+
first := count
681+
d.Dispose()
682+
term.Resize(60, 20)
683+
if count != first {
684+
t.Errorf("OnResize fired after Dispose: count went from %d to %d", first, count)
685+
}
686+
}
687+
688+
func TestTerminalOnScroll(t *testing.T) {
689+
t.Parallel()
690+
term := newTestTerminal(20, 5)
691+
var scrollPositions []int
692+
term.OnScroll(func(pos int) { scrollPositions = append(scrollPositions, pos) })
693+
// Write enough lines to trigger scrolling in a 5-row terminal.
694+
for i := range 8 {
695+
term.WriteString(fmt.Sprintf("Line%d\r\n", i))
696+
}
697+
if len(scrollPositions) == 0 {
698+
t.Fatal("expected OnScroll to fire, got no events")
699+
}
700+
}
701+
702+
func TestTerminalOnScrollDispose(t *testing.T) {
703+
t.Parallel()
704+
term := newTestTerminal(20, 5)
705+
count := 0
706+
d := term.OnScroll(func(int) { count++ })
707+
// Trigger scrolling.
708+
for i := range 8 {
709+
term.WriteString(fmt.Sprintf("Line%d\r\n", i))
710+
}
711+
if count == 0 {
712+
t.Fatal("expected OnScroll to fire")
713+
}
714+
first := count
715+
d.Dispose()
716+
// Write more to trigger additional scrolling.
717+
for i := range 5 {
718+
term.WriteString(fmt.Sprintf("More%d\r\n", i))
719+
}
720+
if count != first {
721+
t.Errorf("OnScroll fired after Dispose: count went from %d to %d", first, count)
722+
}
723+
}
724+
627725
func TestTerminalTabStops(t *testing.T) {
628726
t.Parallel()
629727
term := newTestTerminal(80, 24)

0 commit comments

Comments
 (0)