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 }