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 |