@trioforge/brigadier 0.0.0-stage → 0.1.1

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.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) Microsoft Corporation. All rights reserved.
4
+ Copyright (c) 2026 Trio Forge Studio
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,74 @@
1
- # Temporary Holding Version
1
+ # @trioforge/brigadier
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A roblox-ts port of [Mojang's Brigadier](https://github.com/Mojang/brigadier), the command parser and dispatcher behind Minecraft's commands. It is a class-for-class port of Brigadier 1.3, and it passes all of Brigadier's unit tests.
4
+
5
+ Read the documentation at <https://website.trio-forge.workers.dev/brigadier/>.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ pnpm add @trioforge/brigadier
11
+ ```
12
+
13
+ roblox-ts only looks for packages in the scopes your project lists, so add the `@trioforge` scope in two places.
14
+
15
+ 1. In `tsconfig.json`, add it to `typeRoots`:
16
+
17
+ ```json
18
+ "typeRoots": ["node_modules/@rbxts", "node_modules/@trioforge"]
19
+ ```
20
+
21
+ 2. In your Rojo project, map it beside `@rbxts` under `rbxts_include/node_modules`:
22
+
23
+ ```json
24
+ "node_modules": {
25
+ "$className": "Folder",
26
+ "@rbxts": { "$path": "node_modules/@rbxts" },
27
+ "@trioforge": { "$path": "node_modules/@trioforge" }
28
+ }
29
+ ```
30
+
31
+ ## Example
32
+
33
+ ```ts
34
+ import { CommandDispatcher, argument, getInteger, getString, integer, literal, word } from "@trioforge/brigadier";
35
+
36
+ const dispatcher = new CommandDispatcher<Player>();
37
+
38
+ dispatcher.register(
39
+ literal<Player>("give").then(
40
+ argument<Player, string>("target", word()).then(
41
+ argument<Player, number>("amount", integer(1, 100)).executes((context) => {
42
+ const target = getString(context, "target");
43
+ const amount = getInteger(context, "amount");
44
+ print(`${context.getSource().Name} gives ${amount} to ${target}`);
45
+ return 1;
46
+ }),
47
+ ),
48
+ ),
49
+ );
50
+
51
+ dispatcher.execute("give Bob 50", player);
52
+ // → Ann gives 50 to Bob
53
+ ```
54
+
55
+ A command that fails to parse throws a `CommandSyntaxException`. Its `getMessage()` names the problem and points at the character, such as `Integer must not be more than 100, found 500 at position 9: give Bob <--[HERE]`.
56
+
57
+ ## Differences from the Java
58
+
59
+ The names are Brigadier's. [docs/porting.md](docs/porting.md) lists every place the port differs, such as Promises instead of `CompletableFuture` and `stringArg()` instead of `string()`.
60
+
61
+ ## Development
62
+
63
+ Install the Node tools with `pnpm install`, and Lune and StyLua with `rokit install`.
64
+
65
+ ```sh
66
+ pnpm run check # oxfmt, rbxtsc, StyLua, the tests and the README example
67
+ pnpm run test # build, then run the tests
68
+ ```
69
+
70
+ The tests in `test/` are Brigadier's JUnit tests, ported to Luau one to one. `test/lib/runtime.luau` loads the compiled package from `out/` through roblox-ts's runtime, as a place does, and Lune runs them. [AGENTS.md](AGENTS.md) has the rules for changing the port.
71
+
72
+ ## License
73
+
74
+ MIT, like Brigadier. See [LICENSE](LICENSE).
@@ -0,0 +1,91 @@
1
+ import type { CommandContext } from "./context";
2
+ import { StringReader } from "./string-reader";
3
+ import type { Suggestions, SuggestionsBuilder } from "./suggestion";
4
+ export interface ArgumentType<T> {
5
+ parse(reader: StringReader, source?: unknown): T;
6
+ listSuggestions?<S>(context: CommandContext<S>, builder: SuggestionsBuilder): Promise<Suggestions>;
7
+ getExamples?(): readonly string[];
8
+ }
9
+ export declare class BoolArgumentType implements ArgumentType<boolean> {
10
+ parse(reader: StringReader): boolean;
11
+ listSuggestions<S>(_context: CommandContext<S>, builder: SuggestionsBuilder): Promise<Suggestions>;
12
+ getExamples(): readonly string[];
13
+ }
14
+ export declare function bool(): BoolArgumentType;
15
+ export declare function getBool<S>(context: CommandContext<S>, name: string): boolean;
16
+ export declare class IntegerArgumentType implements ArgumentType<number> {
17
+ private readonly minimum;
18
+ private readonly maximum;
19
+ constructor(minimum: number, maximum: number);
20
+ getMinimum(): number;
21
+ getMaximum(): number;
22
+ parse(reader: StringReader): number;
23
+ equals(other: unknown): boolean;
24
+ toString(): string;
25
+ getExamples(): readonly string[];
26
+ }
27
+ export declare function integer(min?: number, max?: number): IntegerArgumentType;
28
+ export declare function getInteger<S>(context: CommandContext<S>, name: string): number;
29
+ export declare class LongArgumentType implements ArgumentType<number> {
30
+ private readonly minimum;
31
+ private readonly maximum;
32
+ constructor(minimum: number, maximum: number);
33
+ getMinimum(): number;
34
+ getMaximum(): number;
35
+ parse(reader: StringReader): number;
36
+ equals(other: unknown): boolean;
37
+ toString(): string;
38
+ getExamples(): readonly string[];
39
+ }
40
+ export declare function longArg(min?: number, max?: number): LongArgumentType;
41
+ export declare function getLong<S>(context: CommandContext<S>, name: string): number;
42
+ export declare class DoubleArgumentType implements ArgumentType<number> {
43
+ private readonly minimum;
44
+ private readonly maximum;
45
+ constructor(minimum: number, maximum: number);
46
+ getMinimum(): number;
47
+ getMaximum(): number;
48
+ parse(reader: StringReader): number;
49
+ equals(other: unknown): boolean;
50
+ toString(): string;
51
+ getExamples(): readonly string[];
52
+ }
53
+ export declare function doubleArg(min?: number, max?: number): DoubleArgumentType;
54
+ export declare function getDouble<S>(context: CommandContext<S>, name: string): number;
55
+ export declare class FloatArgumentType implements ArgumentType<number> {
56
+ private readonly minimum;
57
+ private readonly maximum;
58
+ constructor(minimum: number, maximum: number);
59
+ getMinimum(): number;
60
+ getMaximum(): number;
61
+ parse(reader: StringReader): number;
62
+ equals(other: unknown): boolean;
63
+ toString(): string;
64
+ getExamples(): readonly string[];
65
+ }
66
+ export declare function floatArg(min?: number, max?: number): FloatArgumentType;
67
+ export declare function getFloat<S>(context: CommandContext<S>, name: string): number;
68
+ export declare class StringType {
69
+ private readonly constantName;
70
+ private readonly examples;
71
+ static readonly SINGLE_WORD: StringType;
72
+ static readonly QUOTABLE_PHRASE: StringType;
73
+ static readonly GREEDY_PHRASE: StringType;
74
+ private constructor();
75
+ name(): string;
76
+ getExamples(): readonly string[];
77
+ toString(): string;
78
+ }
79
+ export declare class StringArgumentType implements ArgumentType<string> {
80
+ private readonly stringType;
81
+ constructor(stringType: StringType);
82
+ getType(): StringType;
83
+ parse(reader: StringReader): string;
84
+ toString(): string;
85
+ getExamples(): readonly string[];
86
+ }
87
+ export declare function word(): StringArgumentType;
88
+ export declare function stringArg(): StringArgumentType;
89
+ export declare function greedyString(): StringArgumentType;
90
+ export declare function getString<S>(context: CommandContext<S>, name: string): string;
91
+ export declare function escapeIfRequired(input: string): string;
@@ -0,0 +1,475 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local CommandSyntaxException = TS.import(script, script.Parent, "exceptions").CommandSyntaxException
4
+ local _java = TS.import(script, script.Parent, "java")
5
+ local formatDouble = _java.formatDouble
6
+ local formatFloat = _java.formatFloat
7
+ local formatInteger = _java.formatInteger
8
+ local startsWith = _java.startsWith
9
+ local toFloat = _java.toFloat
10
+ local StringReader = TS.import(script, script.Parent, "string-reader").StringReader
11
+ -- Java's default methods are optional: a type without listSuggestions suggests nothing, and one without
12
+ -- getExamples has none.
13
+ local BOOL_EXAMPLES = { "true", "false" }
14
+ local BoolArgumentType
15
+ do
16
+ BoolArgumentType = setmetatable({}, {
17
+ __tostring = function()
18
+ return "BoolArgumentType"
19
+ end,
20
+ })
21
+ BoolArgumentType.__index = BoolArgumentType
22
+ function BoolArgumentType.new(...)
23
+ local self = setmetatable({}, BoolArgumentType)
24
+ return self:constructor(...) or self
25
+ end
26
+ function BoolArgumentType:constructor()
27
+ end
28
+ function BoolArgumentType:parse(reader)
29
+ return reader:readBoolean()
30
+ end
31
+ function BoolArgumentType:listSuggestions(_context, builder)
32
+ if startsWith("true", builder:getRemainingLowerCase()) then
33
+ builder:suggest("true")
34
+ end
35
+ if startsWith("false", builder:getRemainingLowerCase()) then
36
+ builder:suggest("false")
37
+ end
38
+ return builder:buildFuture()
39
+ end
40
+ function BoolArgumentType:getExamples()
41
+ return BOOL_EXAMPLES
42
+ end
43
+ end
44
+ local function bool()
45
+ return BoolArgumentType.new()
46
+ end
47
+ local function getBool(context, name)
48
+ return context:getArgument(name, "boolean")
49
+ end
50
+ local INTEGER_EXAMPLES = { "0", "123", "-123" }
51
+ local INTEGER_MIN = -2147483648
52
+ local INTEGER_MAX = 2147483647
53
+ local IntegerArgumentType
54
+ do
55
+ IntegerArgumentType = setmetatable({}, {
56
+ __tostring = function()
57
+ return "IntegerArgumentType"
58
+ end,
59
+ })
60
+ IntegerArgumentType.__index = IntegerArgumentType
61
+ function IntegerArgumentType.new(...)
62
+ local self = setmetatable({}, IntegerArgumentType)
63
+ return self:constructor(...) or self
64
+ end
65
+ function IntegerArgumentType:constructor(minimum, maximum)
66
+ self.minimum = minimum
67
+ self.maximum = maximum
68
+ end
69
+ function IntegerArgumentType:getMinimum()
70
+ return self.minimum
71
+ end
72
+ function IntegerArgumentType:getMaximum()
73
+ return self.maximum
74
+ end
75
+ function IntegerArgumentType:parse(reader)
76
+ local start = reader:getCursor()
77
+ local result = reader:readInt()
78
+ if result < self.minimum then
79
+ reader:setCursor(start)
80
+ error(CommandSyntaxException.BUILT_IN_EXCEPTIONS:integerTooLow():createWithContext(reader, result, self.minimum))
81
+ end
82
+ if result > self.maximum then
83
+ reader:setCursor(start)
84
+ error(CommandSyntaxException.BUILT_IN_EXCEPTIONS:integerTooHigh():createWithContext(reader, result, self.maximum))
85
+ end
86
+ return result
87
+ end
88
+ function IntegerArgumentType:equals(other)
89
+ if rawequal(self, other) then
90
+ return true
91
+ end
92
+ if not (TS.instanceof(other, IntegerArgumentType)) then
93
+ return false
94
+ end
95
+ return self.maximum == other.maximum and self.minimum == other.minimum
96
+ end
97
+ function IntegerArgumentType:toString()
98
+ if self.minimum == INTEGER_MIN and self.maximum == INTEGER_MAX then
99
+ return "integer()"
100
+ elseif self.maximum == INTEGER_MAX then
101
+ return `integer({formatInteger(self.minimum)})`
102
+ end
103
+ return `integer({formatInteger(self.minimum)}, {formatInteger(self.maximum)})`
104
+ end
105
+ function IntegerArgumentType:getExamples()
106
+ return INTEGER_EXAMPLES
107
+ end
108
+ function IntegerArgumentType:__tostring()
109
+ return self:toString()
110
+ end
111
+ end
112
+ local function integer(min, max)
113
+ if min == nil then
114
+ min = INTEGER_MIN
115
+ end
116
+ if max == nil then
117
+ max = INTEGER_MAX
118
+ end
119
+ return IntegerArgumentType.new(min, max)
120
+ end
121
+ local function getInteger(context, name)
122
+ return context:getArgument(name, "number")
123
+ end
124
+ local LONG_MIN = -(2 ^ 63)
125
+ local LONG_MAX = 2 ^ 63
126
+ local LongArgumentType
127
+ do
128
+ LongArgumentType = setmetatable({}, {
129
+ __tostring = function()
130
+ return "LongArgumentType"
131
+ end,
132
+ })
133
+ LongArgumentType.__index = LongArgumentType
134
+ function LongArgumentType.new(...)
135
+ local self = setmetatable({}, LongArgumentType)
136
+ return self:constructor(...) or self
137
+ end
138
+ function LongArgumentType:constructor(minimum, maximum)
139
+ self.minimum = minimum
140
+ self.maximum = maximum
141
+ end
142
+ function LongArgumentType:getMinimum()
143
+ return self.minimum
144
+ end
145
+ function LongArgumentType:getMaximum()
146
+ return self.maximum
147
+ end
148
+ function LongArgumentType:parse(reader)
149
+ local start = reader:getCursor()
150
+ local result = reader:readLong()
151
+ if result < self.minimum then
152
+ reader:setCursor(start)
153
+ error(CommandSyntaxException.BUILT_IN_EXCEPTIONS:longTooLow():createWithContext(reader, result, self.minimum))
154
+ end
155
+ if result > self.maximum then
156
+ reader:setCursor(start)
157
+ error(CommandSyntaxException.BUILT_IN_EXCEPTIONS:longTooHigh():createWithContext(reader, result, self.maximum))
158
+ end
159
+ return result
160
+ end
161
+ function LongArgumentType:equals(other)
162
+ if rawequal(self, other) then
163
+ return true
164
+ end
165
+ if not (TS.instanceof(other, LongArgumentType)) then
166
+ return false
167
+ end
168
+ return self.maximum == other.maximum and self.minimum == other.minimum
169
+ end
170
+ function LongArgumentType:toString()
171
+ if self.minimum == LONG_MIN and self.maximum == LONG_MAX then
172
+ return "longArg()"
173
+ elseif self.maximum == LONG_MAX then
174
+ return `longArg({formatInteger(self.minimum)})`
175
+ end
176
+ return `longArg({formatInteger(self.minimum)}, {formatInteger(self.maximum)})`
177
+ end
178
+ function LongArgumentType:getExamples()
179
+ return INTEGER_EXAMPLES
180
+ end
181
+ function LongArgumentType:__tostring()
182
+ return self:toString()
183
+ end
184
+ end
185
+ local function longArg(min, max)
186
+ if min == nil then
187
+ min = LONG_MIN
188
+ end
189
+ if max == nil then
190
+ max = LONG_MAX
191
+ end
192
+ return LongArgumentType.new(min, max)
193
+ end
194
+ local function getLong(context, name)
195
+ return context:getArgument(name, "number")
196
+ end
197
+ local DECIMAL_EXAMPLES = { "0", "1.2", ".5", "-1", "-.5", "-1234.56" }
198
+ local DOUBLE_MIN = -1.7976931348623157e308
199
+ local DOUBLE_MAX = 1.7976931348623157e308
200
+ local DoubleArgumentType
201
+ do
202
+ DoubleArgumentType = setmetatable({}, {
203
+ __tostring = function()
204
+ return "DoubleArgumentType"
205
+ end,
206
+ })
207
+ DoubleArgumentType.__index = DoubleArgumentType
208
+ function DoubleArgumentType.new(...)
209
+ local self = setmetatable({}, DoubleArgumentType)
210
+ return self:constructor(...) or self
211
+ end
212
+ function DoubleArgumentType:constructor(minimum, maximum)
213
+ self.minimum = minimum
214
+ self.maximum = maximum
215
+ end
216
+ function DoubleArgumentType:getMinimum()
217
+ return self.minimum
218
+ end
219
+ function DoubleArgumentType:getMaximum()
220
+ return self.maximum
221
+ end
222
+ function DoubleArgumentType:parse(reader)
223
+ local start = reader:getCursor()
224
+ local result = reader:readDouble()
225
+ if result < self.minimum then
226
+ reader:setCursor(start)
227
+ error(CommandSyntaxException.BUILT_IN_EXCEPTIONS:doubleTooLow():createWithContext(reader, result, self.minimum))
228
+ end
229
+ if result > self.maximum then
230
+ reader:setCursor(start)
231
+ error(CommandSyntaxException.BUILT_IN_EXCEPTIONS:doubleTooHigh():createWithContext(reader, result, self.maximum))
232
+ end
233
+ return result
234
+ end
235
+ function DoubleArgumentType:equals(other)
236
+ if rawequal(self, other) then
237
+ return true
238
+ end
239
+ if not (TS.instanceof(other, DoubleArgumentType)) then
240
+ return false
241
+ end
242
+ return self.maximum == other.maximum and self.minimum == other.minimum
243
+ end
244
+ function DoubleArgumentType:toString()
245
+ if self.minimum == DOUBLE_MIN and self.maximum == DOUBLE_MAX then
246
+ return "double()"
247
+ elseif self.maximum == DOUBLE_MAX then
248
+ return `double({formatDouble(self.minimum)})`
249
+ end
250
+ return `double({formatDouble(self.minimum)}, {formatDouble(self.maximum)})`
251
+ end
252
+ function DoubleArgumentType:getExamples()
253
+ return DECIMAL_EXAMPLES
254
+ end
255
+ function DoubleArgumentType:__tostring()
256
+ return self:toString()
257
+ end
258
+ end
259
+ local function doubleArg(min, max)
260
+ if min == nil then
261
+ min = DOUBLE_MIN
262
+ end
263
+ if max == nil then
264
+ max = DOUBLE_MAX
265
+ end
266
+ return DoubleArgumentType.new(min, max)
267
+ end
268
+ local function getDouble(context, name)
269
+ return context:getArgument(name, "number")
270
+ end
271
+ local FLOAT_MIN = -3.4028234663852886e38
272
+ local FLOAT_MAX = 3.4028234663852886e38
273
+ local FloatArgumentType
274
+ do
275
+ FloatArgumentType = setmetatable({}, {
276
+ __tostring = function()
277
+ return "FloatArgumentType"
278
+ end,
279
+ })
280
+ FloatArgumentType.__index = FloatArgumentType
281
+ function FloatArgumentType.new(...)
282
+ local self = setmetatable({}, FloatArgumentType)
283
+ return self:constructor(...) or self
284
+ end
285
+ function FloatArgumentType:constructor(minimum, maximum)
286
+ self.minimum = toFloat(minimum)
287
+ self.maximum = toFloat(maximum)
288
+ end
289
+ function FloatArgumentType:getMinimum()
290
+ return self.minimum
291
+ end
292
+ function FloatArgumentType:getMaximum()
293
+ return self.maximum
294
+ end
295
+ function FloatArgumentType:parse(reader)
296
+ local start = reader:getCursor()
297
+ local result = reader:readFloat()
298
+ if result < self.minimum then
299
+ reader:setCursor(start)
300
+ error(CommandSyntaxException.BUILT_IN_EXCEPTIONS:floatTooLow():createWithContext(reader, result, self.minimum))
301
+ end
302
+ if result > self.maximum then
303
+ reader:setCursor(start)
304
+ error(CommandSyntaxException.BUILT_IN_EXCEPTIONS:floatTooHigh():createWithContext(reader, result, self.maximum))
305
+ end
306
+ return result
307
+ end
308
+ function FloatArgumentType:equals(other)
309
+ if rawequal(self, other) then
310
+ return true
311
+ end
312
+ if not (TS.instanceof(other, FloatArgumentType)) then
313
+ return false
314
+ end
315
+ return self.maximum == other.maximum and self.minimum == other.minimum
316
+ end
317
+ function FloatArgumentType:toString()
318
+ if self.minimum == FLOAT_MIN and self.maximum == FLOAT_MAX then
319
+ return "float()"
320
+ elseif self.maximum == FLOAT_MAX then
321
+ return `float({formatFloat(self.minimum)})`
322
+ end
323
+ return `float({formatFloat(self.minimum)}, {formatFloat(self.maximum)})`
324
+ end
325
+ function FloatArgumentType:getExamples()
326
+ return DECIMAL_EXAMPLES
327
+ end
328
+ function FloatArgumentType:__tostring()
329
+ return self:toString()
330
+ end
331
+ end
332
+ local function floatArg(min, max)
333
+ if min == nil then
334
+ min = FLOAT_MIN
335
+ end
336
+ if max == nil then
337
+ max = FLOAT_MAX
338
+ end
339
+ return FloatArgumentType.new(min, max)
340
+ end
341
+ local function getFloat(context, name)
342
+ return context:getArgument(name, "number")
343
+ end
344
+ local StringType
345
+ do
346
+ StringType = setmetatable({}, {
347
+ __tostring = function()
348
+ return "StringType"
349
+ end,
350
+ })
351
+ StringType.__index = StringType
352
+ function StringType.new(...)
353
+ local self = setmetatable({}, StringType)
354
+ return self:constructor(...) or self
355
+ end
356
+ function StringType:constructor(constantName, examples)
357
+ self.constantName = constantName
358
+ self.examples = examples
359
+ end
360
+ function StringType:name()
361
+ return self.constantName
362
+ end
363
+ function StringType:getExamples()
364
+ return self.examples
365
+ end
366
+ function StringType:toString()
367
+ return self.constantName
368
+ end
369
+ function StringType:__tostring()
370
+ return self:toString()
371
+ end
372
+ StringType.SINGLE_WORD = StringType.new("SINGLE_WORD", { "word", "words_with_underscores" })
373
+ StringType.QUOTABLE_PHRASE = StringType.new("QUOTABLE_PHRASE", { '"quoted phrase"', "word", '""' })
374
+ StringType.GREEDY_PHRASE = StringType.new("GREEDY_PHRASE", { "word", "words with spaces", '"and symbols"' })
375
+ end
376
+ local StringArgumentType
377
+ do
378
+ StringArgumentType = setmetatable({}, {
379
+ __tostring = function()
380
+ return "StringArgumentType"
381
+ end,
382
+ })
383
+ StringArgumentType.__index = StringArgumentType
384
+ function StringArgumentType.new(...)
385
+ local self = setmetatable({}, StringArgumentType)
386
+ return self:constructor(...) or self
387
+ end
388
+ function StringArgumentType:constructor(stringType)
389
+ self.stringType = stringType
390
+ end
391
+ function StringArgumentType:getType()
392
+ return self.stringType
393
+ end
394
+ function StringArgumentType:parse(reader)
395
+ if self.stringType == StringType.GREEDY_PHRASE then
396
+ local text = reader:getRemaining()
397
+ reader:setCursor(reader:getTotalLength())
398
+ return text
399
+ elseif self.stringType == StringType.SINGLE_WORD then
400
+ return reader:readUnquotedString()
401
+ end
402
+ return reader:readString()
403
+ end
404
+ function StringArgumentType:toString()
405
+ return "string()"
406
+ end
407
+ function StringArgumentType:getExamples()
408
+ return self.stringType:getExamples()
409
+ end
410
+ function StringArgumentType:__tostring()
411
+ return self:toString()
412
+ end
413
+ end
414
+ local function word()
415
+ return StringArgumentType.new(StringType.SINGLE_WORD)
416
+ end
417
+ local function stringArg()
418
+ return StringArgumentType.new(StringType.QUOTABLE_PHRASE)
419
+ end
420
+ local function greedyString()
421
+ return StringArgumentType.new(StringType.GREEDY_PHRASE)
422
+ end
423
+ local function getString(context, name)
424
+ return context:getArgument(name, "string")
425
+ end
426
+ local function escape(input)
427
+ local result = { '"' }
428
+ for index = 1, #input do
429
+ local _input = input
430
+ local _index = index
431
+ local _index_1 = index
432
+ local c = string.sub(_input, _index, _index_1)
433
+ if c == "\\" or c == '"' then
434
+ table.insert(result, "\\")
435
+ end
436
+ table.insert(result, c)
437
+ end
438
+ table.insert(result, '"')
439
+ return table.concat(result, "")
440
+ end
441
+ local function escapeIfRequired(input)
442
+ for index = 1, #input do
443
+ local _input = input
444
+ local _index = index
445
+ local _index_1 = index
446
+ if not StringReader:isAllowedInUnquotedString(string.sub(_input, _index, _index_1)) then
447
+ return escape(input)
448
+ end
449
+ end
450
+ return input
451
+ end
452
+ return {
453
+ bool = bool,
454
+ getBool = getBool,
455
+ integer = integer,
456
+ getInteger = getInteger,
457
+ longArg = longArg,
458
+ getLong = getLong,
459
+ doubleArg = doubleArg,
460
+ getDouble = getDouble,
461
+ floatArg = floatArg,
462
+ getFloat = getFloat,
463
+ word = word,
464
+ stringArg = stringArg,
465
+ greedyString = greedyString,
466
+ getString = getString,
467
+ escapeIfRequired = escapeIfRequired,
468
+ BoolArgumentType = BoolArgumentType,
469
+ IntegerArgumentType = IntegerArgumentType,
470
+ LongArgumentType = LongArgumentType,
471
+ DoubleArgumentType = DoubleArgumentType,
472
+ FloatArgumentType = FloatArgumentType,
473
+ StringType = StringType,
474
+ StringArgumentType = StringArgumentType,
475
+ }
@@ -0,0 +1,38 @@
1
+ import type { AmbiguityConsumer, ResultConsumer } from "./command";
2
+ import { CommandContextBuilder } from "./context";
3
+ import { CommandSyntaxException } from "./exceptions";
4
+ import { type ImmutableStringReader, StringReader } from "./string-reader";
5
+ import { Suggestions } from "./suggestion";
6
+ import { type CommandNode, type LiteralArgumentBuilder, type LiteralCommandNode, RootCommandNode } from "./tree";
7
+ export declare class ParseResults<S> {
8
+ private readonly context;
9
+ private readonly reader;
10
+ private readonly exceptions;
11
+ constructor(context: CommandContextBuilder<S>, reader?: ImmutableStringReader, exceptions?: ReadonlyMap<CommandNode<S>, CommandSyntaxException>);
12
+ getContext(): CommandContextBuilder<S>;
13
+ getReader(): ImmutableStringReader;
14
+ getExceptions(): ReadonlyMap<CommandNode<S>, CommandSyntaxException>;
15
+ }
16
+ export declare class CommandDispatcher<S> {
17
+ static readonly ARGUMENT_SEPARATOR = " ";
18
+ static readonly ARGUMENT_SEPARATOR_CHAR = " ";
19
+ private readonly root;
20
+ private consumer;
21
+ constructor(root?: RootCommandNode<S>);
22
+ register(command: LiteralArgumentBuilder<S>): LiteralCommandNode<S>;
23
+ setConsumer(consumer: ResultConsumer<S>): void;
24
+ execute(input: string | StringReader, source: S): number;
25
+ execute(parse: ParseResults<S>): number;
26
+ private executeParse;
27
+ parse(command: string | StringReader, source: S): ParseResults<S>;
28
+ private parseNodes;
29
+ getAllUsage(node: CommandNode<S>, source: S, restricted: boolean): string[];
30
+ private getAllUsageRecursive;
31
+ getSmartUsage(node: CommandNode<S>, source: S): Map<CommandNode<S>, string>;
32
+ private getSmartUsageRecursive;
33
+ getCompletionSuggestions(parse: ParseResults<S>, cursor?: number): Promise<Suggestions>;
34
+ getRoot(): RootCommandNode<S>;
35
+ getPath(target: CommandNode<S>): string[];
36
+ findNode(path: readonly string[]): CommandNode<S> | undefined;
37
+ findAmbiguities(consumer: AmbiguityConsumer<S>): void;
38
+ }