-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathparser.dart
More file actions
158 lines (131 loc) · 4.87 KB
/
parser.dart
File metadata and controls
158 lines (131 loc) · 4.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import 'package:args/args.dart';
import 'package:cli_completion/parser.dart';
import 'package:cli_completion/src/parser/arg_parser_extension.dart';
/// {@template completion_parser}
/// The workhorse of the completion system.
///
/// Responsible for discovering the possible completions given a
/// [CompletionLevel]
/// {@endtemplate}
class CompletionParser {
/// {@macro completion_parser}
CompletionParser({
required this.completionLevel,
});
/// The [CompletionLevel] to parse.
final CompletionLevel completionLevel;
/// Parse the given [CompletionState] into a [CompletionResult] given the
/// structure of commands and options declared by the CLIs [ArgParser].
List<CompletionResult> parse() => _parse().toList();
Iterable<CompletionResult> _parse() sync* {
final rawArgs = completionLevel.rawArgs;
final visibleSubcommands = completionLevel.visibleSubcommands;
final nonEmptyArgs = rawArgs.where((element) => element.isNotEmpty);
if (nonEmptyArgs.isEmpty) {
// There is nothing in the user prompt between the last known command and
// the cursor
// e.g. `my_cli commandName|`
yield AllOptionsAndCommandsCompletionResult(
completionLevel: completionLevel,
);
return;
}
final argOnCursor = rawArgs.last;
if (argOnCursor.isEmpty) {
// Check if the last given argument, if it is an option,
// complete with the known allowed values.
// e.g. `my_cli commandName --option |` or `my_cli commandName -o |`
final lastNonEmpty = nonEmptyArgs.last;
final suggestionForValues = _getOptionValues(lastNonEmpty);
if (suggestionForValues != null) {
yield suggestionForValues;
return;
}
// User pressed space before tab (not currently writing any arg ot the
// arg is a flag)
// e.g. `my_cli commandName something |`
yield AllOptionsAndCommandsCompletionResult(
completionLevel: completionLevel,
);
return;
}
// Check if the user has started to type the value of an
// option with "allowed" values
// e.g. `my_cli --option valueNam|` or `my_cli -o valueNam|`
if (nonEmptyArgs.length > 1) {
final secondLastNonEmpty = nonEmptyArgs.elementAt(
nonEmptyArgs.length - 2,
);
final resultForValues = _getOptionValues(secondLastNonEmpty, argOnCursor);
if (resultForValues != null) {
yield resultForValues;
return;
}
}
// Further code cover the case where the user is in the middle of writing a
// word.
// From now on, avoid early returns since completions may include commands
// and options alike
// Check if the user has started to type a sub command and pressed tab
// e.g. `my_cli commandNam|`
if (visibleSubcommands.isNotEmpty) {
yield MatchingCommandsCompletionResult(
completionLevel: completionLevel,
pattern: argOnCursor,
);
}
// Check if the user has started to type an option
// e.g. `my_cli commandName --optionNam|`
if (isOption(argOnCursor)) {
yield MatchingOptionsCompletionResult(
completionLevel: completionLevel,
pattern: argOnCursor.substring(2),
);
}
// Abbreviation cases
if (isAbbr(argOnCursor)) {
// Check if the user typed only a dash
if (argOnCursor.length == 1) {
yield AllAbbrOptionsCompletionResult(completionLevel: completionLevel);
} else {
// The user has started to type the value of an
// option with "allowed" in an abbreviated form or just the abbreviation
// e.g. `my_cli commandName -a|` or `my_cli commandName -avalueNam|`
final abbrName = argOnCursor.substring(1, 2);
final abbrValue = argOnCursor.substring(2);
yield OptionValuesCompletionResult.abbr(
abbrName: abbrName,
completionLevel: completionLevel,
pattern: abbrValue,
includeAbbrName: true,
);
}
}
}
CompletionResult? _getOptionValues(String value, [String? valuePattern]) {
if (isOption(value)) {
final optionName = value.substring(2);
final option = completionLevel.grammar.findByNameOrAlias(optionName);
final receivesValue = option != null && !option.isFlag;
if (receivesValue) {
return OptionValuesCompletionResult(
optionName: optionName,
completionLevel: completionLevel,
pattern: valuePattern,
);
}
} else if (isAbbr(value) && value.length > 1) {
final abbrName = value.substring(1, 2);
final option = completionLevel.grammar.findByAbbreviation(abbrName);
final receivesValue = option != null && !option.isFlag;
if (receivesValue) {
return OptionValuesCompletionResult.abbr(
abbrName: abbrName,
completionLevel: completionLevel,
pattern: valuePattern,
);
}
}
return null;
}
}