OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 import 'dart:async'; | 5 import 'dart:async'; |
6 import 'dart:convert'; | 6 import 'dart:convert'; |
7 import 'dart:io'; | 7 import 'dart:io'; |
8 import 'dart:math'; | 8 import 'dart:math'; |
9 | 9 |
10 import 'terminfo.dart'; | 10 import 'terminfo.dart'; |
(...skipping 12 matching lines...) Expand all Loading... |
23 static const runeCtrlK = 0x0b; | 23 static const runeCtrlK = 0x0b; |
24 static const runeCtrlL = 0x0c; | 24 static const runeCtrlL = 0x0c; |
25 static const runeCtrlN = 0x0e; | 25 static const runeCtrlN = 0x0e; |
26 static const runeCtrlP = 0x10; | 26 static const runeCtrlP = 0x10; |
27 static const runeCtrlU = 0x15; | 27 static const runeCtrlU = 0x15; |
28 static const runeCtrlY = 0x19; | 28 static const runeCtrlY = 0x19; |
29 static const runeESC = 0x1b; | 29 static const runeESC = 0x1b; |
30 static const runeSpace = 0x20; | 30 static const runeSpace = 0x20; |
31 static const runeDEL = 0x7F; | 31 static const runeDEL = 0x7F; |
32 | 32 |
33 Commando(this._stdin, | 33 StreamController<String> _commandController; |
34 this._stdout, | 34 |
35 this._handleCommand, | 35 Stream get commands => _commandController.stream; |
36 {this.prompt : '> ', this.completer : null}) { | 36 |
| 37 Commando({consoleIn, |
| 38 consoleOut, |
| 39 this.prompt : '> ', |
| 40 this.completer : null}) { |
| 41 _stdin = (consoleIn != null ? consoleIn : stdin); |
| 42 _stdout = (consoleOut != null ? consoleOut : stdout); |
| 43 _commandController = new StreamController<String>( |
| 44 onCancel: _onCancel); |
37 _stdin.echoMode = false; | 45 _stdin.echoMode = false; |
38 _stdin.lineMode = false; | 46 _stdin.lineMode = false; |
39 _screenWidth = _term.cols - 1; | 47 _screenWidth = _term.cols - 1; |
40 _writePrompt(); | 48 _writePrompt(); |
| 49 // TODO(turnidge): Handle errors in _stdin here. |
41 _stdinSubscription = | 50 _stdinSubscription = |
42 _stdin.transform(UTF8.decoder).listen(_handleText, onDone:done); | 51 _stdin.transform(UTF8.decoder).listen(_handleText, onDone:_done); |
43 } | 52 } |
44 | 53 |
| 54 Future _onCancel() { |
| 55 _stdin.echoMode = true; |
| 56 _stdin.lineMode = true; |
| 57 var future = _stdinSubscription.cancel(); |
| 58 if (future != null) { |
| 59 return future; |
| 60 } else { |
| 61 return new Future.value(); |
| 62 } |
| 63 } |
| 64 |
| 65 // Before terminating, call close() to restore terminal settings. |
| 66 void _done() { |
| 67 _onCancel().then((_) { |
| 68 _commandController.close(); |
| 69 }); |
| 70 } |
| 71 |
45 void _handleText(String text) { | 72 void _handleText(String text) { |
46 try { | 73 try { |
47 if (!_promptShown) { | 74 if (!_promptShown) { |
48 _bufferedInput.write(text); | 75 _bufferedInput.write(text); |
49 return; | 76 return; |
50 } | 77 } |
51 | 78 |
52 var runes = text.runes.toList(); | 79 var runes = text.runes.toList(); |
53 var pos = 0; | 80 var pos = 0; |
54 while (pos < runes.length) { | 81 while (pos < runes.length) { |
(...skipping 17 matching lines...) Expand all Loading... |
72 _tabCount = 0; | 99 _tabCount = 0; |
73 } | 100 } |
74 | 101 |
75 if (_isControlRune(rune)) { | 102 if (_isControlRune(rune)) { |
76 pos += _handleControlSequence(runes, pos); | 103 pos += _handleControlSequence(runes, pos); |
77 } else { | 104 } else { |
78 pos += _handleRegularSequence(runes, pos); | 105 pos += _handleRegularSequence(runes, pos); |
79 } | 106 } |
80 } | 107 } |
81 } catch(e, trace) { | 108 } catch(e, trace) { |
82 stderr.writeln('\nUnexpected exception: $e'); | 109 _commandController.addError(e, trace); |
83 stderr.writeln(trace); | |
84 stderr.close().then((_) { | |
85 done(); | |
86 }); | |
87 } | 110 } |
88 } | 111 } |
89 | 112 |
90 int _handleControlSequence(List<int> runes, int pos) { | 113 int _handleControlSequence(List<int> runes, int pos) { |
91 var runesConsumed = 1; // Most common result. | 114 var runesConsumed = 1; // Most common result. |
92 var char = runes[pos]; | 115 var char = runes[pos]; |
93 switch (char) { | 116 switch (char) { |
94 case runeCtrlA: | 117 case runeCtrlA: |
95 _home(); | 118 _home(); |
96 break; | 119 break; |
97 | 120 |
98 case runeCtrlB: | 121 case runeCtrlB: |
99 _leftArrow(); | 122 _leftArrow(); |
100 break; | 123 break; |
101 | 124 |
102 case runeCtrlD: | 125 case runeCtrlD: |
103 if (_currentLine.length == 0) { | 126 if (_currentLine.length == 0) { |
104 // ^D on an empty line means quit. | 127 // ^D on an empty line means quit. |
105 _stdout.writeln(); | 128 _stdout.writeln("^D"); |
106 done(); | 129 _done(); |
107 } else { | 130 } else { |
108 _delete(); | 131 _delete(); |
109 } | 132 } |
110 break; | 133 break; |
111 | 134 |
112 case runeCtrlE: | 135 case runeCtrlE: |
113 _end(); | 136 _end(); |
114 break; | 137 break; |
115 | 138 |
116 case runeCtrlF: | 139 case runeCtrlF: |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
200 len++; | 223 len++; |
201 } | 224 } |
202 _addChars(runes.getRange(pos, len)); | 225 _addChars(runes.getRange(pos, len)); |
203 return len; | 226 return len; |
204 } | 227 } |
205 | 228 |
206 bool _isControlRune(int char) { | 229 bool _isControlRune(int char) { |
207 return (char >= 0x00 && char < 0x20) || (char == 0x7f); | 230 return (char >= 0x00 && char < 0x20) || (char == 0x7f); |
208 } | 231 } |
209 | 232 |
210 void done() { | |
211 _stdin.echoMode = true; | |
212 _stdin.lineMode = true; | |
213 _stdinSubscription.cancel(); | |
214 } | |
215 | |
216 void _writePromptAndLine() { | 233 void _writePromptAndLine() { |
217 _writePrompt(); | 234 _writePrompt(); |
218 var pos = _writeRange(_currentLine, 0, _currentLine.length); | 235 var pos = _writeRange(_currentLine, 0, _currentLine.length); |
219 _cursorPos = _move(pos, _cursorPos); | 236 _cursorPos = _move(pos, _cursorPos); |
220 } | 237 } |
221 | 238 |
222 void _writePrompt() { | 239 void _writePrompt() { |
223 _stdout.write(prompt); | 240 _stdout.write(prompt); |
224 } | 241 } |
225 | 242 |
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
361 } | 378 } |
362 | 379 |
363 void _newline() { | 380 void _newline() { |
364 _addLineToHistory(_currentLine); | 381 _addLineToHistory(_currentLine); |
365 _linePos = _lines.length; | 382 _linePos = _lines.length; |
366 | 383 |
367 _end(); | 384 _end(); |
368 _stdout.writeln(); | 385 _stdout.writeln(); |
369 | 386 |
370 // Call the user's command handler. | 387 // Call the user's command handler. |
371 _handleCommand(new String.fromCharCodes(_currentLine)); | 388 _commandController.add(new String.fromCharCodes(_currentLine)); |
372 | 389 |
373 _currentLine = []; | 390 _currentLine = []; |
374 _cursorPos = 0; | 391 _cursorPos = 0; |
375 _linePos = _lines.length; | 392 _linePos = _lines.length; |
376 if (_promptShown) { | 393 if (_promptShown) { |
377 _writePrompt(); | 394 _writePrompt(); |
378 } | 395 } |
379 } | 396 } |
380 | 397 |
381 void _leftArrow() { | 398 void _leftArrow() { |
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
603 } | 620 } |
604 | 621 |
605 int _getCol(int pos) { | 622 int _getCol(int pos) { |
606 var truePos = pos + prompt.length; | 623 var truePos = pos + prompt.length; |
607 return truePos % _screenWidth; | 624 return truePos % _screenWidth; |
608 } | 625 } |
609 | 626 |
610 Stdin _stdin; | 627 Stdin _stdin; |
611 StreamSubscription _stdinSubscription; | 628 StreamSubscription _stdinSubscription; |
612 IOSink _stdout; | 629 IOSink _stdout; |
613 final _handleCommand; | |
614 final String prompt; | 630 final String prompt; |
615 bool _promptShown = true; | 631 bool _promptShown = true; |
616 final CommandCompleter completer; | 632 final CommandCompleter completer; |
617 TermInfo _term = new TermInfo(); | 633 TermInfo _term = new TermInfo(); |
618 | 634 |
619 // TODO(turnidge): Update screenwidth when we clear the screen. See | 635 // TODO(turnidge): Update screenwidth when we clear the screen. See |
620 // if we can get screen resize events too. | 636 // if we can get screen resize events too. |
621 int _screenWidth; | 637 int _screenWidth; |
622 List<int> _currentLine = []; // A list of runes. | 638 List<int> _currentLine = []; // A list of runes. |
623 StringBuffer _bufferedInput = new StringBuffer(); | 639 StringBuffer _bufferedInput = new StringBuffer(); |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
669 return completions; | 685 return completions; |
670 } | 686 } |
671 | 687 |
672 | 688 |
673 int _helpCount = 0; | 689 int _helpCount = 0; |
674 Commando cmdo; | 690 Commando cmdo; |
675 | 691 |
676 | 692 |
677 void _handleCommand(String rawCommand) { | 693 void _handleCommand(String rawCommand) { |
678 String command = rawCommand.trim(); | 694 String command = rawCommand.trim(); |
| 695 cmdo.hide(); |
679 if (command == 'quit') { | 696 if (command == 'quit') { |
680 cmdo.done(); | 697 cmdo.close().then((_) { |
| 698 print('Exiting'); |
| 699 }); |
681 } else if (command == 'help') { | 700 } else if (command == 'help') { |
682 switch (_helpCount) { | 701 switch (_helpCount) { |
683 case 0: | 702 case 0: |
684 print('I will not help you.'); | 703 print('I will not help you.'); |
685 break; | 704 break; |
686 case 1: | 705 case 1: |
687 print('I mean it.'); | 706 print('I mean it.'); |
688 break; | 707 break; |
689 case 2: | 708 case 2: |
690 print('Seriously.'); | 709 print('Seriously.'); |
691 break; | 710 break; |
692 case 100: | 711 case 100: |
693 print('Well now.'); | 712 print('Well now.'); |
694 break; | 713 break; |
695 default: | 714 default: |
696 print("Okay. Type 'quit' to quit"); | 715 print("Okay. Type 'quit' to quit"); |
697 break; | 716 break; |
698 } | 717 } |
699 _helpCount++; | 718 _helpCount++; |
700 } else if (command == 'happyface') { | 719 } else if (command == 'happyface') { |
701 print(':-)'); | 720 print(':-)'); |
702 } else { | 721 } else { |
703 print('Received command($command)'); | 722 print('Received command($command)'); |
704 } | 723 } |
| 724 cmdo.show(); |
705 } | 725 } |
706 | 726 |
707 | 727 |
708 void main() { | 728 void main() { |
709 stdout.writeln('[Commando demo]'); | 729 print('[Commando demo]'); |
710 cmd = new Commando(stdin, stdout, _handleCommand, | 730 cmdo = new Commando(completer:_myCompleter); |
711 completer:_myCompleter); | 731 cmdo.commands.listen(_handleCommand); |
712 } | 732 } |
OLD | NEW |