@wq2/brigadier-ts 1.0.0

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 (104) hide show
  1. package/.github/workflows/publish.yml +49 -0
  2. package/.github/workflows/test.yml +23 -0
  3. package/LICENSE +21 -0
  4. package/README.md +131 -0
  5. package/dist/Command.d.ts +2 -0
  6. package/dist/Command.js +2 -0
  7. package/dist/CommandDispatcher.d.ts +20 -0
  8. package/dist/CommandDispatcher.js +416 -0
  9. package/dist/ParseResults.d.ts +10 -0
  10. package/dist/ParseResults.js +21 -0
  11. package/dist/Predicate.d.ts +1 -0
  12. package/dist/Predicate.js +2 -0
  13. package/dist/StringReader.d.ts +26 -0
  14. package/dist/StringReader.js +188 -0
  15. package/dist/arguments/ArgumentType.d.ts +5 -0
  16. package/dist/arguments/ArgumentType.js +13 -0
  17. package/dist/arguments/BoolArgumentType.d.ts +6 -0
  18. package/dist/arguments/BoolArgumentType.js +43 -0
  19. package/dist/arguments/FloatArgumentType.d.ts +7 -0
  20. package/dist/arguments/FloatArgumentType.js +38 -0
  21. package/dist/arguments/IntegerArgumentType.d.ts +7 -0
  22. package/dist/arguments/IntegerArgumentType.js +38 -0
  23. package/dist/arguments/LongArgumentType.d.ts +9 -0
  24. package/dist/arguments/LongArgumentType.js +40 -0
  25. package/dist/arguments/NumberArgumentType.d.ts +12 -0
  26. package/dist/arguments/NumberArgumentType.js +49 -0
  27. package/dist/arguments/StringArgumentType.d.ts +12 -0
  28. package/dist/arguments/StringArgumentType.js +57 -0
  29. package/dist/builder/ArgumentBuilder.d.ts +25 -0
  30. package/dist/builder/ArgumentBuilder.js +95 -0
  31. package/dist/builder/LiteralArgumentBuilder.d.ts +9 -0
  32. package/dist/builder/LiteralArgumentBuilder.js +47 -0
  33. package/dist/builder/RequiredArgumentBuilder.d.ts +11 -0
  34. package/dist/builder/RequiredArgumentBuilder.js +51 -0
  35. package/dist/context/CommandContext.d.ts +27 -0
  36. package/dist/context/CommandContext.js +67 -0
  37. package/dist/context/CommandContextBuilder.d.ts +31 -0
  38. package/dist/context/CommandContextBuilder.js +118 -0
  39. package/dist/context/ParsedArgument.d.ts +8 -0
  40. package/dist/context/ParsedArgument.js +18 -0
  41. package/dist/context/ParsedCommandNode.d.ts +8 -0
  42. package/dist/context/ParsedCommandNode.js +17 -0
  43. package/dist/context/StringRange.d.ts +11 -0
  44. package/dist/context/StringRange.js +31 -0
  45. package/dist/context/SuggestionContext.d.ts +6 -0
  46. package/dist/context/SuggestionContext.js +11 -0
  47. package/dist/exceptions/CommandErrorType.d.ts +9 -0
  48. package/dist/exceptions/CommandErrorType.js +27 -0
  49. package/dist/exceptions/CommandSyntaxError.d.ts +28 -0
  50. package/dist/exceptions/CommandSyntaxError.js +61 -0
  51. package/dist/index.d.ts +30 -0
  52. package/dist/index.js +46 -0
  53. package/dist/suggestion/Suggestion.d.ts +12 -0
  54. package/dist/suggestion/Suggestion.js +49 -0
  55. package/dist/suggestion/Suggestions.d.ts +13 -0
  56. package/dist/suggestion/Suggestions.js +58 -0
  57. package/dist/suggestion/SuggestionsBuilder.d.ts +17 -0
  58. package/dist/suggestion/SuggestionsBuilder.js +46 -0
  59. package/dist/tree/ArgumentCommandNode.d.ts +11 -0
  60. package/dist/tree/ArgumentCommandNode.js +49 -0
  61. package/dist/tree/CommandNode.d.ts +25 -0
  62. package/dist/tree/CommandNode.js +74 -0
  63. package/dist/tree/LiteralCommandNode.d.ts +10 -0
  64. package/dist/tree/LiteralCommandNode.js +68 -0
  65. package/dist/tree/RootCommandNode.d.ts +8 -0
  66. package/dist/tree/RootCommandNode.js +77 -0
  67. package/dist/tsconfig.tsbuildinfo +1 -0
  68. package/jest.config.js +12 -0
  69. package/package.json +34 -0
  70. package/src/Command.ts +3 -0
  71. package/src/CommandDispatcher.ts +300 -0
  72. package/src/ParseResults.ts +30 -0
  73. package/src/Predicate.ts +1 -0
  74. package/src/StringReader.ts +197 -0
  75. package/src/arguments/ArgumentType.ts +14 -0
  76. package/src/arguments/BoolArgumentType.ts +28 -0
  77. package/src/arguments/FloatArgumentType.ts +20 -0
  78. package/src/arguments/IntegerArgumentType.ts +20 -0
  79. package/src/arguments/LongArgumentType.ts +23 -0
  80. package/src/arguments/NumberArgumentType.ts +39 -0
  81. package/src/arguments/StringArgumentType.ts +40 -0
  82. package/src/builder/ArgumentBuilder.ts +82 -0
  83. package/src/builder/LiteralArgumentBuilder.ts +30 -0
  84. package/src/builder/RequiredArgumentBuilder.ts +40 -0
  85. package/src/context/CommandContext.ts +94 -0
  86. package/src/context/CommandContextBuilder.ts +148 -0
  87. package/src/context/ParsedArgument.ts +19 -0
  88. package/src/context/ParsedCommandNode.ts +19 -0
  89. package/src/context/StringRange.ts +35 -0
  90. package/src/context/SuggestionContext.ts +11 -0
  91. package/src/exceptions/CommandErrorType.ts +20 -0
  92. package/src/exceptions/CommandSyntaxError.ts +48 -0
  93. package/src/index.ts +30 -0
  94. package/src/suggestion/Suggestion.ts +55 -0
  95. package/src/suggestion/Suggestions.ts +60 -0
  96. package/src/suggestion/SuggestionsBuilder.ts +60 -0
  97. package/src/tree/ArgumentCommandNode.ts +48 -0
  98. package/src/tree/CommandNode.ts +105 -0
  99. package/src/tree/LiteralCommandNode.ts +64 -0
  100. package/src/tree/RootCommandNode.ts +30 -0
  101. package/test/Arguments.test.ts +36 -0
  102. package/test/CommandDispatcher.test.ts +25 -0
  103. package/test/StringReader.test.ts +47 -0
  104. package/tsconfig.json +16 -0
