@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.
- package/.github/workflows/publish.yml +49 -0
- package/.github/workflows/test.yml +23 -0
- package/LICENSE +21 -0
- package/README.md +131 -0
- package/dist/Command.d.ts +2 -0
- package/dist/Command.js +2 -0
- package/dist/CommandDispatcher.d.ts +20 -0
- package/dist/CommandDispatcher.js +416 -0
- package/dist/ParseResults.d.ts +10 -0
- package/dist/ParseResults.js +21 -0
- package/dist/Predicate.d.ts +1 -0
- package/dist/Predicate.js +2 -0
- package/dist/StringReader.d.ts +26 -0
- package/dist/StringReader.js +188 -0
- package/dist/arguments/ArgumentType.d.ts +5 -0
- package/dist/arguments/ArgumentType.js +13 -0
- package/dist/arguments/BoolArgumentType.d.ts +6 -0
- package/dist/arguments/BoolArgumentType.js +43 -0
- package/dist/arguments/FloatArgumentType.d.ts +7 -0
- package/dist/arguments/FloatArgumentType.js +38 -0
- package/dist/arguments/IntegerArgumentType.d.ts +7 -0
- package/dist/arguments/IntegerArgumentType.js +38 -0
- package/dist/arguments/LongArgumentType.d.ts +9 -0
- package/dist/arguments/LongArgumentType.js +40 -0
- package/dist/arguments/NumberArgumentType.d.ts +12 -0
- package/dist/arguments/NumberArgumentType.js +49 -0
- package/dist/arguments/StringArgumentType.d.ts +12 -0
- package/dist/arguments/StringArgumentType.js +57 -0
- package/dist/builder/ArgumentBuilder.d.ts +25 -0
- package/dist/builder/ArgumentBuilder.js +95 -0
- package/dist/builder/LiteralArgumentBuilder.d.ts +9 -0
- package/dist/builder/LiteralArgumentBuilder.js +47 -0
- package/dist/builder/RequiredArgumentBuilder.d.ts +11 -0
- package/dist/builder/RequiredArgumentBuilder.js +51 -0
- package/dist/context/CommandContext.d.ts +27 -0
- package/dist/context/CommandContext.js +67 -0
- package/dist/context/CommandContextBuilder.d.ts +31 -0
- package/dist/context/CommandContextBuilder.js +118 -0
- package/dist/context/ParsedArgument.d.ts +8 -0
- package/dist/context/ParsedArgument.js +18 -0
- package/dist/context/ParsedCommandNode.d.ts +8 -0
- package/dist/context/ParsedCommandNode.js +17 -0
- package/dist/context/StringRange.d.ts +11 -0
- package/dist/context/StringRange.js +31 -0
- package/dist/context/SuggestionContext.d.ts +6 -0
- package/dist/context/SuggestionContext.js +11 -0
- package/dist/exceptions/CommandErrorType.d.ts +9 -0
- package/dist/exceptions/CommandErrorType.js +27 -0
- package/dist/exceptions/CommandSyntaxError.d.ts +28 -0
- package/dist/exceptions/CommandSyntaxError.js +61 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +46 -0
- package/dist/suggestion/Suggestion.d.ts +12 -0
- package/dist/suggestion/Suggestion.js +49 -0
- package/dist/suggestion/Suggestions.d.ts +13 -0
- package/dist/suggestion/Suggestions.js +58 -0
- package/dist/suggestion/SuggestionsBuilder.d.ts +17 -0
- package/dist/suggestion/SuggestionsBuilder.js +46 -0
- package/dist/tree/ArgumentCommandNode.d.ts +11 -0
- package/dist/tree/ArgumentCommandNode.js +49 -0
- package/dist/tree/CommandNode.d.ts +25 -0
- package/dist/tree/CommandNode.js +74 -0
- package/dist/tree/LiteralCommandNode.d.ts +10 -0
- package/dist/tree/LiteralCommandNode.js +68 -0
- package/dist/tree/RootCommandNode.d.ts +8 -0
- package/dist/tree/RootCommandNode.js +77 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/jest.config.js +12 -0
- package/package.json +34 -0
- package/src/Command.ts +3 -0
- package/src/CommandDispatcher.ts +300 -0
- package/src/ParseResults.ts +30 -0
- package/src/Predicate.ts +1 -0
- package/src/StringReader.ts +197 -0
- package/src/arguments/ArgumentType.ts +14 -0
- package/src/arguments/BoolArgumentType.ts +28 -0
- package/src/arguments/FloatArgumentType.ts +20 -0
- package/src/arguments/IntegerArgumentType.ts +20 -0
- package/src/arguments/LongArgumentType.ts +23 -0
- package/src/arguments/NumberArgumentType.ts +39 -0
- package/src/arguments/StringArgumentType.ts +40 -0
- package/src/builder/ArgumentBuilder.ts +82 -0
- package/src/builder/LiteralArgumentBuilder.ts +30 -0
- package/src/builder/RequiredArgumentBuilder.ts +40 -0
- package/src/context/CommandContext.ts +94 -0
- package/src/context/CommandContextBuilder.ts +148 -0
- package/src/context/ParsedArgument.ts +19 -0
- package/src/context/ParsedCommandNode.ts +19 -0
- package/src/context/StringRange.ts +35 -0
- package/src/context/SuggestionContext.ts +11 -0
- package/src/exceptions/CommandErrorType.ts +20 -0
- package/src/exceptions/CommandSyntaxError.ts +48 -0
- package/src/index.ts +30 -0
- package/src/suggestion/Suggestion.ts +55 -0
- package/src/suggestion/Suggestions.ts +60 -0
- package/src/suggestion/SuggestionsBuilder.ts +60 -0
- package/src/tree/ArgumentCommandNode.ts +48 -0
- package/src/tree/CommandNode.ts +105 -0
- package/src/tree/LiteralCommandNode.ts +64 -0
- package/src/tree/RootCommandNode.ts +30 -0
- package/test/Arguments.test.ts +36 -0
- package/test/CommandDispatcher.test.ts +25 -0
- package/test/StringReader.test.ts +47 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ArgumentType,
|
|
3
|
+
StringReader,
|
|
4
|
+
CommandContext,
|
|
5
|
+
SuggestionsBuilder,
|
|
6
|
+
Suggestions
|
|
7
|
+
} from "..";
|
|
8
|
+
|
|
9
|
+
export class BoolArgumentType extends ArgumentType<boolean> {
|
|
10
|
+
|
|
11
|
+
parse(reader: StringReader): boolean {
|
|
12
|
+
return reader.readBoolean();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
listSuggestions(context: CommandContext<any>, builder: SuggestionsBuilder): Promise<Suggestions> {
|
|
16
|
+
if ("true".startsWith(builder.getRemaining().toLowerCase())) {
|
|
17
|
+
builder.suggest("true");
|
|
18
|
+
}
|
|
19
|
+
if ("false".startsWith(builder.getRemaining().toLowerCase())) {
|
|
20
|
+
builder.suggest("false");
|
|
21
|
+
}
|
|
22
|
+
return builder.buildPromise();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function bool(): BoolArgumentType {
|
|
27
|
+
return new BoolArgumentType();
|
|
28
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { StringReader, NumberArgumentType, CommandSyntaxError } from "..";
|
|
2
|
+
|
|
3
|
+
export class FloatArgumentType extends NumberArgumentType {
|
|
4
|
+
|
|
5
|
+
constructor(minimum = -Infinity, maximum = Infinity) {
|
|
6
|
+
super(minimum, maximum);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
readNumber(reader: StringReader): number {
|
|
10
|
+
return reader.readFloat();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
getTooSmallError() {
|
|
14
|
+
return CommandSyntaxError.FLOAT_TOO_SMALL;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
getTooBigError() {
|
|
18
|
+
return CommandSyntaxError.FLOAT_TOO_BIG;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { StringReader, NumberArgumentType, CommandSyntaxError } from "..";
|
|
2
|
+
|
|
3
|
+
export class IntegerArgumentType extends NumberArgumentType {
|
|
4
|
+
|
|
5
|
+
constructor(minimum = -2147483648, maximum = 2147483647) {
|
|
6
|
+
super(minimum, maximum);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
readNumber(reader: StringReader): number {
|
|
10
|
+
return reader.readInt();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
getTooSmallError() {
|
|
14
|
+
return CommandSyntaxError.INTEGER_TOO_SMALL;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
getTooBigError() {
|
|
18
|
+
return CommandSyntaxError.INTEGER_TOO_BIG;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { StringReader, NumberArgumentType, CommandSyntaxError } from "..";
|
|
2
|
+
|
|
3
|
+
export class LongArgumentType extends NumberArgumentType<BigInt> {
|
|
4
|
+
|
|
5
|
+
private static readonly MIN = BigInt("-9223372036854775808")
|
|
6
|
+
private static readonly MAX = BigInt("9223372036854775807")
|
|
7
|
+
|
|
8
|
+
constructor(minimum = LongArgumentType.MIN, maximum = LongArgumentType.MAX) {
|
|
9
|
+
super(minimum, maximum);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
readNumber(reader: StringReader): BigInt {
|
|
13
|
+
return reader.readLong();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
getTooSmallError() {
|
|
17
|
+
return CommandSyntaxError.LONG_TOO_SMALL;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getTooBigError() {
|
|
21
|
+
return CommandSyntaxError.LONG_TOO_BIG;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ArgumentType, StringReader, CommandErrorType } from "..";
|
|
2
|
+
|
|
3
|
+
export abstract class NumberArgumentType<N extends number | BigInt = number> extends ArgumentType<N> {
|
|
4
|
+
private minimum: N;
|
|
5
|
+
private maximum: N;
|
|
6
|
+
|
|
7
|
+
constructor(minimum: N, maximum: N) {
|
|
8
|
+
super();
|
|
9
|
+
this.minimum = minimum;
|
|
10
|
+
this.maximum = maximum;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
getMinimum(): N {
|
|
14
|
+
return this.minimum;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
getMaximum(): N {
|
|
18
|
+
return this.maximum;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
parse(reader: StringReader): N {
|
|
22
|
+
const start = reader.getCursor();
|
|
23
|
+
const result = this.readNumber(reader);
|
|
24
|
+
if (result < this.minimum) {
|
|
25
|
+
reader.setCursor(start);
|
|
26
|
+
throw this.getTooSmallError().createWithContext(reader, result, this.minimum);
|
|
27
|
+
} else if (result > this.maximum) {
|
|
28
|
+
reader.setCursor(start);
|
|
29
|
+
throw this.getTooBigError().createWithContext(reader, result, this.maximum);
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
abstract readNumber(reader: StringReader): N;
|
|
35
|
+
|
|
36
|
+
abstract getTooSmallError(): CommandErrorType;
|
|
37
|
+
|
|
38
|
+
abstract getTooBigError(): CommandErrorType;
|
|
39
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ArgumentType, StringReader } from "..";
|
|
2
|
+
|
|
3
|
+
type StringType = "single_word" | "quotable_phrase" | "greedy_phrase";
|
|
4
|
+
|
|
5
|
+
export class StringArgumentType extends ArgumentType<string> {
|
|
6
|
+
private type: StringType;
|
|
7
|
+
|
|
8
|
+
constructor(type: StringType) {
|
|
9
|
+
super();
|
|
10
|
+
this.type = type;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
getType(): StringType {
|
|
14
|
+
return this.type;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
parse(reader: StringReader): string {
|
|
18
|
+
if (this.type === "greedy_phrase") {
|
|
19
|
+
const text = reader.getRemaining();
|
|
20
|
+
reader.setCursor(reader.getTotalLength());
|
|
21
|
+
return text;
|
|
22
|
+
} else if (this.type === "single_word") {
|
|
23
|
+
return reader.readUnquotedString();
|
|
24
|
+
} else {
|
|
25
|
+
return reader.readString();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function word(): StringArgumentType {
|
|
31
|
+
return new StringArgumentType("single_word");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function string(): StringArgumentType {
|
|
35
|
+
return new StringArgumentType("quotable_phrase");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function greedyString(): StringArgumentType {
|
|
39
|
+
return new StringArgumentType("greedy_phrase");
|
|
40
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CommandNode,
|
|
3
|
+
RootCommandNode,
|
|
4
|
+
Command,
|
|
5
|
+
Predicate,
|
|
6
|
+
CommandContext
|
|
7
|
+
} from "..";
|
|
8
|
+
|
|
9
|
+
export type RedirectModifier<S> = (context: CommandContext<S>) => S | S[];
|
|
10
|
+
|
|
11
|
+
export abstract class ArgumentBuilder<S, T extends ArgumentBuilder<S, T>> {
|
|
12
|
+
private arguments: RootCommandNode<S>;
|
|
13
|
+
private command: Command<S>;
|
|
14
|
+
private requirement: Predicate<S>;
|
|
15
|
+
private target: CommandNode<S>;
|
|
16
|
+
private modifier: RedirectModifier<S>;
|
|
17
|
+
private forks: boolean;
|
|
18
|
+
|
|
19
|
+
constructor() {
|
|
20
|
+
this.arguments = new RootCommandNode();
|
|
21
|
+
this.requirement = async s => true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
abstract getThis(): T;
|
|
25
|
+
|
|
26
|
+
then(argument: ArgumentBuilder<S, any> | CommandNode<S>): T {
|
|
27
|
+
const child = argument instanceof CommandNode ? argument : argument.build();
|
|
28
|
+
this.arguments.addChild(child);
|
|
29
|
+
return this.getThis();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
executes(command: Command<S>): T {
|
|
33
|
+
this.command = command;
|
|
34
|
+
return this.getThis();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
requires(requirement: Predicate<S>): T {
|
|
38
|
+
this.requirement = requirement;
|
|
39
|
+
return this.getThis();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
redirect(target: CommandNode<S>, modifier: RedirectModifier<S> = null): T {
|
|
43
|
+
return this.forward(target, modifier, false)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
fork(target: CommandNode<S>, modifier: RedirectModifier<S>): T {
|
|
47
|
+
return this.forward(target, modifier, true);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
forward(target: CommandNode<S>, modifier: RedirectModifier<S>, forks: boolean): T {
|
|
51
|
+
this.target = target;
|
|
52
|
+
this.modifier = modifier;
|
|
53
|
+
this.forks = forks;
|
|
54
|
+
return this.getThis();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
getArguments(): CommandNode<S>[] {
|
|
58
|
+
return this.arguments.getChildren();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
getCommand(): Command<S> {
|
|
62
|
+
return this.command;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
getRequirement(): Predicate<S> {
|
|
66
|
+
return this.requirement;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
getRedirect(): CommandNode<S> {
|
|
70
|
+
return this.target;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
getRedirectModifier(): RedirectModifier<S> {
|
|
74
|
+
return this.modifier;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
isFork(): boolean {
|
|
78
|
+
return this.forks;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
abstract build(): CommandNode<S>;
|
|
82
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ArgumentBuilder, LiteralCommandNode } from "..";
|
|
2
|
+
|
|
3
|
+
export class LiteralArgumentBuilder<S> extends ArgumentBuilder<S, LiteralArgumentBuilder<S>> {
|
|
4
|
+
private literal: string;
|
|
5
|
+
|
|
6
|
+
constructor(literal: string) {
|
|
7
|
+
super();
|
|
8
|
+
this.literal = literal
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
getThis(): LiteralArgumentBuilder<S> {
|
|
12
|
+
return this;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
getLiteral(): string {
|
|
16
|
+
return this.literal;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
build(): LiteralCommandNode<S> {
|
|
20
|
+
const result = new LiteralCommandNode<S>(this.getLiteral(), this.getCommand(), this.getRequirement(), this.getRedirect(), this.getRedirectModifier(), this.isFork());
|
|
21
|
+
for (const argument of this.getArguments()) {
|
|
22
|
+
result.addChild(argument);
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function literal<S = any>(name: string): LiteralArgumentBuilder<S> {
|
|
29
|
+
return new LiteralArgumentBuilder(name);
|
|
30
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ArgumentBuilder,
|
|
3
|
+
ArgumentType,
|
|
4
|
+
ArgumentCommandNode
|
|
5
|
+
} from "..";
|
|
6
|
+
|
|
7
|
+
export class RequiredArgumentBuilder<S, T> extends ArgumentBuilder<S, RequiredArgumentBuilder<S, T>> {
|
|
8
|
+
private name: string;
|
|
9
|
+
private type: ArgumentType<T>;
|
|
10
|
+
|
|
11
|
+
constructor(name: string, type: ArgumentType<T>) {
|
|
12
|
+
super();
|
|
13
|
+
this.name = name;
|
|
14
|
+
this.type = type;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
getThis(): RequiredArgumentBuilder<S, T> {
|
|
18
|
+
return this;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
getName(): string {
|
|
22
|
+
return this.name;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
getType(): ArgumentType<T> {
|
|
26
|
+
return this.type;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
build(): ArgumentCommandNode<S, T> {
|
|
30
|
+
const result = new ArgumentCommandNode(this.getName(), this.getType(), this.getCommand(), this.getRequirement(), this.getRedirect(), this.getRedirectModifier(), this.isFork());
|
|
31
|
+
for (const argument of this.getArguments()) {
|
|
32
|
+
result.addChild(argument);
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function argument<S = any, T = any>(name: string, type: ArgumentType<T>): RequiredArgumentBuilder<S, T> {
|
|
39
|
+
return new RequiredArgumentBuilder(name, type);
|
|
40
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Command,
|
|
2
|
+
CommandNode,
|
|
3
|
+
StringRange,
|
|
4
|
+
ParsedArgument,
|
|
5
|
+
ParsedCommandNode,
|
|
6
|
+
RedirectModifier
|
|
7
|
+
} from "..";
|
|
8
|
+
|
|
9
|
+
export class CommandContext<S> {
|
|
10
|
+
private source: S;
|
|
11
|
+
private input: string;
|
|
12
|
+
private arguments: Map<string, ParsedArgument<any>>;
|
|
13
|
+
private nodes: ParsedCommandNode<S>[];
|
|
14
|
+
private command: Command<S>;
|
|
15
|
+
private rootNode: CommandNode<S>;
|
|
16
|
+
private child: CommandContext<S>;
|
|
17
|
+
private range: StringRange;
|
|
18
|
+
private modifier: RedirectModifier<S>;
|
|
19
|
+
private forks: boolean;
|
|
20
|
+
|
|
21
|
+
constructor(source: S, input: string, parsedArguments: Map<string, ParsedArgument<any>>, command: Command<S>, rootNode: CommandNode<S>, nodes: ParsedCommandNode<S>[], range: StringRange, child: CommandContext<S>, modifier: RedirectModifier<S>, forks: boolean) {
|
|
22
|
+
this.source = source;
|
|
23
|
+
this.input = input;
|
|
24
|
+
this.arguments = parsedArguments;
|
|
25
|
+
this.command = command;
|
|
26
|
+
this.rootNode = rootNode;
|
|
27
|
+
this.nodes = nodes;
|
|
28
|
+
this.range = range;
|
|
29
|
+
this.child = child;
|
|
30
|
+
this.modifier = modifier;
|
|
31
|
+
this.forks = forks;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
copyFor(source: S): CommandContext<S> {
|
|
35
|
+
if (this.source === source) {
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
return new CommandContext<S>(source, this.input, this.arguments, this.command, this.rootNode, this.nodes, this.range, this.child, this.modifier, this.forks);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getChild(): CommandContext<S> {
|
|
42
|
+
return this.child;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
getLastChild(): CommandContext<S> {
|
|
46
|
+
let result: CommandContext<S> = this;
|
|
47
|
+
while (result.getChild() != null) {
|
|
48
|
+
result = result.getChild();
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
getCommand(): Command<S> {
|
|
54
|
+
return this.command;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
getSource(): S {
|
|
58
|
+
return this.source;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
getRootNode(): CommandNode<S> {
|
|
62
|
+
return this.rootNode;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
get(name: string): any {
|
|
66
|
+
const argument = this.arguments.get(name);
|
|
67
|
+
// TODO: Throw exception when argument is null
|
|
68
|
+
return argument.getResult();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
getRedirectModifier(): RedirectModifier<S> {
|
|
72
|
+
return this.modifier;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
getRange(): StringRange {
|
|
76
|
+
return this.range;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
getInput(): string {
|
|
80
|
+
return this.input;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
getNodes(): ParsedCommandNode<S>[] {
|
|
84
|
+
return this.nodes;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
hasNodes(): boolean {
|
|
88
|
+
return this.nodes.length !== 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
isForked(): boolean {
|
|
92
|
+
return this.forks;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CommandNode,
|
|
3
|
+
CommandDispatcher,
|
|
4
|
+
Command,
|
|
5
|
+
CommandContext,
|
|
6
|
+
StringRange,
|
|
7
|
+
ParsedCommandNode,
|
|
8
|
+
ParsedArgument,
|
|
9
|
+
RedirectModifier,
|
|
10
|
+
SuggestionContext
|
|
11
|
+
} from "..";
|
|
12
|
+
|
|
13
|
+
export class CommandContextBuilder<S> {
|
|
14
|
+
private source: S;
|
|
15
|
+
private arguments: Map<string, ParsedArgument<any>>;
|
|
16
|
+
private rootNode: CommandNode<S>;
|
|
17
|
+
private dispatcher: CommandDispatcher<S>;
|
|
18
|
+
private command: Command<S>;
|
|
19
|
+
private child: CommandContextBuilder<S>;
|
|
20
|
+
private range: StringRange;
|
|
21
|
+
private nodes: ParsedCommandNode<S>[];
|
|
22
|
+
private modifier: RedirectModifier<S>;
|
|
23
|
+
private forks: boolean;
|
|
24
|
+
|
|
25
|
+
constructor(dispatcher: CommandDispatcher<S>, source: S, rootNode: CommandNode<S>, start: number) {
|
|
26
|
+
this.dispatcher = dispatcher;
|
|
27
|
+
this.source = source;
|
|
28
|
+
this.rootNode = rootNode;
|
|
29
|
+
this.range = StringRange.at(start);
|
|
30
|
+
this.nodes = [];
|
|
31
|
+
this.arguments = new Map();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
withSource(source: S): CommandContextBuilder<S> {
|
|
35
|
+
this.source = source;
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getSource(): S {
|
|
40
|
+
return this.source;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getRootNode(): CommandNode<S> {
|
|
44
|
+
return this.rootNode;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
withArgument(name: string, argument: ParsedArgument<any>): CommandContextBuilder<S> {
|
|
48
|
+
this.arguments.set(name, argument);
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getArguments(): Map<string, ParsedArgument<any>> {
|
|
53
|
+
return this.arguments;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
withChild(child: CommandContextBuilder<S>): CommandContextBuilder<S> {
|
|
57
|
+
this.child = child;
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
getChild(): CommandContextBuilder<S> {
|
|
62
|
+
return this.child;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
getLastChild(): CommandContextBuilder<S> {
|
|
66
|
+
let result: CommandContextBuilder<S> = this;
|
|
67
|
+
while (result.getChild() != null) {
|
|
68
|
+
result = result.getChild();
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
withCommand(command: Command<S>): CommandContextBuilder<S> {
|
|
74
|
+
this.command = command;
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
getCommand(): Command<S> {
|
|
79
|
+
return this.command;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
withNode(node: CommandNode<S>, range: StringRange): CommandContextBuilder<S> {
|
|
83
|
+
this.nodes.push(new ParsedCommandNode<S>(node, range));
|
|
84
|
+
this.range = StringRange.encompassing(this.range, range);
|
|
85
|
+
this.modifier = node.getRedirectModifier();
|
|
86
|
+
this.forks = node.isFork();
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
getNodes(): ParsedCommandNode<S>[] {
|
|
91
|
+
return this.nodes;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
copy(): CommandContextBuilder<S> {
|
|
95
|
+
const copy = new CommandContextBuilder<S>(this.dispatcher, this.source, this.rootNode, this.range.getStart());
|
|
96
|
+
copy.command = this.command;
|
|
97
|
+
copy.child = this.child;
|
|
98
|
+
copy.range = this.range;
|
|
99
|
+
copy.nodes.push(...this.nodes);
|
|
100
|
+
this.arguments.forEach((v, k) => {
|
|
101
|
+
copy.arguments.set(k, v);
|
|
102
|
+
});
|
|
103
|
+
return copy;
|
|
104
|
+
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
build(input: string): CommandContext<S> {
|
|
108
|
+
const child = this.child == null ? null : this.child.build(input);
|
|
109
|
+
return new CommandContext(this.source, input, this.arguments, this.command, this.rootNode, this.nodes, this.range, child, this.modifier, this.forks);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
getDispatcher(): CommandDispatcher<S> {
|
|
113
|
+
return this.dispatcher;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
getRange(): StringRange {
|
|
117
|
+
return this.range;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
findSuggestionContext(cursor: number): SuggestionContext<S> {
|
|
121
|
+
if (this.range.getStart() <= cursor) {
|
|
122
|
+
if (this.range.getEnd() < cursor) {
|
|
123
|
+
if (this.child != null) {
|
|
124
|
+
return this.child.findSuggestionContext(cursor);
|
|
125
|
+
} else if (this.nodes.length > 0) {
|
|
126
|
+
const last = this.nodes[this.nodes.length - 1];
|
|
127
|
+
return new SuggestionContext(last.getNode(), last.getRange().getEnd() + 1);
|
|
128
|
+
} else {
|
|
129
|
+
return new SuggestionContext(this.rootNode, this.range.getStart());
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
let prev = this.rootNode;
|
|
133
|
+
for (const node of this.nodes) {
|
|
134
|
+
const nodeRange = node.getRange();
|
|
135
|
+
if (nodeRange.getStart() <= cursor && cursor <= nodeRange.getEnd()) {
|
|
136
|
+
return new SuggestionContext(prev, nodeRange.getStart());
|
|
137
|
+
}
|
|
138
|
+
prev = node.getNode();
|
|
139
|
+
}
|
|
140
|
+
if (prev === null) {
|
|
141
|
+
throw new Error("Can't find node before cursor");
|
|
142
|
+
}
|
|
143
|
+
return new SuggestionContext(prev, this.range.getStart());
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
throw new Error("Can't find node before cursor");
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { StringRange } from "..";
|
|
2
|
+
|
|
3
|
+
export class ParsedArgument<T> {
|
|
4
|
+
private range: StringRange;
|
|
5
|
+
private result: T;
|
|
6
|
+
|
|
7
|
+
constructor(start: number, end: number, result: T) {
|
|
8
|
+
this.range = new StringRange(start, end);
|
|
9
|
+
this.result = result;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
getRange(): StringRange {
|
|
13
|
+
return this.range;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
getResult(): T {
|
|
17
|
+
return this.result;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { CommandNode, StringRange } from "..";
|
|
2
|
+
|
|
3
|
+
export class ParsedCommandNode<S> {
|
|
4
|
+
private node: CommandNode<S>;
|
|
5
|
+
private range: StringRange;
|
|
6
|
+
|
|
7
|
+
constructor(node: CommandNode<S>, range: StringRange) {
|
|
8
|
+
this.node = node;
|
|
9
|
+
this.range = range;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
getNode(): CommandNode<S> {
|
|
13
|
+
return this.node;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
getRange(): StringRange {
|
|
17
|
+
return this.range;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export class StringRange {
|
|
2
|
+
private start: number;
|
|
3
|
+
private end: number;
|
|
4
|
+
|
|
5
|
+
constructor(start: number, end: number) {
|
|
6
|
+
this.start = start;
|
|
7
|
+
this.end = end;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
static at(pos: number): StringRange {
|
|
11
|
+
return new StringRange(pos, pos);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
static encompassing(a: StringRange, b: StringRange): StringRange {
|
|
15
|
+
const start = Math.min(a.getStart(), b.getStart());
|
|
16
|
+
const end = Math.max(a.getEnd(), b.getEnd());
|
|
17
|
+
return new StringRange(start, end)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getStart(): number {
|
|
21
|
+
return this.start;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
getEnd(): number {
|
|
25
|
+
return this.end;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
isEmpty(): boolean {
|
|
29
|
+
return this.start === this.end;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getLength(): number {
|
|
33
|
+
return this.end - this.start;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { CommandSyntaxError, StringReader } from "..";
|
|
2
|
+
|
|
3
|
+
type CommandErrorFunction = (...args: any[]) => string;
|
|
4
|
+
|
|
5
|
+
export class CommandErrorType {
|
|
6
|
+
private func: CommandErrorFunction
|
|
7
|
+
constructor(func: CommandErrorFunction) {
|
|
8
|
+
this.func = func;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
create(...args: any[]): CommandSyntaxError {
|
|
12
|
+
const message = this.func(...args);
|
|
13
|
+
return new CommandSyntaxError(message);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
createWithContext(reader: StringReader, ...args: any[]): CommandSyntaxError {
|
|
17
|
+
const message = this.func(...args);
|
|
18
|
+
return new CommandSyntaxError(message, reader.getString(), reader.getCursor());
|
|
19
|
+
}
|
|
20
|
+
}
|