@wq2/brigadier-ts 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +1 -0
  2. package/dist/Command.d.ts +1 -1
  3. package/dist/CommandDispatcher.d.ts +1 -1
  4. package/dist/CommandDispatcher.js +231 -357
  5. package/dist/ParseResults.d.ts +1 -1
  6. package/dist/ParseResults.js +9 -10
  7. package/dist/StringReader.d.ts +1 -1
  8. package/dist/StringReader.js +75 -77
  9. package/dist/arguments/ArgumentType.d.ts +1 -1
  10. package/dist/arguments/ArgumentType.js +9 -8
  11. package/dist/arguments/BoolArgumentType.d.ts +5 -1
  12. package/dist/arguments/BoolArgumentType.js +9 -27
  13. package/dist/arguments/FloatArgumentType.d.ts +1 -1
  14. package/dist/arguments/FloatArgumentType.js +11 -30
  15. package/dist/arguments/IntegerArgumentType.d.ts +1 -1
  16. package/dist/arguments/IntegerArgumentType.js +11 -30
  17. package/dist/arguments/LongArgumentType.d.ts +3 -3
  18. package/dist/arguments/LongArgumentType.js +13 -32
  19. package/dist/arguments/NumberArgumentType.d.ts +2 -2
  20. package/dist/arguments/NumberArgumentType.js +15 -33
  21. package/dist/arguments/StringArgumentType.d.ts +1 -1
  22. package/dist/arguments/StringArgumentType.js +11 -29
  23. package/dist/builder/ArgumentBuilder.d.ts +1 -1
  24. package/dist/builder/ArgumentBuilder.js +31 -71
  25. package/dist/builder/LiteralArgumentBuilder.js +14 -33
  26. package/dist/builder/RequiredArgumentBuilder.d.ts +1 -1
  27. package/dist/builder/RequiredArgumentBuilder.js +18 -37
  28. package/dist/context/CommandContext.d.ts +1 -1
  29. package/dist/context/CommandContext.js +31 -32
  30. package/dist/context/CommandContextBuilder.d.ts +1 -1
  31. package/dist/context/CommandContextBuilder.js +47 -50
  32. package/dist/context/ParsedArgument.js +8 -9
  33. package/dist/context/ParsedCommandNode.d.ts +1 -1
  34. package/dist/context/ParsedCommandNode.js +7 -8
  35. package/dist/context/StringRange.js +17 -18
  36. package/dist/context/SuggestionContext.d.ts +1 -1
  37. package/dist/context/SuggestionContext.js +3 -4
  38. package/dist/exceptions/CommandErrorType.d.ts +3 -3
  39. package/dist/exceptions/CommandErrorType.js +10 -19
  40. package/dist/exceptions/CommandSyntaxError.js +36 -54
  41. package/dist/suggestion/Suggestion.d.ts +1 -1
  42. package/dist/suggestion/Suggestion.js +16 -17
  43. package/dist/suggestion/Suggestions.d.ts +1 -1
  44. package/dist/suggestion/Suggestions.js +28 -30
  45. package/dist/suggestion/SuggestionsBuilder.d.ts +1 -1
  46. package/dist/suggestion/SuggestionsBuilder.js +22 -23
  47. package/dist/tree/ArgumentCommandNode.d.ts +1 -1
  48. package/dist/tree/ArgumentCommandNode.js +22 -40
  49. package/dist/tree/CommandNode.d.ts +1 -1
  50. package/dist/tree/CommandNode.js +28 -29
  51. package/dist/tree/LiteralCommandNode.d.ts +1 -1
  52. package/dist/tree/LiteralCommandNode.js +28 -41
  53. package/dist/tree/RootCommandNode.d.ts +1 -1
  54. package/dist/tree/RootCommandNode.js +17 -69
  55. package/jest.config.js +5 -5
  56. package/package.json +32 -32
  57. package/src/Command.ts +2 -1
  58. package/src/CommandDispatcher.ts +397 -295
  59. package/src/ParseResults.ts +26 -22
  60. package/src/StringReader.ts +212 -193
  61. package/src/arguments/ArgumentType.ts +13 -8
  62. package/src/arguments/BoolArgumentType.ts +22 -21
  63. package/src/arguments/FloatArgumentType.ts +13 -14
  64. package/src/arguments/IntegerArgumentType.ts +13 -14
  65. package/src/arguments/LongArgumentType.ts +16 -17
  66. package/src/arguments/NumberArgumentType.ts +48 -38
  67. package/src/arguments/StringArgumentType.ts +26 -26
  68. package/src/builder/ArgumentBuilder.ts +80 -75
  69. package/src/builder/LiteralArgumentBuilder.ts +31 -21
  70. package/src/builder/RequiredArgumentBuilder.ts +42 -32
  71. package/src/context/CommandContext.ts +99 -76
  72. package/src/context/CommandContextBuilder.ts +169 -143
  73. package/src/context/ParsedArgument.ts +13 -13
  74. package/src/context/ParsedCommandNode.ts +14 -14
  75. package/src/context/StringRange.ts +26 -26
  76. package/src/context/SuggestionContext.ts +7 -7
  77. package/src/exceptions/CommandErrorType.ts +20 -13
  78. package/src/exceptions/CommandSyntaxError.ts +78 -37
  79. package/src/index.ts +30 -30
  80. package/src/suggestion/Suggestion.ts +46 -46
  81. package/src/suggestion/Suggestions.ts +59 -57
  82. package/src/suggestion/SuggestionsBuilder.ts +59 -57
  83. package/src/tree/ArgumentCommandNode.ts +51 -40
  84. package/src/tree/CommandNode.ts +96 -87
  85. package/src/tree/LiteralCommandNode.ts +78 -57
  86. package/src/tree/RootCommandNode.ts +33 -23
  87. package/test/Arguments.test.ts +47 -33
  88. package/test/CommandDispatcher.test.ts +18 -22
  89. package/test/StringReader.test.ts +47 -47
  90. package/tsconfig.json +9 -14