@@ -0,0 +1,48 @@
1
+ import { CommandErrorType } from "..";
2
+
3
+ const CONTEXT_AMOUNT = 10;
4
+
5
+ export class CommandSyntaxError extends Error {
6
+ private input: string;
7
+ private cursor: number;
8
+
9
+ constructor(message: string, input?: string, cursor?: number) {
10
+ super(message);
11
+ Object.setPrototypeOf(this, CommandSyntaxError.prototype);
12
+ this.input = input;
13
+ this.cursor = cursor;
14
+
15
+ if (input && cursor >= 0) {
16
+ this.message += ` at position ${cursor}: `;
17
+ const cursor2 = Math.min(this.input.length, this.cursor);
18
+ this.message += cursor > CONTEXT_AMOUNT ? "..." : "";
19
+ this.message += this.input.substring(Math.max(0, cursor2 - CONTEXT_AMOUNT), cursor2);
20
+ this.message += "<--[HERE]";
21
+ }
22
+ }
23
+
24
+ static DOUBLE_TOO_SMALL = new CommandErrorType((found, min) => `Double must not be less than ${min}, found ${found}`);
25
+ static DOUBLE_TOO_BIG = new CommandErrorType((found, max) => `Double must not be more than ${max}, found ${found}`);
26
+ static FLOAT_TOO_SMALL = new CommandErrorType((found, min) => `Float must not be less than ${min}, found ${found}`);
27
+ static FLOAT_TOO_BIG = new CommandErrorType((found, max) => `Float must not be more than ${max}, found ${found}`);
28
+ static INTEGER_TOO_SMALL = new CommandErrorType((found, min) => `Integer must not be less than ${min}, found ${found}`);
29
+ static INTEGER_TOO_BIG = new CommandErrorType((found, max) => `Integer must not be more than ${max}, found ${found}`);
30
+ static LONG_TOO_SMALL = new CommandErrorType((found, min) => `Long must not be less than ${min}, found ${found}`);
31
+ static LONG_TOO_BIG = new CommandErrorType((found, max) => `Long must not be more than ${max}, found ${found}`);
32
+ static LITERAL_INCORRECT = new CommandErrorType((expected) => `Expected literal ${expected}`);
33
+
34
+ static READER_EXPECTED_START_OF_QUOTE = new CommandErrorType(() => `Expected quote to start a string`);
35
+ static READER_EXPECTED_END_OF_QUOTE = new CommandErrorType(() => `Unclosed quoted string`);
36
+ static READER_INVALID_ESCAPE = new CommandErrorType((character) => `Invalid escape sequence '${character}' in quoted string`);
37
+ static READER_INVALID_BOOL = new CommandErrorType((value) => `Invalid bool, expected true or false but found '${value}'`);
38
+ static READER_EXPECTED_BOOL = new CommandErrorType(() => `Expected bool`);
39
+ static READER_INVALID_INT = new CommandErrorType((value) => `Invalid integer '${value}'`);
40
+ static READER_EXPECTED_INT = new CommandErrorType(() => `Expected integer`);
41
+ static READER_INVALID_FLOAT = new CommandErrorType((value) => `Invalid float '${value}'`);
42
+ static READER_EXPECTED_FLOAT = new CommandErrorType(() => `Expected float`);
43
+
44
+ static DISPATCHER_UNKNOWN_COMMAND = new CommandErrorType(() => `Unknown Command`);
45
+ static DISPATCHER_UNKNOWN_ARGUMENT = new CommandErrorType(() => `Incorrect argument for command`);
46
+ static DISPATCHER_EXPECTED_ARGUMENT_SEPARATOR = new CommandErrorType(() => `Expected whitespace to end one argument, but found trailing data`);
47
+ static DISPATCHER_PARSE_ERROR = new CommandErrorType((message) => `Could not parse command: ${message}`);
48
+ }
package/src/index.ts ADDED
@@ -0,0 +1,30 @@
1
+ export * from "./Command"
2
+ export * from "./Predicate"
3
+ export * from "./context/StringRange"
4
+ export * from "./exceptions/CommandErrorType"
5
+ export * from "./exceptions/CommandSyntaxError"
6
+ export * from "./StringReader"
7
+ export * from "./suggestion/Suggestion"
8
+ export * from "./suggestion/Suggestions"
9
+ export * from "./suggestion/SuggestionsBuilder"
10
+ export * from "./tree/CommandNode"
11
+ export * from "./tree/LiteralCommandNode"
12
+ export * from "./tree/ArgumentCommandNode"
13
+ export * from "./tree/RootCommandNode"
14
+ export * from "./arguments/ArgumentType"
15
+ export * from "./arguments/NumberArgumentType"
16
+ export * from "./arguments/FloatArgumentType"
17
+ export * from "./arguments/IntegerArgumentType"
18
+ export * from "./arguments/LongArgumentType"
19
+ export * from "./arguments/BoolArgumentType"
20
+ export * from "./arguments/StringArgumentType"
21
+ export * from "./builder/ArgumentBuilder"
22
+ export * from "./builder/LiteralArgumentBuilder"
23
+ export * from "./builder/RequiredArgumentBuilder"
24
+ export * from "./context/ParsedArgument"
25
+ export * from "./context/ParsedCommandNode"
26
+ export * from "./context/CommandContext"
27
+ export * from "./context/CommandContextBuilder"
28
+ export * from "./context/SuggestionContext"
29
+ export * from "./ParseResults"
30
+ export * from "./CommandDispatcher"
@@ -0,0 +1,55 @@
1
+ import { StringRange } from "..";
2
+
3
+ export class Suggestion {
4
+ private range: StringRange;
5
+ private text: string;
6
+ private tooltip: string;
7
+
8
+ constructor(range: StringRange, text: string, tooltip?: string) {
9
+ this.range = range;
10
+ this.text = text;
11
+ this.tooltip = tooltip;
12
+ }
13
+
14
+ getRange() {
15
+ return this.range;
16
+ }
17
+
18
+ getText() {
19
+ return this.text;
20
+ }
21
+
22
+ getTooltip() {
23
+ return this.tooltip;
24
+ }
25
+
26
+ apply(input: string): string {
27
+ if (this.range.getStart() == 0 && this.range.getEnd() === input.length) {
28
+ return this.text;
29
+ }
30
+ let result = "";
31
+ if (this.range.getStart() > 0) {
32
+ result += input.substring(0, this.range.getStart());
33
+ }
34
+ result += this.text;
35
+ if (this.range.getEnd() < input.length) {
36
+ result += input.substring(this.range.getEnd());
37
+ }
38
+ return result;
39
+ }
40
+
41
+ expand(command: string, range: StringRange): Suggestion {
42
+ if (range === this.range) {
43
+ return this;
44
+ }
45
+ let result = "";
46
+ if (range.getStart() < this.range.getStart()) {
47
+ result += command.substring(range.getStart(), this.range.getStart());
48
+ }
49
+ result += this.text;
50
+ if (range.getEnd() > this.range.getEnd()) {
51
+ result += command.substring(this.range.getEnd(), range.getEnd());
52
+ }
53
+ return new Suggestion(range, result, this.tooltip);
54
+ }
55
+ }
@@ -0,0 +1,60 @@
1
+ import { StringRange, Suggestion } from "..";
2
+
3
+ export class Suggestions {
4
+ static EMPTY = new Suggestions(StringRange.at(0), []);
5
+
6
+ private range: StringRange;
7
+ private suggestions: Suggestion[];
8
+
9
+ constructor(range: StringRange, suggestions: Suggestion[]) {
10
+ this.range = range;
11
+ this.suggestions = suggestions;
12
+ }
13
+
14
+ getRange(): StringRange {
15
+ return this.range;
16
+ }
17
+
18
+ getList(): Suggestion[] {
19
+ return this.suggestions;
20
+ }
21
+
22
+ isEmpty(): boolean {
23
+ return this.suggestions.length === 0;
24
+ }
25
+
26
+ static empty(): Promise<Suggestions> {
27
+ return Promise.resolve(Suggestions.EMPTY);
28
+ }
29
+
30
+ static merge(command: string, input: Suggestions[]): Suggestions {
31
+ if (input.length === 0) {
32
+ return Suggestions.EMPTY;
33
+ } else if (input.length === 1) {
34
+ return input[0];
35
+ }
36
+ const texts = new Set<Suggestion>();
37
+ for (const suggestions of input) {
38
+ suggestions.getList().forEach(s => texts.add(s))
39
+ }
40
+ return Suggestions.create(command, Array.from(texts));
41
+ }
42
+
43
+ static create(command: string, suggestions: Suggestion[]): Suggestions {
44
+ if (suggestions.length === 0) {
45
+ return Suggestions.EMPTY;
46
+ }
47
+ let start = Infinity;
48
+ let end = -Infinity;
49
+ for (const suggestion of suggestions) {
50
+ start = Math.min(suggestion.getRange().getStart(), start);
51
+ end = Math.max(suggestion.getRange().getEnd(), end);
52
+ }
53
+ const range = new StringRange(start, end);
54
+ const texts = [];
55
+ for (const suggestion of suggestions) {
56
+ texts.push(suggestion.expand(command, range));
57
+ }
58
+ return new Suggestions(range, texts.sort());
59
+ }
60
+ }
@@ -0,0 +1,60 @@
1
+ import {
2
+ Suggestion,
3
+ Suggestions,
4
+ StringRange
5
+ } from "..";
6
+
7
+ export class SuggestionsBuilder {
8
+ private input: string;
9
+ private start: number;
10
+ private remaining: string;
11
+ private result: Suggestion[];
12
+
13
+ constructor(input: string, start: number) {
14
+ this.input = input;
15
+ this.start = start;
16
+ this.remaining = input.substring(start);
17
+ this.result = [];
18
+ }
19
+
20
+ getInput(): string {
21
+ return this.input;
22
+ }
23
+
24
+ getStart(): number {
25
+ return this.start;
26
+ }
27
+
28
+ getRemaining(): string {
29
+ return this.remaining;
30
+ }
31
+
32
+ build(): Suggestions {
33
+ return Suggestions.create(this.input, this.result);
34
+ }
35
+
36
+ buildPromise(): Promise<Suggestions> {
37
+ return Promise.resolve(this.build());
38
+ }
39
+
40
+ suggest(text: string, tooltip?: string): SuggestionsBuilder {
41
+ if (text === this.remaining) {
42
+ return this;
43
+ }
44
+ this.result.push(new Suggestion(new StringRange(this.start, this.input.length), text, tooltip))
45
+ return this;
46
+ }
47
+
48
+ add(other: SuggestionsBuilder): SuggestionsBuilder {
49
+ this.result.concat(other.result);
50
+ return this;
51
+ }
52
+
53
+ createOffset(start: number): SuggestionsBuilder {
54
+ return new SuggestionsBuilder(this.input, start);
55
+ }
56
+
57
+ restart(start: number): SuggestionsBuilder {
58
+ return new SuggestionsBuilder(this.input, this.start);
59
+ }
60
+ }
@@ -0,0 +1,48 @@
1
+ import {
2
+ ArgumentType,
3
+ CommandNode,
4
+ StringReader,
5
+ Command,
6
+ CommandContext,
7
+ CommandContextBuilder,
8
+ Predicate,
9
+ RedirectModifier,
10
+ ParsedArgument,
11
+ Suggestions,
12
+ SuggestionsBuilder
13
+ } from '..';
14
+
15
+ export class ArgumentCommandNode<S, T> extends CommandNode<S> {
16
+ name: string;
17
+ type: ArgumentType<T>;
18
+
19
+ constructor(name: string, type: ArgumentType<T>, command: Command<S>, requirement: Predicate<S>, redirect: CommandNode<S>, modifier: RedirectModifier<S>, forks: boolean) {
20
+ super(command, requirement, redirect, modifier, forks);
21
+ this.name = name;
22
+ this.type = type;
23
+ }
24
+
25
+ getType(): ArgumentType<T> {
26
+ return this.type;
27
+ }
28
+
29
+ parse(reader: StringReader, contextBuilder: CommandContextBuilder<S>): void {
30
+ const start = reader.getCursor();
31
+ const result = this.type.parse(reader);
32
+ const parsed = new ParsedArgument<T>(start, reader.getCursor(), result);
33
+ contextBuilder.withArgument(this.name, parsed);
34
+ contextBuilder.withNode(this, parsed.getRange());
35
+ }
36
+
37
+ getName(): string {
38
+ return this.name;
39
+ }
40
+
41
+ getUsageText(): string {
42
+ return "<" + this.name + ">";
43
+ }
44
+
45
+ listSuggestions(context: CommandContext<S>, builder: SuggestionsBuilder): Promise<Suggestions> {
46
+ return Suggestions.empty();
47
+ }
48
+ }
@@ -0,0 +1,105 @@
1
+ import {
2
+ StringReader,
3
+ Command,
4
+ LiteralCommandNode,
5
+ ArgumentCommandNode,
6
+ CommandContext,
7
+ CommandContextBuilder,
8
+ Predicate,
9
+ RedirectModifier,
10
+ SuggestionsBuilder,
11
+ Suggestions
12
+ } from '..';
13
+
14
+ export abstract class CommandNode<S> {
15
+ private children: Map<string, CommandNode<S>>;
16
+ private literals: Map<string, LiteralCommandNode<S>>;
17
+ private arguments: Map<string, ArgumentCommandNode<S, any>>;
18
+ private command: Command<S>;
19
+ private requirement: Predicate<S>;
20
+ private redirect: CommandNode<S>;
21
+ private modifier: RedirectModifier<S>;
22
+ private forks: boolean;
23
+
24
+ constructor(command: Command<S>, requirement: Predicate<S>, redirect: CommandNode<S>, modifier: RedirectModifier<S>, forks: boolean) {
25
+ this.children = new Map();
26
+ this.literals = new Map();
27
+ this.arguments = new Map();
28
+ this.command = command;
29
+ this.requirement = requirement;
30
+ this.redirect = redirect;
31
+ this.modifier = modifier;
32
+ this.forks = forks;
33
+ }
34
+
35
+ getCommand(): Command<S> {
36
+ return this.command;
37
+ }
38
+
39
+ getChildren(): CommandNode<S>[] {
40
+ return Array.from(this.children.values());
41
+ }
42
+
43
+ getChild(name: string): CommandNode<S> {
44
+ return this.children.get(name);
45
+ }
46
+
47
+ getRedirect(): CommandNode<S> {
48
+ return this.redirect;
49
+ }
50
+
51
+ getRedirectModifier(): RedirectModifier<S> {
52
+ return this.modifier;
53
+ }
54
+
55
+ isFork(): boolean {
56
+ return this.forks;
57
+ }
58
+
59
+ canUse(source: S) {
60
+ return this.requirement(source);
61
+ }
62
+
63
+ addChild(node: CommandNode<S>): void {
64
+ const child = this.children.get(node.getName());
65
+ if (child != null) {
66
+ if (node.getCommand() != null) {
67
+ child.command = node.getCommand();
68
+ }
69
+ node.getChildren().forEach((grandChild) => {
70
+ child.addChild(grandChild);
71
+ });
72
+ } else {
73
+ this.children.set(node.getName(), node);
74
+ if (node instanceof LiteralCommandNode) {
75
+ this.literals.set(node.getName(), node);
76
+ } else if (node instanceof ArgumentCommandNode) {
77
+ this.arguments.set(node.getName(), node);
78
+ }
79
+ }
80
+ }
81
+
82
+ abstract parse(reader: StringReader, context: CommandContextBuilder<S>): void;
83
+
84
+ abstract getName(): string;
85
+
86
+ abstract getUsageText(): string;
87
+
88
+ abstract listSuggestions(context: CommandContext<S>, builder: SuggestionsBuilder): Promise<Suggestions>;
89
+
90
+ getRelevantNodes(input: StringReader): CommandNode<S>[] {
91
+ if (this.literals.size > 0) {
92
+ const cursor = input.getCursor();
93
+ while (input.canRead() && input.peek() != " ") {
94
+ input.skip();
95
+ }
96
+ const text = input.getString().substring(cursor, input.getCursor());
97
+ input.setCursor(cursor);
98
+ const literal = this.literals.get(text);
99
+ if (literal != null) {
100
+ return [literal];
101
+ }
102
+ }
103
+ return Array.from(this.arguments.values());
104
+ }
105
+ }
@@ -0,0 +1,64 @@
1
+ import {
2
+ CommandNode,
3
+ StringReader,
4
+ Command,
5
+ StringRange,
6
+ CommandContext,
7
+ CommandContextBuilder,
8
+ Predicate,
9
+ RedirectModifier,
10
+ CommandSyntaxError,
11
+ Suggestions,
12
+ SuggestionsBuilder
13
+ } from '..';
14
+
15
+ export class LiteralCommandNode<S> extends CommandNode<S> {
16
+ private literal: string;
17
+
18
+ constructor(literal: string, command: Command<S>, requirement: Predicate<S>, redirect: CommandNode<S>, modifier: RedirectModifier<S>, forks: boolean) {
19
+ super(command, requirement, redirect, modifier, forks);
20
+ this.literal = literal;
21
+ }
22
+
23
+ parse(reader: StringReader, contextBuilder: CommandContextBuilder<S>): void {
24
+ const start = reader.getCursor();
25
+ const end = this.parseInternal(reader);
26
+ if (end > -1) {
27
+ contextBuilder.withNode(this, new StringRange(start, end));
28
+ return;
29
+ }
30
+ throw CommandSyntaxError.LITERAL_INCORRECT.createWithContext(reader, this.literal);
31
+ }
32
+
33
+ private parseInternal(reader: StringReader): number {
34
+ const start = reader.getCursor();
35
+ if (reader.canRead(this.literal.length)) {
36
+ const end = start + this.literal.length;
37
+ if (reader.getString().substr(start, this.literal.length) === this.literal) {
38
+ reader.setCursor(end);
39
+ if (!reader.canRead() || reader.peek() == " ") {
40
+ return end;
41
+ } else {
42
+ reader.setCursor(start);
43
+ }
44
+ }
45
+ }
46
+ return -1;
47
+ }
48
+
49
+ getName(): string {
50
+ return this.literal;
51
+ }
52
+
53
+ getUsageText(): string {
54
+ return this.literal;
55
+ }
56
+
57
+ listSuggestions(context: CommandContext<S>, builder: SuggestionsBuilder): Promise<Suggestions> {
58
+ if (this.literal.toLowerCase().startsWith(builder.getRemaining().toLowerCase())) {
59
+ return builder.suggest(this.literal).buildPromise();
60
+ } else {
61
+ return Suggestions.empty();
62
+ }
63
+ }
64
+ }
@@ -0,0 +1,30 @@
1
+ import {
2
+ CommandNode,
3
+ StringReader,
4
+ CommandContextBuilder,
5
+ CommandContext,
6
+ Suggestions,
7
+ SuggestionsBuilder
8
+ } from '..';
9
+
10
+ export class RootCommandNode<S> extends CommandNode<S> {
11
+
12
+ constructor() {
13
+ super(null, async c => true, null, c => null, false);
14
+ }
15
+
16
+ parse(reader: StringReader, contextBuilder: CommandContextBuilder<S>): void {
17
+ }
18
+
19
+ getName(): string {
20
+ return "";
21
+ }
22
+
23
+ getUsageText(): string {
24
+ return "";
25
+ }
26
+
27
+ listSuggestions(context: CommandContext<S>, builder: SuggestionsBuilder): Promise<Suggestions> {
28
+ return Suggestions.empty();
29
+ }
30
+ }
@@ -0,0 +1,36 @@
1
+ import { argument, CommandDispatcher, FloatArgumentType, IntegerArgumentType, literal, LongArgumentType } from '../src'
2
+
3
+ describe('Arguments', () => {
4
+ test('Integer', async () => {
5
+ const dispatcher = new CommandDispatcher()
6
+ dispatcher.register(literal('foo')
7
+ .then(argument('bar', new IntegerArgumentType())
8
+ .executes(async ctx => ctx.get('bar') * 2)
9
+ )
10
+ )
11
+ const result = await dispatcher.execute('foo 6', undefined)
12
+ expect(result).toEqual(12)
13
+ })
14
+
15
+ test('Long', async () => {
16
+ const dispatcher = new CommandDispatcher()
17
+ dispatcher.register(literal('foo')
18
+ .then(argument('bar', new LongArgumentType())
19
+ .executes(async ctx => ctx.get('bar').toString().length)
20
+ )
21
+ )
22
+ const result = await dispatcher.execute('foo 123456789012345', undefined)
23
+ expect(result).toEqual(15)
24
+ })
25
+
26
+ test('Float', async () => {
27
+ const dispatcher = new CommandDispatcher()
28
+ dispatcher.register(literal('foo')
29
+ .then(argument('bar', new FloatArgumentType())
30
+ .executes(async ctx => Math.floor(ctx.get('bar')))
31
+ )
32
+ )
33
+ const result = await dispatcher.execute('foo 6.2', undefined)
34
+ expect(result).toEqual(6)
35
+ })
36
+ })
@@ -0,0 +1,25 @@
1
+ import { CommandDispatcher, literal } from '../src'
2
+
3
+ describe('CommandDispatcher', () => {
4
+ test('create', () => {
5
+ const dispatcher = new CommandDispatcher()
6
+ })
7
+
8
+ test('execute', async () => {
9
+ const dispatcher = new CommandDispatcher()
10
+ dispatcher.register(literal('foo')
11
+ .executes(async () => 2)
12
+ )
13
+ const result = await dispatcher.execute('foo', undefined)
14
+ expect(result).toEqual(2)
15
+ })
16
+
17
+ test('execute (zero result)', async () => {
18
+ const dispatcher = new CommandDispatcher()
19
+ dispatcher.register(literal('foo')
20
+ .executes(async () => 0)
21
+ )
22
+ const result = await dispatcher.execute('foo', undefined)
23
+ expect(result).toEqual(0)
24
+ })
25
+ })
@@ -0,0 +1,47 @@
1
+ import { StringReader } from '../src/StringReader'
2
+
3
+ describe('StringReader', () => {
4
+ test('readInt', () => {
5
+ const reader = new StringReader('5')
6
+ const value = reader.readInt()
7
+ expect(value).toEqual(5)
8
+ })
9
+
10
+ test('readInt (trailing)', () => {
11
+ const reader = new StringReader('5a')
12
+ const value = reader.readInt()
13
+ expect(value).toEqual(5)
14
+ expect(reader.getCursor()).toEqual(1)
15
+ })
16
+
17
+ test('readInt (NaN)', () => {
18
+ const reader = new StringReader('5-')
19
+ expect(() => reader.readInt()).toThrow('Invalid integer')
20
+ })
21
+
22
+ test('readInt (invalid)', () => {
23
+ const reader = new StringReader('a')
24
+ expect(() => reader.readInt()).toThrow('Expected integer')
25
+ })
26
+
27
+ test('readInt (float)', () => {
28
+ const reader = new StringReader('1.3')
29
+ expect(() => reader.readInt()).toThrow('Invalid integer')
30
+ })
31
+
32
+ test('readFloat', () => {
33
+ const reader = new StringReader('1.3')
34
+ const value = reader.readFloat()
35
+ expect(value).toEqual(1.3)
36
+ })
37
+
38
+ test('readFloat (NaN)', () => {
39
+ const reader = new StringReader('1.3-')
40
+ expect(() => reader.readFloat()).toThrow('Invalid float')
41
+ })
42
+
43
+ test('readFloat (invalid)', () => {
44
+ const reader = new StringReader('sw')
45
+ expect(() => reader.readFloat()).toThrow('Expected float')
46
+ })
47
+ })
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es5",
4
+ "module": "commonjs",
5
+ "lib": [ "es2015", "dom" ],
6
+ "declaration": true,
7
+ "outDir": "dist"
8
+ },
9
+ "include": [
10
+ "src"
11
+ ],
12
+ "exclude": [
13
+ "node_modules",
14
+ "dist"
15
+ ]
16
+ }