@@ -4,7 +4,9 @@ package xterm
44//
55// ApcParser handles Application Program Command sequences. Unlike OSC which
66// uses numeric identifiers, APC uses the first character as the identifier
7- // (e.g., 'G' for Kitty graphics protocol).
7+ // (e.g., 'G' for Kitty graphics protocol). The main parser drives APC state
8+ // transitions (APC_ENTRY → APC_INTERMEDIATE → APC_PASSTHROUGH) and calls
9+ // Start(ident) with the computed identifier directly.
810
911// ApcHandler is the interface for handlers that process APC sequences.
1012// Intentionally separate from OscHandler to mirror xterm.js type structure.
@@ -19,24 +21,21 @@ type ApcFallbackHandler func(ident int, action string, payload ...interface{})
1921
2022// ApcParser parses APC sequences and dispatches to registered handlers.
2123type ApcParser struct {
22- state ApcState
2324 active []ApcHandler
24- id int
25+ ident int
2526 handlers map [int ][]ApcHandler
2627 handlerFb ApcFallbackHandler
2728}
2829
2930// NewApcParser creates a new ApcParser.
3031func NewApcParser () * ApcParser {
3132 return & ApcParser {
32- state : ApcStateStart ,
33- id : - 1 ,
3433 handlers : make (map [int ][]ApcHandler ),
3534 handlerFb : func (int , string , ... interface {}) {},
3635 }
3736}
3837
39- // RegisterHandler registers a handler for the given APC identifier (character code) .
38+ // RegisterHandler registers a handler for the given APC identifier.
4039// Returns a Disposable that removes the handler when disposed.
4140func (p * ApcParser ) RegisterHandler (ident int , handler ApcHandler ) Disposable {
4241 p .handlers [ident ] = append (p .handlers [ident ], handler )
@@ -70,93 +69,60 @@ func (p *ApcParser) Dispose() {
7069
7170// Reset forces cleanup of active handlers and resets parser state.
7271func (p * ApcParser ) Reset () {
73- if p . state == ApcStatePayload {
72+ if len ( p . active ) > 0 {
7473 for j := len (p .active ) - 1 ; j >= 0 ; j -- {
7574 p .active [j ].End (false )
7675 }
7776 }
7877 p .active = nil
79- p .id = - 1
80- p .state = ApcStateStart
78+ p .ident = 0
8179}
8280
83- func (p * ApcParser ) start () {
84- if handlers , ok := p .handlers [p .id ]; ok && len (handlers ) > 0 {
81+ // Start begins a new APC sequence with the given identifier.
82+ // The identifier is computed by the main parser from collect bytes and the
83+ // final character (collect<<8 | code), matching the DCS pattern.
84+ func (p * ApcParser ) Start (ident int ) {
85+ p .Reset ()
86+ p .ident = ident
87+ if handlers , ok := p .handlers [ident ]; ok && len (handlers ) > 0 {
8588 p .active = handlers
8689 for j := len (p .active ) - 1 ; j >= 0 ; j -- {
8790 p .active [j ].Start ()
8891 }
8992 } else {
9093 p .active = nil
91- p .handlerFb (p .id , "START" )
94+ p .handlerFb (p .ident , "START" )
9295 }
9396}
9497
95- func (p * ApcParser ) put (data []uint32 , start , end int ) {
98+ // Put feeds payload data to the active APC handlers.
99+ func (p * ApcParser ) Put (data []uint32 , start , end int ) {
96100 if len (p .active ) == 0 {
97- p .handlerFb (p .id , "PUT" , utf32ToString (data , start , end ))
101+ p .handlerFb (p .ident , "PUT" , utf32ToString (data , start , end ))
98102 } else {
99103 for j := len (p .active ) - 1 ; j >= 0 ; j -- {
100104 p .active [j ].Put (data , start , end )
101105 }
102106 }
103107}
104108
105- // Start begins a new APC sequence, resetting any leftover state.
106- func (p * ApcParser ) Start () {
107- p .Reset ()
108- p .state = ApcStateID
109- }
110-
111- // Put feeds data to the current APC command. The first character is used as
112- // the identifier, and subsequent data is passed as payload.
113- func (p * ApcParser ) Put (data []uint32 , start , end int ) {
114- if p .state == ApcStateAbort {
115- return
116- }
117- if p .state == ApcStateID {
118- if start < end {
119- p .id = int (data [start ])
120- start ++
121- p .state = ApcStatePayload
122- p .start ()
123- }
124- }
125- if p .state == ApcStatePayload && end - start > 0 {
126- p .put (data , start , end )
127- }
128- }
129-
130109// End signals the end of an APC sequence. success indicates whether the
131110// sequence terminated normally or was aborted.
132111func (p * ApcParser ) End (success bool ) {
133- if p .state == ApcStateStart {
134- return
135- }
136- if p .state != ApcStateAbort {
137- // Early end in ID state means empty APC — invalid, just reset.
138- if p .state == ApcStateID {
139- p .active = nil
140- p .id = - 1
141- p .state = ApcStateStart
142- return
143- }
144- if len (p .active ) == 0 {
145- p .handlerFb (p .id , "END" , success )
146- } else {
147- for j := len (p .active ) - 1 ; j >= 0 ; j -- {
148- if p .active [j ].End (success ) {
149- for k := j - 1 ; k >= 0 ; k -- {
150- p .active [k ].End (false )
151- }
152- break
112+ if len (p .active ) == 0 {
113+ p .handlerFb (p .ident , "END" , success )
114+ } else {
115+ for j := len (p .active ) - 1 ; j >= 0 ; j -- {
116+ if p .active [j ].End (success ) {
117+ for k := j - 1 ; k >= 0 ; k -- {
118+ p .active [k ].End (false )
153119 }
120+ break
154121 }
155122 }
156123 }
157124 p .active = nil
158- p .id = - 1
159- p .state = ApcStateStart
125+ p .ident = 0
160126}
161127
162128// ApcStringHandler is a convenience wrapper that collects APC payload as a
0 commit comments