@@ -1,300 +1,402 @@
1
1
  import {
2
- RootCommandNode,
3
- LiteralCommandNode,
4
- StringReader,
5
- LiteralArgumentBuilder,
6
- CommandContextBuilder,
7
- CommandNode,
8
- ParseResults,
9
- CommandSyntaxError,
10
- Suggestions,
11
- SuggestionsBuilder
2
+ RootCommandNode,
3
+ type LiteralCommandNode,
4
+ StringReader,
5
+ type LiteralArgumentBuilder,
6
+ CommandContextBuilder,
7
+ type CommandNode,
8
+ ParseResults,
9
+ CommandSyntaxError,
10
+ Suggestions,
11
+ SuggestionsBuilder,
12
12
  } from ".";
13
13
 
14
14
  export class CommandDispatcher<S> {
15
-
16
- private root: RootCommandNode<S>;
17
-
18
- private static USAGE_OPTIONAL_OPEN = "[";
19
- private static USAGE_OPTIONAL_CLOSE = "]";
20
- private static USAGE_REQUIRED_OPEN = "(";
21
- private static USAGE_REQUIRED_CLOSE = ")";
22
- private static USAGE_OR = "|";
23
-
24
- constructor() {
25
- this.root = new RootCommandNode();
26
- }
27
-
28
- register(command: LiteralArgumentBuilder<S>): LiteralCommandNode<S> {
29
- const build = command.build();
30
- this.root.addChild(build);
31
- return build;
32
- }
33
-
34
- async execute(parse: ParseResults<S> | string, source: S): Promise<number> {
35
- if (typeof(parse) === "string") {
36
- parse = await this.parse(new StringReader(parse), source);
37
- }
38
-
39
- if (parse.getReader().canRead()) {
40
- if (parse.getErrors().size == 1) {
41
- throw parse.getErrors().values().next();
42
- } else if (parse.getContext().getRange().isEmpty()) {
43
- throw CommandSyntaxError.DISPATCHER_UNKNOWN_COMMAND.createWithContext(parse.getReader());
44
- } else {
45
- throw CommandSyntaxError.DISPATCHER_UNKNOWN_ARGUMENT.createWithContext(parse.getReader());
46
- }
47
- }
48
-
49
- let result = 0;
50
- let successfulForks = 0;
51
- let forked = false;
52
- let foundCommand = false;
53
- const command = parse.getReader().getString();
54
- const original = parse.getContext().build(command);
55
- let contexts = [original];
56
- let next = [];
57
-
58
- while (contexts.length > 0) {
59
- const size = contexts.length;
60
- for (let i = 0; i < size; i++) {
61
- const context = contexts[i];
62
- const child = context.getChild();
63
- if (child !== null) {
64
- forked = forked || context.isForked();
65
- if (child.hasNodes()) {
66
- foundCommand = true;
67
- const modifier = context.getRedirectModifier();
68
- if (modifier === null) {
69
- next.push(child.copyFor(context.getSource()));
70
- } else {
71
- try {
72
- const results = (<S[]> modifier(context));
73
- results.forEach(source => {
74
- next.push(child.copyFor(source));
75
- })
76
- } catch (e) {
77
- if (!forked) throw e;
78
- }
79
- }
80
- }
81
- } else if (context.getCommand()) {
82
- foundCommand = true;
83
- try {
84
- const value = await context.getCommand()(context);
85
- result += (value || value === 0) ? value : 1;
86
- successfulForks++;
87
- } catch (e) {
88
- if (!forked) throw e;
89
- }
90
- }
91
- }
92
- contexts = next;
93
- next = [];
94
- }
95
-
96
- if (!foundCommand) {
97
- throw CommandSyntaxError.DISPATCHER_UNKNOWN_COMMAND.createWithContext(parse.getReader());
98
- }
99
- return forked ? successfulForks : result;
100
- }
101
-
102
- async parse(reader: StringReader | string, source: S): Promise<ParseResults<S>> {
103
- reader = new StringReader(reader);
104
- const context = new CommandContextBuilder<S>(this, source, this.root, reader.getCursor());
105
- return this.parseNodes(this.root, reader, context);
106
- }
107
-
108
- private async parseNodes(node: CommandNode<S>, originalReader: StringReader, contextSoFar: CommandContextBuilder<S>): Promise<ParseResults<S>> {
109
- const source = contextSoFar.getSource();
110
- const errors = new Map<CommandNode<S>, CommandSyntaxError>();
111
- let potentials = [];
112
- const cursor = originalReader.getCursor();
113
-
114
- for (const child of node.getRelevantNodes(originalReader)) {
115
- if (!await child.canUse(source)) {
116
- continue;
117
- }
118
- const context = contextSoFar.copy();
119
- const reader = new StringReader(originalReader);
120
-
121
- try {
122
- try {
123
- child.parse(reader, context);
124
- } catch (e) {
125
- if (e instanceof CommandSyntaxError) {
126
- throw e;
127
- } else {
128
- throw CommandSyntaxError.DISPATCHER_PARSE_ERROR.createWithContext(reader, e.message);
129
- }
130
- }
131
- if (reader.canRead() && reader.peek() !== " ") {
132
- throw CommandSyntaxError.DISPATCHER_EXPECTED_ARGUMENT_SEPARATOR.createWithContext(reader);
133
- }
134
- } catch (e) {
135
- if (e instanceof CommandSyntaxError) {
136
- errors.set(child, e);
137
- reader.setCursor(cursor);
138
- continue;
139
- } else {
140
- throw e;
141
- }
142
- }
143
-
144
- context.withCommand(child.getCommand());
145
- if (reader.canRead(child.getRedirect() === null ? 2 : 1)) {
146
- reader.skip();
147
- if (child.getRedirect()) {
148
- const childContext = new CommandContextBuilder<S>(this, source, child.getRedirect(), reader.getCursor());
149
- const parse = await this.parseNodes(child.getRedirect(), reader, childContext);
150
- context.withChild(parse.getContext());
151
- return new ParseResults<S>(context, parse.getReader(), parse.getErrors());
152
- } else {
153
- potentials.push(this.parseNodes(child, reader, context));
154
- }
155
- } else {
156
- potentials.push(new ParseResults(context, reader, new Map()));
157
- }
158
- }
159
- if (potentials.length == 0) {
160
- potentials.push(new ParseResults(contextSoFar, originalReader, errors));
161
- }
162
- return potentials[0];
163
- }
164
-
165
- async getAllUsage(node: CommandNode<S>, source: S, restricted: boolean): Promise<string[]> {
166
- const result = [];
167
- await this.getAllUsageImpl(node, source, result, "", restricted);
168
- return result;
169
- }
170
-
171
- private async getAllUsageImpl(node: CommandNode<S>, source: S, result: string[], prefix: string, restricted: boolean): Promise<void> {
172
- if (restricted && !await node.canUse(source)) {
173
- return;
174
- }
175
-
176
- if (node.getCommand() != null) {
177
- result.push(prefix);
178
- }
179
-
180
- if (node.getRedirect() != null) {
181
- const redirect = node.getRedirect() === this.root ? "..." : "-> " + node.getRedirect().getUsageText();
182
- result.push(prefix.length === 0 ? node.getUsageText() + " " + redirect : prefix + " " + redirect);
183
- } else if (node.getChildren().length > 0) {
184
- for (const child of node.getChildren()) {
185
- const newPrefix = prefix.length === 0 ? child.getUsageText() : prefix + " " + child.getUsageText();
186
- await this.getAllUsageImpl(child, source, result, newPrefix, restricted);
187
- }
188
- }
189
- }
190
-
191
- async getCompletionSuggestions(parse: ParseResults<S>, cursor?: number): Promise<Suggestions> {
192
- if (cursor === undefined) {
193
- cursor = parse.getReader().getTotalLength();
194
- }
195
- const context = parse.getContext();
196
- const nodeBeforeCursor = context.findSuggestionContext(cursor);
197
- const parent = nodeBeforeCursor.parent;
198
- const start = Math.min(nodeBeforeCursor.startPos, cursor);
199
-
200
- const fullInput = parse.getReader().getString();
201
- const truncatedInput = fullInput.substring(0, cursor);
202
- let promises: Promise<Suggestions>[] = [];
203
- for (const node of parent.getChildren()) {
204
- let promise = Suggestions.empty();
205
- try {
206
- promise = node.listSuggestions(context.build(truncatedInput), new SuggestionsBuilder(truncatedInput, start));
207
- } catch(ignored) {
208
- console.log("???", ignored)
209
- }
210
- promises.push(promise);
211
- }
212
- const suggestions = await Promise.all(promises);
213
- return Suggestions.merge(fullInput, suggestions);
214
- }
215
-
216
- getSmartUsage(node: CommandNode<S>, source: S): Map<CommandNode<S>, string>;
217
- getSmartUsage(node: CommandNode<S>, source: S, optional: boolean, deep: boolean): string;
218
-
219
- getSmartUsage(node: CommandNode<S>, source: S, optional?: boolean, deep?: boolean): Map<CommandNode<S>, string> | string {
220
- if (optional === undefined && deep === undefined) {
221
- const result = new Map<CommandNode<S>, string>();
222
- const optional: boolean = node.getCommand() !== undefined && node.getCommand() !== null;
223
- const children = node.getChildren();
224
-
225
- for (const index in children) {
226
- const child = children[index];
227
- const usage = this.getSmartUsage(child, source, optional, false);
228
-
229
- if (usage !== undefined) {
230
- result.set(child, usage);
231
- }
232
- }
233
-
234
- return result;
235
- } else {
236
-
237
- if (!node.canUse(source)) {
238
- return undefined
239
- }
240
-
241
- const self: string = optional ? CommandDispatcher.USAGE_OPTIONAL_OPEN + node.getUsageText() + CommandDispatcher.USAGE_OPTIONAL_CLOSE : node.getUsageText();
242
- const childOptional: boolean = node.getCommand() !== undefined;
243
- const open: string = childOptional ? CommandDispatcher.USAGE_OPTIONAL_OPEN : CommandDispatcher.USAGE_REQUIRED_OPEN
244
- const close: string = childOptional ? CommandDispatcher.USAGE_OPTIONAL_CLOSE : CommandDispatcher.USAGE_REQUIRED_CLOSE
245
-
246
- if (!deep) {
247
- if (node.getRedirect() !== undefined) {
248
- const redirect: String = node.getRedirect() === this.root ? "..." : "-> " + node.getRedirect().getUsageText();
249
- return self + " " + redirect;
250
- } else {
251
- const children: CommandNode<S>[] = node.getChildren().filter(c => c.canUse(source))
252
-
253
- if (children.length === 1) {
254
- const usage = String(this.getSmartUsage(children[0], source, childOptional, childOptional));
255
-
256
- if (usage !== undefined) {
257
- return self + " " + usage;
258
- }
259
- } else if (children.length > 1) {
260
- let childUsage = new Set<String>();
261
-
262
- for (const index in children) {
263
- const child = children[index];
264
- const usage = this.getSmartUsage(child, source, childOptional, true);
265
-
266
- if (usage !== undefined) {
267
- childUsage.add(usage)
268
- }
269
- }
270
-
271
- if (childUsage.size === 1) {
272
- const usage = childUsage.values().next().value
273
- return self + " " + (childOptional ? CommandDispatcher.USAGE_OPTIONAL_OPEN + usage + CommandDispatcher.USAGE_OPTIONAL_CLOSE: usage);
274
- } else if (childUsage.size > 1) {
275
- let builder = open
276
-
277
- for (let index = 0; index < children.length; index++) {
278
- const child = children[index]
279
-
280
- if (index > 0) {
281
- builder += CommandDispatcher.USAGE_OR
282
- }
283
- builder += child.getUsageText()
284
- }
285
-
286
- if (children.length > 0) {
287
- builder += close
288
- return self + " " + builder;
289
- }
290
- }
291
- }
292
- }
293
- }
294
- }
295
- }
296
-
297
- getRoot(): RootCommandNode<S> {
298
- return this.root;
299
- }
15
+ private root: RootCommandNode<S>;
16
+
17
+ private static USAGE_OPTIONAL_OPEN = "[";
18
+ private static USAGE_OPTIONAL_CLOSE = "]";
19
+ private static USAGE_REQUIRED_OPEN = "(";
20
+ private static USAGE_REQUIRED_CLOSE = ")";
21
+ private static USAGE_OR = "|";
22
+
23
+ constructor() {
24
+ this.root = new RootCommandNode();
25
+ }
26
+
27
+ register(command: LiteralArgumentBuilder<S>): LiteralCommandNode<S> {
28
+ const build = command.build();
29
+ this.root.addChild(build);
30
+ return build;
31
+ }
32
+
33
+ async execute(parse: ParseResults<S> | string, source: S): Promise<number> {
34
+ if (typeof parse === "string") {
35
+ parse = await this.parse(new StringReader(parse), source);
36
+ }
37
+
38
+ if (parse.getReader().canRead()) {
39
+ if (parse.getErrors().size === 1) {
40
+ throw parse.getErrors().values().next();
41
+ } else if (parse.getContext().getRange().isEmpty()) {
42
+ throw CommandSyntaxError.DISPATCHER_UNKNOWN_COMMAND.createWithContext(
43
+ parse.getReader(),
44
+ );
45
+ } else {
46
+ throw CommandSyntaxError.DISPATCHER_UNKNOWN_ARGUMENT.createWithContext(
47
+ parse.getReader(),
48
+ );
49
+ }
50
+ }
51
+
52
+ let result = 0;
53
+ let successfulForks = 0;
54
+ let forked = false;
55
+ let foundCommand = false;
56
+ const command = parse.getReader().getString();
57
+ const original = parse.getContext().build(command);
58
+ let contexts = [original];
59
+ let next = [];
60
+
61
+ while (contexts.length > 0) {
62
+ const size = contexts.length;
63
+ for (let i = 0; i < size; i++) {
64
+ const context = contexts[i];
65
+ const child = context.getChild();
66
+ if (child !== null) {
67
+ forked = forked || context.isForked();
68
+ if (child.hasNodes()) {
69
+ foundCommand = true;
70
+ const modifier = context.getRedirectModifier();
71
+ if (modifier === null) {
72
+ next.push(child.copyFor(context.getSource()));
73
+ } else {
74
+ try {
75
+ const results = <S[]>modifier(context);
76
+ results.forEach((source) => {
77
+ next.push(child.copyFor(source));
78
+ });
79
+ } catch (e) {
80
+ if (!forked) throw e;
81
+ }
82
+ }
83
+ }
84
+ } else if (context.getCommand()) {
85
+ foundCommand = true;
86
+ try {
87
+ const value = await context.getCommand()(context);
88
+ result += value || value === 0 ? value : 1;
89
+ successfulForks++;
90
+ } catch (e) {
91
+ if (!forked) throw e;
92
+ }
93
+ }
94
+ }
95
+ contexts = next;
96
+ next = [];
97
+ }
98
+
99
+ if (!foundCommand) {
100
+ throw CommandSyntaxError.DISPATCHER_UNKNOWN_COMMAND.createWithContext(
101
+ parse.getReader(),
102
+ );
103
+ }
104
+ return forked ? successfulForks : result;
105
+ }
106
+
107
+ async parse(
108
+ reader: StringReader | string,
109
+ source: S,
110
+ ): Promise<ParseResults<S>> {
111
+ reader = new StringReader(reader);
112
+ const context = new CommandContextBuilder<S>(
113
+ this,
114
+ source,
115
+ this.root,
116
+ reader.getCursor(),
117
+ );
118
+ return this.parseNodes(this.root, reader, context);
119
+ }
120
+
121
+ private async parseNodes(
122
+ node: CommandNode<S>,
123
+ originalReader: StringReader,
124
+ contextSoFar: CommandContextBuilder<S>,
125
+ ): Promise<ParseResults<S>> {
126
+ const source = contextSoFar.getSource();
127
+ const errors = new Map<CommandNode<S>, CommandSyntaxError>();
128
+ const potentials = [];
129
+ const cursor = originalReader.getCursor();
130
+
131
+ for (const child of node.getRelevantNodes(originalReader)) {
132
+ if (!(await child.canUse(source))) {
133
+ continue;
134
+ }
135
+ const context = contextSoFar.copy();
136
+ const reader = new StringReader(originalReader);
137
+
138
+ try {
139
+ try {
140
+ child.parse(reader, context);
141
+ } catch (e) {
142
+ if (e instanceof CommandSyntaxError) {
143
+ throw e;
144
+ } else {
145
+ throw CommandSyntaxError.DISPATCHER_PARSE_ERROR.createWithContext(
146
+ reader,
147
+ e.message,
148
+ );
149
+ }
150
+ }
151
+ if (reader.canRead() && reader.peek() !== " ") {
152
+ throw CommandSyntaxError.DISPATCHER_EXPECTED_ARGUMENT_SEPARATOR.createWithContext(
153
+ reader,
154
+ );
155
+ }
156
+ } catch (e) {
157
+ if (e instanceof CommandSyntaxError) {
158
+ errors.set(child, e);
159
+ reader.setCursor(cursor);
160
+ continue;
161
+ } else {
162
+ throw e;
163
+ }
164
+ }
165
+
166
+ context.withCommand(child.getCommand());
167
+ if (reader.canRead(child.getRedirect() === null ? 2 : 1)) {
168
+ reader.skip();
169
+ if (child.getRedirect()) {
170
+ const childContext = new CommandContextBuilder<S>(
171
+ this,
172
+ source,
173
+ child.getRedirect(),
174
+ reader.getCursor(),
175
+ );
176
+ const parse = await this.parseNodes(
177
+ child.getRedirect(),
178
+ reader,
179
+ childContext,
180
+ );
181
+ context.withChild(parse.getContext());
182
+ return new ParseResults<S>(
183
+ context,
184
+ parse.getReader(),
185
+ parse.getErrors(),
186
+ );
187
+ } else {
188
+ potentials.push(this.parseNodes(child, reader, context));
189
+ }
190
+ } else {
191
+ potentials.push(new ParseResults(context, reader, new Map()));
192
+ }
193
+ }
194
+ if (potentials.length === 0) {
195
+ potentials.push(new ParseResults(contextSoFar, originalReader, errors));
196
+ }
197
+ return potentials[0];
198
+ }
199
+
200
+ async getAllUsage(
201
+ node: CommandNode<S>,
202
+ source: S,
203
+ restricted: boolean,
204
+ ): Promise<string[]> {
205
+ const result = [];
206
+ await this.getAllUsageImpl(node, source, result, "", restricted);
207
+ return result;
208
+ }
209
+
210
+ private async getAllUsageImpl(
211
+ node: CommandNode<S>,
212
+ source: S,
213
+ result: string[],
214
+ prefix: string,
215
+ restricted: boolean,
216
+ ): Promise<void> {
217
+ if (restricted && !(await node.canUse(source))) {
218
+ return;
219
+ }
220
+
221
+ if (node.getCommand() != null) {
222
+ result.push(prefix);
223
+ }
224
+
225
+ if (node.getRedirect() != null) {
226
+ const redirect =
227
+ node.getRedirect() === this.root
228
+ ? "..."
229
+ : `-> ${node.getRedirect().getUsageText()}`;
230
+ result.push(
231
+ prefix.length === 0
232
+ ? `${node.getUsageText()} ${redirect}`
233
+ : `${prefix} ${redirect}`,
234
+ );
235
+ } else if (node.getChildren().length > 0) {
236
+ for (const child of node.getChildren()) {
237
+ const newPrefix =
238
+ prefix.length === 0
239
+ ? child.getUsageText()
240
+ : `${prefix} ${child.getUsageText()}`;
241
+ await this.getAllUsageImpl(
242
+ child,
243
+ source,
244
+ result,
245
+ newPrefix,
246
+ restricted,
247
+ );
248
+ }
249
+ }
250
+ }
251
+
252
+ async getCompletionSuggestions(
253
+ parse: ParseResults<S>,
254
+ cursor?: number,
255
+ ): Promise<Suggestions> {
256
+ if (cursor === undefined) {
257
+ cursor = parse.getReader().getTotalLength();
258
+ }
259
+ const context = parse.getContext();
260
+ const nodeBeforeCursor = context.findSuggestionContext(cursor);
261
+ const parent = nodeBeforeCursor.parent;
262
+ const start = Math.min(nodeBeforeCursor.startPos, cursor);
263
+
264
+ const fullInput = parse.getReader().getString();
265
+ const truncatedInput = fullInput.substring(0, cursor);
266
+ const promises: Promise<Suggestions>[] = [];
267
+ for (const node of parent.getChildren()) {
268
+ let promise = Suggestions.empty();
269
+ try {
270
+ promise = node.listSuggestions(
271
+ context.build(truncatedInput),
272
+ new SuggestionsBuilder(truncatedInput, start),
273
+ );
274
+ } catch (ignored) {
275
+ console.log("???", ignored);
276
+ }
277
+ promises.push(promise);
278
+ }
279
+ const suggestions = await Promise.all(promises);
280
+ return Suggestions.merge(fullInput, suggestions);
281
+ }
282
+
283
+ getSmartUsage(node: CommandNode<S>, source: S): Map<CommandNode<S>, string>;
284
+ getSmartUsage(
285
+ node: CommandNode<S>,
286
+ source: S,
287
+ optional: boolean,
288
+ deep: boolean,
289
+ ): string;
290
+
291
+ getSmartUsage(
292
+ node: CommandNode<S>,
293
+ source: S,
294
+ optional?: boolean,
295
+ deep?: boolean,
296
+ ): Map<CommandNode<S>, string> | string {
297
+ if (optional === undefined && deep === undefined) {
298
+ const result = new Map<CommandNode<S>, string>();
299
+ const optional: boolean =
300
+ node.getCommand() !== undefined && node.getCommand() !== null;
301
+ const children = node.getChildren();
302
+
303
+ for (const index in children) {
304
+ const child = children[index];
305
+ const usage = this.getSmartUsage(child, source, optional, false);
306
+
307
+ if (usage !== undefined) {
308
+ result.set(child, usage);
309
+ }
310
+ }
311
+
312
+ return result;
313
+ } else {
314
+ if (!node.canUse(source)) {
315
+ return undefined;
316
+ }
317
+
318
+ const self: string = optional
319
+ ? CommandDispatcher.USAGE_OPTIONAL_OPEN +
320
+ node.getUsageText() +
321
+ CommandDispatcher.USAGE_OPTIONAL_CLOSE
322
+ : node.getUsageText();
323
+ const childOptional: boolean = node.getCommand() !== undefined;
324
+ const open: string = childOptional
325
+ ? CommandDispatcher.USAGE_OPTIONAL_OPEN
326
+ : CommandDispatcher.USAGE_REQUIRED_OPEN;
327
+ const close: string = childOptional
328
+ ? CommandDispatcher.USAGE_OPTIONAL_CLOSE
329
+ : CommandDispatcher.USAGE_REQUIRED_CLOSE;
330
+
331
+ if (!deep) {
332
+ if (node.getRedirect() !== undefined) {
333
+ const redirect =
334
+ node.getRedirect() === this.root
335
+ ? "..."
336
+ : `-> ${node.getRedirect().getUsageText()}`;
337
+ return `${self} ${redirect}`;
338
+ } else {
339
+ const children: CommandNode<S>[] = node
340
+ .getChildren()
341
+ .filter((c) => c.canUse(source));
342
+
343
+ if (children.length === 1) {
344
+ const usage = String(
345
+ this.getSmartUsage(
346
+ children[0],
347
+ source,
348
+ childOptional,
349
+ childOptional,
350
+ ),
351
+ );
352
+
353
+ if (usage !== undefined) {
354
+ return `${self} ${usage}`;
355
+ }
356
+ } else if (children.length > 1) {
357
+ const childUsage = new Set<string>();
358
+
359
+ for (const index in children) {
360
+ const child = children[index];
361
+ const usage = this.getSmartUsage(
362
+ child,
363
+ source,
364
+ childOptional,
365
+ true,
366
+ );
367
+
368
+ if (usage !== undefined) {
369
+ childUsage.add(usage);
370
+ }
371
+ }
372
+
373
+ if (childUsage.size === 1) {
374
+ const usage = childUsage.values().next().value;
375
+ return `${self} ${childOptional ? CommandDispatcher.USAGE_OPTIONAL_OPEN + usage + CommandDispatcher.USAGE_OPTIONAL_CLOSE : usage}`;
376
+ } else if (childUsage.size > 1) {
377
+ let builder = open;
378
+
379
+ for (let index = 0; index < children.length; index++) {
380
+ const child = children[index];
381
+
382
+ if (index > 0) {
383
+ builder += CommandDispatcher.USAGE_OR;
384
+ }
385
+ builder += child.getUsageText();
386
+ }
387
+
388
+ if (children.length > 0) {
389
+ builder += close;
390
+ return `${self} ${builder}`;
391
+ }
392
+ }
393
+ }
394
+ }
395
+ }
396
+ }
397
+ }
398
+
399
+ getRoot(): RootCommandNode<S> {
400
+ return this.root;
401
+ }
300
402
  }