Compare commits
31 commits
suggestion
...
master
Author | SHA1 | Date | |
---|---|---|---|
6c71b666af | |||
|
f20bede62a | ||
|
a3f3eb2929 | ||
|
514387ba11 | ||
|
a6c4baa1b6 | ||
|
b0f5818a59 | ||
|
b411777f47 | ||
|
0c9c87e4e6 | ||
|
3c773a7188 | ||
|
06df81922f | ||
|
cf754c4ef6 | ||
|
60a94e529d | ||
|
242de3fe73 | ||
|
55f6e25c03 | ||
|
559d8f3972 | ||
|
447845ba89 | ||
|
6eec4e50ac | ||
|
8986ae2428 | ||
|
e527fec986 | ||
|
5a7ea58024 | ||
|
7ee589b29b | ||
|
bcbd596c24 | ||
|
d156febab6 | ||
|
9d8019b407 | ||
|
d3cb749c4b | ||
|
e60b24f36b | ||
|
bbfb8a7da1 | ||
|
107b852c74 | ||
|
b10b4479e2 | ||
|
88714288a7 | ||
|
6a15305c3d |
46 changed files with 985 additions and 219 deletions
51
.ado/build.yml
Normal file
51
.ado/build.yml
Normal file
|
@ -0,0 +1,51 @@
|
|||
name: $(Rev:r)
|
||||
|
||||
trigger:
|
||||
branches:
|
||||
include:
|
||||
- '*'
|
||||
exclude:
|
||||
- master
|
||||
|
||||
pr:
|
||||
branches:
|
||||
include:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
- job: 'Build'
|
||||
displayName: 'Build for testing'
|
||||
|
||||
pool: 'MC-Build-1ES-Azure-Pipeline-Linux'
|
||||
container: adoptopenjdk/openjdk8:latest
|
||||
|
||||
workspace:
|
||||
clean: all
|
||||
|
||||
steps:
|
||||
- task: Gradle@2
|
||||
displayName: Build and Test
|
||||
inputs:
|
||||
workingDirectory: ''
|
||||
gradleWrapperFile: 'gradlew'
|
||||
gradleOptions: '-Xmx3072m'
|
||||
options: '-PbuildNumber=0'
|
||||
javaHomeOption: 'JDKVersion'
|
||||
jdkUserInputPath: '/usr/java/openjdk-8'
|
||||
testResultsFiles: '**/TEST-*.xml'
|
||||
tasks: 'build test publish'
|
||||
|
||||
# This is a workaround for ComponentGovernanceComponentDetection@0 not recognizing the generated `.pom` file(s)
|
||||
- task: Bash@3
|
||||
displayName: Copy pom for component governance
|
||||
inputs:
|
||||
targetType: 'inline'
|
||||
script: |
|
||||
pompath=`find build/repo -name *.pom`
|
||||
cp "${pompath}" build/pom.xml
|
||||
|
||||
- task: ComponentGovernanceComponentDetection@0
|
||||
inputs:
|
||||
scanType: 'Register'
|
||||
verbosity: 'Verbose'
|
||||
alertWarningLevel: 'High'
|
87
.ado/release.yml
Normal file
87
.ado/release.yml
Normal file
|
@ -0,0 +1,87 @@
|
|||
name: $(Rev:r)
|
||||
|
||||
trigger:
|
||||
branches:
|
||||
include:
|
||||
- master
|
||||
|
||||
pr: none
|
||||
|
||||
variables:
|
||||
rConnection: 'mc-java-sc'
|
||||
storageAccount: 'librariesminecraftnet'
|
||||
storageAccountContainer: 'librariesminecraftnet'
|
||||
keyVault: 'mc-java-vault'
|
||||
|
||||
jobs:
|
||||
- job: 'Build'
|
||||
displayName: 'Build for release'
|
||||
|
||||
pool: 'MC-Build-1ES-Azure-Pipeline-Linux'
|
||||
container: adoptopenjdk/openjdk8:latest
|
||||
|
||||
workspace:
|
||||
clean: all
|
||||
|
||||
steps:
|
||||
- task: Gradle@2
|
||||
displayName: Build and Test
|
||||
inputs:
|
||||
workingDirectory: ''
|
||||
gradleWrapperFile: 'gradlew'
|
||||
gradleOptions: '-Xmx3072m'
|
||||
options: '-PbuildNumber=$(Build.BuildNumber)'
|
||||
javaHomeOption: 'JDKVersion'
|
||||
jdkUserInputPath: '/usr/java/openjdk-8'
|
||||
testResultsFiles: '**/TEST-*.xml'
|
||||
tasks: 'build test publish report'
|
||||
|
||||
# This is a workaround for ComponentGovernanceComponentDetection@0 not recognizing the generated `.pom` file(s)
|
||||
- task: Bash@3
|
||||
displayName: Copy pom for component governance
|
||||
inputs:
|
||||
targetType: 'inline'
|
||||
script: |
|
||||
pompath=`find build/repo -name *.pom`
|
||||
cp "${pompath}" build/pom.xml
|
||||
|
||||
- task: ComponentGovernanceComponentDetection@0
|
||||
inputs:
|
||||
scanType: 'Register'
|
||||
verbosity: 'Verbose'
|
||||
alertWarningLevel: 'High'
|
||||
|
||||
- publish: 'build/repo/'
|
||||
artifact: repo
|
||||
|
||||
- job: 'Publish'
|
||||
displayName: 'Publish release'
|
||||
dependsOn: Build
|
||||
condition: eq(variables['Build.SourceBranch'], 'refs/heads/master')
|
||||
|
||||
pool: 'MC-Build-1ES-Azure-Pipeline-Linux'
|
||||
|
||||
workspace:
|
||||
clean: all
|
||||
|
||||
steps:
|
||||
- download: current
|
||||
artifact: repo
|
||||
|
||||
- task: AzureKeyVault@1
|
||||
displayName: 'Fetching secrets'
|
||||
name: secrets
|
||||
inputs:
|
||||
azureSubscription: '$(rConnection)'
|
||||
KeyVaultName: '$(keyVault)'
|
||||
SecretsFilter: 'access-key-prod-librariesminecraftnet'
|
||||
RunAsPreJob: false
|
||||
|
||||
- task: AzureCLI@2
|
||||
displayName: Azure CLI
|
||||
inputs:
|
||||
azureSubscription: '$(rConnection)'
|
||||
scriptType: 'bash'
|
||||
scriptLocation: 'inlineScript'
|
||||
inlineScript: |
|
||||
az storage blob upload-batch -s '$(Pipeline.Workspace)/repo' -d $(storageAccountContainer) --account-name $(storageAccount) --account-key $(access-key-prod-librariesminecraftnet)
|
22
.github/workflows/pr-check.yml
vendored
Normal file
22
.github/workflows/pr-check.yml
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
name: pr-check
|
||||
|
||||
on: [ pull_request ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build and test
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: adoptopenjdk/openjdk8:latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build with Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
arguments: build test
|
||||
- name: Publish Test Report
|
||||
uses: mikepenz/action-junit-report@v3
|
||||
if: always()
|
||||
with:
|
||||
report_paths: '**/build/test-results/test/TEST-*.xml'
|
18
LICENSE
18
LICENSE
|
@ -2,8 +2,20 @@ MIT License
|
|||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
22
README.md
22
README.md
|
@ -1,4 +1,5 @@
|
|||
# Brigadier
|
||||
# Brigadier [![Latest release](https://img.shields.io/github/release/Mojang/brigadier.svg)](https://github.com/Mojang/brigadier/releases/latest) [![License](https://img.shields.io/github/license/Mojang/brigadier.svg)](https://github.com/Mojang/brigadier/blob/master/LICENSE)
|
||||
|
||||
Brigadier is a command parser & dispatcher, designed and developed for Minecraft: Java Edition and now freely available for use elsewhere under the MIT license.
|
||||
|
||||
# Installation
|
||||
|
@ -37,7 +38,7 @@ And then use this library (change `(the latest version)` to the latest version!)
|
|||
```
|
||||
|
||||
# Contributing
|
||||
Contributions are suspended until we have a process in place to handle them.
|
||||
Contributions are welcome! :D
|
||||
|
||||
Most contributions will require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to,
|
||||
and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com.
|
||||
|
@ -49,20 +50,20 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio
|
|||
# Usage
|
||||
At the heart of Brigadier, you need a `CommandDispatcher<S>`, where `<S>` is any custom object you choose to identify a "command source".
|
||||
|
||||
A command dispatcher holds a "command tree", which are a series of `CommandNode` which represent the various possible syntax that form a valid command.
|
||||
A command dispatcher holds a "command tree", which is a series of `CommandNode`s that represent the various possible syntax options that form a valid command.
|
||||
|
||||
## Registering a new command
|
||||
Before we can start parsing and dispatching commands, we need to build up our command tree. Every registration is an append operation,
|
||||
so you can freely extend existing commands in a project without needing access to the source code that created them.
|
||||
|
||||
Command registration also encourages use of a builder pattern to keep code cruft to a minimum.
|
||||
Command registration also encourages the use of a builder pattern to keep code cruft to a minimum.
|
||||
|
||||
A "command" is a fairly loose term, but typically it means an exit point of the command tree.
|
||||
Every node can have an `executes` function attached to it, which signifies that if the input stops here then this function will be called with the context so far.
|
||||
|
||||
Consider the following example:
|
||||
```java
|
||||
CommandDispatcher<CommandSourceStack> dispatcher = new CommandDispatcher();
|
||||
CommandDispatcher<CommandSourceStack> dispatcher = new CommandDispatcher<>();
|
||||
|
||||
dispatcher.register(
|
||||
literal("foo")
|
||||
|
@ -94,12 +95,12 @@ Argument types will be asked to parse input as much as they can, and then store
|
|||
|
||||
For example, an integer argument would parse "123" and store it as `123` (`int`), but throw an error if the input were `onetwothree`.
|
||||
|
||||
When a command is actually ran, it can access these arguments in the context provided to the registered function.
|
||||
When a command is actually run, it can access these arguments in the context provided to the registered function.
|
||||
|
||||
## Parsing user input
|
||||
So, we've registered some commands and now we're ready to take in user input. If you're in a rush, you can just call `dispatcher.execute("foo 123", source)` and call it a day.
|
||||
|
||||
The result of `execute` is an integer returned by the command it evaluated. Its meaning varies depending on command, and typically will not be useful to programmers.
|
||||
The result of `execute` is an integer that was returned from an evaluated command. The meaning of this integer depends on the command, and will typically not be useful to programmers.
|
||||
|
||||
The `source` is an object of `<S>`, your own custom class to track users/players/etc. It will be provided to the command so that it has some context on what's happening.
|
||||
|
||||
|
@ -125,9 +126,10 @@ It also contains a map of parse exceptions for each command node it encountered.
|
|||
the reason why is inside this exception map.
|
||||
|
||||
## Displaying usage info
|
||||
There's two forms of "usage strings" provided by this library, both require a target node.
|
||||
There are two forms of "usage strings" provided by this library, both require a target node.
|
||||
|
||||
`getAllUsage(node, source, restricted)` will return a list of all possible commands (executable end-points) under the target node and their human readable path. If `restricted`, it will ignore commands that `source` does not have access to. This will look like [`foo`, `foo <bar>`]
|
||||
`getAllUsage(node, source, restricted)` will return a list of all possible commands (executable end-points) under the target node and their human readable path. If `restricted`, it will ignore commands that `source` does not have access to. This will look like [`foo`, `foo <bar>`].
|
||||
|
||||
`getSmartUsage(node, source)` will return a map of the child nodes to their "smart usage" human readable path. This tries to squash future-nodes together and show optional & typed information, and can look like `foo (<bar>)`
|
||||
`getSmartUsage(node, source)` will return a map of the child nodes to their "smart usage" human readable path. This tries to squash future-nodes together and show optional & typed information, and can look like `foo (<bar>)`.
|
||||
|
||||
[![GitHub forks](https://img.shields.io/github/forks/Mojang/brigadier.svg?style=social&label=Fork)](https://github.com/Mojang/brigadier/fork) [![GitHub stars](https://img.shields.io/github/stars/Mojang/brigadier.svg?style=social&label=Stars)](https://github.com/Mojang/brigadier/stargazers)
|
||||
|
|
45
build.gradle
45
build.gradle
|
@ -1,6 +1,4 @@
|
|||
import groovy.io.FileType
|
||||
import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider
|
||||
import com.amazonaws.services.s3.AmazonS3Client
|
||||
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: 'maven-publish'
|
||||
|
@ -8,10 +6,6 @@ apply plugin: 'maven-publish'
|
|||
group = 'com.mojang'
|
||||
version = project.hasProperty('buildNumber') ? "${project.majorMinor}.${project.buildNumber}" : "${project.majorMinor}.0-SNAPSHOT"
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '4.0'
|
||||
}
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
@ -19,10 +13,6 @@ buildscript {
|
|||
url "https://libraries.minecraft.net"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.amazonaws:aws-java-sdk:1.11.33'
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
@ -33,13 +23,13 @@ repositories {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
api 'com.google.guava:guava:21.0'
|
||||
testCompile 'junit:junit-dep:4.10'
|
||||
testCompile 'org.hamcrest:hamcrest-library:1.2.1'
|
||||
testCompile 'org.mockito:mockito-core:1.8.5'
|
||||
testCompile 'com.google.guava:guava-testlib:21.0'
|
||||
testCompile 'org.openjdk.jmh:jmh-core:1.19'
|
||||
testCompile 'org.openjdk.jmh:jmh-generator-annprocess:1.19'
|
||||
testCompile 'com.google.guava:guava:26.0-jre'
|
||||
testCompile 'junit:junit-dep:4.11'
|
||||
testCompile 'org.hamcrest:hamcrest-library:1.3'
|
||||
testCompile 'org.mockito:mockito-core:1.10.19'
|
||||
testCompile 'com.google.guava:guava-testlib:26.0-jre'
|
||||
testCompile 'org.openjdk.jmh:jmh-core:1.21'
|
||||
annotationProcessor 'org.openjdk.jmh:jmh-generator-annprocess:1.21'
|
||||
}
|
||||
|
||||
task sourcesJar(type: Jar) {
|
||||
|
@ -90,18 +80,12 @@ publishing {
|
|||
|
||||
task report {
|
||||
doLast {
|
||||
println "##teamcity[buildNumber '${project.version}']"
|
||||
println "##vso[build.updatebuildnumber]${project.version}"
|
||||
}
|
||||
}
|
||||
|
||||
def publishDir = file("$buildDir/repo")
|
||||
|
||||
|
||||
def uploadFile(s3, bucket, path, filename) {
|
||||
println "Uploading $filename to $bucket as $path"
|
||||
s3.putObject(bucket, path, filename)
|
||||
}
|
||||
|
||||
clean.doLast {
|
||||
delete publishDir
|
||||
}
|
||||
|
@ -118,14 +102,13 @@ if (version.endsWith("SNAPSHOT")) {
|
|||
}
|
||||
|
||||
publish.doLast {
|
||||
def AWSRoleARN = (System.getenv("AWS_ROLE_ARN") != null && System.getenv("AWS_ROLE_ARN") != "" ? System.getenv("AWS_ROLE_ARN") : null)
|
||||
if (AWSRoleARN == null) throw new GradleException("AWS Role has not been configured, use the `AWS_ROLE_ARN` environment variable")
|
||||
def auth = new STSAssumeRoleSessionCredentialsProvider.Builder(AWSRoleARN, "JavaBrigadierPublish").build()
|
||||
def s3 = new AmazonS3Client(auth)
|
||||
publishDir.eachFileRecurse {
|
||||
if (!it.name.contains(".xml") && !it.name.contains(".md5") && it.isFile()) {
|
||||
def relPath = publishDir.toPath().relativize(it.toPath()).toFile().toString().replaceAll('\\\\', '/')
|
||||
uploadFile(s3, "minecraft-libraries", relPath, it)
|
||||
if (!it.isFile()) {
|
||||
return
|
||||
}
|
||||
// Remove junk files
|
||||
if (it.name.contains(".xml") || it.name.contains(".md5")) {
|
||||
it.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
majorMinor: 1.0
|
||||
majorMinor: 1.1
|
||||
|
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
|||
#Wed Jun 21 14:07:34 CEST 2017
|
||||
#Sat Oct 06 16:17:40 EEST 2018
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip
|
||||
|
|
0
gradlew
vendored
Normal file → Executable file
0
gradlew
vendored
Normal file → Executable file
|
@ -1 +1,2 @@
|
|||
rootProject.name = 'brigadier'
|
||||
enableFeaturePreview('STABLE_PUBLISHING')
|
||||
|
|
|
@ -3,14 +3,10 @@
|
|||
|
||||
package com.mojang.brigadier;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.context.CommandContextBuilder;
|
||||
import com.mojang.brigadier.context.StringRange;
|
||||
import com.mojang.brigadier.context.SuggestionContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
|
@ -22,7 +18,9 @@ import java.util.ArrayList;
|
|||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
@ -57,6 +55,7 @@ public class CommandDispatcher<S> {
|
|||
private static final String USAGE_OR = "|";
|
||||
|
||||
private final RootCommandNode<S> root;
|
||||
|
||||
private final Predicate<CommandNode<S>> hasCommand = new Predicate<CommandNode<S>>() {
|
||||
@Override
|
||||
public boolean test(final CommandNode<S> input) {
|
||||
|
@ -231,8 +230,7 @@ public class CommandDispatcher<S> {
|
|||
final CommandContext<S> child = context.getChild();
|
||||
if (child != null) {
|
||||
forked |= context.isForked();
|
||||
if (!child.getNodes().isEmpty()) {
|
||||
foundCommand = true;
|
||||
if (child.hasNodes()) {
|
||||
final RedirectModifier<S> modifier = context.getRedirectModifier();
|
||||
if (modifier == null) {
|
||||
if (next == null) {
|
||||
|
@ -249,6 +247,8 @@ public class CommandDispatcher<S> {
|
|||
for (final S source : results) {
|
||||
next.add(child.copyFor(source));
|
||||
}
|
||||
} else {
|
||||
foundCommand = true;
|
||||
}
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
consumer.onCommandComplete(context, false, 0);
|
||||
|
@ -345,24 +345,14 @@ public class CommandDispatcher<S> {
|
|||
* @see #execute(String, Object)
|
||||
*/
|
||||
public ParseResults<S> parse(final StringReader command, final S source) {
|
||||
final CommandContextBuilder<S> context = new CommandContextBuilder<>(this, source, 0);
|
||||
final CommandContextBuilder<S> context = new CommandContextBuilder<>(this, source, root, command.getCursor());
|
||||
return parseNodes(root, command, context);
|
||||
}
|
||||
|
||||
private static class PartialParse<S> {
|
||||
public final CommandContextBuilder<S> context;
|
||||
public final ParseResults<S> parse;
|
||||
|
||||
private PartialParse(final CommandContextBuilder<S> context, final ParseResults<S> parse) {
|
||||
this.context = context;
|
||||
this.parse = parse;
|
||||
}
|
||||
}
|
||||
|
||||
private ParseResults<S> parseNodes(final CommandNode<S> node, final StringReader originalReader, final CommandContextBuilder<S> contextSoFar) {
|
||||
final S source = contextSoFar.getSource();
|
||||
Map<CommandNode<S>, CommandSyntaxException> errors = null;
|
||||
List<PartialParse<S>> potentials = null;
|
||||
List<ParseResults<S>> potentials = null;
|
||||
final int cursor = originalReader.getCursor();
|
||||
|
||||
for (final CommandNode<S> child : node.getRelevantNodes(originalReader)) {
|
||||
|
@ -395,48 +385,47 @@ public class CommandDispatcher<S> {
|
|||
if (reader.canRead(child.getRedirect() == null ? 2 : 1)) {
|
||||
reader.skip();
|
||||
if (child.getRedirect() != null) {
|
||||
final CommandContextBuilder<S> childContext = new CommandContextBuilder<>(this, source, reader.getCursor());
|
||||
childContext.withNode(child.getRedirect(), StringRange.between(cursor, reader.getCursor() - 1));
|
||||
final CommandContextBuilder<S> childContext = new CommandContextBuilder<>(this, source, child.getRedirect(), reader.getCursor());
|
||||
final ParseResults<S> parse = parseNodes(child.getRedirect(), reader, childContext);
|
||||
context.withChild(parse.getContext());
|
||||
return new ParseResults<>(context, originalReader.getCursor(), parse.getReader(), parse.getExceptions());
|
||||
return new ParseResults<>(context, parse.getReader(), parse.getExceptions());
|
||||
} else {
|
||||
final ParseResults<S> parse = parseNodes(child, reader, context);
|
||||
if (potentials == null) {
|
||||
potentials = new ArrayList<>(1);
|
||||
}
|
||||
potentials.add(new PartialParse<>(context, parse));
|
||||
potentials.add(parse);
|
||||
}
|
||||
} else {
|
||||
if (potentials == null) {
|
||||
potentials = new ArrayList<>(1);
|
||||
}
|
||||
potentials.add(new PartialParse<>(context, new ParseResults<>(context, originalReader.getCursor(), reader, Collections.emptyMap())));
|
||||
potentials.add(new ParseResults<>(context, reader, Collections.emptyMap()));
|
||||
}
|
||||
}
|
||||
|
||||
if (potentials != null) {
|
||||
if (potentials.size() > 1) {
|
||||
potentials.sort((a, b) -> {
|
||||
if (!a.parse.getReader().canRead() && b.parse.getReader().canRead()) {
|
||||
if (!a.getReader().canRead() && b.getReader().canRead()) {
|
||||
return -1;
|
||||
}
|
||||
if (a.parse.getReader().canRead() && !b.parse.getReader().canRead()) {
|
||||
if (a.getReader().canRead() && !b.getReader().canRead()) {
|
||||
return 1;
|
||||
}
|
||||
if (a.parse.getExceptions().isEmpty() && !b.parse.getExceptions().isEmpty()) {
|
||||
if (a.getExceptions().isEmpty() && !b.getExceptions().isEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
if (!a.parse.getExceptions().isEmpty() && b.parse.getExceptions().isEmpty()) {
|
||||
if (!a.getExceptions().isEmpty() && b.getExceptions().isEmpty()) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
return potentials.get(0).parse;
|
||||
return potentials.get(0);
|
||||
}
|
||||
|
||||
return new ParseResults<>(contextSoFar, originalReader.getCursor(), originalReader, errors == null ? Collections.emptyMap() : errors);
|
||||
return new ParseResults<>(contextSoFar, originalReader, errors == null ? Collections.emptyMap() : errors);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -461,7 +450,7 @@ public class CommandDispatcher<S> {
|
|||
* @return array of full usage strings under the target node
|
||||
*/
|
||||
public String[] getAllUsage(final CommandNode<S> node, final S source, final boolean restricted) {
|
||||
final ArrayList<String> result = Lists.newArrayList();
|
||||
final ArrayList<String> result = new ArrayList<>();
|
||||
getAllUsage(node, source, result, "", restricted);
|
||||
return result.toArray(new String[result.size()]);
|
||||
}
|
||||
|
@ -507,7 +496,7 @@ public class CommandDispatcher<S> {
|
|||
* @return array of full usage strings under the target node
|
||||
*/
|
||||
public Map<CommandNode<S>, String> getSmartUsage(final CommandNode<S> node, final S source) {
|
||||
final Map<CommandNode<S>, String> result = Maps.newLinkedHashMap();
|
||||
final Map<CommandNode<S>, String> result = new LinkedHashMap<>();
|
||||
|
||||
final boolean optional = node.getCommand() != null;
|
||||
for (final CommandNode<S> child : node.getChildren()) {
|
||||
|
@ -541,7 +530,7 @@ public class CommandDispatcher<S> {
|
|||
return self + ARGUMENT_SEPARATOR + usage;
|
||||
}
|
||||
} else if (children.size() > 1) {
|
||||
final Set<String> childUsage = Sets.newLinkedHashSet();
|
||||
final Set<String> childUsage = new LinkedHashSet<>();
|
||||
for (final CommandNode<S> child : children) {
|
||||
final String usage = getSmartUsage(child, source, childOptional, true);
|
||||
if (usage != null) {
|
||||
|
@ -589,37 +578,25 @@ public class CommandDispatcher<S> {
|
|||
* @return a future that will eventually resolve into a {@link Suggestions} object
|
||||
*/
|
||||
public CompletableFuture<Suggestions> getCompletionSuggestions(final ParseResults<S> parse) {
|
||||
final CommandContextBuilder<S> rootContext = parse.getContext();
|
||||
final CommandContextBuilder<S> context = rootContext.getLastChild();
|
||||
final CommandNode<S> parent;
|
||||
final int start;
|
||||
return getCompletionSuggestions(parse, parse.getReader().getTotalLength());
|
||||
}
|
||||
|
||||
if (context.getNodes().isEmpty()) {
|
||||
parent = root;
|
||||
start = parse.getStartIndex();
|
||||
} else if (parse.getReader().canRead()) {
|
||||
final Map.Entry<CommandNode<S>, StringRange> entry = Iterables.getLast(context.getNodes().entrySet());
|
||||
parent = entry.getKey();
|
||||
start = entry.getValue().getEnd() + 1;
|
||||
} else if (context.getNodes().size() > 1) {
|
||||
final Map.Entry<CommandNode<S>, StringRange> entry = Iterables.get(context.getNodes().entrySet(), context.getNodes().size() - 2);
|
||||
parent = entry.getKey();
|
||||
start = entry.getValue().getEnd() + 1;
|
||||
} else if (rootContext != context && context.getNodes().size() > 0) {
|
||||
final Map.Entry<CommandNode<S>, StringRange> entry = Iterables.getLast(context.getNodes().entrySet());
|
||||
parent = entry.getKey();
|
||||
start = entry.getValue().getEnd() + 1;
|
||||
} else {
|
||||
parent = root;
|
||||
start = parse.getStartIndex();
|
||||
}
|
||||
public CompletableFuture<Suggestions> getCompletionSuggestions(final ParseResults<S> parse, int cursor) {
|
||||
final CommandContextBuilder<S> context = parse.getContext();
|
||||
|
||||
final SuggestionContext<S> nodeBeforeCursor = context.findSuggestionContext(cursor);
|
||||
final CommandNode<S> parent = nodeBeforeCursor.parent;
|
||||
final int start = Math.min(nodeBeforeCursor.startPos, cursor);
|
||||
|
||||
final String fullInput = parse.getReader().getString();
|
||||
final String truncatedInput = fullInput.substring(0, cursor);
|
||||
final String truncatedInputLowerCase = truncatedInput.toLowerCase(Locale.ROOT);
|
||||
@SuppressWarnings("unchecked") final CompletableFuture<Suggestions>[] futures = new CompletableFuture[parent.getChildren().size()];
|
||||
int i = 0;
|
||||
for (final CommandNode<S> node : parent.getChildren()) {
|
||||
CompletableFuture<Suggestions> future = Suggestions.empty();
|
||||
try {
|
||||
future = node.listSuggestions(context.build(parse.getReader().getString()), new SuggestionsBuilder(parse.getReader().getString(), start));
|
||||
future = node.listSuggestions(context.build(truncatedInput), new SuggestionsBuilder(truncatedInput, truncatedInputLowerCase, start));
|
||||
} catch (final CommandSyntaxException ignored) {
|
||||
}
|
||||
futures[i++] = future;
|
||||
|
@ -627,11 +604,11 @@ public class CommandDispatcher<S> {
|
|||
|
||||
final CompletableFuture<Suggestions> result = new CompletableFuture<>();
|
||||
CompletableFuture.allOf(futures).thenRun(() -> {
|
||||
final List<Suggestions> suggestions = Lists.newArrayList();
|
||||
final List<Suggestions> suggestions = new ArrayList<>();
|
||||
for (final CompletableFuture<Suggestions> future : futures) {
|
||||
suggestions.add(future.join());
|
||||
}
|
||||
result.complete(Suggestions.merge(parse.getReader().getString(), suggestions));
|
||||
result.complete(Suggestions.merge(fullInput, suggestions));
|
||||
});
|
||||
|
||||
return result;
|
||||
|
|
|
@ -13,22 +13,16 @@ import java.util.Map;
|
|||
public class ParseResults<S> {
|
||||
private final CommandContextBuilder<S> context;
|
||||
private final Map<CommandNode<S>, CommandSyntaxException> exceptions;
|
||||
private final int startIndex;
|
||||
private final ImmutableStringReader reader;
|
||||
|
||||
public ParseResults(final CommandContextBuilder<S> context, final int startIndex, final ImmutableStringReader reader, final Map<CommandNode<S>, CommandSyntaxException> exceptions) {
|
||||
public ParseResults(final CommandContextBuilder<S> context, final ImmutableStringReader reader, final Map<CommandNode<S>, CommandSyntaxException> exceptions) {
|
||||
this.context = context;
|
||||
this.startIndex = startIndex;
|
||||
this.reader = reader;
|
||||
this.exceptions = exceptions;
|
||||
}
|
||||
|
||||
public ParseResults(final CommandContextBuilder<S> context) {
|
||||
this(context, 0, new StringReader(""), Collections.emptyMap());
|
||||
}
|
||||
|
||||
public int getStartIndex() {
|
||||
return startIndex;
|
||||
this(context, new StringReader(""), Collections.emptyMap());
|
||||
}
|
||||
|
||||
public CommandContextBuilder<S> getContext() {
|
||||
|
|
|
@ -6,8 +6,6 @@ package com.mojang.brigadier;
|
|||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface SingleRedirectModifier<S> {
|
||||
S apply(CommandContext<S> context) throws CommandSyntaxException;
|
||||
|
|
|
@ -7,7 +7,8 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
|||
|
||||
public class StringReader implements ImmutableStringReader {
|
||||
private static final char SYNTAX_ESCAPE = '\\';
|
||||
private static final char SYNTAX_QUOTE = '"';
|
||||
private static final char SYNTAX_DOUBLE_QUOTE = '"';
|
||||
private static final char SYNTAX_SINGLE_QUOTE = '\'';
|
||||
|
||||
private final String string;
|
||||
private int cursor;
|
||||
|
@ -87,6 +88,10 @@ public class StringReader implements ImmutableStringReader {
|
|||
return c >= '0' && c <= '9' || c == '.' || c == '-';
|
||||
}
|
||||
|
||||
public static boolean isQuotedStringStart(char c) {
|
||||
return c == SYNTAX_DOUBLE_QUOTE || c == SYNTAX_SINGLE_QUOTE;
|
||||
}
|
||||
|
||||
public void skipWhitespace() {
|
||||
while (canRead() && Character.isWhitespace(peek())) {
|
||||
skip();
|
||||
|
@ -110,6 +115,23 @@ public class StringReader implements ImmutableStringReader {
|
|||
}
|
||||
}
|
||||
|
||||
public long readLong() throws CommandSyntaxException {
|
||||
final int start = cursor;
|
||||
while (canRead() && isAllowedNumber(peek())) {
|
||||
skip();
|
||||
}
|
||||
final String number = string.substring(start, cursor);
|
||||
if (number.isEmpty()) {
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedLong().createWithContext(this);
|
||||
}
|
||||
try {
|
||||
return Long.parseLong(number);
|
||||
} catch (final NumberFormatException ex) {
|
||||
cursor = start;
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidLong().createWithContext(this, number);
|
||||
}
|
||||
}
|
||||
|
||||
public double readDouble() throws CommandSyntaxException {
|
||||
final int start = cursor;
|
||||
while (canRead() && isAllowedNumber(peek())) {
|
||||
|
@ -163,16 +185,22 @@ public class StringReader implements ImmutableStringReader {
|
|||
public String readQuotedString() throws CommandSyntaxException {
|
||||
if (!canRead()) {
|
||||
return "";
|
||||
} else if (peek() != SYNTAX_QUOTE) {
|
||||
}
|
||||
final char next = peek();
|
||||
if (!isQuotedStringStart(next)) {
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedStartOfQuote().createWithContext(this);
|
||||
}
|
||||
skip();
|
||||
return readStringUntil(next);
|
||||
}
|
||||
|
||||
public String readStringUntil(char terminator) throws CommandSyntaxException {
|
||||
final StringBuilder result = new StringBuilder();
|
||||
boolean escaped = false;
|
||||
while (canRead()) {
|
||||
final char c = read();
|
||||
if (escaped) {
|
||||
if (c == SYNTAX_QUOTE || c == SYNTAX_ESCAPE) {
|
||||
if (c == terminator || c == SYNTAX_ESCAPE) {
|
||||
result.append(c);
|
||||
escaped = false;
|
||||
} else {
|
||||
|
@ -181,7 +209,7 @@ public class StringReader implements ImmutableStringReader {
|
|||
}
|
||||
} else if (c == SYNTAX_ESCAPE) {
|
||||
escaped = true;
|
||||
} else if (c == SYNTAX_QUOTE) {
|
||||
} else if (c == terminator) {
|
||||
return result.toString();
|
||||
} else {
|
||||
result.append(c);
|
||||
|
@ -192,11 +220,15 @@ public class StringReader implements ImmutableStringReader {
|
|||
}
|
||||
|
||||
public String readString() throws CommandSyntaxException {
|
||||
if (canRead() && peek() == SYNTAX_QUOTE) {
|
||||
return readQuotedString();
|
||||
} else {
|
||||
return readUnquotedString();
|
||||
if (!canRead()) {
|
||||
return "";
|
||||
}
|
||||
final char next = peek();
|
||||
if (isQuotedStringStart(next)) {
|
||||
skip();
|
||||
return readStringUntil(next);
|
||||
}
|
||||
return readUnquotedString();
|
||||
}
|
||||
|
||||
public boolean readBoolean() throws CommandSyntaxException {
|
||||
|
|
|
@ -5,7 +5,6 @@ package com.mojang.brigadier.arguments;
|
|||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.context.CommandContextBuilder;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
|
@ -15,7 +14,7 @@ import java.util.Collections;
|
|||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface ArgumentType<T> {
|
||||
<S> T parse(StringReader reader) throws CommandSyntaxException;
|
||||
T parse(StringReader reader) throws CommandSyntaxException;
|
||||
|
||||
default <S> CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) {
|
||||
return Suggestions.empty();
|
||||
|
|
|
@ -5,7 +5,6 @@ package com.mojang.brigadier.arguments;
|
|||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.context.CommandContextBuilder;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
|
@ -29,16 +28,16 @@ public class BoolArgumentType implements ArgumentType<Boolean> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public <S> Boolean parse(final StringReader reader) throws CommandSyntaxException {
|
||||
public Boolean parse(final StringReader reader) throws CommandSyntaxException {
|
||||
return reader.readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) {
|
||||
if ("true".startsWith(builder.getRemaining().toLowerCase())) {
|
||||
if ("true".startsWith(builder.getRemainingLowerCase())) {
|
||||
builder.suggest("true");
|
||||
}
|
||||
if ("false".startsWith(builder.getRemaining().toLowerCase())) {
|
||||
if ("false".startsWith(builder.getRemainingLowerCase())) {
|
||||
builder.suggest("false");
|
||||
}
|
||||
return builder.buildFuture();
|
||||
|
|
|
@ -46,7 +46,7 @@ public class DoubleArgumentType implements ArgumentType<Double> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public <S> Double parse(final StringReader reader) throws CommandSyntaxException {
|
||||
public Double parse(final StringReader reader) throws CommandSyntaxException {
|
||||
final int start = reader.getCursor();
|
||||
final double result = reader.readDouble();
|
||||
if (result < minimum) {
|
||||
|
|
|
@ -46,7 +46,7 @@ public class FloatArgumentType implements ArgumentType<Float> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public <S> Float parse(final StringReader reader) throws CommandSyntaxException {
|
||||
public Float parse(final StringReader reader) throws CommandSyntaxException {
|
||||
final int start = reader.getCursor();
|
||||
final float result = reader.readFloat();
|
||||
if (result < minimum) {
|
||||
|
|
|
@ -46,7 +46,7 @@ public class IntegerArgumentType implements ArgumentType<Integer> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public <S> Integer parse(final StringReader reader) throws CommandSyntaxException {
|
||||
public Integer parse(final StringReader reader) throws CommandSyntaxException {
|
||||
final int start = reader.getCursor();
|
||||
final int result = reader.readInt();
|
||||
if (result < minimum) {
|
||||
|
@ -84,4 +84,9 @@ public class IntegerArgumentType implements ArgumentType<Integer> {
|
|||
return "integer(" + minimum + ", " + maximum + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getExamples() {
|
||||
return EXAMPLES;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.arguments;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
public class LongArgumentType implements ArgumentType<Long> {
|
||||
private static final Collection<String> EXAMPLES = Arrays.asList("0", "123", "-123");
|
||||
|
||||
private final long minimum;
|
||||
private final long maximum;
|
||||
|
||||
private LongArgumentType(final long minimum, final long maximum) {
|
||||
this.minimum = minimum;
|
||||
this.maximum = maximum;
|
||||
}
|
||||
|
||||
public static LongArgumentType longArg() {
|
||||
return longArg(Long.MIN_VALUE);
|
||||
}
|
||||
|
||||
public static LongArgumentType longArg(final long min) {
|
||||
return longArg(min, Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
public static LongArgumentType longArg(final long min, final long max) {
|
||||
return new LongArgumentType(min, max);
|
||||
}
|
||||
|
||||
public static long getLong(final CommandContext<?> context, final String name) {
|
||||
return context.getArgument(name, long.class);
|
||||
}
|
||||
|
||||
public long getMinimum() {
|
||||
return minimum;
|
||||
}
|
||||
|
||||
public long getMaximum() {
|
||||
return maximum;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long parse(final StringReader reader) throws CommandSyntaxException {
|
||||
final int start = reader.getCursor();
|
||||
final long result = reader.readLong();
|
||||
if (result < minimum) {
|
||||
reader.setCursor(start);
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.longTooLow().createWithContext(reader, result, minimum);
|
||||
}
|
||||
if (result > maximum) {
|
||||
reader.setCursor(start);
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.longTooHigh().createWithContext(reader, result, maximum);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof LongArgumentType)) return false;
|
||||
|
||||
final LongArgumentType that = (LongArgumentType) o;
|
||||
return maximum == that.maximum && minimum == that.minimum;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * Long.hashCode(minimum) + Long.hashCode(maximum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (minimum == Long.MIN_VALUE && maximum == Long.MAX_VALUE) {
|
||||
return "longArg()";
|
||||
} else if (maximum == Long.MAX_VALUE) {
|
||||
return "longArg(" + minimum + ")";
|
||||
} else {
|
||||
return "longArg(" + minimum + ", " + maximum + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getExamples() {
|
||||
return EXAMPLES;
|
||||
}
|
||||
}
|
|
@ -5,7 +5,6 @@ package com.mojang.brigadier.arguments;
|
|||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.context.CommandContextBuilder;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
@ -39,7 +38,7 @@ public class StringArgumentType implements ArgumentType<String> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public <S> String parse(final StringReader reader) throws CommandSyntaxException {
|
||||
public String parse(final StringReader reader) throws CommandSyntaxException {
|
||||
if (type == StringType.GREEDY_PHRASE) {
|
||||
final String text = reader.getRemaining();
|
||||
reader.setCursor(reader.getTotalLength());
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
package com.mojang.brigadier.builder;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.Message;
|
||||
import com.mojang.brigadier.RedirectModifier;
|
||||
import com.mojang.brigadier.SingleRedirectModifier;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
|
@ -20,6 +21,7 @@ public abstract class ArgumentBuilder<S, T extends ArgumentBuilder<S, T>> {
|
|||
private CommandNode<S> target;
|
||||
private RedirectModifier<S> modifier = null;
|
||||
private boolean forks;
|
||||
private Message description;
|
||||
|
||||
protected abstract T getThis();
|
||||
|
||||
|
@ -52,6 +54,15 @@ public abstract class ArgumentBuilder<S, T extends ArgumentBuilder<S, T>> {
|
|||
return command;
|
||||
}
|
||||
|
||||
public T describe(final Message description) {
|
||||
this.description = description;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
public Message getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public T requires(final Predicate<S> requirement) {
|
||||
this.requirement = requirement;
|
||||
return getThis();
|
||||
|
|
|
@ -28,7 +28,7 @@ public class LiteralArgumentBuilder<S> extends ArgumentBuilder<S, LiteralArgumen
|
|||
|
||||
@Override
|
||||
public LiteralCommandNode<S> build() {
|
||||
final LiteralCommandNode<S> result = new LiteralCommandNode<>(getLiteral(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork());
|
||||
final LiteralCommandNode<S> result = new LiteralCommandNode<>(getLiteral(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork(), getDescription());
|
||||
|
||||
for (final CommandNode<S> argument : getArguments()) {
|
||||
result.addChild(argument);
|
||||
|
|
|
@ -45,7 +45,7 @@ public class RequiredArgumentBuilder<S, T> extends ArgumentBuilder<S, RequiredAr
|
|||
}
|
||||
|
||||
public ArgumentCommandNode<S, T> build() {
|
||||
final ArgumentCommandNode<S, T> result = new ArgumentCommandNode<>(getName(), getType(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork(), getSuggestionsProvider());
|
||||
final ArgumentCommandNode<S, T> result = new ArgumentCommandNode<>(getName(), getType(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork(), getSuggestionsProvider(), getDescription());
|
||||
|
||||
for (final CommandNode<S> argument : getArguments()) {
|
||||
result.addChild(argument);
|
||||
|
|
|
@ -3,30 +3,46 @@
|
|||
|
||||
package com.mojang.brigadier.context;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.primitives.Primitives;
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.RedirectModifier;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class CommandContext<S> {
|
||||
|
||||
private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPER = new HashMap<>();
|
||||
|
||||
static {
|
||||
PRIMITIVE_TO_WRAPPER.put(boolean.class, Boolean.class);
|
||||
PRIMITIVE_TO_WRAPPER.put(byte.class, Byte.class);
|
||||
PRIMITIVE_TO_WRAPPER.put(short.class, Short.class);
|
||||
PRIMITIVE_TO_WRAPPER.put(char.class, Character.class);
|
||||
PRIMITIVE_TO_WRAPPER.put(int.class, Integer.class);
|
||||
PRIMITIVE_TO_WRAPPER.put(long.class, Long.class);
|
||||
PRIMITIVE_TO_WRAPPER.put(float.class, Float.class);
|
||||
PRIMITIVE_TO_WRAPPER.put(double.class, Double.class);
|
||||
}
|
||||
|
||||
private final S source;
|
||||
private final String input;
|
||||
private final Command<S> command;
|
||||
private final Map<String, ParsedArgument<S, ?>> arguments;
|
||||
private final Map<CommandNode<S>, StringRange> nodes;
|
||||
private final CommandNode<S> rootNode;
|
||||
private final List<ParsedCommandNode<S>> nodes;
|
||||
private final StringRange range;
|
||||
private final CommandContext<S> child;
|
||||
private final RedirectModifier<S> modifier;
|
||||
private final boolean forks;
|
||||
|
||||
public CommandContext(final S source, final String input, final Map<String, ParsedArgument<S, ?>> arguments, final Command<S> command, final Map<CommandNode<S>, StringRange> nodes, final StringRange range, final CommandContext<S> child, final RedirectModifier<S> modifier, boolean forks) {
|
||||
public CommandContext(final S source, final String input, final Map<String, ParsedArgument<S, ?>> arguments, final Command<S> command, final CommandNode<S> rootNode, final List<ParsedCommandNode<S>> nodes, final StringRange range, final CommandContext<S> child, final RedirectModifier<S> modifier, boolean forks) {
|
||||
this.source = source;
|
||||
this.input = input;
|
||||
this.arguments = arguments;
|
||||
this.command = command;
|
||||
this.rootNode = rootNode;
|
||||
this.nodes = nodes;
|
||||
this.range = range;
|
||||
this.child = child;
|
||||
|
@ -38,7 +54,7 @@ public class CommandContext<S> {
|
|||
if (this.source == source) {
|
||||
return this;
|
||||
}
|
||||
return new CommandContext<>(source, input, arguments, command, nodes, range, child, modifier, forks);
|
||||
return new CommandContext<>(source, input, arguments, command, rootNode, nodes, range, child, modifier, forks);
|
||||
}
|
||||
|
||||
public CommandContext<S> getChild() {
|
||||
|
@ -70,7 +86,7 @@ public class CommandContext<S> {
|
|||
}
|
||||
|
||||
final Object result = argument.getResult();
|
||||
if (Primitives.wrap(clazz).isAssignableFrom(result.getClass())) {
|
||||
if (PRIMITIVE_TO_WRAPPER.getOrDefault(clazz, clazz).isAssignableFrom(result.getClass())) {
|
||||
return (V) result;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Argument '" + name + "' is defined as " + result.getClass().getSimpleName() + ", not " + clazz);
|
||||
|
@ -85,7 +101,8 @@ public class CommandContext<S> {
|
|||
final CommandContext that = (CommandContext) o;
|
||||
|
||||
if (!arguments.equals(that.arguments)) return false;
|
||||
if (!Iterables.elementsEqual(nodes.entrySet(), that.nodes.entrySet())) return false;
|
||||
if (!rootNode.equals(that.rootNode)) return false;
|
||||
if (nodes.size() != that.nodes.size() || !nodes.equals(that.nodes)) return false;
|
||||
if (command != null ? !command.equals(that.command) : that.command != null) return false;
|
||||
if (!source.equals(that.source)) return false;
|
||||
if (child != null ? !child.equals(that.child) : that.child != null) return false;
|
||||
|
@ -98,6 +115,7 @@ public class CommandContext<S> {
|
|||
int result = source.hashCode();
|
||||
result = 31 * result + arguments.hashCode();
|
||||
result = 31 * result + (command != null ? command.hashCode() : 0);
|
||||
result = 31 * result + rootNode.hashCode();
|
||||
result = 31 * result + nodes.hashCode();
|
||||
result = 31 * result + (child != null ? child.hashCode() : 0);
|
||||
return result;
|
||||
|
@ -115,10 +133,18 @@ public class CommandContext<S> {
|
|||
return input;
|
||||
}
|
||||
|
||||
public Map<CommandNode<S>, StringRange> getNodes() {
|
||||
public CommandNode<S> getRootNode() {
|
||||
return rootNode;
|
||||
}
|
||||
|
||||
public List<ParsedCommandNode<S>> getNodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
public boolean hasNodes() {
|
||||
return !nodes.isEmpty();
|
||||
}
|
||||
|
||||
public boolean isForked() {
|
||||
return forks;
|
||||
}
|
||||
|
|
|
@ -3,17 +3,20 @@
|
|||
|
||||
package com.mojang.brigadier.context;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.RedirectModifier;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class CommandContextBuilder<S> {
|
||||
private final Map<String, ParsedArgument<S, ?>> arguments = Maps.newLinkedHashMap();
|
||||
private final Map<CommandNode<S>, StringRange> nodes = Maps.newLinkedHashMap();
|
||||
private final Map<String, ParsedArgument<S, ?>> arguments = new LinkedHashMap<>();
|
||||
private final CommandNode<S> rootNode;
|
||||
private final List<ParsedCommandNode<S>> nodes = new ArrayList<>();
|
||||
private final CommandDispatcher<S> dispatcher;
|
||||
private S source;
|
||||
private Command<S> command;
|
||||
|
@ -22,7 +25,8 @@ public class CommandContextBuilder<S> {
|
|||
private RedirectModifier<S> modifier = null;
|
||||
private boolean forks;
|
||||
|
||||
public CommandContextBuilder(final CommandDispatcher<S> dispatcher, final S source, final int start) {
|
||||
public CommandContextBuilder(final CommandDispatcher<S> dispatcher, final S source, final CommandNode<S> rootNode, final int start) {
|
||||
this.rootNode = rootNode;
|
||||
this.dispatcher = dispatcher;
|
||||
this.source = source;
|
||||
this.range = StringRange.at(start);
|
||||
|
@ -37,6 +41,10 @@ public class CommandContextBuilder<S> {
|
|||
return source;
|
||||
}
|
||||
|
||||
public CommandNode<S> getRootNode() {
|
||||
return rootNode;
|
||||
}
|
||||
|
||||
public CommandContextBuilder<S> withArgument(final String name, final ParsedArgument<S, ?> argument) {
|
||||
this.arguments.put(name, argument);
|
||||
return this;
|
||||
|
@ -52,7 +60,7 @@ public class CommandContextBuilder<S> {
|
|||
}
|
||||
|
||||
public CommandContextBuilder<S> withNode(final CommandNode<S> node, final StringRange range) {
|
||||
nodes.put(node, range);
|
||||
nodes.add(new ParsedCommandNode<>(node, range));
|
||||
this.range = StringRange.encompassing(this.range, range);
|
||||
this.modifier = node.getRedirectModifier();
|
||||
this.forks = node.isFork();
|
||||
|
@ -60,10 +68,10 @@ public class CommandContextBuilder<S> {
|
|||
}
|
||||
|
||||
public CommandContextBuilder<S> copy() {
|
||||
final CommandContextBuilder<S> copy = new CommandContextBuilder<>(dispatcher, source, range.getStart());
|
||||
final CommandContextBuilder<S> copy = new CommandContextBuilder<>(dispatcher, source, rootNode, range.getStart());
|
||||
copy.command = command;
|
||||
copy.arguments.putAll(arguments);
|
||||
copy.nodes.putAll(nodes);
|
||||
copy.nodes.addAll(nodes);
|
||||
copy.child = child;
|
||||
copy.range = range;
|
||||
copy.forks = forks;
|
||||
|
@ -91,12 +99,12 @@ public class CommandContextBuilder<S> {
|
|||
return command;
|
||||
}
|
||||
|
||||
public Map<CommandNode<S>, StringRange> getNodes() {
|
||||
public List<ParsedCommandNode<S>> getNodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
public CommandContext<S> build(final String input) {
|
||||
return new CommandContext<>(source, input, arguments, command, nodes, range, child == null ? null : child.build(input), modifier, forks);
|
||||
return new CommandContext<>(source, input, arguments, command, rootNode, nodes, range, child == null ? null : child.build(input), modifier, forks);
|
||||
}
|
||||
|
||||
public CommandDispatcher<S> getDispatcher() {
|
||||
|
@ -106,4 +114,33 @@ public class CommandContextBuilder<S> {
|
|||
public StringRange getRange() {
|
||||
return range;
|
||||
}
|
||||
|
||||
public SuggestionContext<S> findSuggestionContext(final int cursor) {
|
||||
if (range.getStart() <= cursor) {
|
||||
if (range.getEnd() < cursor) {
|
||||
if (child != null) {
|
||||
return child.findSuggestionContext(cursor);
|
||||
} else if (!nodes.isEmpty()) {
|
||||
final ParsedCommandNode<S> last = nodes.get(nodes.size() - 1);
|
||||
return new SuggestionContext<>(last.getNode(), last.getRange().getEnd() + 1);
|
||||
} else {
|
||||
return new SuggestionContext<>(rootNode, range.getStart());
|
||||
}
|
||||
} else {
|
||||
CommandNode<S> prev = rootNode;
|
||||
for (final ParsedCommandNode<S> node : nodes) {
|
||||
final StringRange nodeRange = node.getRange();
|
||||
if (nodeRange.getStart() <= cursor && cursor <= nodeRange.getEnd()) {
|
||||
return new SuggestionContext<>(prev, nodeRange.getStart());
|
||||
}
|
||||
prev = node.getNode();
|
||||
}
|
||||
if (prev == null) {
|
||||
throw new IllegalStateException("Can't find node before cursor");
|
||||
}
|
||||
return new SuggestionContext<>(prev, range.getStart());
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Can't find node before cursor");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.context;
|
||||
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class ParsedCommandNode<S> {
|
||||
|
||||
private final CommandNode<S> node;
|
||||
|
||||
private final StringRange range;
|
||||
|
||||
public ParsedCommandNode(CommandNode<S> node, StringRange range) {
|
||||
this.node = node;
|
||||
this.range = range;
|
||||
}
|
||||
|
||||
public CommandNode<S> getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
public StringRange getRange() {
|
||||
return range;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return node + "@" + range;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ParsedCommandNode<?> that = (ParsedCommandNode<?>) o;
|
||||
return Objects.equals(node, that.node) &&
|
||||
Objects.equals(range, that.range);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(node, range);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.context;
|
||||
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
|
||||
public class SuggestionContext<S> {
|
||||
public final CommandNode<S> parent;
|
||||
public final int startPos;
|
||||
|
||||
public SuggestionContext(CommandNode<S> parent, int startPos) {
|
||||
this.parent = parent;
|
||||
this.startPos = startPos;
|
||||
}
|
||||
}
|
|
@ -16,6 +16,10 @@ public interface BuiltInExceptionProvider {
|
|||
|
||||
Dynamic2CommandExceptionType integerTooHigh();
|
||||
|
||||
Dynamic2CommandExceptionType longTooLow();
|
||||
|
||||
Dynamic2CommandExceptionType longTooHigh();
|
||||
|
||||
DynamicCommandExceptionType literalIncorrect();
|
||||
|
||||
SimpleCommandExceptionType readerExpectedStartOfQuote();
|
||||
|
@ -30,6 +34,10 @@ public interface BuiltInExceptionProvider {
|
|||
|
||||
SimpleCommandExceptionType readerExpectedInt();
|
||||
|
||||
DynamicCommandExceptionType readerInvalidLong();
|
||||
|
||||
SimpleCommandExceptionType readerExpectedLong();
|
||||
|
||||
DynamicCommandExceptionType readerInvalidDouble();
|
||||
|
||||
SimpleCommandExceptionType readerExpectedDouble();
|
||||
|
|
|
@ -15,6 +15,9 @@ public class BuiltInExceptions implements BuiltInExceptionProvider {
|
|||
private static final Dynamic2CommandExceptionType INTEGER_TOO_SMALL = new Dynamic2CommandExceptionType((found, min) -> new LiteralMessage("Integer must not be less than " + min + ", found " + found));
|
||||
private static final Dynamic2CommandExceptionType INTEGER_TOO_BIG = new Dynamic2CommandExceptionType((found, max) -> new LiteralMessage("Integer must not be more than " + max + ", found " + found));
|
||||
|
||||
private static final Dynamic2CommandExceptionType LONG_TOO_SMALL = new Dynamic2CommandExceptionType((found, min) -> new LiteralMessage("Long must not be less than " + min + ", found " + found));
|
||||
private static final Dynamic2CommandExceptionType LONG_TOO_BIG = new Dynamic2CommandExceptionType((found, max) -> new LiteralMessage("Long must not be more than " + max + ", found " + found));
|
||||
|
||||
private static final DynamicCommandExceptionType LITERAL_INCORRECT = new DynamicCommandExceptionType(expected -> new LiteralMessage("Expected literal " + expected));
|
||||
|
||||
private static final SimpleCommandExceptionType READER_EXPECTED_START_OF_QUOTE = new SimpleCommandExceptionType(new LiteralMessage("Expected quote to start a string"));
|
||||
|
@ -23,6 +26,8 @@ public class BuiltInExceptions implements BuiltInExceptionProvider {
|
|||
private static final DynamicCommandExceptionType READER_INVALID_BOOL = new DynamicCommandExceptionType(value -> new LiteralMessage("Invalid bool, expected true or false but found '" + value + "'"));
|
||||
private static final DynamicCommandExceptionType READER_INVALID_INT = new DynamicCommandExceptionType(value -> new LiteralMessage("Invalid integer '" + value + "'"));
|
||||
private static final SimpleCommandExceptionType READER_EXPECTED_INT = new SimpleCommandExceptionType(new LiteralMessage("Expected integer"));
|
||||
private static final DynamicCommandExceptionType READER_INVALID_LONG = new DynamicCommandExceptionType(value -> new LiteralMessage("Invalid long '" + value + "'"));
|
||||
private static final SimpleCommandExceptionType READER_EXPECTED_LONG = new SimpleCommandExceptionType((new LiteralMessage("Expected long")));
|
||||
private static final DynamicCommandExceptionType READER_INVALID_DOUBLE = new DynamicCommandExceptionType(value -> new LiteralMessage("Invalid double '" + value + "'"));
|
||||
private static final SimpleCommandExceptionType READER_EXPECTED_DOUBLE = new SimpleCommandExceptionType(new LiteralMessage("Expected double"));
|
||||
private static final DynamicCommandExceptionType READER_INVALID_FLOAT = new DynamicCommandExceptionType(value -> new LiteralMessage("Invalid float '" + value + "'"));
|
||||
|
@ -65,6 +70,16 @@ public class BuiltInExceptions implements BuiltInExceptionProvider {
|
|||
return INTEGER_TOO_BIG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic2CommandExceptionType longTooLow() {
|
||||
return LONG_TOO_SMALL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic2CommandExceptionType longTooHigh() {
|
||||
return LONG_TOO_BIG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicCommandExceptionType literalIncorrect() {
|
||||
return LITERAL_INCORRECT;
|
||||
|
@ -100,6 +115,16 @@ public class BuiltInExceptions implements BuiltInExceptionProvider {
|
|||
return READER_EXPECTED_INT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicCommandExceptionType readerInvalidLong() {
|
||||
return READER_INVALID_LONG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleCommandExceptionType readerExpectedLong() {
|
||||
return READER_EXPECTED_LONG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicCommandExceptionType readerInvalidDouble() {
|
||||
return READER_INVALID_DOUBLE;
|
||||
|
|
|
@ -3,10 +3,9 @@
|
|||
|
||||
package com.mojang.brigadier.suggestion;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.mojang.brigadier.context.StringRange;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -15,7 +14,7 @@ import java.util.Set;
|
|||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class Suggestions {
|
||||
private static final Suggestions EMPTY = new Suggestions(StringRange.at(0), Lists.newArrayList());
|
||||
private static final Suggestions EMPTY = new Suggestions(StringRange.at(0), new ArrayList<>());
|
||||
|
||||
private final StringRange range;
|
||||
private final List<Suggestion> suggestions;
|
||||
|
@ -92,11 +91,11 @@ public class Suggestions {
|
|||
end = Math.max(suggestion.getRange().getEnd(), end);
|
||||
}
|
||||
final StringRange range = new StringRange(start, end);
|
||||
final Set<Suggestion> texts = Sets.newHashSet();
|
||||
final Set<Suggestion> texts = new HashSet<>();
|
||||
for (final Suggestion suggestion : suggestions) {
|
||||
texts.add(suggestion.expand(command, range));
|
||||
}
|
||||
final List<Suggestion> sorted = Lists.newArrayList(texts);
|
||||
final List<Suggestion> sorted = new ArrayList<>(texts);
|
||||
sorted.sort((a, b) -> a.compareToIgnoreCase(b));
|
||||
return new Suggestions(range, sorted);
|
||||
}
|
||||
|
|
|
@ -8,18 +8,27 @@ import com.mojang.brigadier.context.StringRange;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class SuggestionsBuilder {
|
||||
private final String input;
|
||||
private final String inputLowerCase;
|
||||
private final int start;
|
||||
private final String remaining;
|
||||
private final String remainingLowerCase;
|
||||
private final List<Suggestion> result = new ArrayList<>();
|
||||
|
||||
public SuggestionsBuilder(final String input, final int start) {
|
||||
public SuggestionsBuilder(final String input, final String inputLowerCase, final int start) {
|
||||
this.input = input;
|
||||
this.inputLowerCase = inputLowerCase;
|
||||
this.start = start;
|
||||
this.remaining = input.substring(start);
|
||||
this.remainingLowerCase = inputLowerCase.substring(start);
|
||||
}
|
||||
|
||||
public SuggestionsBuilder(final String input, final int start) {
|
||||
this(input, input.toLowerCase(Locale.ROOT), start);
|
||||
}
|
||||
|
||||
public String getInput() {
|
||||
|
@ -34,6 +43,10 @@ public class SuggestionsBuilder {
|
|||
return remaining;
|
||||
}
|
||||
|
||||
public String getRemainingLowerCase() {
|
||||
return remainingLowerCase;
|
||||
}
|
||||
|
||||
public Suggestions build() {
|
||||
return Suggestions.create(input, result);
|
||||
}
|
||||
|
@ -74,10 +87,10 @@ public class SuggestionsBuilder {
|
|||
}
|
||||
|
||||
public SuggestionsBuilder createOffset(final int start) {
|
||||
return new SuggestionsBuilder(input, start);
|
||||
return new SuggestionsBuilder(input, inputLowerCase, start);
|
||||
}
|
||||
|
||||
public SuggestionsBuilder restart() {
|
||||
return new SuggestionsBuilder(input, start);
|
||||
return createOffset(start);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
package com.mojang.brigadier.tree;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.Message;
|
||||
import com.mojang.brigadier.RedirectModifier;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
|
@ -29,7 +30,11 @@ public class ArgumentCommandNode<S, T> extends CommandNode<S> {
|
|||
private final SuggestionProvider<S> customSuggestions;
|
||||
|
||||
public ArgumentCommandNode(final String name, final ArgumentType<T> type, final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks, final SuggestionProvider<S> customSuggestions) {
|
||||
super(command, requirement, redirect, modifier, forks);
|
||||
this(name, type, command, requirement, redirect, modifier, forks, customSuggestions, null);
|
||||
}
|
||||
|
||||
public ArgumentCommandNode(final String name, final ArgumentType<T> type, final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks, final SuggestionProvider<S> customSuggestions, final Message description) {
|
||||
super(command, requirement, redirect, modifier, forks, description);
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.customSuggestions = customSuggestions;
|
||||
|
|
|
@ -3,13 +3,7 @@
|
|||
|
||||
package com.mojang.brigadier.tree;
|
||||
|
||||
import com.google.common.collect.ComparisonChain;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.mojang.brigadier.AmbiguityConsumer;
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.RedirectModifier;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.*;
|
||||
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.context.CommandContextBuilder;
|
||||
|
@ -19,35 +13,45 @@ import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
|||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
|
||||
private Map<String, CommandNode<S>> children = Maps.newLinkedHashMap();
|
||||
private Map<String, LiteralCommandNode<S>> literals = Maps.newLinkedHashMap();
|
||||
private Map<String, ArgumentCommandNode<S, ?>> arguments = Maps.newLinkedHashMap();
|
||||
private final Map<String, CommandNode<S>> children = new LinkedHashMap<>();
|
||||
private final Map<String, LiteralCommandNode<S>> literals = new LinkedHashMap<>();
|
||||
private final Map<String, ArgumentCommandNode<S, ?>> arguments = new LinkedHashMap<>();
|
||||
private final Predicate<S> requirement;
|
||||
private final CommandNode<S> redirect;
|
||||
private final RedirectModifier<S> modifier;
|
||||
private final boolean forks;
|
||||
private Command<S> command;
|
||||
private Message description;
|
||||
|
||||
protected CommandNode(final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks) {
|
||||
this(command, requirement, redirect, modifier, forks, null);
|
||||
}
|
||||
|
||||
protected CommandNode(final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks, final Message description) {
|
||||
this.command = command;
|
||||
this.requirement = requirement;
|
||||
this.redirect = redirect;
|
||||
this.modifier = modifier;
|
||||
this.forks = forks;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Command<S> getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
public Message getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public Collection<CommandNode<S>> getChildren() {
|
||||
return children.values();
|
||||
}
|
||||
|
@ -90,12 +94,10 @@ public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
|
|||
arguments.put(node.getName(), (ArgumentCommandNode<S, ?>) node);
|
||||
}
|
||||
}
|
||||
|
||||
children = children.entrySet().stream().sorted(Map.Entry.comparingByValue()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
|
||||
}
|
||||
|
||||
public void findAmbiguities(final AmbiguityConsumer<S> consumer) {
|
||||
Set<String> matches = Sets.newHashSet();
|
||||
Set<String> matches = new HashSet<>();
|
||||
|
||||
for (final CommandNode<S> child : children.values()) {
|
||||
for (final CommandNode<S> sibling : children.values()) {
|
||||
|
@ -111,7 +113,7 @@ public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
|
|||
|
||||
if (matches.size() > 0) {
|
||||
consumer.ambiguous(this, child, sibling, matches);
|
||||
matches = Sets.newHashSet();
|
||||
matches = new HashSet<>();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,11 +178,11 @@ public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
|
|||
|
||||
@Override
|
||||
public int compareTo(final CommandNode<S> o) {
|
||||
return ComparisonChain
|
||||
.start()
|
||||
.compareTrueFirst(this instanceof LiteralCommandNode, o instanceof LiteralCommandNode)
|
||||
.compare(getSortedKey(), o.getSortedKey())
|
||||
.result();
|
||||
if (this instanceof LiteralCommandNode == o instanceof LiteralCommandNode) {
|
||||
return getSortedKey().compareTo(o.getSortedKey());
|
||||
}
|
||||
|
||||
return (o instanceof LiteralCommandNode) ? 1 : -1;
|
||||
}
|
||||
|
||||
public boolean isFork() {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
package com.mojang.brigadier.tree;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.Message;
|
||||
import com.mojang.brigadier.RedirectModifier;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
|
@ -16,15 +17,22 @@ import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
|||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class LiteralCommandNode<S> extends CommandNode<S> {
|
||||
private final String literal;
|
||||
private final String literalLowerCase;
|
||||
|
||||
public LiteralCommandNode(final String literal, final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks) {
|
||||
super(command, requirement, redirect, modifier, forks);
|
||||
this(literal, command, requirement, redirect, modifier, forks, null);
|
||||
}
|
||||
|
||||
public LiteralCommandNode(final String literal, final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks, final Message description) {
|
||||
super(command, requirement, redirect, modifier, forks, description);
|
||||
this.literal = literal;
|
||||
this.literalLowerCase = literal.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
public String getLiteral() {
|
||||
|
@ -66,7 +74,7 @@ public class LiteralCommandNode<S> extends CommandNode<S> {
|
|||
|
||||
@Override
|
||||
public CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) {
|
||||
if (literal.toLowerCase().startsWith(builder.getRemaining().toLowerCase())) {
|
||||
if (literalLowerCase.startsWith(builder.getRemainingLowerCase())) {
|
||||
return builder.suggest(literal).buildFuture();
|
||||
} else {
|
||||
return Suggestions.empty();
|
||||
|
|
|
@ -17,7 +17,7 @@ import java.util.concurrent.CompletableFuture;
|
|||
|
||||
public class RootCommandNode<S> extends CommandNode<S> {
|
||||
public RootCommandNode() {
|
||||
super(null, c -> true, null, s -> Collections.singleton(s.getSource()), false);
|
||||
super(null, c -> true, null, s -> Collections.singleton(s.getSource()), false, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
package com.mojang.brigadier;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.context.CommandContextBuilder;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
|
@ -14,6 +15,9 @@ import org.junit.runner.RunWith;
|
|||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
||||
import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal;
|
||||
import static com.mojang.brigadier.builder.RequiredArgumentBuilder.argument;
|
||||
|
@ -259,23 +263,33 @@ public class CommandDispatcherTest {
|
|||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testExecuteRedirectedMultipleTimes() throws Exception {
|
||||
subject.register(literal("actual").executes(command));
|
||||
subject.register(literal("redirected").redirect(subject.getRoot()));
|
||||
final LiteralCommandNode<Object> concreteNode = subject.register(literal("actual").executes(command));
|
||||
final LiteralCommandNode<Object> redirectNode = subject.register(literal("redirected").redirect(subject.getRoot()));
|
||||
|
||||
final String input = "redirected redirected actual";
|
||||
|
||||
final ParseResults<Object> parse = subject.parse(input, source);
|
||||
assertThat(parse.getContext().getRange().get(input), equalTo("redirected"));
|
||||
assertThat(parse.getContext().getNodes().size(), is(1));
|
||||
assertThat(parse.getContext().getRootNode(), is(subject.getRoot()));
|
||||
assertThat(parse.getContext().getNodes().get(0).getRange(), equalTo(parse.getContext().getRange()));
|
||||
assertThat(parse.getContext().getNodes().get(0).getNode(), is(redirectNode));
|
||||
|
||||
final CommandContextBuilder<Object> child1 = parse.getContext().getChild();
|
||||
assertThat(child1, is(notNullValue()));
|
||||
assertThat(child1.getRange().get(input), equalTo("redirected redirected"));
|
||||
assertThat(child1.getNodes().size(), is(2));
|
||||
assertThat(child1.getRange().get(input), equalTo("redirected"));
|
||||
assertThat(child1.getNodes().size(), is(1));
|
||||
assertThat(child1.getRootNode(), is(subject.getRoot()));
|
||||
assertThat(child1.getNodes().get(0).getRange(), equalTo(child1.getRange()));
|
||||
assertThat(child1.getNodes().get(0).getNode(), is(redirectNode));
|
||||
|
||||
final CommandContextBuilder<Object> child2 = child1.getChild();
|
||||
assertThat(child2, is(notNullValue()));
|
||||
assertThat(child2.getRange().get(input), equalTo("redirected actual"));
|
||||
assertThat(child2.getNodes().size(), is(2));
|
||||
assertThat(child2.getRange().get(input), equalTo("actual"));
|
||||
assertThat(child2.getNodes().size(), is(1));
|
||||
assertThat(child2.getRootNode(), is(subject.getRoot()));
|
||||
assertThat(child2.getNodes().get(0).getRange(), equalTo(child2.getRange()));
|
||||
assertThat(child2.getNodes().get(0).getNode(), is(concreteNode));
|
||||
|
||||
assertThat(subject.execute(parse), is(42));
|
||||
verify(command).run(any(CommandContext.class));
|
||||
|
@ -290,19 +304,25 @@ public class CommandDispatcherTest {
|
|||
|
||||
when(modifier.apply(argThat(hasProperty("source", is(source))))).thenReturn(Lists.newArrayList(source1, source2));
|
||||
|
||||
subject.register(literal("actual").executes(command));
|
||||
subject.register(literal("redirected").fork(subject.getRoot(), modifier));
|
||||
final LiteralCommandNode<Object> concreteNode = subject.register(literal("actual").executes(command));
|
||||
final LiteralCommandNode<Object> redirectNode = subject.register(literal("redirected").fork(subject.getRoot(), modifier));
|
||||
|
||||
final String input = "redirected actual";
|
||||
final ParseResults<Object> parse = subject.parse(input, source);
|
||||
assertThat(parse.getContext().getRange().get(input), equalTo("redirected"));
|
||||
assertThat(parse.getContext().getNodes().size(), is(1));
|
||||
assertThat(parse.getContext().getRootNode(), equalTo(subject.getRoot()));
|
||||
assertThat(parse.getContext().getNodes().get(0).getRange(), equalTo(parse.getContext().getRange()));
|
||||
assertThat(parse.getContext().getNodes().get(0).getNode(), is(redirectNode));
|
||||
assertThat(parse.getContext().getSource(), is(source));
|
||||
|
||||
final CommandContextBuilder<Object> parent = parse.getContext().getChild();
|
||||
assertThat(parent, is(notNullValue()));
|
||||
assertThat(parent.getRange().get(input), equalTo("redirected actual"));
|
||||
assertThat(parent.getNodes().size(), is(2));
|
||||
assertThat(parent.getRange().get(input), equalTo("actual"));
|
||||
assertThat(parent.getNodes().size(), is(1));
|
||||
assertThat(parse.getContext().getRootNode(), equalTo(subject.getRoot()));
|
||||
assertThat(parent.getNodes().get(0).getRange(), equalTo(parent.getRange()));
|
||||
assertThat(parent.getNodes().get(0).getNode(), is(concreteNode));
|
||||
assertThat(parent.getSource(), is(source));
|
||||
|
||||
assertThat(subject.execute(parse), is(2));
|
||||
|
@ -310,6 +330,37 @@ public class CommandDispatcherTest {
|
|||
verify(command).run(argThat(hasProperty("source", is(source2))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncompleteRedirectShouldThrow() {
|
||||
final LiteralCommandNode<Object> foo = subject.register(literal("foo")
|
||||
.then(literal("bar")
|
||||
.then(argument("value", integer()).executes(context -> IntegerArgumentType.getInteger(context, "value"))))
|
||||
.then(literal("awa").executes(context -> 2)));
|
||||
final LiteralCommandNode<Object> baz = subject.register(literal("baz").redirect(foo));
|
||||
try {
|
||||
int result = subject.execute("baz bar", source);
|
||||
fail("Should have thrown an exception");
|
||||
} catch (CommandSyntaxException e) {
|
||||
assertThat(e.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedirectModifierEmptyResult() {
|
||||
final LiteralCommandNode<Object> foo = subject.register(literal("foo")
|
||||
.then(literal("bar")
|
||||
.then(argument("value", integer()).executes(context -> IntegerArgumentType.getInteger(context, "value"))))
|
||||
.then(literal("awa").executes(context -> 2)));
|
||||
final RedirectModifier<Object> emptyModifier = context -> Collections.emptyList();
|
||||
final LiteralCommandNode<Object> baz = subject.register(literal("baz").fork(foo, emptyModifier));
|
||||
try {
|
||||
int result = subject.execute("baz bar 100", source);
|
||||
assertThat(result, is(0)); // No commands executed, so result is 0
|
||||
} catch (CommandSyntaxException e) {
|
||||
fail("Should not throw an exception");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteOrphanedSubcommand() throws Exception {
|
||||
subject.register(literal("foo").then(
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
package com.mojang.brigadier;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Iterators;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
|
@ -102,11 +103,11 @@ public class CommandDispatcherUsagesTest {
|
|||
}
|
||||
|
||||
private CommandNode<Object> get(final String command) {
|
||||
return Iterators.getLast(subject.parse(command, source).getContext().getNodes().keySet().iterator());
|
||||
return Iterables.getLast(subject.parse(command, source).getContext().getNodes()).getNode();
|
||||
}
|
||||
|
||||
private CommandNode<Object> get(final StringReader command) {
|
||||
return Iterators.getLast(subject.parse(command, source).getContext().getNodes().keySet().iterator());
|
||||
return Iterables.getLast(subject.parse(command, source).getContext().getNodes()).getNode();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -189,10 +190,10 @@ public class CommandDispatcherUsagesTest {
|
|||
|
||||
final Map<CommandNode<Object>, String> results = subject.getSmartUsage(get(offsetH), source);
|
||||
assertThat(results, equalTo(ImmutableMap.builder()
|
||||
.put(get("h 1"), "[1] i")
|
||||
.put(get("h 2"), "[2] i ii")
|
||||
.put(get("h 3"), "[3]")
|
||||
.build()
|
||||
.put(get("h 1"), "[1] i")
|
||||
.put(get("h 2"), "[2] i ii")
|
||||
.put(get("h 3"), "[3]")
|
||||
.build()
|
||||
));
|
||||
}
|
||||
}
|
|
@ -14,6 +14,8 @@ import org.junit.runner.RunWith;
|
|||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.word;
|
||||
import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal;
|
||||
|
@ -33,6 +35,18 @@ public class CommandSuggestionsTest {
|
|||
subject = new CommandDispatcher<>();
|
||||
}
|
||||
|
||||
private void testSuggestions(final String contents, final int cursor, final StringRange range, final String... suggestions) {
|
||||
final Suggestions result = subject.getCompletionSuggestions(subject.parse(contents, source), cursor).join();
|
||||
assertThat(result.getRange(), equalTo(range));
|
||||
|
||||
final List<Suggestion> expected = Lists.newArrayList();
|
||||
for (final String suggestion : suggestions) {
|
||||
expected.add(new Suggestion(range, suggestion));
|
||||
}
|
||||
|
||||
assertThat(result.getList(), equalTo(expected));
|
||||
}
|
||||
|
||||
private static StringReader inputWithOffset(final String input, final int offset) {
|
||||
final StringReader result = new StringReader(input);
|
||||
result.setCursor(offset);
|
||||
|
@ -102,6 +116,31 @@ public class CommandSuggestionsTest {
|
|||
assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(StringRange.at(7), "bar"), new Suggestion(StringRange.at(7), "baz"), new Suggestion(StringRange.at(7), "foo"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCompletionSuggestions_movingCursor_subCommands() throws Exception {
|
||||
subject.register(
|
||||
literal("parent_one")
|
||||
.then(literal("faz"))
|
||||
.then(literal("fbz"))
|
||||
.then(literal("gaz"))
|
||||
);
|
||||
|
||||
subject.register(
|
||||
literal("parent_two")
|
||||
);
|
||||
|
||||
testSuggestions("parent_one faz ", 0, StringRange.at(0), "parent_one", "parent_two");
|
||||
testSuggestions("parent_one faz ", 1, StringRange.between(0, 1), "parent_one", "parent_two");
|
||||
testSuggestions("parent_one faz ", 7, StringRange.between(0, 7), "parent_one", "parent_two");
|
||||
testSuggestions("parent_one faz ", 8, StringRange.between(0, 8), "parent_one");
|
||||
testSuggestions("parent_one faz ", 10, StringRange.at(0));
|
||||
testSuggestions("parent_one faz ", 11, StringRange.at(11), "faz", "fbz", "gaz");
|
||||
testSuggestions("parent_one faz ", 12, StringRange.between(11, 12), "faz", "fbz");
|
||||
testSuggestions("parent_one faz ", 13, StringRange.between(11, 13), "faz");
|
||||
testSuggestions("parent_one faz ", 14, StringRange.at(0));
|
||||
testSuggestions("parent_one faz ", 15, StringRange.at(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCompletionSuggestions_subCommands_partial() throws Exception {
|
||||
subject.register(
|
||||
|
@ -121,10 +160,10 @@ public class CommandSuggestionsTest {
|
|||
@Test
|
||||
public void getCompletionSuggestions_subCommands_partial_withInputOffset() throws Exception {
|
||||
subject.register(
|
||||
literal("parent")
|
||||
.then(literal("foo"))
|
||||
.then(literal("bar"))
|
||||
.then(literal("baz"))
|
||||
literal("parent")
|
||||
.then(literal("foo"))
|
||||
.then(literal("bar"))
|
||||
.then(literal("baz"))
|
||||
);
|
||||
|
||||
final ParseResults<Object> parse = subject.parse(inputWithOffset("junk parent b", 5), source);
|
||||
|
@ -158,6 +197,29 @@ public class CommandSuggestionsTest {
|
|||
assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(StringRange.between(9, 10), "sub"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCompletionSuggestions_movingCursor_redirect() throws Exception {
|
||||
final LiteralCommandNode<Object> actualOne = subject.register(literal("actual_one")
|
||||
.then(literal("faz"))
|
||||
.then(literal("fbz"))
|
||||
.then(literal("gaz"))
|
||||
);
|
||||
|
||||
final LiteralCommandNode<Object> actualTwo = subject.register(literal("actual_two"));
|
||||
|
||||
subject.register(literal("redirect_one").redirect(actualOne));
|
||||
subject.register(literal("redirect_two").redirect(actualOne));
|
||||
|
||||
testSuggestions("redirect_one faz ", 0, StringRange.at(0), "actual_one", "actual_two", "redirect_one", "redirect_two");
|
||||
testSuggestions("redirect_one faz ", 9, StringRange.between(0, 9), "redirect_one", "redirect_two");
|
||||
testSuggestions("redirect_one faz ", 10, StringRange.between(0, 10), "redirect_one");
|
||||
testSuggestions("redirect_one faz ", 12, StringRange.at(0));
|
||||
testSuggestions("redirect_one faz ", 13, StringRange.at(13), "faz", "fbz", "gaz");
|
||||
testSuggestions("redirect_one faz ", 14, StringRange.between(13, 14), "faz", "fbz");
|
||||
testSuggestions("redirect_one faz ", 15, StringRange.between(13, 15), "faz");
|
||||
testSuggestions("redirect_one faz ", 16, StringRange.at(0));
|
||||
testSuggestions("redirect_one faz ", 17, StringRange.at(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCompletionSuggestions_redirectPartial_withInputOffset() throws Exception {
|
||||
|
|
|
@ -156,6 +156,30 @@ public class StringReaderTest {
|
|||
assertThat(reader.getRemaining(), equalTo(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readSingleQuotedString() throws Exception {
|
||||
final StringReader reader = new StringReader("'hello world'");
|
||||
assertThat(reader.readQuotedString(), equalTo("hello world"));
|
||||
assertThat(reader.getRead(), equalTo("'hello world'"));
|
||||
assertThat(reader.getRemaining(), equalTo(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readMixedQuotedString_doubleInsideSingle() throws Exception {
|
||||
final StringReader reader = new StringReader("'hello \"world\"'");
|
||||
assertThat(reader.readQuotedString(), equalTo("hello \"world\""));
|
||||
assertThat(reader.getRead(), equalTo("'hello \"world\"'"));
|
||||
assertThat(reader.getRemaining(), equalTo(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readMixedQuotedString_singleInsideDouble() throws Exception {
|
||||
final StringReader reader = new StringReader("\"hello 'world'\"");
|
||||
assertThat(reader.readQuotedString(), equalTo("hello 'world'"));
|
||||
assertThat(reader.getRead(), equalTo("\"hello 'world'\""));
|
||||
assertThat(reader.getRemaining(), equalTo(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readQuotedString_empty() throws Exception {
|
||||
final StringReader reader = new StringReader("");
|
||||
|
@ -242,6 +266,40 @@ public class StringReaderTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readQuotedString_invalidQuoteEscape() throws Exception {
|
||||
try {
|
||||
new StringReader("'hello\\\"\'world").readQuotedString();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidEscape()));
|
||||
assertThat(ex.getCursor(), is(7));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readString_noQuotes() throws Exception {
|
||||
final StringReader reader = new StringReader("hello world");
|
||||
assertThat(reader.readString(), equalTo("hello"));
|
||||
assertThat(reader.getRead(), equalTo("hello"));
|
||||
assertThat(reader.getRemaining(), equalTo(" world"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readString_singleQuotes() throws Exception {
|
||||
final StringReader reader = new StringReader("'hello world'");
|
||||
assertThat(reader.readString(), equalTo("hello world"));
|
||||
assertThat(reader.getRead(), equalTo("'hello world'"));
|
||||
assertThat(reader.getRemaining(), equalTo(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readString_doubleQuotes() throws Exception {
|
||||
final StringReader reader = new StringReader("\"hello world\"");
|
||||
assertThat(reader.readString(), equalTo("hello world"));
|
||||
assertThat(reader.getRead(), equalTo("\"hello world\""));
|
||||
assertThat(reader.getRemaining(), equalTo(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readInt() throws Exception {
|
||||
final StringReader reader = new StringReader("1234567890");
|
||||
|
@ -294,6 +352,58 @@ public class StringReaderTest {
|
|||
assertThat(reader.getRemaining(), equalTo("foo bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readLong() throws Exception {
|
||||
final StringReader reader = new StringReader("1234567890");
|
||||
assertThat(reader.readLong(), is(1234567890L));
|
||||
assertThat(reader.getRead(), equalTo("1234567890"));
|
||||
assertThat(reader.getRemaining(), equalTo(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readLong_negative() throws Exception {
|
||||
final StringReader reader = new StringReader("-1234567890");
|
||||
assertThat(reader.readLong(), is(-1234567890L));
|
||||
assertThat(reader.getRead(), equalTo("-1234567890"));
|
||||
assertThat(reader.getRemaining(), equalTo(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readLong_invalid() throws Exception {
|
||||
try {
|
||||
new StringReader("12.34").readLong();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidLong()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readLong_none() throws Exception {
|
||||
try {
|
||||
new StringReader("").readLong();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedLong()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readLong_withRemaining() throws Exception {
|
||||
final StringReader reader = new StringReader("1234567890 foo bar");
|
||||
assertThat(reader.readLong(), is(1234567890L));
|
||||
assertThat(reader.getRead(), equalTo("1234567890"));
|
||||
assertThat(reader.getRemaining(), equalTo(" foo bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readLong_withRemainingImmediate() throws Exception {
|
||||
final StringReader reader = new StringReader("1234567890foo bar");
|
||||
assertThat(reader.readLong(), is(1234567890L));
|
||||
assertThat(reader.getRead(), equalTo("1234567890"));
|
||||
assertThat(reader.getRemaining(), equalTo("foo bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readDouble() throws Exception {
|
||||
final StringReader reader = new StringReader("123");
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.arguments;
|
||||
|
||||
import com.google.common.testing.EqualsTester;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.context.CommandContextBuilder;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import static com.mojang.brigadier.arguments.LongArgumentType.longArg;
|
||||
import static org.hamcrest.Matchers.hasToString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class LongArgumentTypeTest {
|
||||
private LongArgumentType type;
|
||||
@Mock
|
||||
private CommandContextBuilder<Object> context;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
type = longArg(-100, 1_000_000_000_000L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse() throws Exception {
|
||||
final StringReader reader = new StringReader("15");
|
||||
assertThat(longArg().parse(reader), is(15L));
|
||||
assertThat(reader.canRead(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse_tooSmall() throws Exception {
|
||||
final StringReader reader = new StringReader("-5");
|
||||
try {
|
||||
longArg(0, 100).parse(reader);
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.longTooLow()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse_tooBig() throws Exception {
|
||||
final StringReader reader = new StringReader("5");
|
||||
try {
|
||||
longArg(-100, 0).parse(reader);
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.longTooHigh()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals() throws Exception {
|
||||
new EqualsTester()
|
||||
.addEqualityGroup(longArg(), longArg())
|
||||
.addEqualityGroup(longArg(-100, 100), longArg(-100, 100))
|
||||
.addEqualityGroup(longArg(-100, 50), longArg(-100, 50))
|
||||
.addEqualityGroup(longArg(-50, 100), longArg(-50, 100))
|
||||
.testEquals();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToString() throws Exception {
|
||||
assertThat(longArg(), hasToString("longArg()"));
|
||||
assertThat(longArg(-100), hasToString("longArg(-100)"));
|
||||
assertThat(longArg(-100, 100), hasToString("longArg(-100, 100)"));
|
||||
assertThat(longArg(Long.MIN_VALUE, 100), hasToString("longArg(-9223372036854775808, 100)"));
|
||||
}
|
||||
}
|
|
@ -7,13 +7,13 @@ import com.google.common.testing.EqualsTester;
|
|||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
import com.mojang.brigadier.tree.RootCommandNode;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
@ -26,9 +26,12 @@ public class CommandContextTest {
|
|||
@Mock
|
||||
private CommandDispatcher<Object> dispatcher;
|
||||
|
||||
@Mock
|
||||
private CommandNode<Object> rootNode;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
builder = new CommandContextBuilder<>(dispatcher, source, 0);
|
||||
builder = new CommandContextBuilder<>(dispatcher, source, rootNode, 0);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
|
@ -53,22 +56,30 @@ public class CommandContextTest {
|
|||
assertThat(builder.build("").getSource(), is(source));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRootNode() throws Exception {
|
||||
assertThat(builder.build("").getRootNode(), is(rootNode));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testEquals() throws Exception {
|
||||
final Object otherSource = new Object();
|
||||
final Command<Object> command = mock(Command.class);
|
||||
final Command<Object> otherCommand = mock(Command.class);
|
||||
final CommandNode<Object> rootNode = mock(CommandNode.class);
|
||||
final CommandNode<Object> otherRootNode = mock(CommandNode.class);
|
||||
final CommandNode<Object> node = mock(CommandNode.class);
|
||||
final CommandNode<Object> otherNode = mock(CommandNode.class);
|
||||
new EqualsTester()
|
||||
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, 0).build(""), new CommandContextBuilder<>(dispatcher, source, 0).build(""))
|
||||
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, otherSource, 0).build(""), new CommandContextBuilder<>(dispatcher, otherSource, 0).build(""))
|
||||
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, 0).withCommand(command).build(""), new CommandContextBuilder<>(dispatcher, source, 0).withCommand(command).build(""))
|
||||
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, 0).withCommand(otherCommand).build(""), new CommandContextBuilder<>(dispatcher, source, 0).withCommand(otherCommand).build(""))
|
||||
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, 0).withArgument("foo", new ParsedArgument<>(0, 1, 123)).build("123"), new CommandContextBuilder<>(dispatcher, source, 0).withArgument("foo", new ParsedArgument<>(0, 1, 123)).build("123"))
|
||||
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, 0).withNode(node, StringRange.between(0, 3)).withNode(otherNode, StringRange.between(4, 6)).build("123 456"), new CommandContextBuilder<>(dispatcher, source, 0).withNode(node, StringRange.between(0, 3)).withNode(otherNode, StringRange.between(4, 6)).build("123 456"))
|
||||
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, 0).withNode(otherNode, StringRange.between(0, 3)).withNode(node, StringRange.between(4, 6)).build("123 456"), new CommandContextBuilder<>(dispatcher, source, 0).withNode(otherNode, StringRange.between(0, 3)).withNode(node, StringRange.between(4, 6)).build("123 456"))
|
||||
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, rootNode, 0).build(""), new CommandContextBuilder<>(dispatcher, source, rootNode, 0).build(""))
|
||||
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, otherRootNode, 0).build(""), new CommandContextBuilder<>(dispatcher, source, otherRootNode, 0).build(""))
|
||||
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, otherSource, rootNode, 0).build(""), new CommandContextBuilder<>(dispatcher, otherSource, rootNode, 0).build(""))
|
||||
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, rootNode, 0).withCommand(command).build(""), new CommandContextBuilder<>(dispatcher, source, rootNode, 0).withCommand(command).build(""))
|
||||
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, rootNode, 0).withCommand(otherCommand).build(""), new CommandContextBuilder<>(dispatcher, source, rootNode, 0).withCommand(otherCommand).build(""))
|
||||
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, rootNode, 0).withArgument("foo", new ParsedArgument<>(0, 1, 123)).build("123"), new CommandContextBuilder<>(dispatcher, source, rootNode, 0).withArgument("foo", new ParsedArgument<>(0, 1, 123)).build("123"))
|
||||
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, rootNode, 0).withNode(node, StringRange.between(0, 3)).withNode(otherNode, StringRange.between(4, 6)).build("123 456"), new CommandContextBuilder<>(dispatcher, source, rootNode, 0).withNode(node, StringRange.between(0, 3)).withNode(otherNode, StringRange.between(4, 6)).build("123 456"))
|
||||
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, rootNode, 0).withNode(otherNode, StringRange.between(0, 3)).withNode(node, StringRange.between(4, 6)).build("123 456"), new CommandContextBuilder<>(dispatcher, source, rootNode, 0).withNode(otherNode, StringRange.between(0, 3)).withNode(node, StringRange.between(4, 6)).build("123 456"))
|
||||
.testEquals();
|
||||
}
|
||||
}
|
|
@ -32,7 +32,7 @@ public class ArgumentCommandNodeTest extends AbstractCommandNodeTest {
|
|||
@Before
|
||||
public void setUp() throws Exception {
|
||||
node = argument("foo", integer()).build();
|
||||
contextBuilder = new CommandContextBuilder<>(new CommandDispatcher<>(), new Object(), 0);
|
||||
contextBuilder = new CommandContextBuilder<>(new CommandDispatcher<>(), new Object(), new RootCommandNode<>(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -37,7 +37,7 @@ public class LiteralCommandNodeTest extends AbstractCommandNodeTest {
|
|||
@Before
|
||||
public void setUp() throws Exception {
|
||||
node = literal("foo").build();
|
||||
contextBuilder = new CommandContextBuilder<>(new CommandDispatcher<>(), new Object(), 0);
|
||||
contextBuilder = new CommandContextBuilder<>(new CommandDispatcher<>(), new Object(), new RootCommandNode<>(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -34,7 +34,7 @@ public class RootCommandNodeTest extends AbstractCommandNodeTest {
|
|||
@Test
|
||||
public void testParse() throws Exception {
|
||||
final StringReader reader = new StringReader("hello world");
|
||||
node.parse(reader, new CommandContextBuilder<>(new CommandDispatcher<>(), new Object(), 0));
|
||||
node.parse(reader, new CommandContextBuilder<>(new CommandDispatcher<>(), new Object(), new RootCommandNode<>(), 0));
|
||||
assertThat(reader.getCursor(), is(0));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue