Skip to content

Commit 54d979a

Browse files
feat: add RegisterCsiHandler, RegisterEscHandler, RegisterDcsHandler, RegisterOscHandler to Terminal
Expose the four missing custom handler registration methods on Terminal, forwarding to the underlying parser. The parser already supported all four; only the Terminal-level public API was missing. Fixes #20 Co-authored-by: Ona <no-reply@ona.com>
1 parent 8a85a71 commit 54d979a

2 files changed

Lines changed: 130 additions & 0 deletions

File tree

terminal.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,28 @@ func (t *Terminal) RegisterApcHandler(ident int, handler func(data string) bool)
226226
return t.inputHandler.parser.RegisterApcHandler(ident, NewApcStringHandler(handler))
227227
}
228228

229+
// RegisterCsiHandler registers a custom handler for a CSI escape sequence.
230+
// The handler returns true to stop the handler chain from bubbling further.
231+
func (t *Terminal) RegisterCsiHandler(id FunctionIdentifier, handler CsiHandler) Disposable {
232+
return t.inputHandler.parser.RegisterCsiHandler(id, handler)
233+
}
234+
235+
// RegisterEscHandler registers a custom handler for an ESC escape sequence.
236+
// The handler returns true to stop the handler chain from bubbling further.
237+
func (t *Terminal) RegisterEscHandler(id FunctionIdentifier, handler EscHandler) Disposable {
238+
return t.inputHandler.parser.RegisterEscHandler(id, handler)
239+
}
240+
241+
// RegisterDcsHandler registers a custom handler for a DCS escape sequence.
242+
func (t *Terminal) RegisterDcsHandler(id FunctionIdentifier, handler DcsHandler) Disposable {
243+
return t.inputHandler.parser.RegisterDcsHandler(id, handler)
244+
}
245+
246+
// RegisterOscHandler registers a custom handler for an OSC escape sequence.
247+
func (t *Terminal) RegisterOscHandler(ident int, handler OscHandler) Disposable {
248+
return t.inputHandler.parser.RegisterOscHandler(ident, handler)
249+
}
250+
229251
// NormalBuffer returns the normal (primary) buffer.
230252
func (t *Terminal) NormalBuffer() *Buffer { return t.bufferService.Buffers.Normal() }
231253

terminal_test.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -735,3 +735,111 @@ func TestTerminalTabStops(t *testing.T) {
735735
t.Errorf("CursorX() = %d, want 9 (after tab + B)", term.CursorX())
736736
}
737737
}
738+
739+
func TestTerminalRegisterCsiHandler(t *testing.T) {
740+
t.Parallel()
741+
term := newTestTerminal(80, 24)
742+
743+
var called bool
744+
var gotParams []int32
745+
d := term.RegisterCsiHandler(FunctionIdentifier{Final: 'Z'}, func(params *Params) bool {
746+
called = true
747+
gotParams = make([]int32, params.Length)
748+
copy(gotParams, params.Params[:params.Length])
749+
return true
750+
})
751+
752+
// Send CSI 1;2 Z
753+
term.WriteString("\x1b[1;2Z")
754+
if !called {
755+
t.Fatal("CSI handler was not called")
756+
}
757+
if len(gotParams) < 2 || gotParams[0] != 1 || gotParams[1] != 2 {
758+
t.Fatalf("got params %v, want [1 2]", gotParams)
759+
}
760+
761+
// Dispose and verify handler no longer fires.
762+
called = false
763+
d.Dispose()
764+
term.WriteString("\x1b[1;2Z")
765+
if called {
766+
t.Fatal("CSI handler was called after Dispose")
767+
}
768+
}
769+
770+
func TestTerminalRegisterEscHandler(t *testing.T) {
771+
t.Parallel()
772+
term := newTestTerminal(80, 24)
773+
774+
var called bool
775+
d := term.RegisterEscHandler(FunctionIdentifier{Intermediates: "#", Final: '9'}, func() bool {
776+
called = true
777+
return true
778+
})
779+
780+
// Send ESC # 9
781+
term.WriteString("\x1b#9")
782+
if !called {
783+
t.Fatal("ESC handler was not called")
784+
}
785+
786+
// Dispose and verify handler no longer fires.
787+
called = false
788+
d.Dispose()
789+
term.WriteString("\x1b#9")
790+
if called {
791+
t.Fatal("ESC handler was called after Dispose")
792+
}
793+
}
794+
795+
func TestTerminalRegisterDcsHandler(t *testing.T) {
796+
t.Parallel()
797+
term := newTestTerminal(80, 24)
798+
799+
var gotData string
800+
d := term.RegisterDcsHandler(FunctionIdentifier{Final: 'z'}, NewDcsStringHandler(func(data string, params *Params) bool {
801+
gotData = data
802+
return true
803+
}))
804+
805+
// Send DCS z <payload> ST (DCS = ESC P, ST = ESC \)
806+
term.WriteString("\x1bPz" + "hello" + "\x1b\\")
807+
if gotData != "hello" {
808+
t.Fatalf("DCS handler got data %q, want %q", gotData, "hello")
809+
}
810+
811+
// Dispose and verify handler no longer fires.
812+
gotData = ""
813+
d.Dispose()
814+
term.WriteString("\x1bPz" + "world" + "\x1b\\")
815+
if gotData != "" {
816+
t.Fatalf("DCS handler got data %q after Dispose, want empty", gotData)
817+
}
818+
}
819+
820+
func TestTerminalRegisterOscHandler(t *testing.T) {
821+
t.Parallel()
822+
term := newTestTerminal(80, 24)
823+
824+
var gotData string
825+
// Use a high OSC number unlikely to conflict with built-in handlers.
826+
d := term.RegisterOscHandler(9999, NewOscStringHandler(func(data string) bool {
827+
gotData = data
828+
return true
829+
}))
830+
831+
// Send OSC 9999 ; payload BEL
832+
term.WriteString("\x1b]9999;test-payload\x07")
833+
if gotData != "test-payload" {
834+
t.Fatalf("OSC handler got data %q, want %q", gotData, "test-payload")
835+
}
836+
837+
// Dispose and verify handler no longer fires.
838+
gotData = ""
839+
d.Dispose()
840+
term.WriteString("\x1b]9999;after-dispose\x07")
841+
if gotData != "" {
842+
t.Fatalf("OSC handler got data %q after Dispose, want empty", gotData)
843+
}
844+
}
845+

0 commit comments

Comments
 (0)