@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,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __extends = (this && this.__extends) || (function () {
|
|
3
|
+
var extendStatics = function (d, b) {
|
|
4
|
+
extendStatics = Object.setPrototypeOf ||
|
|
5
|
+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
6
|
+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
7
|
+
return extendStatics(d, b);
|
|
8
|
+
};
|
|
9
|
+
return function (d, b) {
|
|
10
|
+
if (typeof b !== "function" && b !== null)
|
|
11
|
+
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
12
|
+
extendStatics(d, b);
|
|
13
|
+
function __() { this.constructor = d; }
|
|
14
|
+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
15
|
+
};
|
|
16
|
+
})();
|
|
17
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
18
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
19
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
20
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
21
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
22
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
23
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
27
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
28
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
29
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
30
|
+
function step(op) {
|
|
31
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
32
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
33
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
34
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
35
|
+
switch (op[0]) {
|
|
36
|
+
case 0: case 1: t = op; break;
|
|
37
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
38
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
39
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
40
|
+
default:
|
|
41
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
42
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
43
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
44
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
45
|
+
if (t[2]) _.ops.pop();
|
|
46
|
+
_.trys.pop(); continue;
|
|
47
|
+
}
|
|
48
|
+
op = body.call(thisArg, _);
|
|
49
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
50
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
|
+
exports.RootCommandNode = void 0;
|
|
55
|
+
var __1 = require("..");
|
|
56
|
+
var RootCommandNode = /** @class */ (function (_super) {
|
|
57
|
+
__extends(RootCommandNode, _super);
|
|
58
|
+
function RootCommandNode() {
|
|
59
|
+
var _this = _super.call(this, null, function (c) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
60
|
+
return [2 /*return*/, true];
|
|
61
|
+
}); }); }, null, function (c) { return null; }, false) || this;
|
|
62
|
+
return _this;
|
|
63
|
+
}
|
|
64
|
+
RootCommandNode.prototype.parse = function (reader, contextBuilder) {
|
|
65
|
+
};
|
|
66
|
+
RootCommandNode.prototype.getName = function () {
|
|
67
|
+
return "";
|
|
68
|
+
};
|
|
69
|
+
RootCommandNode.prototype.getUsageText = function () {
|
|
70
|
+
return "";
|
|
71
|
+
};
|
|
72
|
+
RootCommandNode.prototype.listSuggestions = function (context, builder) {
|
|
73
|
+
return __1.Suggestions.empty();
|
|
74
|
+
};
|
|
75
|
+
return RootCommandNode;
|
|
76
|
+
}(__1.CommandNode));
|
|
77
|
+
exports.RootCommandNode = RootCommandNode;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["../src/Command.ts","../src/CommandDispatcher.ts","../src/ParseResults.ts","../src/Predicate.ts","../src/StringReader.ts","../src/index.ts","../src/arguments/ArgumentType.ts","../src/arguments/BoolArgumentType.ts","../src/arguments/FloatArgumentType.ts","../src/arguments/IntegerArgumentType.ts","../src/arguments/LongArgumentType.ts","../src/arguments/NumberArgumentType.ts","../src/arguments/StringArgumentType.ts","../src/builder/ArgumentBuilder.ts","../src/builder/LiteralArgumentBuilder.ts","../src/builder/RequiredArgumentBuilder.ts","../src/context/CommandContext.ts","../src/context/CommandContextBuilder.ts","../src/context/ParsedArgument.ts","../src/context/ParsedCommandNode.ts","../src/context/StringRange.ts","../src/context/SuggestionContext.ts","../src/exceptions/CommandErrorType.ts","../src/exceptions/CommandSyntaxError.ts","../src/suggestion/Suggestion.ts","../src/suggestion/Suggestions.ts","../src/suggestion/SuggestionsBuilder.ts","../src/tree/ArgumentCommandNode.ts","../src/tree/CommandNode.ts","../src/tree/LiteralCommandNode.ts","../src/tree/RootCommandNode.ts"],"version":"5.9.3"}
|
package/jest.config.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wq2/brigadier-ts",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Fork of brigadier-ts with some extra changes",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "rimraf dist/ && tsc --build",
|
|
9
|
+
"prepublishOnly": "npm run build",
|
|
10
|
+
"test": "node --experimental-vm-modules --no-warnings node_modules/jest/bin/jest.js"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/MinibloxCheaters2/brigadier-ts.git"
|
|
15
|
+
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public",
|
|
18
|
+
"provenance": true
|
|
19
|
+
},
|
|
20
|
+
"keywords": [],
|
|
21
|
+
"author": "Misode",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/MinibloxCheaters2/brigadier-ts/issues"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/MinibloxCheaters2/brigadier-ts#readme",
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/jest": "^30.0.0",
|
|
29
|
+
"esbuild-jest": "^0.5.0",
|
|
30
|
+
"jest": "^30.2.0",
|
|
31
|
+
"rimraf": "^6.1.2",
|
|
32
|
+
"typescript": "^5.9.3"
|
|
33
|
+
}
|
|
34
|
+
}
|
package/src/Command.ts
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RootCommandNode,
|
|
3
|
+
LiteralCommandNode,
|
|
4
|
+
StringReader,
|
|
5
|
+
LiteralArgumentBuilder,
|
|
6
|
+
CommandContextBuilder,
|
|
7
|
+
CommandNode,
|
|
8
|
+
ParseResults,
|
|
9
|
+
CommandSyntaxError,
|
|
10
|
+
Suggestions,
|
|
11
|
+
SuggestionsBuilder
|
|
12
|
+
} from ".";
|
|
13
|
+
|
|
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
|
+
}
|
|
300
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CommandContextBuilder,
|
|
3
|
+
StringReader,
|
|
4
|
+
CommandNode,
|
|
5
|
+
CommandSyntaxError
|
|
6
|
+
} from ".";
|
|
7
|
+
|
|
8
|
+
export class ParseResults<S> {
|
|
9
|
+
private context: CommandContextBuilder<S>;
|
|
10
|
+
private reader: StringReader;
|
|
11
|
+
private errors: Map<CommandNode<S>, CommandSyntaxError>;
|
|
12
|
+
|
|
13
|
+
constructor(context: CommandContextBuilder<S>, reader: StringReader, errors: Map<CommandNode<S>, CommandSyntaxError>) {
|
|
14
|
+
this.context = context;
|
|
15
|
+
this.reader = reader;
|
|
16
|
+
this.errors = errors;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
getContext(): CommandContextBuilder<S> {
|
|
20
|
+
return this.context;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
getReader(): StringReader {
|
|
24
|
+
return this.reader;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getErrors(): Map<CommandNode<S>, CommandSyntaxError> {
|
|
28
|
+
return this.errors;
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/Predicate.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type Predicate<S> = (c: S) => Promise<boolean>;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { CommandSyntaxError } from "./exceptions/CommandSyntaxError";
|
|
2
|
+
|
|
3
|
+
export class StringReader {
|
|
4
|
+
private string: string;
|
|
5
|
+
private cursor: number;
|
|
6
|
+
|
|
7
|
+
constructor(string: string | StringReader) {
|
|
8
|
+
if (string instanceof StringReader) {
|
|
9
|
+
this.string = string.getString();
|
|
10
|
+
this.cursor = string.getCursor();
|
|
11
|
+
} else {
|
|
12
|
+
this.string = string;
|
|
13
|
+
this.cursor = 0;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
getString(): string {
|
|
18
|
+
return this.string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
getCursor(): number {
|
|
22
|
+
return this.cursor;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
setCursor(cursor: number): void {
|
|
26
|
+
this.cursor = cursor;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
getRemainingLength(): number {
|
|
30
|
+
return this.string.length - this.cursor;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
getTotalLength(): number {
|
|
34
|
+
return this.string.length;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getRead(): string {
|
|
38
|
+
return this.string.substring(0, this.cursor);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getRemaining(): string {
|
|
42
|
+
return this.string.substring(this.cursor);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
canRead(length = 1): boolean {
|
|
46
|
+
return this.cursor + length <= this.string.length;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
peek(offset = 0): string {
|
|
50
|
+
return this.string.charAt(this.cursor + offset);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
read(): string {
|
|
54
|
+
const char = this.string.charAt(this.cursor);
|
|
55
|
+
this.cursor += 1;
|
|
56
|
+
return char;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
skip(): void {
|
|
60
|
+
this.cursor += 1;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
isAllowedNumber(c: string): boolean {
|
|
64
|
+
return c >= "0" && c <= "9" || c === "." || c === "-";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
readInt(): number {
|
|
68
|
+
const start = this.cursor;
|
|
69
|
+
while (this.canRead() && this.isAllowedNumber(this.peek())) {
|
|
70
|
+
this.skip();
|
|
71
|
+
}
|
|
72
|
+
const number = this.string.substring(start, this.cursor);
|
|
73
|
+
if (number.length === 0) {
|
|
74
|
+
throw CommandSyntaxError.READER_EXPECTED_INT.createWithContext(this);
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const value = Number(number);
|
|
78
|
+
if (isNaN(value) || !Number.isInteger(value)) {
|
|
79
|
+
throw new Error()
|
|
80
|
+
}
|
|
81
|
+
return value
|
|
82
|
+
} catch (e) {
|
|
83
|
+
this.cursor = start;
|
|
84
|
+
throw CommandSyntaxError.READER_INVALID_INT.createWithContext(this, number);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
readLong(): BigInt {
|
|
89
|
+
const start = this.cursor;
|
|
90
|
+
while (this.canRead() && this.isAllowedNumber(this.peek())) {
|
|
91
|
+
this.skip();
|
|
92
|
+
}
|
|
93
|
+
const number = this.string.substring(start, this.cursor);
|
|
94
|
+
if (number.length === 0) {
|
|
95
|
+
throw CommandSyntaxError.READER_EXPECTED_INT.createWithContext(this);
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
return BigInt(number);
|
|
99
|
+
} catch (e) {
|
|
100
|
+
this.cursor = start;
|
|
101
|
+
throw CommandSyntaxError.READER_INVALID_INT.createWithContext(this, number);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
readFloat(): number {
|
|
106
|
+
const start = this.cursor;
|
|
107
|
+
while (this.canRead() && this.isAllowedNumber(this.peek())) {
|
|
108
|
+
this.skip();
|
|
109
|
+
}
|
|
110
|
+
const number = this.string.substring(start, this.cursor);
|
|
111
|
+
if (number.length === 0) {
|
|
112
|
+
throw CommandSyntaxError.READER_EXPECTED_FLOAT.createWithContext(this);
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
const value = Number(number);
|
|
116
|
+
if (isNaN(value)) {
|
|
117
|
+
throw new Error()
|
|
118
|
+
}
|
|
119
|
+
return value
|
|
120
|
+
} catch (e) {
|
|
121
|
+
this.cursor = start;
|
|
122
|
+
throw CommandSyntaxError.READER_INVALID_FLOAT.createWithContext(this, number);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
isAllowedInUnquotedString(c: string): boolean {
|
|
127
|
+
return c >= "0" && c <= "9"
|
|
128
|
+
|| c >= "A" && c <= "Z"
|
|
129
|
+
|| c >= "a" && c <= "z"
|
|
130
|
+
|| c == "_" || c == "-"
|
|
131
|
+
|| c == "." || c == "+";
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
isQuotedStringStart(c: string): boolean {
|
|
135
|
+
return c === "'" || c === "\"";
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
readUnquotedString(): string {
|
|
139
|
+
const start = this.cursor;
|
|
140
|
+
while (this.canRead() && this.isAllowedInUnquotedString(this.peek())) {
|
|
141
|
+
this.skip();
|
|
142
|
+
}
|
|
143
|
+
return this.string.substring(start, this.cursor);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
readStringUntil(terminator: string): string {
|
|
147
|
+
let result = [];
|
|
148
|
+
let escaped = false;
|
|
149
|
+
while (this.canRead()) {
|
|
150
|
+
const c = this.read();
|
|
151
|
+
if (escaped) {
|
|
152
|
+
if (c === terminator || c === "\\") {
|
|
153
|
+
result.push(c);
|
|
154
|
+
escaped = false;
|
|
155
|
+
} else {
|
|
156
|
+
this.setCursor(this.cursor - 1);
|
|
157
|
+
throw CommandSyntaxError.READER_INVALID_ESCAPE.createWithContext(this, c);
|
|
158
|
+
}
|
|
159
|
+
} else if (c === "\\") {
|
|
160
|
+
escaped = true;
|
|
161
|
+
} else if (c === terminator) {
|
|
162
|
+
return result.join("");
|
|
163
|
+
} else {
|
|
164
|
+
result.push(c);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
throw CommandSyntaxError.READER_EXPECTED_END_OF_QUOTE.createWithContext(this);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
readString(): string {
|
|
171
|
+
if (!this.canRead()) {
|
|
172
|
+
return "";
|
|
173
|
+
}
|
|
174
|
+
const next = this.peek();
|
|
175
|
+
if (this.isQuotedStringStart(next)) {
|
|
176
|
+
this.skip();
|
|
177
|
+
return this.readStringUntil(next);
|
|
178
|
+
}
|
|
179
|
+
return this.readUnquotedString();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
readBoolean(): boolean {
|
|
183
|
+
const start = this.cursor;
|
|
184
|
+
const value = this.readUnquotedString();
|
|
185
|
+
if (value.length === 0) {
|
|
186
|
+
throw CommandSyntaxError.READER_EXPECTED_BOOL.createWithContext(this);
|
|
187
|
+
}
|
|
188
|
+
if (value === "true") {
|
|
189
|
+
return true
|
|
190
|
+
} else if (value === "false") {
|
|
191
|
+
return false
|
|
192
|
+
} else {
|
|
193
|
+
throw CommandSyntaxError.READER_INVALID_BOOL.createWithContext(this, value);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
StringReader,
|
|
3
|
+
CommandContext,
|
|
4
|
+
Suggestions,
|
|
5
|
+
SuggestionsBuilder
|
|
6
|
+
} from "..";
|
|
7
|
+
|
|
8
|
+
export abstract class ArgumentType<T> {
|
|
9
|
+
abstract parse(reader: StringReader): T;
|
|
10
|
+
|
|
11
|
+
listSuggestions(context: CommandContext<any>, builder: SuggestionsBuilder): Promise<Suggestions> {
|
|
12
|
+
return Suggestions.empty();
|
|
13
|
+
}
|
|
14
|
+
}
|