OLD | NEW |
1 // Copyright 2011 The Go Authors. All rights reserved. | 1 // Copyright 2011 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 package terminal | 5 package terminal |
6 | 6 |
7 import ( | 7 import ( |
8 "io" | 8 "io" |
9 "sync" | 9 "sync" |
10 "unicode/utf8" | 10 "unicode/utf8" |
(...skipping 18 matching lines...) Expand all Loading... |
29 Magenta: []byte{keyEscape, '[', '3', '5', 'm'}, | 29 Magenta: []byte{keyEscape, '[', '3', '5', 'm'}, |
30 Cyan: []byte{keyEscape, '[', '3', '6', 'm'}, | 30 Cyan: []byte{keyEscape, '[', '3', '6', 'm'}, |
31 White: []byte{keyEscape, '[', '3', '7', 'm'}, | 31 White: []byte{keyEscape, '[', '3', '7', 'm'}, |
32 | 32 |
33 Reset: []byte{keyEscape, '[', '0', 'm'}, | 33 Reset: []byte{keyEscape, '[', '0', 'm'}, |
34 } | 34 } |
35 | 35 |
36 // Terminal contains the state for running a VT100 terminal that is capable of | 36 // Terminal contains the state for running a VT100 terminal that is capable of |
37 // reading lines of input. | 37 // reading lines of input. |
38 type Terminal struct { | 38 type Terminal struct { |
39 » // AutoCompleteCallback, if non-null, is called for each keypress with | 39 » // AutoCompleteCallback, if non-nil, is called for each keypress with |
40 // the full input line and the current position of the cursor (in | 40 // the full input line and the current position of the cursor (in |
41 // bytes, as an index into |line|). If it returns ok=false, the key | 41 // bytes, as an index into |line|). If it returns ok=false, the key |
42 // press is processed normally. Otherwise it returns a replacement line | 42 // press is processed normally. Otherwise it returns a replacement line |
43 // and the new cursor position. | 43 // and the new cursor position. |
44 AutoCompleteCallback func(line string, pos int, key rune) (newLine strin
g, newPos int, ok bool) | 44 AutoCompleteCallback func(line string, pos int, key rune) (newLine strin
g, newPos int, ok bool) |
45 | 45 |
| 46 // SignalHandler, if non-nil, is called when ^C or ^Z has been pressed. |
| 47 // If it returns ok=false, the key processing is continued. Otherwise |
| 48 // the given line and error will be returned from ReadLine. |
| 49 SignalHandler func(key rune) (line string, err error, ok bool) |
| 50 |
46 // Escape contains a pointer to the escape codes for this terminal. | 51 // Escape contains a pointer to the escape codes for this terminal. |
47 // It's always a valid pointer, although the escape codes themselves | 52 // It's always a valid pointer, although the escape codes themselves |
48 // may be empty if the terminal doesn't support them. | 53 // may be empty if the terminal doesn't support them. |
49 Escape *EscapeCodes | 54 Escape *EscapeCodes |
50 | 55 |
51 // lock protects the terminal and the state in this object from | 56 // lock protects the terminal and the state in this object from |
52 // concurrent processing of a key press and a Write() call. | 57 // concurrent processing of a key press and a Write() call. |
53 lock sync.Mutex | 58 lock sync.Mutex |
54 | 59 |
55 c io.ReadWriter | 60 c io.ReadWriter |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
100 c: c, | 105 c: c, |
101 prompt: prompt, | 106 prompt: prompt, |
102 termWidth: 80, | 107 termWidth: 80, |
103 termHeight: 24, | 108 termHeight: 24, |
104 echo: true, | 109 echo: true, |
105 historyIndex: -1, | 110 historyIndex: -1, |
106 } | 111 } |
107 } | 112 } |
108 | 113 |
109 const ( | 114 const ( |
| 115 keyCtrlC = 3 |
110 keyCtrlD = 4 | 116 keyCtrlD = 4 |
111 keyEnter = '\r' | 117 keyEnter = '\r' |
| 118 keyCtrlZ = 26 |
112 keyEscape = 27 | 119 keyEscape = 27 |
113 keyBackspace = 127 | 120 keyBackspace = 127 |
114 keyUnknown = 0xd800 /* UTF-16 surrogate area */ + iota | 121 keyUnknown = 0xd800 /* UTF-16 surrogate area */ + iota |
115 keyUp | 122 keyUp |
116 keyDown | 123 keyDown |
117 keyLeft | 124 keyLeft |
118 keyRight | 125 keyRight |
119 keyAltLeft | 126 keyAltLeft |
120 keyAltRight | 127 keyAltRight |
121 keyHome | 128 keyHome |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
202 } | 209 } |
203 | 210 |
204 var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'} | 211 var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'} |
205 var space = []rune{' '} | 212 var space = []rune{' '} |
206 | 213 |
207 func isPrintable(key rune) bool { | 214 func isPrintable(key rune) bool { |
208 isInSurrogateArea := key >= 0xd800 && key <= 0xdbff | 215 isInSurrogateArea := key >= 0xd800 && key <= 0xdbff |
209 return key >= 32 && !isInSurrogateArea | 216 return key >= 32 && !isInSurrogateArea |
210 } | 217 } |
211 | 218 |
| 219 func isSignal(key rune) bool { |
| 220 return key == keyCtrlC || key == keyCtrlZ |
| 221 } |
| 222 |
212 // moveCursorToPos appends data to t.outBuf which will move the cursor to the | 223 // moveCursorToPos appends data to t.outBuf which will move the cursor to the |
213 // given, logical position in the text. | 224 // given, logical position in the text. |
214 func (t *Terminal) moveCursorToPos(pos int) { | 225 func (t *Terminal) moveCursorToPos(pos int) { |
215 if !t.echo { | 226 if !t.echo { |
216 return | 227 return |
217 } | 228 } |
218 | 229 |
219 x := len(t.prompt) + pos | 230 x := len(t.prompt) + pos |
220 y := x / t.termWidth | 231 y := x / t.termWidth |
221 x = x % t.termWidth | 232 x = x % t.termWidth |
(...skipping 377 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
599 lineOk := false | 610 lineOk := false |
600 for !lineOk { | 611 for !lineOk { |
601 var key rune | 612 var key rune |
602 key, rest = bytesToKey(rest) | 613 key, rest = bytesToKey(rest) |
603 if key == utf8.RuneError { | 614 if key == utf8.RuneError { |
604 break | 615 break |
605 } | 616 } |
606 if key == keyCtrlD { | 617 if key == keyCtrlD { |
607 return "", io.EOF | 618 return "", io.EOF |
608 } | 619 } |
| 620 if isSignal(key) { |
| 621 if t.SignalHandler != nil { |
| 622 if line, err, ok := t.SignalHandler(key)
; ok { |
| 623 return line, err |
| 624 } |
| 625 } |
| 626 continue |
| 627 } |
609 line, lineOk = t.handleKey(key) | 628 line, lineOk = t.handleKey(key) |
610 } | 629 } |
611 if len(rest) > 0 { | 630 if len(rest) > 0 { |
612 n := copy(t.inBuf[:], rest) | 631 n := copy(t.inBuf[:], rest) |
613 t.remainder = t.inBuf[:n] | 632 t.remainder = t.inBuf[:n] |
614 } else { | 633 } else { |
615 t.remainder = nil | 634 t.remainder = nil |
616 } | 635 } |
617 t.c.Write(t.outBuf) | 636 t.c.Write(t.outBuf) |
618 t.outBuf = t.outBuf[:0] | 637 t.outBuf = t.outBuf[:0] |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
690 func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) { | 709 func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) { |
691 if n >= s.size { | 710 if n >= s.size { |
692 return "", false | 711 return "", false |
693 } | 712 } |
694 index := s.head - n | 713 index := s.head - n |
695 if index < 0 { | 714 if index < 0 { |
696 index += s.max | 715 index += s.max |
697 } | 716 } |
698 return s.entries[index], true | 717 return s.entries[index], true |
699 } | 718 } |
OLD | NEW |