| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 library commando; | 5 library commando; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:convert'; | 8 import 'dart:convert'; |
| 9 import 'dart:io'; | 9 import 'dart:io'; |
| 10 import 'dart:math'; | 10 import 'dart:math'; |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 136 | 136 |
| 137 case runeCtrlE: | 137 case runeCtrlE: |
| 138 _end(); | 138 _end(); |
| 139 break; | 139 break; |
| 140 | 140 |
| 141 case runeCtrlF: | 141 case runeCtrlF: |
| 142 _rightArrow(); | 142 _rightArrow(); |
| 143 break; | 143 break; |
| 144 | 144 |
| 145 case runeTAB: | 145 case runeTAB: |
| 146 if (_complete(_tabCount > 1)) { | 146 _complete(_tabCount > 1); |
| 147 _tabCount = 0; | |
| 148 } | |
| 149 break; | 147 break; |
| 150 | 148 |
| 151 case runeNewline: | 149 case runeNewline: |
| 152 _newline(); | 150 _newline(); |
| 153 break; | 151 break; |
| 154 | 152 |
| 155 case runeCtrlK: | 153 case runeCtrlK: |
| 156 _kill(); | 154 _kill(); |
| 157 break; | 155 break; |
| 158 | 156 |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 320 var pos; | 318 var pos; |
| 321 for (pos = 0; pos < len; pos++) { | 319 for (pos = 0; pos < len; pos++) { |
| 322 if (runesOne[pos] != runesTwo[pos]) { | 320 if (runesOne[pos] != runesTwo[pos]) { |
| 323 break; | 321 break; |
| 324 } | 322 } |
| 325 } | 323 } |
| 326 var shared = new String.fromCharCodes(runesOne.take(pos)); | 324 var shared = new String.fromCharCodes(runesOne.take(pos)); |
| 327 return shared; | 325 return shared; |
| 328 } | 326 } |
| 329 | 327 |
| 330 bool _complete(bool showCompletions) { | 328 void _complete(bool showCompletions) { |
| 331 if (completer == null) { | 329 if (completer == null) { |
| 332 return false; | 330 return; |
| 333 } | 331 } |
| 334 | 332 |
| 335 var linePrefix = _currentLine.take(_cursorPos).toList(); | 333 var linePrefix = _currentLine.take(_cursorPos).toList(); |
| 336 List<String> commandParts = | 334 var lineAsString = new String.fromCharCodes(linePrefix); |
| 337 _trimLeadingSpaces(new String.fromCharCodes(linePrefix)).split(' '); | 335 var commandParts = new List.from(lineAsString.split(' ').where((line) { |
| 336 return line != ' ' && line != ''; |
| 337 })); |
| 338 if (lineAsString.endsWith(' ')) { |
| 339 // If the current line ends with a space, they are hoping to |
| 340 // complete the next word in the command. Add an empty string |
| 341 // to the commandParts to signal this to the completer. |
| 342 commandParts.add(''); |
| 343 } |
| 338 List<String> completionList = completer(commandParts); | 344 List<String> completionList = completer(commandParts); |
| 339 var completion = ''; | 345 var completion = null; |
| 340 | 346 |
| 341 if (completionList.length == 0) { | 347 if (completionList.length == 0) { |
| 342 // The current line admits no possible completion. | 348 // The current line admits no possible completion. |
| 343 return false; | 349 return; |
| 344 | 350 |
| 345 } else if (completionList.length == 1) { | 351 } else if (completionList.length == 1) { |
| 346 // There is a single, non-ambiguous completion for the current line. | 352 // There is a single, non-ambiguous completion for the current line. |
| 347 completion = completionList[0]; | 353 completion = completionList[0]; |
| 348 | 354 |
| 349 // If we are at the end of the line, add a space to signal that | |
| 350 // the completion is unambiguous. | |
| 351 if (_currentLine.length == _cursorPos) { | |
| 352 completion = completion + ' '; | |
| 353 } | |
| 354 } else { | 355 } else { |
| 355 // There are ambiguous completions. Find the longest common | 356 // There are ambiguous completions. Find the longest common |
| 356 // shared prefix of all of the completions. | 357 // shared prefix of all of the completions. |
| 357 completion = completionList.fold(completionList[0], _sharedPrefix); | 358 completion = completionList.fold(completionList[0], _sharedPrefix); |
| 358 } | 359 } |
| 359 | 360 |
| 360 var lastWord = commandParts.last; | 361 if (showCompletions) { |
| 361 if (completion == lastWord) { | 362 // User hit double-TAB. Show them all possible completions. |
| 362 // The completion does not add anything. | 363 completionList.sort((a,b) => a.compareTo(b)); |
| 363 if (showCompletions) { | 364 _move(_cursorPos, _currentLine.length); |
| 364 // User hit double-TAB. Show them all possible completions. | 365 _stdout.writeln(); |
| 365 _move(_cursorPos, _currentLine.length); | 366 _stdout.writeln(completionList); |
| 366 _stdout.writeln(); | 367 _writePromptAndLine(); |
| 367 _stdout.writeln(completionList); | 368 return; |
| 368 _writePromptAndLine(); | 369 |
| 369 } | |
| 370 return false; | |
| 371 } else { | 370 } else { |
| 372 // Apply the current completion. | 371 // Apply the current completion. |
| 373 var completionRunes = completion.runes.toList(); | 372 var completionRunes = completion.runes.toList(); |
| 374 | 373 |
| 375 var newLine = []; | 374 var newLine = []; |
| 376 newLine..addAll(linePrefix) | 375 newLine..addAll(completionRunes) |
| 377 ..addAll(completionRunes.skip(lastWord.length)) | |
| 378 ..addAll(_currentLine.skip(_cursorPos)); | 376 ..addAll(_currentLine.skip(_cursorPos)); |
| 379 _update(newLine, _cursorPos + completionRunes.length - lastWord.length); | 377 _update(newLine, completionRunes.length); |
| 380 return true; | 378 return; |
| 381 } | 379 } |
| 382 } | 380 } |
| 383 | 381 |
| 384 void _newline() { | 382 void _newline() { |
| 385 _addLineToHistory(_currentLine); | 383 _addLineToHistory(_currentLine); |
| 386 _linePos = _lines.length; | 384 _linePos = _lines.length; |
| 387 | 385 |
| 388 _end(); | 386 _end(); |
| 389 _stdout.writeln(); | 387 _stdout.writeln(); |
| 390 | 388 |
| (...skipping 352 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 743 } | 741 } |
| 744 cmdo.show(); | 742 cmdo.show(); |
| 745 } | 743 } |
| 746 | 744 |
| 747 | 745 |
| 748 void main() { | 746 void main() { |
| 749 print('[Commando demo]'); | 747 print('[Commando demo]'); |
| 750 cmdo = new Commando(completer:_myCompleter); | 748 cmdo = new Commando(completer:_myCompleter); |
| 751 cmdoSubscription = cmdo.commands.listen(_handleCommand); | 749 cmdoSubscription = cmdo.commands.listen(_handleCommand); |
| 752 } | 750 } |
| OLD | NEW |