| 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 |