src

Go monorepo.
Log | Files | Refs

input.go (5119B)


      1 package input
      2 
      3 import (
      4 	"bufio"
      5 	"fmt"
      6 	"os"
      7 	"unicode/utf8"
      8 
      9 	"code.dwrz.net/src/pkg/editor/command"
     10 	"code.dwrz.net/src/pkg/log"
     11 	"code.dwrz.net/src/pkg/terminal"
     12 )
     13 
     14 type Event struct {
     15 	Command command.Command
     16 	Rune    rune
     17 }
     18 
     19 type Reader struct {
     20 	buf    *bufio.Reader
     21 	events chan *Event
     22 	log    *log.Logger
     23 }
     24 
     25 type Parameters struct {
     26 	Chan chan *Event
     27 	In   *os.File
     28 	Log  *log.Logger
     29 }
     30 
     31 func New(p Parameters) *Reader {
     32 	return &Reader{
     33 		buf:    bufio.NewReader(p.In),
     34 		events: p.Chan,
     35 		log:    p.Log,
     36 	}
     37 }
     38 
     39 // TODO: reading one rune at a time is slow, especially when pasting large
     40 // quantities of text into the active buffer. It would be nice to take more
     41 // input at once, while still being able to handle escape sequences without
     42 // too many edge cases.
     43 func (i *Reader) Run() error {
     44 	for {
     45 		r, size, err := i.buf.ReadRune()
     46 		if err != nil {
     47 			return fmt.Errorf("failed to read: %w", err)
     48 		}
     49 		i.log.Debug.Printf(
     50 			"read rune %s %v (%d)",
     51 			string(r), []byte(string(r)), size,
     52 		)
     53 		switch r {
     54 		case utf8.RuneError:
     55 			i.log.Error.Printf(
     56 				"rune error: %s (%d)", string(r), size,
     57 			)
     58 
     59 		// Handle escape sequences.
     60 		case terminal.Escape:
     61 			if err := i.parseEscapeSequence(); err != nil {
     62 				return fmt.Errorf("failed to read: %w", err)
     63 			}
     64 
     65 		case terminal.Delete:
     66 			i.events <- &Event{Command: command.Backspace}
     67 
     68 		case 'q' & terminal.Control:
     69 			i.events <- &Event{Command: command.Quit}
     70 
     71 		case 's' & terminal.Control:
     72 			i.events <- &Event{Command: command.Save}
     73 
     74 		default:
     75 			i.events <- &Event{Command: command.Insert, Rune: r}
     76 		}
     77 	}
     78 }
     79 
     80 func (i *Reader) parseEscapeSequence() error {
     81 	r1, _, err := i.buf.ReadRune()
     82 	if err != nil {
     83 		return fmt.Errorf("failed to read: %w", err)
     84 	}
     85 
     86 	// Ignore invalid escape sequences.
     87 	if r1 != '[' && r1 != 'O' {
     88 		i.events <- &Event{Command: command.Insert, Rune: r1}
     89 		return nil
     90 	}
     91 
     92 	// We've received an input of Esc + [ or Esc + O.
     93 	// Determine the escape sequence.
     94 	r2, _, err := i.buf.ReadRune()
     95 	if err != nil {
     96 		return fmt.Errorf("failed to read: %w", err)
     97 
     98 	}
     99 
    100 	// Check letter escape sequences.
    101 	switch r2 {
    102 	case 'A':
    103 		i.events <- &Event{Command: command.CursorUp}
    104 		return nil
    105 	case 'B':
    106 		i.events <- &Event{Command: command.CursorDown}
    107 		return nil
    108 	case 'C':
    109 		i.events <- &Event{Command: command.CursorRight}
    110 		return nil
    111 	case 'D':
    112 		i.events <- &Event{Command: command.CursorLeft}
    113 		return nil
    114 
    115 	case 'O':
    116 		r3, _, err := i.buf.ReadRune()
    117 		if err != nil {
    118 			return fmt.Errorf("failed to read: %w", err)
    119 		}
    120 		switch r3 {
    121 		case 'P': // F1
    122 			return nil
    123 		case 'Q': // F2
    124 			return nil
    125 		case 'R': // F3
    126 			return nil
    127 		case 'S': // F4
    128 			return nil
    129 		default:
    130 			// No match.
    131 			i.events <- &Event{Command: command.Insert, Rune: r1}
    132 			i.events <- &Event{Command: command.Insert, Rune: r2}
    133 			i.events <- &Event{Command: command.Insert, Rune: r3}
    134 			return nil
    135 		}
    136 	}
    137 
    138 	// Check for single digit numerical escape sequences.
    139 	r3, _, err := i.buf.ReadRune()
    140 	if err != nil {
    141 		return fmt.Errorf("failed to read: %w", err)
    142 	}
    143 	switch {
    144 	case r2 == '1' && r3 == '~':
    145 		i.events <- &Event{Command: command.Home}
    146 		return nil
    147 	case r2 == '2' && r3 == '~':
    148 		i.events <- &Event{Command: command.Insert}
    149 		return nil
    150 	case r2 == '3' && r3 == '~':
    151 		i.events <- &Event{Command: command.Delete}
    152 		return nil
    153 	case r2 == '4' && r3 == '~':
    154 		i.events <- &Event{Command: command.End}
    155 		return nil
    156 	case r2 == '5' && r3 == '~':
    157 		i.events <- &Event{Command: command.PageUp}
    158 		return nil
    159 	case r2 == '6' && r3 == '~':
    160 		i.events <- &Event{Command: command.PageDown}
    161 		return nil
    162 	case r2 == '7' && r3 == '~':
    163 		i.events <- &Event{Command: command.Home}
    164 		return nil
    165 	case r2 == '8' && r3 == '~':
    166 		i.events <- &Event{Command: command.End}
    167 		return nil
    168 	case r2 == '9' && r3 == '~':
    169 		i.events <- &Event{Command: command.End}
    170 		return nil
    171 	}
    172 
    173 	// Check for double digit numerical escape sequences.
    174 	r4, _, err := i.buf.ReadRune()
    175 	if err != nil {
    176 		return fmt.Errorf("failed to read: %w", err)
    177 	}
    178 	switch {
    179 	case r2 == '1' && r3 == '0' && r4 == '~':
    180 		return nil
    181 	case r2 == '1' && r3 == '1' && r4 == '~':
    182 		return nil
    183 	case r2 == '1' && r3 == '2' && r4 == '~':
    184 		return nil
    185 	case r2 == '1' && r3 == '3' && r4 == '~':
    186 		return nil
    187 	case r2 == '1' && r3 == '4' && r4 == '~':
    188 		return nil
    189 	case r2 == '1' && r3 == '4' && r4 == '~':
    190 		return nil
    191 	case r2 == '1' && r3 == '6' && r4 == '~':
    192 		return nil
    193 	case r2 == '1' && r3 == '7' && r4 == '~':
    194 		return nil
    195 	case r2 == '1' && r3 == '8' && r4 == '~':
    196 		return nil
    197 	case r2 == '1' && r3 == '9' && r4 == '~':
    198 		return nil
    199 	case r2 == '2' && r3 == '0' && r4 == '~':
    200 		return nil
    201 	case r2 == '2' && r3 == '1' && r4 == '~':
    202 		return nil
    203 	case r2 == '2' && r3 == '2' && r4 == '~':
    204 		return nil
    205 	case r2 == '2' && r3 == '3' && r4 == '~':
    206 		return nil
    207 	case r2 == '2' && r3 == '4' && r4 == '~':
    208 		return nil
    209 	case r4 == '~':
    210 		return nil
    211 	}
    212 
    213 	// No match.
    214 	i.events <- &Event{Command: command.Insert, Rune: r1}
    215 	i.events <- &Event{Command: command.Insert, Rune: r2}
    216 	i.events <- &Event{Command: command.Insert, Rune: r3}
    217 	i.events <- &Event{Command: command.Insert, Rune: r4}
    218 
    219 	return nil
    220 }