LEFT | RIGHT |
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 "fmt" | |
9 "io" | 8 "io" |
10 "sync" | 9 "sync" |
11 "unicode/utf8" | 10 "unicode/utf8" |
12 ) | 11 ) |
13 | 12 |
14 // EscapeCodes contains escape sequences that can be written to the terminal in | 13 // EscapeCodes contains escape sequences that can be written to the terminal in |
15 // order to achieve different styles of text. | 14 // order to achieve different styles of text. |
16 type EscapeCodes struct { | 15 type EscapeCodes struct { |
17 // Foreground colors | 16 // Foreground colors |
18 Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte | 17 Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte |
(...skipping 11 matching lines...) Expand all Loading... |
30 Magenta: []byte{keyEscape, '[', '3', '5', 'm'}, | 29 Magenta: []byte{keyEscape, '[', '3', '5', 'm'}, |
31 Cyan: []byte{keyEscape, '[', '3', '6', 'm'}, | 30 Cyan: []byte{keyEscape, '[', '3', '6', 'm'}, |
32 White: []byte{keyEscape, '[', '3', '7', 'm'}, | 31 White: []byte{keyEscape, '[', '3', '7', 'm'}, |
33 | 32 |
34 Reset: []byte{keyEscape, '[', '0', 'm'}, | 33 Reset: []byte{keyEscape, '[', '0', 'm'}, |
35 } | 34 } |
36 | 35 |
37 // 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 |
38 // reading lines of input. | 37 // reading lines of input. |
39 type Terminal struct { | 38 type Terminal struct { |
40 » // AutoCompleteCallback, if non-null, is called for each keypress with | 39 » // AutoCompleteCallback, if non-nil, is called for each keypress with |
41 // 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 |
42 // 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 |
43 // press is processed normally. Otherwise it returns a replacement line | 42 // press is processed normally. Otherwise it returns a replacement line |
44 // and the new cursor position. | 43 // and the new cursor position. |
45 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 |
| 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) |
46 | 50 |
47 // Escape contains a pointer to the escape codes for this terminal. | 51 // Escape contains a pointer to the escape codes for this terminal. |
48 // It's always a valid pointer, although the escape codes themselves | 52 // It's always a valid pointer, although the escape codes themselves |
49 // may be empty if the terminal doesn't support them. | 53 // may be empty if the terminal doesn't support them. |
50 Escape *EscapeCodes | 54 Escape *EscapeCodes |
51 | 55 |
52 // lock protects the terminal and the state in this object from | 56 // lock protects the terminal and the state in this object from |
53 // concurrent processing of a key press and a Write() call. | 57 // concurrent processing of a key press and a Write() call. |
54 lock sync.Mutex | 58 lock sync.Mutex |
55 | 59 |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
205 } | 209 } |
206 | 210 |
207 var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'} | 211 var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'} |
208 var space = []rune{' '} | 212 var space = []rune{' '} |
209 | 213 |
210 func isPrintable(key rune) bool { | 214 func isPrintable(key rune) bool { |
211 isInSurrogateArea := key >= 0xd800 && key <= 0xdbff | 215 isInSurrogateArea := key >= 0xd800 && key <= 0xdbff |
212 return key >= 32 && !isInSurrogateArea | 216 return key >= 32 && !isInSurrogateArea |
213 } | 217 } |
214 | 218 |
| 219 func isSignal(key rune) bool { |
| 220 return key == keyCtrlC || key == keyCtrlZ |
| 221 } |
| 222 |
215 // 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 |
216 // given, logical position in the text. | 224 // given, logical position in the text. |
217 func (t *Terminal) moveCursorToPos(pos int) { | 225 func (t *Terminal) moveCursorToPos(pos int) { |
218 if !t.echo { | 226 if !t.echo { |
219 return | 227 return |
220 } | 228 } |
221 | 229 |
222 x := len(t.prompt) + pos | 230 x := len(t.prompt) + pos |
223 y := x / t.termWidth | 231 y := x / t.termWidth |
224 x = x % t.termWidth | 232 x = x % t.termWidth |
(...skipping 377 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
602 lineOk := false | 610 lineOk := false |
603 for !lineOk { | 611 for !lineOk { |
604 var key rune | 612 var key rune |
605 key, rest = bytesToKey(rest) | 613 key, rest = bytesToKey(rest) |
606 if key == utf8.RuneError { | 614 if key == utf8.RuneError { |
607 break | 615 break |
608 } | 616 } |
609 if key == keyCtrlD { | 617 if key == keyCtrlD { |
610 return "", io.EOF | 618 return "", io.EOF |
611 } | 619 } |
612 » » » if key == keyCtrlC { | 620 » » » if isSignal(key) { |
613 » » » » return "", fmt.Errorf("interrupted with SIGINT") | 621 » » » » if t.SignalHandler != nil { |
614 » » » } | 622 » » » » » if line, err, ok := t.SignalHandler(key)
; ok { |
615 » » » if key == keyCtrlZ { | 623 » » » » » » return line, err |
616 » » » » return "", fmt.Errorf("interrupted with SIGSTP") | 624 » » » » » } |
| 625 » » » » } |
| 626 » » » » continue |
617 } | 627 } |
618 line, lineOk = t.handleKey(key) | 628 line, lineOk = t.handleKey(key) |
619 } | 629 } |
620 if len(rest) > 0 { | 630 if len(rest) > 0 { |
621 n := copy(t.inBuf[:], rest) | 631 n := copy(t.inBuf[:], rest) |
622 t.remainder = t.inBuf[:n] | 632 t.remainder = t.inBuf[:n] |
623 } else { | 633 } else { |
624 t.remainder = nil | 634 t.remainder = nil |
625 } | 635 } |
626 t.c.Write(t.outBuf) | 636 t.c.Write(t.outBuf) |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
699 func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) { | 709 func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) { |
700 if n >= s.size { | 710 if n >= s.size { |
701 return "", false | 711 return "", false |
702 } | 712 } |
703 index := s.head - n | 713 index := s.head - n |
704 if index < 0 { | 714 if index < 0 { |
705 index += s.max | 715 index += s.max |
706 } | 716 } |
707 return s.entries[index], true | 717 return s.entries[index], true |
708 } | 718 } |
LEFT | RIGHT |