Compare commits
35 commits
feature/co
...
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 | ||
|
8e9859e471 | ||
|
1fe283c136 | ||
|
189c7d0433 | ||
|
872658c737 |
83 changed files with 1602 additions and 206 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'
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
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:
|
||||
|
||||
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.
|
135
README.md
Normal file
135
README.md
Normal file
|
@ -0,0 +1,135 @@
|
|||
# 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
|
||||
Brigadier is available to Maven & Gradle via `libraries.minecraft.net`. Its group is `com.mojang`, and artifact name is `brigadier`.
|
||||
|
||||
## Gradle
|
||||
First include our repository:
|
||||
```groovy
|
||||
maven {
|
||||
url "https://libraries.minecraft.net"
|
||||
}
|
||||
```
|
||||
|
||||
And then use this library (change `(the latest version)` to the latest version!):
|
||||
```groovy
|
||||
compile 'com.mojang:brigadier:(the latest version)'
|
||||
```
|
||||
|
||||
## Maven
|
||||
First include our repository:
|
||||
```xml
|
||||
<repository>
|
||||
<id>minecraft-libraries</id>
|
||||
<name>Minecraft Libraries</name>
|
||||
<url>https://libraries.minecraft.net</url>
|
||||
</repository>
|
||||
```
|
||||
|
||||
And then use this library (change `(the latest version)` to the latest version!):
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.mojang</groupId>
|
||||
<artifactId>brigadier</artifactId>
|
||||
<version>(the latest version)</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
# Contributing
|
||||
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.
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
||||
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
# 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 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 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<>();
|
||||
|
||||
dispatcher.register(
|
||||
literal("foo")
|
||||
.then(
|
||||
argument("bar", integer())
|
||||
.executes(c -> {
|
||||
System.out.println("Bar is " + getInteger(c, "bar"));
|
||||
return 1;
|
||||
})
|
||||
)
|
||||
.executes(c -> {
|
||||
System.out.println("Called foo with no arguments");
|
||||
return 1;
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
This snippet registers two "commands": `foo` and `foo <bar>`. It is also common to refer to the `<bar>` as a "subcommand" of `foo`, as it's a child node.
|
||||
|
||||
At the start of the tree is a "root node", and it **must** have `LiteralCommandNode`s as children. Here, we register one command under the root: `literal("foo")`, which means "the user must type the literal string 'foo'".
|
||||
|
||||
Under that is two extra definitions: a child node for possible further evaluation, or an `executes` block if the user input stops here.
|
||||
|
||||
The child node works exactly the same way, but is no longer limited to literals. The other type of node that is now allowed is an `ArgumentCommandNode`, which takes in a name and an argument type.
|
||||
|
||||
Arguments can be anything, and you are encouraged to build your own for seamless integration into your own product. There are some standard arguments included in brigadier, such as `IntegerArgumentType`.
|
||||
|
||||
Argument types will be asked to parse input as much as they can, and then store the "result" of that argument however they see fit or throw a relevant error if they can't parse.
|
||||
|
||||
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 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 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.
|
||||
|
||||
If the command failed or could not parse, some form of `CommandSyntaxException` will be thrown. It is also possible for a `RuntimeException` to be bubbled up, if not properly handled in a command.
|
||||
|
||||
If you wish to have more control over the parsing & executing of commands, or wish to cache the parse results so you can execute it multiple times, you can split it up into two steps:
|
||||
|
||||
```java
|
||||
final ParseResults<S> parse = dispatcher.parse("foo 123", source);
|
||||
final int result = execute(parse);
|
||||
```
|
||||
|
||||
This is highly recommended as the parse step is the most expensive, and may be easily cached depending on your application.
|
||||
|
||||
You can also use this to do further introspection on a command, before (or without) actually running it.
|
||||
|
||||
## Inspecting a command
|
||||
If you `parse` some input, you can find out what it will perform (if anything) and provide hints to the user safely and immediately.
|
||||
|
||||
The parse will never fail, and the `ParseResults<S>` it returns will contain a *possible* context that a command may be called with
|
||||
(and from that, you can inspect which nodes the user entered, complete with start/end positions in the input string).
|
||||
It also contains a map of parse exceptions for each command node it encountered. If it couldn't build a valid context, then
|
||||
the reason why is inside this exception map.
|
||||
|
||||
## Displaying usage info
|
||||
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>`].
|
||||
|
||||
`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'
|
||||
rootProject.name = 'brigadier'
|
||||
enableFeaturePreview('STABLE_PUBLISHING')
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier;
|
||||
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier;
|
||||
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
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;
|
||||
|
@ -19,16 +18,36 @@ 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;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
/**
|
||||
* The core command dispatcher, for registering, parsing, and executing commands.
|
||||
*
|
||||
* @param <S> a custom "source" type, such as a user or originator of a command
|
||||
*/
|
||||
public class CommandDispatcher<S> {
|
||||
/**
|
||||
* The string required to separate individual arguments in an input string
|
||||
*
|
||||
* @see #ARGUMENT_SEPARATOR_CHAR
|
||||
*/
|
||||
public static final String ARGUMENT_SEPARATOR = " ";
|
||||
|
||||
/**
|
||||
* The char required to separate individual arguments in an input string
|
||||
*
|
||||
* @see #ARGUMENT_SEPARATOR
|
||||
*/
|
||||
public static final char ARGUMENT_SEPARATOR_CHAR = ' ';
|
||||
|
||||
private static final String USAGE_OPTIONAL_OPEN = "[";
|
||||
private static final String USAGE_OPTIONAL_CLOSE = "]";
|
||||
private static final String USAGE_REQUIRED_OPEN = "(";
|
||||
|
@ -36,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) {
|
||||
|
@ -45,33 +65,144 @@ public class CommandDispatcher<S> {
|
|||
private ResultConsumer<S> consumer = (c, s, r) -> {
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new {@link CommandDispatcher} with the specified root node.
|
||||
*
|
||||
* <p>This is often useful to copy existing or pre-defined command trees.</p>
|
||||
*
|
||||
* @param root the existing {@link RootCommandNode} to use as the basis for this tree
|
||||
*/
|
||||
public CommandDispatcher(final RootCommandNode<S> root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link CommandDispatcher} with an empty command tree.
|
||||
*/
|
||||
public CommandDispatcher() {
|
||||
this(new RootCommandNode<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method for registering new commands.
|
||||
*
|
||||
* <p>This is a shortcut for calling {@link RootCommandNode#addChild(CommandNode)} after building the provided {@code command}.</p>
|
||||
*
|
||||
* <p>As {@link RootCommandNode} can only hold literals, this method will only allow literal arguments.</p>
|
||||
*
|
||||
* @param command a literal argument builder to add to this command tree
|
||||
* @return the node added to this tree
|
||||
*/
|
||||
public LiteralCommandNode<S> register(final LiteralArgumentBuilder<S> command) {
|
||||
final LiteralCommandNode<S> build = command.build();
|
||||
root.addChild(build);
|
||||
return build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a callback to be informed of the result of every command.
|
||||
*
|
||||
* @param consumer the new result consumer to be called
|
||||
*/
|
||||
public void setConsumer(final ResultConsumer<S> consumer) {
|
||||
this.consumer = consumer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses and executes a given command.
|
||||
*
|
||||
* <p>This is a shortcut to first {@link #parse(StringReader, Object)} and then {@link #execute(ParseResults)}.</p>
|
||||
*
|
||||
* <p>It is recommended to parse and execute as separate steps, as parsing is often the most expensive step, and easiest to cache.</p>
|
||||
*
|
||||
* <p>If this command returns a value, then it successfully executed something. If it could not parse the command, or the execution was a failure,
|
||||
* then an exception will be thrown. Most exceptions will be of type {@link CommandSyntaxException}, but it is possible that a {@link RuntimeException}
|
||||
* may bubble up from the result of a command. The meaning behind the returned result is arbitrary, and will depend
|
||||
* entirely on what command was performed.</p>
|
||||
*
|
||||
* <p>If the command passes through a node that is {@link CommandNode#isFork()} then it will be 'forked'.
|
||||
* A forked command will not bubble up any {@link CommandSyntaxException}s, and the 'result' returned will turn into
|
||||
* 'amount of successful commands executes'.</p>
|
||||
*
|
||||
* <p>After each and any command is ran, a registered callback given to {@link #setConsumer(ResultConsumer)}
|
||||
* will be notified of the result and success of the command. You can use that method to gather more meaningful
|
||||
* results than this method will return, especially when a command forks.</p>
|
||||
*
|
||||
* @param input a command string to parse & execute
|
||||
* @param source a custom "source" object, usually representing the originator of this command
|
||||
* @return a numeric result from a "command" that was performed
|
||||
* @throws CommandSyntaxException if the command failed to parse or execute
|
||||
* @throws RuntimeException if the command failed to execute and was not handled gracefully
|
||||
* @see #parse(String, Object)
|
||||
* @see #parse(StringReader, Object)
|
||||
* @see #execute(ParseResults)
|
||||
* @see #execute(StringReader, Object)
|
||||
*/
|
||||
public int execute(final String input, final S source) throws CommandSyntaxException {
|
||||
return execute(new StringReader(input), source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses and executes a given command.
|
||||
*
|
||||
* <p>This is a shortcut to first {@link #parse(StringReader, Object)} and then {@link #execute(ParseResults)}.</p>
|
||||
*
|
||||
* <p>It is recommended to parse and execute as separate steps, as parsing is often the most expensive step, and easiest to cache.</p>
|
||||
*
|
||||
* <p>If this command returns a value, then it successfully executed something. If it could not parse the command, or the execution was a failure,
|
||||
* then an exception will be thrown. Most exceptions will be of type {@link CommandSyntaxException}, but it is possible that a {@link RuntimeException}
|
||||
* may bubble up from the result of a command. The meaning behind the returned result is arbitrary, and will depend
|
||||
* entirely on what command was performed.</p>
|
||||
*
|
||||
* <p>If the command passes through a node that is {@link CommandNode#isFork()} then it will be 'forked'.
|
||||
* A forked command will not bubble up any {@link CommandSyntaxException}s, and the 'result' returned will turn into
|
||||
* 'amount of successful commands executes'.</p>
|
||||
*
|
||||
* <p>After each and any command is ran, a registered callback given to {@link #setConsumer(ResultConsumer)}
|
||||
* will be notified of the result and success of the command. You can use that method to gather more meaningful
|
||||
* results than this method will return, especially when a command forks.</p>
|
||||
*
|
||||
* @param input a command string to parse & execute
|
||||
* @param source a custom "source" object, usually representing the originator of this command
|
||||
* @return a numeric result from a "command" that was performed
|
||||
* @throws CommandSyntaxException if the command failed to parse or execute
|
||||
* @throws RuntimeException if the command failed to execute and was not handled gracefully
|
||||
* @see #parse(String, Object)
|
||||
* @see #parse(StringReader, Object)
|
||||
* @see #execute(ParseResults)
|
||||
* @see #execute(String, Object)
|
||||
*/
|
||||
public int execute(final StringReader input, final S source) throws CommandSyntaxException {
|
||||
final ParseResults<S> parse = parse(input, source);
|
||||
return execute(parse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a given pre-parsed command.
|
||||
*
|
||||
* <p>If this command returns a value, then it successfully executed something. If the execution was a failure,
|
||||
* then an exception will be thrown.
|
||||
* Most exceptions will be of type {@link CommandSyntaxException}, but it is possible that a {@link RuntimeException}
|
||||
* may bubble up from the result of a command. The meaning behind the returned result is arbitrary, and will depend
|
||||
* entirely on what command was performed.</p>
|
||||
*
|
||||
* <p>If the command passes through a node that is {@link CommandNode#isFork()} then it will be 'forked'.
|
||||
* A forked command will not bubble up any {@link CommandSyntaxException}s, and the 'result' returned will turn into
|
||||
* 'amount of successful commands executes'.</p>
|
||||
*
|
||||
* <p>After each and any command is ran, a registered callback given to {@link #setConsumer(ResultConsumer)}
|
||||
* will be notified of the result and success of the command. You can use that method to gather more meaningful
|
||||
* results than this method will return, especially when a command forks.</p>
|
||||
*
|
||||
* @param parse the result of a successful {@link #parse(StringReader, Object)}
|
||||
* @return a numeric result from a "command" that was performed.
|
||||
* @throws CommandSyntaxException if the command failed to parse or execute
|
||||
* @throws RuntimeException if the command failed to execute and was not handled gracefully
|
||||
* @see #parse(String, Object)
|
||||
* @see #parse(StringReader, Object)
|
||||
* @see #execute(String, Object)
|
||||
* @see #execute(StringReader, Object)
|
||||
*/
|
||||
public int execute(final ParseResults<S> parse) throws CommandSyntaxException {
|
||||
if (parse.getReader().canRead()) {
|
||||
if (parse.getExceptions().size() == 1) {
|
||||
|
@ -99,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) {
|
||||
|
@ -117,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);
|
||||
|
@ -154,29 +286,73 @@ public class CommandDispatcher<S> {
|
|||
return forked ? successfulForks : result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a given command.
|
||||
*
|
||||
* <p>The result of this method can be cached, and it is advised to do so where appropriate. Parsing is often the
|
||||
* most expensive step, and this allows you to essentially "precompile" a command if it will be ran often.</p>
|
||||
*
|
||||
* <p>If the command passes through a node that is {@link CommandNode#isFork()} then the resulting context will be marked as 'forked'.
|
||||
* Forked contexts may contain child contexts, which may be modified by the {@link RedirectModifier} attached to the fork.</p>
|
||||
*
|
||||
* <p>Parsing a command can never fail, you will always be provided with a new {@link ParseResults}.
|
||||
* However, that does not mean that it will always parse into a valid command. You should inspect the returned results
|
||||
* to check for validity. If its {@link ParseResults#getReader()} {@link StringReader#canRead()} then it did not finish
|
||||
* parsing successfully. You can use that position as an indicator to the user where the command stopped being valid.
|
||||
* You may inspect {@link ParseResults#getExceptions()} if you know the parse failed, as it will explain why it could
|
||||
* not find any valid commands. It may contain multiple exceptions, one for each "potential node" that it could have visited,
|
||||
* explaining why it did not go down that node.</p>
|
||||
*
|
||||
* <p>When you eventually call {@link #execute(ParseResults)} with the result of this method, the above error checking
|
||||
* will occur. You only need to inspect it yourself if you wish to handle that yourself.</p>
|
||||
*
|
||||
* @param command a command string to parse
|
||||
* @param source a custom "source" object, usually representing the originator of this command
|
||||
* @return the result of parsing this command
|
||||
* @see #parse(StringReader, Object)
|
||||
* @see #execute(ParseResults)
|
||||
* @see #execute(String, Object)
|
||||
*/
|
||||
public ParseResults<S> parse(final String command, final S source) {
|
||||
return parse(new StringReader(command), source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a given command.
|
||||
*
|
||||
* <p>The result of this method can be cached, and it is advised to do so where appropriate. Parsing is often the
|
||||
* most expensive step, and this allows you to essentially "precompile" a command if it will be ran often.</p>
|
||||
*
|
||||
* <p>If the command passes through a node that is {@link CommandNode#isFork()} then the resulting context will be marked as 'forked'.
|
||||
* Forked contexts may contain child contexts, which may be modified by the {@link RedirectModifier} attached to the fork.</p>
|
||||
*
|
||||
* <p>Parsing a command can never fail, you will always be provided with a new {@link ParseResults}.
|
||||
* However, that does not mean that it will always parse into a valid command. You should inspect the returned results
|
||||
* to check for validity. If its {@link ParseResults#getReader()} {@link StringReader#canRead()} then it did not finish
|
||||
* parsing successfully. You can use that position as an indicator to the user where the command stopped being valid.
|
||||
* You may inspect {@link ParseResults#getExceptions()} if you know the parse failed, as it will explain why it could
|
||||
* not find any valid commands. It may contain multiple exceptions, one for each "potential node" that it could have visited,
|
||||
* explaining why it did not go down that node.</p>
|
||||
*
|
||||
* <p>When you eventually call {@link #execute(ParseResults)} with the result of this method, the above error checking
|
||||
* will occur. You only need to inspect it yourself if you wish to handle that yourself.</p>
|
||||
*
|
||||
* @param command a command string to parse
|
||||
* @param source a custom "source" object, usually representing the originator of this command
|
||||
* @return the result of parsing this command
|
||||
* @see #parse(String, Object)
|
||||
* @see #execute(ParseResults)
|
||||
* @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)) {
|
||||
|
@ -209,52 +385,72 @@ 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all possible executable commands following the given node.
|
||||
*
|
||||
* <p>You may use {@link #getRoot()} as a target to get all usage data for the entire command tree.</p>
|
||||
*
|
||||
* <p>The returned syntax will be in "simple" form: {@code <param>} and {@code literal}. "Optional" nodes will be
|
||||
* listed as multiple entries: the parent node, and the child nodes.
|
||||
* For example, a required literal "foo" followed by an optional param "int" will be two nodes:</p>
|
||||
* <ul>
|
||||
* <li>{@code foo}</li>
|
||||
* <li>{@code foo <int>}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>The path to the specified node will <b>not</b> be prepended to the output, as there can theoretically be many
|
||||
* ways to reach a given node. It will only give you paths relative to the specified node, not absolute from root.</p>
|
||||
*
|
||||
* @param node target node to get child usage strings for
|
||||
* @param source a custom "source" object, usually representing the originator of this command
|
||||
* @param restricted if true, commands that the {@code source} cannot access will not be mentioned
|
||||
* @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()]);
|
||||
}
|
||||
|
@ -278,8 +474,29 @@ public class CommandDispatcher<S> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the possible executable commands from a specified node.
|
||||
*
|
||||
* <p>You may use {@link #getRoot()} as a target to get usage data for the entire command tree.</p>
|
||||
*
|
||||
* <p>The returned syntax will be in "smart" form: {@code <param>}, {@code literal}, {@code [optional]} and {@code (either|or)}.
|
||||
* These forms may be mixed and matched to provide as much information about the child nodes as it can, without being too verbose.
|
||||
* For example, a required literal "foo" followed by an optional param "int" can be compressed into one string:</p>
|
||||
* <ul>
|
||||
* <li>{@code foo [<int>]}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>The path to the specified node will <b>not</b> be prepended to the output, as there can theoretically be many
|
||||
* ways to reach a given node. It will only give you paths relative to the specified node, not absolute from root.</p>
|
||||
*
|
||||
* <p>The returned usage will be restricted to only commands that the provided {@code source} can use.</p>
|
||||
*
|
||||
* @param node target node to get child usage strings for
|
||||
* @param source a custom "source" object, usually representing the originator of this command
|
||||
* @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()) {
|
||||
|
@ -313,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) {
|
||||
|
@ -345,38 +562,41 @@ public class CommandDispatcher<S> {
|
|||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets suggestions for a parsed input string on what comes next.
|
||||
*
|
||||
* <p>As it is ultimately up to custom argument types to provide suggestions, it may be an asynchronous operation,
|
||||
* for example getting in-game data or player names etc. As such, this method returns a future and no guarantees
|
||||
* are made to when or how the future completes.</p>
|
||||
*
|
||||
* <p>The suggestions provided will be in the context of the end of the parsed input string, but may suggest
|
||||
* new or replacement strings for earlier in the input string. For example, if the end of the string was
|
||||
* {@code foobar} but an argument preferred it to be {@code minecraft:foobar}, it will suggest a replacement for that
|
||||
* whole segment of the input.</p>
|
||||
*
|
||||
* @param parse the result of a {@link #parse(StringReader, Object)}
|
||||
* @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;
|
||||
|
@ -384,20 +604,43 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the root of this command tree.
|
||||
*
|
||||
* <p>This is often useful as a target of a {@link com.mojang.brigadier.builder.ArgumentBuilder#redirect(CommandNode)},
|
||||
* {@link #getAllUsage(CommandNode, Object, boolean)} or {@link #getSmartUsage(CommandNode, Object)}.
|
||||
* You may also use it to clone the command tree via {@link #CommandDispatcher(RootCommandNode)}.</p>
|
||||
*
|
||||
* @return root of the command tree
|
||||
*/
|
||||
public RootCommandNode<S> getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a valid path to a given node on the command tree.
|
||||
*
|
||||
* <p>There may theoretically be multiple paths to a node on the tree, especially with the use of forking or redirecting.
|
||||
* As such, this method makes no guarantees about which path it finds. It will not look at forks or redirects,
|
||||
* and find the first instance of the target node on the tree.</p>
|
||||
*
|
||||
* <p>The only guarantee made is that for the same command tree and the same version of this library, the result of
|
||||
* this method will <b>always</b> be a valid input for {@link #findNode(Collection)}, which should return the same node
|
||||
* as provided to this method.</p>
|
||||
*
|
||||
* @param target the target node you are finding a path for
|
||||
* @return a path to the resulting node, or an empty list if it was not found
|
||||
*/
|
||||
public Collection<String> getPath(final CommandNode<S> target) {
|
||||
final List<List<CommandNode<S>>> nodes = new ArrayList<>();
|
||||
addPaths(root, nodes, new ArrayList<>());
|
||||
|
@ -417,6 +660,17 @@ public class CommandDispatcher<S> {
|
|||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a node by its path
|
||||
*
|
||||
* <p>Paths may be generated with {@link #getPath(CommandNode)}, and are guaranteed (for the same tree, and the
|
||||
* same version of this library) to always produce the same valid node by this method.</p>
|
||||
*
|
||||
* <p>If a node could not be found at the specified path, then {@code null} will be returned.</p>
|
||||
*
|
||||
* @param path a generated path to a node
|
||||
* @return the node at the given path, or null if not found
|
||||
*/
|
||||
public CommandNode<S> findNode(final Collection<String> path) {
|
||||
CommandNode<S> node = root;
|
||||
for (final String name : path) {
|
||||
|
@ -428,6 +682,16 @@ public class CommandDispatcher<S> {
|
|||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans the command tree for potential ambiguous commands.
|
||||
*
|
||||
* <p>This is a shortcut for {@link CommandNode#findAmbiguities(AmbiguityConsumer)} on {@link #getRoot()}.</p>
|
||||
*
|
||||
* <p>Ambiguities are detected by testing every {@link CommandNode#getExamples()} on one node verses every sibling
|
||||
* node. This is not fool proof, and relies a lot on the providers of the used argument types to give good examples.</p>
|
||||
*
|
||||
* @param consumer a callback to be notified of potential ambiguities
|
||||
*/
|
||||
public void findAmbiguities(final AmbiguityConsumer<S> consumer) {
|
||||
root.findAmbiguities(consumer);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier;
|
||||
|
||||
public interface ImmutableStringReader {
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier;
|
||||
|
||||
public class LiteralMessage implements Message {
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier;
|
||||
|
||||
public interface Message {
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier;
|
||||
|
||||
import com.mojang.brigadier.context.CommandContextBuilder;
|
||||
|
@ -10,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() {
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier;
|
||||
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier;
|
||||
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
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;
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier;
|
||||
|
||||
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;
|
||||
|
@ -84,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();
|
||||
|
@ -107,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())) {
|
||||
|
@ -160,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 {
|
||||
|
@ -178,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);
|
||||
|
@ -189,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 {
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
// 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.context.CommandContextBuilder;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
|
@ -12,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();
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
// 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.context.CommandContextBuilder;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
|
@ -26,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();
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.arguments;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
|
@ -43,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) {
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.arguments;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
|
@ -43,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) {
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.arguments;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
|
@ -43,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) {
|
||||
|
@ -81,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;
|
||||
}
|
||||
}
|
|
@ -1,8 +1,10 @@
|
|||
// 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.context.CommandContextBuilder;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
@ -36,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());
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
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;
|
||||
|
@ -17,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();
|
||||
|
||||
|
@ -49,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();
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.builder;
|
||||
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
|
@ -25,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);
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.builder;
|
||||
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
|
@ -42,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);
|
||||
|
|
|
@ -1,29 +1,48 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
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;
|
||||
|
@ -35,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() {
|
||||
|
@ -67,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);
|
||||
|
@ -82,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;
|
||||
|
@ -95,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;
|
||||
|
@ -112,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;
|
||||
}
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
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;
|
||||
|
@ -19,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);
|
||||
|
@ -34,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;
|
||||
|
@ -49,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();
|
||||
|
@ -57,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;
|
||||
|
@ -88,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() {
|
||||
|
@ -103,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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.context;
|
||||
|
||||
import java.util.Objects;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.context;
|
||||
|
||||
import com.mojang.brigadier.ImmutableStringReader;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
public interface BuiltInExceptionProvider {
|
||||
|
@ -13,6 +16,10 @@ public interface BuiltInExceptionProvider {
|
|||
|
||||
Dynamic2CommandExceptionType integerTooHigh();
|
||||
|
||||
Dynamic2CommandExceptionType longTooLow();
|
||||
|
||||
Dynamic2CommandExceptionType longTooHigh();
|
||||
|
||||
DynamicCommandExceptionType literalIncorrect();
|
||||
|
||||
SimpleCommandExceptionType readerExpectedStartOfQuote();
|
||||
|
@ -27,6 +34,10 @@ public interface BuiltInExceptionProvider {
|
|||
|
||||
SimpleCommandExceptionType readerExpectedInt();
|
||||
|
||||
DynamicCommandExceptionType readerInvalidLong();
|
||||
|
||||
SimpleCommandExceptionType readerExpectedLong();
|
||||
|
||||
DynamicCommandExceptionType readerInvalidDouble();
|
||||
|
||||
SimpleCommandExceptionType readerExpectedDouble();
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import com.mojang.brigadier.LiteralMessage;
|
||||
|
@ -12,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"));
|
||||
|
@ -20,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 + "'"));
|
||||
|
@ -62,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;
|
||||
|
@ -97,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;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
public interface CommandExceptionType {
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import com.mojang.brigadier.Message;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import com.mojang.brigadier.ImmutableStringReader;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import com.mojang.brigadier.ImmutableStringReader;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import com.mojang.brigadier.ImmutableStringReader;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import com.mojang.brigadier.ImmutableStringReader;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import com.mojang.brigadier.ImmutableStringReader;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import com.mojang.brigadier.ImmutableStringReader;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.suggestion;
|
||||
|
||||
import com.mojang.brigadier.Message;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.suggestion;
|
||||
|
||||
import com.mojang.brigadier.Message;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.suggestion;
|
||||
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
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;
|
||||
|
@ -12,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;
|
||||
|
@ -89,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);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.suggestion;
|
||||
|
||||
import com.mojang.brigadier.Message;
|
||||
|
@ -5,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() {
|
||||
|
@ -31,6 +43,10 @@ public class SuggestionsBuilder {
|
|||
return remaining;
|
||||
}
|
||||
|
||||
public String getRemainingLowerCase() {
|
||||
return remainingLowerCase;
|
||||
}
|
||||
|
||||
public Suggestions build() {
|
||||
return Suggestions.create(input, result);
|
||||
}
|
||||
|
@ -71,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
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;
|
||||
|
@ -26,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;
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
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;
|
||||
|
@ -16,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();
|
||||
}
|
||||
|
@ -87,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()) {
|
||||
|
@ -108,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<>();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,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() {
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
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;
|
||||
|
@ -13,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() {
|
||||
|
@ -63,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();
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.tree;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
|
@ -14,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
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
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;
|
||||
|
@ -11,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;
|
||||
|
@ -256,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));
|
||||
|
@ -287,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));
|
||||
|
@ -307,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(
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
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;
|
||||
|
@ -99,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
|
||||
|
@ -186,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()
|
||||
));
|
||||
}
|
||||
}
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
@ -11,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;
|
||||
|
@ -30,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);
|
||||
|
@ -99,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(
|
||||
|
@ -118,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);
|
||||
|
@ -155,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 {
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier;
|
||||
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
|
@ -153,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("");
|
||||
|
@ -239,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");
|
||||
|
@ -291,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");
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.arguments;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.arguments;
|
||||
|
||||
import com.google.common.testing.EqualsTester;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.arguments;
|
||||
|
||||
import com.google.common.testing.EqualsTester;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.arguments;
|
||||
|
||||
import com.google.common.testing.EqualsTester;
|
||||
|
|
|
@ -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)"));
|
||||
}
|
||||
}
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.arguments;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.benchmarks;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.benchmarks;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.builder;
|
||||
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.builder;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.builder;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.context;
|
||||
|
||||
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;
|
||||
|
@ -23,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)
|
||||
|
@ -50,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();
|
||||
}
|
||||
}
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.context;
|
||||
|
||||
import com.google.common.testing.EqualsTester;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import com.mojang.brigadier.LiteralMessage;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import com.mojang.brigadier.LiteralMessage;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.suggestion;
|
||||
|
||||
import com.mojang.brigadier.context.StringRange;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.suggestion;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.suggestion;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.tree;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.tree;
|
||||
|
||||
import com.google.common.testing.EqualsTester;
|
||||
|
@ -29,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
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.tree;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
@ -34,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
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.tree;
|
||||
|
||||
import com.google.common.testing.EqualsTester;
|
||||
|
@ -31,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