@@ -31,42 +31,20 @@ type win32InputState struct {
3131
3232// Reader represents an input event reader. It reads input events and parses
3333// escape sequences from the terminal input buffer and translates them into
34- // human- readable events.
34+ // human‑ readable events.
3535type Reader struct {
36- rd cancelreader.CancelReader
37- table map [string ]Key // table is a lookup table for key sequences.
38-
39- term string // term is the terminal name $TERM.
40-
41- // paste is the bracketed paste mode buffer.
42- // When nil, bracketed paste mode is disabled.
43- paste []byte
44-
45- buf [256 ]byte // do we need a larger buffer?
46-
47- // partialSeq holds incomplete escape sequences that need more data
48- partialSeq []byte
49-
50- // keyState keeps track of the current Windows Console API key events state.
51- // It is used to decode ANSI escape sequences and utf16 sequences.
52- keyState win32InputState
53-
54- parser Parser
55- logger Logger
36+ rd cancelreader.CancelReader
37+ table map [string ]Key // table is a lookup table for key sequences.
38+ term string // $TERM
39+ paste []byte // bracketed paste buffer; nil when disabled
40+ buf [256 ]byte // read buffer
41+ partialSeq []byte // holds incomplete escape sequences
42+ keyState win32InputState
43+ parser Parser
44+ logger Logger
5645}
5746
58- // NewReader returns a new input event reader. The reader reads input events
59- // from the terminal and parses escape sequences into human-readable events. It
60- // supports reading Terminfo databases. See [Parser] for more information.
61- //
62- // Example:
63- //
64- // r, _ := input.NewReader(os.Stdin, os.Getenv("TERM"), 0)
65- // defer r.Close()
66- // events, _ := r.ReadEvents()
67- // for _, ev := range events {
68- // log.Printf("%v", ev)
69- // }
47+ // NewReader returns a new input event reader.
7048func NewReader (r io.Reader , termType string , flags int ) (* Reader , error ) {
7149 d := new (Reader )
7250 cr , err := newCancelreader (r , flags )
@@ -82,46 +60,38 @@ func NewReader(r io.Reader, termType string, flags int) (*Reader, error) {
8260}
8361
8462// SetLogger sets a logger for the reader.
85- func (d * Reader ) SetLogger (l Logger ) {
86- d .logger = l
87- }
63+ func (d * Reader ) SetLogger (l Logger ) { d .logger = l }
8864
89- // Read implements [io.Reader].
90- func (d * Reader ) Read (p []byte ) (int , error ) {
91- return d .rd .Read (p ) //nolint:wrapcheck
92- }
65+ // Read implements io.Reader.
66+ func (d * Reader ) Read (p []byte ) (int , error ) { return d .rd .Read (p ) }
9367
9468// Cancel cancels the underlying reader.
95- func (d * Reader ) Cancel () bool {
96- return d .rd .Cancel ()
97- }
69+ func (d * Reader ) Cancel () bool { return d .rd .Cancel () }
9870
9971// Close closes the underlying reader.
100- func (d * Reader ) Close () error {
101- return d .rd .Close () //nolint:wrapcheck
102- }
72+ func (d * Reader ) Close () error { return d .rd .Close () }
10373
10474func (d * Reader ) readEvents () ([]Event , error ) {
10575 nb , err := d .rd .Read (d .buf [:])
10676 if err != nil {
107- return nil , err //nolint:wrapcheck
77+ return nil , err
10878 }
10979
11080 var events []Event
11181
112- // Combine any partial sequence from previous read with new data
82+ // Combine any partial sequence from previous read with new data.
11383 var buf []byte
11484 if len (d .partialSeq ) > 0 {
11585 buf = make ([]byte , len (d .partialSeq )+ nb )
11686 copy (buf , d .partialSeq )
11787 copy (buf [len (d .partialSeq ):], d .buf [:nb ])
118- d .partialSeq = nil // clear the partial sequence
88+ d .partialSeq = nil
11989 } else {
12090 buf = d .buf [:nb ]
12191 }
12292
123- // Lookup table first
124- if bytes .HasPrefix (buf , []byte {'\x1b' }) {
93+ // Fast path: direct lookup for simple escape sequences.
94+ if bytes .HasPrefix (buf , []byte {0x1b }) {
12595 if k , ok := d .table [string (buf )]; ok {
12696 if d .logger != nil {
12797 d .logger .Printf ("input: %q" , buf )
@@ -133,24 +103,23 @@ func (d *Reader) readEvents() ([]Event, error) {
133103
134104 var i int
135105 for i < len (buf ) {
136- nb , ev := d .parser .parseSequence (buf [i :])
137- if d .logger != nil && nb > 0 {
138- d .logger .Printf ("input: %q" , buf [i :i + nb ])
106+ consumed , ev := d .parser .parseSequence (buf [i :])
107+ if d .logger != nil && consumed > 0 {
108+ d .logger .Printf ("input: %q" , buf [i :i + consumed ])
139109 }
140110
141- // Handle incomplete sequences - when parseSequence returns (0, nil)
142- // it means we need more data to complete the sequence
143- if nb == 0 && ev == nil {
144- // Store the remaining data for the next read
145- remaining := len (buf ) - i
146- if remaining > 0 {
147- d .partialSeq = make ([]byte , remaining )
111+ // Incomplete sequence – store remainder and exit.
112+ if consumed == 0 && ev == nil {
113+ rem := len (buf ) - i
114+ if rem > 0 {
115+ d .partialSeq = make ([]byte , rem )
148116 copy (d .partialSeq , buf [i :])
149117 }
150118 break
151119 }
152120
153- // Handle bracketed-paste
121+ // Handle bracketed paste specially so we don’t emit a paste event for
122+ // every byte.
154123 if d .paste != nil {
155124 if _ , ok := ev .(PasteEndEvent ); ! ok {
156125 d .paste = append (d .paste , buf [i ])
@@ -160,15 +129,9 @@ func (d *Reader) readEvents() ([]Event, error) {
160129 }
161130
162131 switch ev .(type ) {
163- // case UnknownEvent:
164- // // If the sequence is not recognized by the parser, try looking it up.
165- // if k, ok := d.table[string(buf[i:i+nb])]; ok {
166- // ev = KeyPressEvent(k)
167- // }
168132 case PasteStartEvent :
169133 d .paste = []byte {}
170134 case PasteEndEvent :
171- // Decode the captured data into runes.
172135 var paste []rune
173136 for len (d .paste ) > 0 {
174137 r , w := utf8 .DecodeRune (d .paste )
@@ -177,7 +140,7 @@ func (d *Reader) readEvents() ([]Event, error) {
177140 }
178141 d .paste = d .paste [w :]
179142 }
180- d .paste = nil // reset the buffer
143+ d .paste = nil
181144 events = append (events , PasteEvent (paste ))
182145 case nil :
183146 i ++
@@ -189,8 +152,41 @@ func (d *Reader) readEvents() ([]Event, error) {
189152 } else {
190153 events = append (events , ev )
191154 }
192- i += nb
155+ i += consumed
193156 }
194157
158+ // Collapse bursts of wheel/motion events into a single event each.
159+ events = coalesceMouseEvents (events )
195160 return events , nil
196161}
162+
163+ // coalesceMouseEvents reduces the volume of MouseWheelEvent and MouseMotionEvent
164+ // objects that arrive in rapid succession by keeping only the most recent
165+ // event in each contiguous run.
166+ func coalesceMouseEvents (in []Event ) []Event {
167+ if len (in ) < 2 {
168+ return in
169+ }
170+
171+ out := make ([]Event , 0 , len (in ))
172+ for _ , ev := range in {
173+ switch ev .(type ) {
174+ case MouseWheelEvent :
175+ if len (out ) > 0 {
176+ if _ , ok := out [len (out )- 1 ].(MouseWheelEvent ); ok {
177+ out [len (out )- 1 ] = ev // replace previous wheel event
178+ continue
179+ }
180+ }
181+ case MouseMotionEvent :
182+ if len (out ) > 0 {
183+ if _ , ok := out [len (out )- 1 ].(MouseMotionEvent ); ok {
184+ out [len (out )- 1 ] = ev // replace previous motion event
185+ continue
186+ }
187+ }
188+ }
189+ out = append (out , ev )
190+ }
191+ return out
192+ }
0 commit comments