277 lines
5.6 KiB
Go
277 lines
5.6 KiB
Go
package input
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
type Event struct {
|
|
Key Key
|
|
Rune rune
|
|
}
|
|
|
|
type Reader struct {
|
|
buf *bufio.Reader
|
|
events chan *Event
|
|
log *slog.Logger
|
|
}
|
|
|
|
type Parameters struct {
|
|
Chan chan *Event
|
|
In *os.File
|
|
Log *slog.Logger
|
|
}
|
|
|
|
func New(p Parameters) *Reader {
|
|
return &Reader{
|
|
buf: bufio.NewReader(p.In),
|
|
events: p.Chan,
|
|
log: p.Log,
|
|
}
|
|
}
|
|
|
|
// send delivers e, or gives up when ctx is done.
|
|
func (i *Reader) send(ctx context.Context, e *Event) error {
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case i.events <- e:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// TODO: reading one rune at a time is slow, especially when pasting large
|
|
// quantities of text into the active buffer. It would be nice to take more
|
|
// input at once, while still being able to handle escape sequences without
|
|
// too many edge cases.
|
|
func (i *Reader) Run(ctx context.Context) error {
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return nil
|
|
default:
|
|
r, size, err := i.buf.ReadRune()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read: %w", err)
|
|
}
|
|
i.log.DebugContext(
|
|
ctx,
|
|
"read rune",
|
|
slog.String("rune", string(r)),
|
|
slog.Any("bytes", []byte(string(r))),
|
|
slog.Int("size", size),
|
|
)
|
|
switch r {
|
|
case utf8.RuneError:
|
|
i.log.ErrorContext(
|
|
ctx,
|
|
"rune error",
|
|
slog.String("rune", string(r)),
|
|
slog.Int("size", size),
|
|
)
|
|
|
|
// Handle escape sequences.
|
|
case Escape:
|
|
if err := i.parseEscapeSequence(ctx); err != nil {
|
|
return fmt.Errorf(
|
|
"failed to read: %w", err,
|
|
)
|
|
}
|
|
|
|
default:
|
|
if err := i.send(ctx, &Event{Rune: r}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (i *Reader) parseEscapeSequence(ctx context.Context) error {
|
|
r1, _, err := i.buf.ReadRune()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read: %w", err)
|
|
}
|
|
|
|
// Ignore invalid escape sequences.
|
|
if r1 != '[' && r1 != 'O' {
|
|
if err := i.send(ctx, &Event{Rune: r1}); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// We've received an input of Esc + [ or Esc + O.
|
|
// Determine the escape sequence.
|
|
r2, _, err := i.buf.ReadRune()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read: %w", err)
|
|
|
|
}
|
|
|
|
// Check letter escape sequences.
|
|
switch r2 {
|
|
case 'A':
|
|
if err := i.send(ctx, &Event{Key: Up}); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case 'B':
|
|
if err := i.send(ctx, &Event{Key: Down}); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case 'C':
|
|
if err := i.send(ctx, &Event{Key: Right}); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case 'D':
|
|
if err := i.send(ctx, &Event{Key: Left}); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
|
|
case 'O':
|
|
r3, _, err := i.buf.ReadRune()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read: %w", err)
|
|
}
|
|
switch r3 {
|
|
case 'P': // F1
|
|
return nil
|
|
case 'Q': // F2
|
|
return nil
|
|
case 'R': // F3
|
|
return nil
|
|
case 'S': // F4
|
|
return nil
|
|
default:
|
|
// No match.
|
|
if err := i.send(ctx, &Event{Rune: r1}); err != nil {
|
|
return err
|
|
}
|
|
if err := i.send(ctx, &Event{Rune: r2}); err != nil {
|
|
return err
|
|
}
|
|
if err := i.send(ctx, &Event{Rune: r3}); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// Check for single digit numerical escape sequences.
|
|
r3, _, err := i.buf.ReadRune()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read: %w", err)
|
|
}
|
|
switch {
|
|
case r2 == '1' && r3 == '~':
|
|
if err := i.send(ctx, &Event{Key: Home}); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case r2 == '2' && r3 == '~':
|
|
if err := i.send(ctx, &Event{Key: Insert}); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case r2 == '3' && r3 == '~':
|
|
if err := i.send(ctx, &Event{Rune: Delete}); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case r2 == '4' && r3 == '~':
|
|
if err := i.send(ctx, &Event{Key: End}); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case r2 == '5' && r3 == '~':
|
|
if err := i.send(ctx, &Event{Key: PageUp}); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case r2 == '6' && r3 == '~':
|
|
if err := i.send(ctx, &Event{Key: PageDown}); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case r2 == '7' && r3 == '~':
|
|
if err := i.send(ctx, &Event{Key: Home}); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case r2 == '8' && r3 == '~':
|
|
if err := i.send(ctx, &Event{Key: End}); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case r2 == '9' && r3 == '~':
|
|
if err := i.send(ctx, &Event{Key: End}); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Check for double digit numerical escape sequences.
|
|
r4, _, err := i.buf.ReadRune()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read: %w", err)
|
|
}
|
|
switch {
|
|
case r2 == '1' && r3 == '0' && r4 == '~':
|
|
return nil
|
|
case r2 == '1' && r3 == '1' && r4 == '~':
|
|
return nil
|
|
case r2 == '1' && r3 == '2' && r4 == '~':
|
|
return nil
|
|
case r2 == '1' && r3 == '3' && r4 == '~':
|
|
return nil
|
|
case r2 == '1' && r3 == '4' && r4 == '~':
|
|
return nil
|
|
case r2 == '1' && r3 == '4' && r4 == '~':
|
|
return nil
|
|
case r2 == '1' && r3 == '6' && r4 == '~':
|
|
return nil
|
|
case r2 == '1' && r3 == '7' && r4 == '~':
|
|
return nil
|
|
case r2 == '1' && r3 == '8' && r4 == '~':
|
|
return nil
|
|
case r2 == '1' && r3 == '9' && r4 == '~':
|
|
return nil
|
|
case r2 == '2' && r3 == '0' && r4 == '~':
|
|
return nil
|
|
case r2 == '2' && r3 == '1' && r4 == '~':
|
|
return nil
|
|
case r2 == '2' && r3 == '2' && r4 == '~':
|
|
return nil
|
|
case r2 == '2' && r3 == '3' && r4 == '~':
|
|
return nil
|
|
case r2 == '2' && r3 == '4' && r4 == '~':
|
|
return nil
|
|
case r4 == '~':
|
|
return nil
|
|
}
|
|
|
|
// No match.
|
|
if err := i.send(ctx, &Event{Rune: r1}); err != nil {
|
|
return err
|
|
}
|
|
if err := i.send(ctx, &Event{Rune: r2}); err != nil {
|
|
return err
|
|
}
|
|
if err := i.send(ctx, &Event{Rune: r3}); err != nil {
|
|
return err
|
|
}
|
|
if err := i.send(ctx, &Event{Rune: r4}); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|