Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(895)

Side by Side Diff: ssh/terminal/terminal.go

Issue 93010046: code review 93010046: ssh/terminal: handles interruptions in user-friendly manner
Patch Set: diff -r a3d6743eb586 https://code.google.com/p/go.crypto/ Created 9 years, 10 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | ssh/terminal/terminal_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | ssh/terminal/terminal_test.go » ('j') | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b