| Index: tools/ddbg_service/lib/commando.dart
|
| diff --git a/tools/ddbg_service/lib/commando.dart b/tools/ddbg_service/lib/commando.dart
|
| deleted file mode 100644
|
| index 3996880d41ba363af8e3b6b1a149c7040adb06f3..0000000000000000000000000000000000000000
|
| --- a/tools/ddbg_service/lib/commando.dart
|
| +++ /dev/null
|
| @@ -1,750 +0,0 @@
|
| -// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -library commando;
|
| -
|
| -import 'dart:async';
|
| -import 'dart:convert';
|
| -import 'dart:io';
|
| -import 'dart:math';
|
| -
|
| -import 'package:ddbg/terminfo.dart';
|
| -
|
| -typedef List<String> CommandCompleter(List<String> commandParts);
|
| -
|
| -class Commando {
|
| - // Ctrl keys
|
| - static const runeCtrlA = 0x01;
|
| - static const runeCtrlB = 0x02;
|
| - static const runeCtrlD = 0x04;
|
| - static const runeCtrlE = 0x05;
|
| - static const runeCtrlF = 0x06;
|
| - static const runeTAB = 0x09;
|
| - static const runeNewline = 0x0a;
|
| - static const runeCtrlK = 0x0b;
|
| - static const runeCtrlL = 0x0c;
|
| - static const runeCtrlN = 0x0e;
|
| - static const runeCtrlP = 0x10;
|
| - static const runeCtrlU = 0x15;
|
| - static const runeCtrlY = 0x19;
|
| - static const runeESC = 0x1b;
|
| - static const runeSpace = 0x20;
|
| - static const runeDEL = 0x7F;
|
| -
|
| - StreamController<String> _commandController;
|
| -
|
| - Stream get commands => _commandController.stream;
|
| -
|
| - Commando({consoleIn,
|
| - consoleOut,
|
| - this.prompt : '> ',
|
| - this.completer : null}) {
|
| - _stdin = (consoleIn != null ? consoleIn : stdin);
|
| - _stdout = (consoleOut != null ? consoleOut : stdout);
|
| - _commandController = new StreamController<String>(
|
| - onCancel: _onCancel);
|
| - _stdin.echoMode = false;
|
| - _stdin.lineMode = false;
|
| - _screenWidth = _term.cols - 1;
|
| - _writePrompt();
|
| - // TODO(turnidge): Handle errors in _stdin here.
|
| - _stdinSubscription =
|
| - _stdin.transform(UTF8.decoder).listen(_handleText, onDone:_done);
|
| - }
|
| -
|
| - Future _onCancel() {
|
| - _stdin.echoMode = true;
|
| - _stdin.lineMode = true;
|
| - var future = _stdinSubscription.cancel();
|
| - if (future != null) {
|
| - return future;
|
| - } else {
|
| - return new Future.value();
|
| - }
|
| - }
|
| -
|
| - // Before terminating, call close() to restore terminal settings.
|
| - void _done() {
|
| - _onCancel().then((_) {
|
| - _commandController.close();
|
| - });
|
| - }
|
| -
|
| - void _handleText(String text) {
|
| - try {
|
| - if (!_promptShown) {
|
| - _bufferedInput.write(text);
|
| - return;
|
| - }
|
| -
|
| - var runes = text.runes.toList();
|
| - var pos = 0;
|
| - while (pos < runes.length) {
|
| - if (!_promptShown) {
|
| - // A command was processed which hid the prompt. Buffer
|
| - // the rest of the input.
|
| - //
|
| - // TODO(turnidge): Here and elsewhere in the file I pass
|
| - // runes to String.fromCharCodes. Does this work?
|
| - _bufferedInput.write(
|
| - new String.fromCharCodes(runes.skip(pos)));
|
| - return;
|
| - }
|
| -
|
| - var rune = runes[pos];
|
| -
|
| - // Count consecutive tabs because double-tab is meaningful.
|
| - if (rune == runeTAB) {
|
| - _tabCount++;
|
| - } else {
|
| - _tabCount = 0;
|
| - }
|
| -
|
| - if (_isControlRune(rune)) {
|
| - pos += _handleControlSequence(runes, pos);
|
| - } else {
|
| - pos += _handleRegularSequence(runes, pos);
|
| - }
|
| - }
|
| - } catch(e, trace) {
|
| - _commandController.addError(e, trace);
|
| - }
|
| - }
|
| -
|
| - int _handleControlSequence(List<int> runes, int pos) {
|
| - var runesConsumed = 1; // Most common result.
|
| - var char = runes[pos];
|
| - switch (char) {
|
| - case runeCtrlA:
|
| - _home();
|
| - break;
|
| -
|
| - case runeCtrlB:
|
| - _leftArrow();
|
| - break;
|
| -
|
| - case runeCtrlD:
|
| - if (_currentLine.length == 0) {
|
| - // ^D on an empty line means quit.
|
| - _stdout.writeln("^D");
|
| - _done();
|
| - } else {
|
| - _delete();
|
| - }
|
| - break;
|
| -
|
| - case runeCtrlE:
|
| - _end();
|
| - break;
|
| -
|
| - case runeCtrlF:
|
| - _rightArrow();
|
| - break;
|
| -
|
| - case runeTAB:
|
| - _complete(_tabCount > 1);
|
| - break;
|
| -
|
| - case runeNewline:
|
| - _newline();
|
| - break;
|
| -
|
| - case runeCtrlK:
|
| - _kill();
|
| - break;
|
| -
|
| - case runeCtrlL:
|
| - _clearScreen();
|
| - break;
|
| -
|
| - case runeCtrlN:
|
| - _historyNext();
|
| - break;
|
| -
|
| - case runeCtrlP:
|
| - _historyPrevious();
|
| - break;
|
| -
|
| - case runeCtrlU:
|
| - _clearLine();
|
| - break;
|
| -
|
| - case runeCtrlY:
|
| - _yank();
|
| - break;
|
| -
|
| - case runeESC:
|
| - // Check to see if this is an arrow key.
|
| - if (pos + 2 < runes.length && // must be a 3 char sequence.
|
| - runes[pos + 1] == 0x5b) { // second char must be '['.
|
| - switch (runes[pos + 2]) {
|
| - case 0x41: // ^[[A = up arrow
|
| - _historyPrevious();
|
| - runesConsumed = 3;
|
| - break;
|
| -
|
| - case 0x42: // ^[[B = down arrow
|
| - _historyNext();
|
| - runesConsumed = 3;
|
| - break;
|
| -
|
| - case 0x43: // ^[[C = right arrow
|
| - _rightArrow();
|
| - runesConsumed = 3;
|
| - break;
|
| -
|
| - case 0x44: // ^[[D = left arrow
|
| - _leftArrow();
|
| - runesConsumed = 3;
|
| - break;
|
| -
|
| - default:
|
| - // Ignore the escape character.
|
| - break;
|
| - }
|
| - }
|
| - break;
|
| -
|
| - case runeDEL:
|
| - _backspace();
|
| - break;
|
| -
|
| - default:
|
| - // Ignore the escape character.
|
| - break;
|
| - }
|
| - return runesConsumed;
|
| - }
|
| -
|
| - int _handleRegularSequence(List<int> runes, int pos) {
|
| - var len = pos + 1;
|
| - while (len < runes.length && !_isControlRune(runes[len])) {
|
| - len++;
|
| - }
|
| - _addChars(runes.getRange(pos, len));
|
| - return len;
|
| - }
|
| -
|
| - bool _isControlRune(int char) {
|
| - return (char >= 0x00 && char < 0x20) || (char == 0x7f);
|
| - }
|
| -
|
| - void _writePromptAndLine() {
|
| - _writePrompt();
|
| - var pos = _writeRange(_currentLine, 0, _currentLine.length);
|
| - _cursorPos = _move(pos, _cursorPos);
|
| - }
|
| -
|
| - void _writePrompt() {
|
| - _stdout.write(prompt);
|
| - }
|
| -
|
| - void _addChars(Iterable<int> chars) {
|
| - var newLine = [];
|
| - newLine..addAll(_currentLine.take(_cursorPos))
|
| - ..addAll(chars)
|
| - ..addAll(_currentLine.skip(_cursorPos));
|
| - _update(newLine, (_cursorPos + chars.length));
|
| - }
|
| -
|
| - void _backspace() {
|
| - if (_cursorPos == 0) {
|
| - return;
|
| - }
|
| -
|
| - var newLine = [];
|
| - newLine..addAll(_currentLine.take(_cursorPos - 1))
|
| - ..addAll(_currentLine.skip(_cursorPos));
|
| - _update(newLine, (_cursorPos - 1));
|
| - }
|
| -
|
| - void _delete() {
|
| - if (_cursorPos == _currentLine.length) {
|
| - return;
|
| - }
|
| -
|
| - var newLine = [];
|
| - newLine..addAll(_currentLine.take(_cursorPos))
|
| - ..addAll(_currentLine.skip(_cursorPos + 1));
|
| - _update(newLine, _cursorPos);
|
| - }
|
| -
|
| - void _home() {
|
| - _updatePos(0);
|
| - }
|
| -
|
| - void _end() {
|
| - _updatePos(_currentLine.length);
|
| - }
|
| -
|
| - void _clearScreen() {
|
| - _stdout.write(_term.clear);
|
| - _term.resize();
|
| - _screenWidth = _term.cols - 1;
|
| - _writePromptAndLine();
|
| - }
|
| -
|
| - void _kill() {
|
| - var newLine = [];
|
| - newLine.addAll(_currentLine.take(_cursorPos));
|
| - _killBuffer = _currentLine.skip(_cursorPos).toList();
|
| - _update(newLine, _cursorPos);
|
| - }
|
| -
|
| - void _clearLine() {
|
| - _update([], 0);
|
| - }
|
| -
|
| - void _yank() {
|
| - var newLine = [];
|
| - newLine..addAll(_currentLine.take(_cursorPos))
|
| - ..addAll(_killBuffer)
|
| - ..addAll(_currentLine.skip(_cursorPos));
|
| - _update(newLine, (_cursorPos + _killBuffer.length));
|
| - }
|
| -
|
| - static String _trimLeadingSpaces(String line) {
|
| - bool _isSpace(int rune) {
|
| - return rune == runeSpace;
|
| - }
|
| - return new String.fromCharCodes(line.runes.skipWhile(_isSpace));
|
| - }
|
| -
|
| - static String _sharedPrefix(String one, String two) {
|
| - var len = min(one.length, two.length);
|
| - var runesOne = one.runes.toList();
|
| - var runesTwo = two.runes.toList();
|
| - var pos;
|
| - for (pos = 0; pos < len; pos++) {
|
| - if (runesOne[pos] != runesTwo[pos]) {
|
| - break;
|
| - }
|
| - }
|
| - var shared = new String.fromCharCodes(runesOne.take(pos));
|
| - return shared;
|
| - }
|
| -
|
| - void _complete(bool showCompletions) {
|
| - if (completer == null) {
|
| - return;
|
| - }
|
| -
|
| - var linePrefix = _currentLine.take(_cursorPos).toList();
|
| - var lineAsString = new String.fromCharCodes(linePrefix);
|
| - var commandParts = new List.from(lineAsString.split(' ').where((line) {
|
| - return line != ' ' && line != '';
|
| - }));
|
| - if (lineAsString.endsWith(' ')) {
|
| - // If the current line ends with a space, they are hoping to
|
| - // complete the next word in the command. Add an empty string
|
| - // to the commandParts to signal this to the completer.
|
| - commandParts.add('');
|
| - }
|
| - List<String> completionList = completer(commandParts);
|
| - var completion = null;
|
| -
|
| - if (completionList.length == 0) {
|
| - // The current line admits no possible completion.
|
| - return;
|
| -
|
| - } else if (completionList.length == 1) {
|
| - // There is a single, non-ambiguous completion for the current line.
|
| - completion = completionList[0];
|
| -
|
| - } else {
|
| - // There are ambiguous completions. Find the longest common
|
| - // shared prefix of all of the completions.
|
| - completion = completionList.fold(completionList[0], _sharedPrefix);
|
| - }
|
| -
|
| - if (showCompletions) {
|
| - // User hit double-TAB. Show them all possible completions.
|
| - completionList.sort((a,b) => a.compareTo(b));
|
| - _move(_cursorPos, _currentLine.length);
|
| - _stdout.writeln();
|
| - _stdout.writeln(completionList);
|
| - _writePromptAndLine();
|
| - return;
|
| -
|
| - } else {
|
| - // Apply the current completion.
|
| - var completionRunes = completion.runes.toList();
|
| -
|
| - var newLine = [];
|
| - newLine..addAll(completionRunes)
|
| - ..addAll(_currentLine.skip(_cursorPos));
|
| - _update(newLine, completionRunes.length);
|
| - return;
|
| - }
|
| - }
|
| -
|
| - void _newline() {
|
| - _addLineToHistory(_currentLine);
|
| - _linePos = _lines.length;
|
| -
|
| - _end();
|
| - _stdout.writeln();
|
| -
|
| - // Call the user's command handler.
|
| - _commandController.add(new String.fromCharCodes(_currentLine));
|
| -
|
| - _currentLine = [];
|
| - _cursorPos = 0;
|
| - if (_promptShown) {
|
| - _writePrompt();
|
| - }
|
| - }
|
| -
|
| - void _leftArrow() {
|
| - _updatePos(_cursorPos - 1);
|
| - }
|
| -
|
| - void _rightArrow() {
|
| - _updatePos(_cursorPos + 1);
|
| - }
|
| -
|
| - void _addLineToHistory(List<int> line) {
|
| - if (_tempLineAdded) {
|
| - _lines.removeLast();
|
| - _tempLineAdded = false;
|
| - }
|
| - if (line.length > 0) {
|
| - _lines.add(line);
|
| - }
|
| - }
|
| -
|
| - void _addTempLineToHistory(List<int> line) {
|
| - _lines.add(line);
|
| - _tempLineAdded = true;
|
| - }
|
| -
|
| - void _replaceHistory(List<int> line, int linePos) {
|
| - _lines[linePos] = line;
|
| - }
|
| -
|
| - void _historyPrevious() {
|
| - if (_linePos == 0) {
|
| - return;
|
| - }
|
| -
|
| - if (_linePos == _lines.length) {
|
| - // The current in-progress line gets temporarily stored in history.
|
| - _addTempLineToHistory(_currentLine);
|
| - } else {
|
| - // Any edits get committed to history.
|
| - _replaceHistory(_currentLine, _linePos);
|
| - }
|
| -
|
| - _linePos -= 1;
|
| - var line = _lines[_linePos];
|
| - _update(line, line.length);
|
| - }
|
| -
|
| - void _historyNext() {
|
| - // For the very first command, _linePos (0) will exceed
|
| - // (_lines.length - 1) (-1) so we use a ">=" here instead of an "==".
|
| - if (_linePos >= (_lines.length - 1)) {
|
| - return;
|
| - }
|
| -
|
| - // Any edits get committed to history.
|
| - _replaceHistory(_currentLine, _linePos);
|
| -
|
| - _linePos += 1;
|
| - var line = _lines[_linePos];
|
| - _update(line, line.length);
|
| - }
|
| -
|
| - void _updatePos(int newCursorPos) {
|
| - if (newCursorPos < 0) {
|
| - return;
|
| - }
|
| - if (newCursorPos > _currentLine.length) {
|
| - return;
|
| - }
|
| -
|
| - _cursorPos = _move(_cursorPos, newCursorPos);
|
| - }
|
| -
|
| - void _update(List<int> newLine, int newCursorPos) {
|
| - var pos = _cursorPos;
|
| - var diffPos;
|
| - var sharedLen = min(_currentLine.length, newLine.length);
|
| -
|
| - // Find first difference.
|
| - for (diffPos = 0; diffPos < sharedLen; diffPos++) {
|
| - if (_currentLine[diffPos] != newLine[diffPos]) {
|
| - break;
|
| - }
|
| - }
|
| -
|
| - // Move the cursor to where the difference begins.
|
| - pos = _move(pos, diffPos);
|
| -
|
| - // Write the new text.
|
| - pos = _writeRange(newLine, pos, newLine.length);
|
| -
|
| - // Clear any extra characters at the end.
|
| - pos = _clearRange(pos, _currentLine.length);
|
| -
|
| - // Move the cursor back to the input point.
|
| - _cursorPos = _move(pos, newCursorPos);
|
| - _currentLine = newLine;
|
| - }
|
| -
|
| - void print(String text) {
|
| - bool togglePrompt = _promptShown;
|
| - if (togglePrompt) {
|
| - hide();
|
| - }
|
| - _stdout.writeln(text);
|
| - if (togglePrompt) {
|
| - show();
|
| - }
|
| - }
|
| -
|
| - void hide() {
|
| - if (!_promptShown) {
|
| - return;
|
| - }
|
| - _promptShown = false;
|
| - // We need to erase everything, including the prompt.
|
| - var curLine = _getLine(_cursorPos);
|
| - var lastLine = _getLine(_currentLine.length);
|
| -
|
| - // Go to last line.
|
| - if (curLine < lastLine) {
|
| - for (var i = 0; i < (lastLine - curLine); i++) {
|
| - // This moves us to column 0.
|
| - _stdout.write(_term.cursorDown);
|
| - }
|
| - curLine = lastLine;
|
| - } else {
|
| - // Move to column 0.
|
| - _stdout.write('\r');
|
| - }
|
| -
|
| - // Work our way up, clearing lines.
|
| - while (true) {
|
| - _stdout.write(_term.clrEOL);
|
| - if (curLine > 0) {
|
| - _stdout.write(_term.cursorUp);
|
| - } else {
|
| - break;
|
| - }
|
| - }
|
| - }
|
| -
|
| - void show() {
|
| - if (_promptShown) {
|
| - return;
|
| - }
|
| - _promptShown = true;
|
| - _writePromptAndLine();
|
| -
|
| - // If input was buffered while the prompt was hidden, process it
|
| - // now.
|
| - if (!_bufferedInput.isEmpty) {
|
| - var input = _bufferedInput.toString();
|
| - _bufferedInput.clear();
|
| - _handleText(input);
|
| - }
|
| - }
|
| -
|
| - int _writeRange(List<int> text, int pos, int writeToPos) {
|
| - if (pos >= writeToPos) {
|
| - return pos;
|
| - }
|
| - while (pos < writeToPos) {
|
| - var margin = _nextMargin(pos);
|
| - var limit = min(writeToPos, margin);
|
| - _stdout.write(new String.fromCharCodes(text.getRange(pos, limit)));
|
| - pos = limit;
|
| - if (pos == margin) {
|
| - _stdout.write('\n');
|
| - }
|
| - }
|
| - return pos;
|
| - }
|
| -
|
| - int _clearRange(int pos, int clearToPos) {
|
| - if (pos >= clearToPos) {
|
| - return pos;
|
| - }
|
| - while (true) {
|
| - var limit = _nextMargin(pos);
|
| - _stdout.write(_term.clrEOL);
|
| - if (limit >= clearToPos) {
|
| - return pos;
|
| - }
|
| - _stdout.write('\n');
|
| - pos = limit;
|
| - }
|
| - }
|
| -
|
| - int _move(int pos, int newPos) {
|
| - if (pos == newPos) {
|
| - return pos;
|
| - }
|
| -
|
| - var curCol = _getCol(pos);
|
| - var curLine = _getLine(pos);
|
| - var newCol = _getCol(newPos);
|
| - var newLine = _getLine(newPos);
|
| -
|
| - if (curLine > newLine) {
|
| - for (var i = 0; i < (curLine - newLine); i++) {
|
| - _stdout.write(_term.cursorUp);
|
| - }
|
| - }
|
| - if (curLine < newLine) {
|
| - for (var i = 0; i < (newLine - curLine); i++) {
|
| - _stdout.write(_term.cursorDown);
|
| - }
|
| -
|
| - // Moving down resets column to zero, oddly.
|
| - curCol = 0;
|
| - }
|
| - if (curCol > newCol) {
|
| - for (var i = 0; i < (curCol - newCol); i++) {
|
| - _stdout.write(_term.cursorBack);
|
| - }
|
| - }
|
| - if (curCol < newCol) {
|
| - for (var i = 0; i < (newCol - curCol); i++) {
|
| - _stdout.write(_term.cursorForward);
|
| - }
|
| - }
|
| -
|
| - return newPos;
|
| - }
|
| -
|
| - int _nextMargin(int pos) {
|
| - var truePos = pos + prompt.length;
|
| - return ((truePos ~/ _screenWidth) + 1) * _screenWidth - prompt.length;
|
| - }
|
| -
|
| - int _getLine(int pos) {
|
| - var truePos = pos + prompt.length;
|
| - return truePos ~/ _screenWidth;
|
| - }
|
| -
|
| - int _getCol(int pos) {
|
| - var truePos = pos + prompt.length;
|
| - return truePos % _screenWidth;
|
| - }
|
| -
|
| - Stdin _stdin;
|
| - StreamSubscription _stdinSubscription;
|
| - IOSink _stdout;
|
| - final String prompt;
|
| - bool _promptShown = true;
|
| - final CommandCompleter completer;
|
| - TermInfo _term = new TermInfo();
|
| -
|
| - // TODO(turnidge): See if we can get screen resize events.
|
| - int _screenWidth;
|
| - List<int> _currentLine = []; // A list of runes.
|
| - StringBuffer _bufferedInput = new StringBuffer();
|
| - List<List<int>> _lines = [];
|
| -
|
| - // When using the command history, the current line is temporarily
|
| - // added to the history to allow the user to return to it. This
|
| - // values tracks whether the history has a temporary line at the end.
|
| - bool _tempLineAdded = false;
|
| - int _linePos = 0;
|
| - int _cursorPos = 0;
|
| - int _tabCount = 0;
|
| - List<int> _killBuffer = [];
|
| -}
|
| -
|
| -
|
| -// Demo code.
|
| -
|
| -
|
| -List<String> _myCompleter(List<String> commandTokens) {
|
| - List<String> completions = new List<String>();
|
| -
|
| - // First word completions.
|
| - if (commandTokens.length <= 1) {
|
| - String prefix = '';
|
| - if (commandTokens.length == 1) {
|
| - prefix = commandTokens.first;
|
| - }
|
| - if ('quit'.startsWith(prefix)) {
|
| - completions.add('quit');
|
| - }
|
| - if ('help'.startsWith(prefix)) {
|
| - completions.add('help');
|
| - }
|
| - if ('happyface'.startsWith(prefix)) {
|
| - completions.add('happyface');
|
| - }
|
| - }
|
| -
|
| - // Complete 'foobar' or 'gondola' anywhere in string.
|
| - String lastWord = commandTokens.last;
|
| - if ('foobar'.startsWith(lastWord)) {
|
| - completions.add('foobar');
|
| - }
|
| - if ('gondola'.startsWith(lastWord)) {
|
| - completions.add('gondola');
|
| - }
|
| -
|
| - return completions;
|
| -}
|
| -
|
| -
|
| -int _helpCount = 0;
|
| -Commando cmdo;
|
| -var cmdoSubscription;
|
| -
|
| -
|
| -void _handleCommand(String rawCommand) {
|
| - String command = rawCommand.trim();
|
| - cmdo.hide();
|
| - if (command == 'quit') {
|
| - var future = cmdoSubscription.cancel();
|
| - if (future != null) {
|
| - future.then((_) {
|
| - print('Exiting');
|
| - exit(0);
|
| - });
|
| - } else {
|
| - print('Exiting');
|
| - exit(0);
|
| - }
|
| - } else if (command == 'help') {
|
| - switch (_helpCount) {
|
| - case 0:
|
| - print('I will not help you.');
|
| - break;
|
| - case 1:
|
| - print('I mean it.');
|
| - break;
|
| - case 2:
|
| - print('Seriously.');
|
| - break;
|
| - case 100:
|
| - print('Well now.');
|
| - break;
|
| - default:
|
| - print("Okay. Type 'quit' to quit");
|
| - break;
|
| - }
|
| - _helpCount++;
|
| - } else if (command == 'happyface') {
|
| - print(':-)');
|
| - } else {
|
| - print('Received command($command)');
|
| - }
|
| - cmdo.show();
|
| -}
|
| -
|
| -
|
| -void main() {
|
| - print('[Commando demo]');
|
| - cmdo = new Commando(completer:_myCompleter);
|
| - cmdoSubscription = cmdo.commands.listen(_handleCommand);
|
| -}
|
|
|