Compare commits
44 commits
searge_fix
...
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 | ||
|
019c0e3727 | ||
|
4dca222938 | ||
|
235f0c9b28 | ||
|
1ecf0558a0 | ||
|
805c001658 | ||
|
631096e871 | ||
|
46a0d3be41 | ||
|
19c991b8b2 | ||
|
5c50fa7691 |
85 changed files with 2290 additions and 515 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)
|
88
build.gradle
88
build.gradle
|
@ -1,13 +1,18 @@
|
|||
import groovy.io.FileType
|
||||
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: 'maven'
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
version = '0.1.24'
|
||||
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()
|
||||
maven {
|
||||
url "https://libraries.minecraft.net"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
@ -18,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) {
|
||||
|
@ -64,29 +69,48 @@ test {
|
|||
}
|
||||
}
|
||||
|
||||
def repoDir = new File(projectDir, "repo")
|
||||
repoDir.mkdirs()
|
||||
|
||||
uploadArchives {
|
||||
repositories {
|
||||
mavenDeployer {
|
||||
repository(url: "file://" + repoDir.absolutePath)
|
||||
|
||||
pom.project {
|
||||
description 'Command Registration & Dispatch System'
|
||||
url 'http://github.com/Mojang/brigadier'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doLast {
|
||||
// Purge all annoying files that arent needed
|
||||
repoDir.traverse(type: FileType.FILES, nameFilter: ~/.*\.(xml|xml\.sha1|md5)$/) {
|
||||
it.delete()
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
from components.java
|
||||
artifact sourcesJar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task report {
|
||||
doLast {
|
||||
println "##vso[build.updatebuildnumber]${project.version}"
|
||||
}
|
||||
}
|
||||
|
||||
def publishDir = file("$buildDir/repo")
|
||||
|
||||
clean.doLast {
|
||||
repoDir.deleteDir()
|
||||
}
|
||||
delete publishDir
|
||||
}
|
||||
|
||||
if (version.endsWith("SNAPSHOT")) {
|
||||
publishing.repositories {
|
||||
mavenLocal()
|
||||
}
|
||||
} else {
|
||||
publishing.repositories {
|
||||
maven {
|
||||
url "$buildDir/repo"
|
||||
}
|
||||
}
|
||||
|
||||
publish.doLast {
|
||||
publishDir.eachFileRecurse {
|
||||
if (!it.isFile()) {
|
||||
return
|
||||
}
|
||||
// Remove junk files
|
||||
if (it.name.contains(".xml") || it.name.contains(".md5")) {
|
||||
it.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
1
gradle.properties
Normal file
1
gradle.properties
Normal file
|
@ -0,0 +1 @@
|
|||
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,17 +1,13 @@
|
|||
// 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.exceptions.ParameterizedCommandExceptionType;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
import com.mojang.brigadier.suggestion.Suggestion;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
|
@ -22,21 +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;
|
||||
|
||||
public class CommandDispatcher<S> {
|
||||
public static final SimpleCommandExceptionType ERROR_UNKNOWN_COMMAND = new SimpleCommandExceptionType("command.unknown.command", "Unknown command");
|
||||
public static final SimpleCommandExceptionType ERROR_UNKNOWN_ARGUMENT = new SimpleCommandExceptionType("command.unknown.argument", "Incorrect argument for command");
|
||||
public static final SimpleCommandExceptionType ERROR_EXPECTED_ARGUMENT_SEPARATOR = new SimpleCommandExceptionType("command.expected.separator", "Expected whitespace to end one argument, but found trailing data");
|
||||
public static final ParameterizedCommandExceptionType ERROR_PARSE_EXCEPTION = new ParameterizedCommandExceptionType("command.exception", "Could not parse command: ${message}", "message");
|
||||
|
||||
/**
|
||||
* 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 = "(";
|
||||
|
@ -44,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) {
|
||||
|
@ -53,37 +65,152 @@ 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) {
|
||||
throw parse.getExceptions().values().iterator().next();
|
||||
} else if (parse.getContext().getRange().isEmpty()) {
|
||||
throw ERROR_UNKNOWN_COMMAND.createWithContext(parse.getReader());
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand().createWithContext(parse.getReader());
|
||||
} else {
|
||||
throw ERROR_UNKNOWN_ARGUMENT.createWithContext(parse.getReader());
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(parse.getReader());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,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) {
|
||||
|
@ -121,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);
|
||||
|
@ -152,32 +280,79 @@ public class CommandDispatcher<S> {
|
|||
|
||||
if (!foundCommand) {
|
||||
consumer.onCommandComplete(original, false, 0);
|
||||
throw ERROR_UNKNOWN_COMMAND.createWithContext(parse.getReader());
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand().createWithContext(parse.getReader());
|
||||
}
|
||||
|
||||
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) {
|
||||
final StringReader reader = new StringReader(command);
|
||||
final CommandContextBuilder<S> context = new CommandContextBuilder<>(this, source, 0);
|
||||
return parseNodes(root, reader, context);
|
||||
return parse(new StringReader(command), source);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
/**
|
||||
* 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, root, command.getCursor());
|
||||
return parseNodes(root, command, context);
|
||||
}
|
||||
|
||||
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)) {
|
||||
|
@ -190,11 +365,11 @@ public class CommandDispatcher<S> {
|
|||
try {
|
||||
child.parse(reader, context);
|
||||
} catch (final RuntimeException ex) {
|
||||
throw ERROR_PARSE_EXCEPTION.createWithContext(reader, ex.getMessage());
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherParseException().createWithContext(reader, ex.getMessage());
|
||||
}
|
||||
if (reader.canRead()) {
|
||||
if (reader.peek() != ARGUMENT_SEPARATOR_CHAR) {
|
||||
throw ERROR_EXPECTED_ARGUMENT_SEPARATOR.createWithContext(reader);
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherExpectedArgumentSeparator().createWithContext(reader);
|
||||
}
|
||||
}
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
|
@ -210,8 +385,7 @@ 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, parse.getReader(), parse.getExceptions());
|
||||
|
@ -220,42 +394,63 @@ public class CommandDispatcher<S> {
|
|||
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, 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, 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()]);
|
||||
}
|
||||
|
@ -279,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()) {
|
||||
|
@ -314,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) {
|
||||
|
@ -346,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 = 0;
|
||||
} 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 = 0;
|
||||
}
|
||||
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;
|
||||
|
@ -385,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<>());
|
||||
|
@ -418,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) {
|
||||
|
@ -429,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 {
|
||||
|
|
22
src/main/java/com/mojang/brigadier/LiteralMessage.java
Normal file
22
src/main/java/com/mojang/brigadier/LiteralMessage.java
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier;
|
||||
|
||||
public class LiteralMessage implements Message {
|
||||
private final String string;
|
||||
|
||||
public LiteralMessage(final String string) {
|
||||
this.string = string;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString() {
|
||||
return string;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return string;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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,25 +1,14 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier;
|
||||
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.ParameterizedCommandExceptionType;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
|
||||
public class StringReader implements ImmutableStringReader {
|
||||
private static final char SYNTAX_ESCAPE = '\\';
|
||||
private static final char SYNTAX_QUOTE = '"';
|
||||
|
||||
public static final SimpleCommandExceptionType ERROR_EXPECTED_START_OF_QUOTE = new SimpleCommandExceptionType("parsing.quote.expected.start", "Expected quote to start a string");
|
||||
public static final SimpleCommandExceptionType ERROR_EXPECTED_END_OF_QUOTE = new SimpleCommandExceptionType("parsing.quote.expected.end", "Unclosed quoted string");
|
||||
public static final ParameterizedCommandExceptionType ERROR_INVALID_ESCAPE = new ParameterizedCommandExceptionType("parsing.quote.escape", "Invalid escape sequence '\\${character}' in quoted string)", "character");
|
||||
public static final ParameterizedCommandExceptionType ERROR_INVALID_BOOL = new ParameterizedCommandExceptionType("parsing.bool.invalid", "Invalid bool, expected true or false but found '${value}'", "value");
|
||||
public static final ParameterizedCommandExceptionType ERROR_INVALID_INT = new ParameterizedCommandExceptionType("parsing.int.invalid", "Invalid integer '${value}'", "value");
|
||||
public static final SimpleCommandExceptionType ERROR_EXPECTED_INT = new SimpleCommandExceptionType("parsing.int.expected", "Expected integer");
|
||||
public static final ParameterizedCommandExceptionType ERROR_INVALID_DOUBLE = new ParameterizedCommandExceptionType("parsing.double.invalid", "Invalid double '${value}'", "value");
|
||||
public static final SimpleCommandExceptionType ERROR_EXPECTED_DOUBLE = new SimpleCommandExceptionType("parsing.double.expected", "Expected double");
|
||||
public static final ParameterizedCommandExceptionType ERROR_INVALID_FLOAT = new ParameterizedCommandExceptionType("parsing.float.invalid", "Invalid float '${value}'", "value");
|
||||
public static final SimpleCommandExceptionType ERROR_EXPECTED_FLOAT = new SimpleCommandExceptionType("parsing.float.expected", "Expected float");
|
||||
public static final SimpleCommandExceptionType ERROR_EXPECTED_BOOL = new SimpleCommandExceptionType("parsing.bool.expected", "Expected bool");
|
||||
public static final ParameterizedCommandExceptionType ERROR_EXPECTED_SYMBOL = new ParameterizedCommandExceptionType("parsing.expected", "Expected '${symbol}'", "symbol");
|
||||
private static final char SYNTAX_DOUBLE_QUOTE = '"';
|
||||
private static final char SYNTAX_SINGLE_QUOTE = '\'';
|
||||
|
||||
private final String string;
|
||||
private int cursor;
|
||||
|
@ -99,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();
|
||||
|
@ -112,13 +105,30 @@ public class StringReader implements ImmutableStringReader {
|
|||
}
|
||||
final String number = string.substring(start, cursor);
|
||||
if (number.isEmpty()) {
|
||||
throw ERROR_EXPECTED_INT.createWithContext(this);
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedInt().createWithContext(this);
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(number);
|
||||
} catch (final NumberFormatException ex) {
|
||||
cursor = start;
|
||||
throw ERROR_INVALID_INT.createWithContext(this, number);
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidInt().createWithContext(this, number);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,13 +139,13 @@ public class StringReader implements ImmutableStringReader {
|
|||
}
|
||||
final String number = string.substring(start, cursor);
|
||||
if (number.isEmpty()) {
|
||||
throw ERROR_EXPECTED_DOUBLE.createWithContext(this);
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedDouble().createWithContext(this);
|
||||
}
|
||||
try {
|
||||
return Double.parseDouble(number);
|
||||
} catch (final NumberFormatException ex) {
|
||||
cursor = start;
|
||||
throw ERROR_INVALID_DOUBLE.createWithContext(this, number);
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidDouble().createWithContext(this, number);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,13 +156,13 @@ public class StringReader implements ImmutableStringReader {
|
|||
}
|
||||
final String number = string.substring(start, cursor);
|
||||
if (number.isEmpty()) {
|
||||
throw ERROR_EXPECTED_FLOAT.createWithContext(this);
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedFloat().createWithContext(this);
|
||||
}
|
||||
try {
|
||||
return Float.parseFloat(number);
|
||||
} catch (final NumberFormatException ex) {
|
||||
cursor = start;
|
||||
throw ERROR_INVALID_FLOAT.createWithContext(this, number);
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidFloat().createWithContext(this, number);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,47 +185,57 @@ public class StringReader implements ImmutableStringReader {
|
|||
public String readQuotedString() throws CommandSyntaxException {
|
||||
if (!canRead()) {
|
||||
return "";
|
||||
} else if (peek() != SYNTAX_QUOTE) {
|
||||
throw ERROR_EXPECTED_START_OF_QUOTE.createWithContext(this);
|
||||
}
|
||||
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 {
|
||||
setCursor(getCursor() - 1);
|
||||
throw ERROR_INVALID_ESCAPE.createWithContext(this, String.valueOf(c));
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidEscape().createWithContext(this, String.valueOf(c));
|
||||
}
|
||||
} else if (c == SYNTAX_ESCAPE) {
|
||||
escaped = true;
|
||||
} else if (c == SYNTAX_QUOTE) {
|
||||
} else if (c == terminator) {
|
||||
return result.toString();
|
||||
} else {
|
||||
result.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
throw ERROR_EXPECTED_END_OF_QUOTE.createWithContext(this);
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedEndOfQuote().createWithContext(this);
|
||||
}
|
||||
|
||||
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 {
|
||||
final int start = cursor;
|
||||
final String value = readString();
|
||||
if (value.isEmpty()) {
|
||||
throw ERROR_EXPECTED_BOOL.createWithContext(this);
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedBool().createWithContext(this);
|
||||
}
|
||||
|
||||
if (value.equals("true")) {
|
||||
|
@ -224,13 +244,13 @@ public class StringReader implements ImmutableStringReader {
|
|||
return false;
|
||||
} else {
|
||||
cursor = start;
|
||||
throw ERROR_INVALID_BOOL.createWithContext(this, value);
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidBool().createWithContext(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void expect(final char c) throws CommandSyntaxException {
|
||||
if (!canRead() || peek() != c) {
|
||||
throw ERROR_EXPECTED_SYMBOL.createWithContext(this, String.valueOf(c));
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedSymbol().createWithContext(this, String.valueOf(c));
|
||||
}
|
||||
skip();
|
||||
}
|
||||
|
|
|
@ -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,17 +1,16 @@
|
|||
// 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.exceptions.ParameterizedCommandExceptionType;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
public class DoubleArgumentType implements ArgumentType<Double> {
|
||||
public static final ParameterizedCommandExceptionType ERROR_TOO_SMALL = new ParameterizedCommandExceptionType("argument.double.low", "Double must not be less than ${minimum}, found ${found}", "found", "minimum");
|
||||
public static final ParameterizedCommandExceptionType ERROR_TOO_BIG = new ParameterizedCommandExceptionType("argument.double.big", "Double must not be more than ${maximum}, found ${found}", "found", "maximum");
|
||||
private static final Collection<String> EXAMPLES = Arrays.asList("0", "1.2", ".5", "-1", "-.5", "-1234.56");
|
||||
|
||||
private final double minimum;
|
||||
|
@ -47,16 +46,16 @@ 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) {
|
||||
reader.setCursor(start);
|
||||
throw ERROR_TOO_SMALL.createWithContext(reader, result, minimum);
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.doubleTooLow().createWithContext(reader, result, minimum);
|
||||
}
|
||||
if (result > maximum) {
|
||||
reader.setCursor(start);
|
||||
throw ERROR_TOO_BIG.createWithContext(reader, result, maximum);
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.doubleTooHigh().createWithContext(reader, result, maximum);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
// 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.exceptions.ParameterizedCommandExceptionType;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
public class FloatArgumentType implements ArgumentType<Float> {
|
||||
public static final ParameterizedCommandExceptionType ERROR_TOO_SMALL = new ParameterizedCommandExceptionType("argument.float.low", "Float must not be less than ${minimum}, found ${found}", "found", "minimum");
|
||||
public static final ParameterizedCommandExceptionType ERROR_TOO_BIG = new ParameterizedCommandExceptionType("argument.float.big", "Float must not be more than ${maximum}, found ${found}", "found", "maximum");
|
||||
private static final Collection<String> EXAMPLES = Arrays.asList("0", "1.2", ".5", "-1", "-.5", "-1234.56");
|
||||
|
||||
private final float minimum;
|
||||
|
@ -47,16 +46,16 @@ 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) {
|
||||
reader.setCursor(start);
|
||||
throw ERROR_TOO_SMALL.createWithContext(reader, result, minimum);
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.floatTooLow().createWithContext(reader, result, minimum);
|
||||
}
|
||||
if (result > maximum) {
|
||||
reader.setCursor(start);
|
||||
throw ERROR_TOO_BIG.createWithContext(reader, result, maximum);
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.floatTooHigh().createWithContext(reader, result, maximum);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
// 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.exceptions.ParameterizedCommandExceptionType;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
public class IntegerArgumentType implements ArgumentType<Integer> {
|
||||
public static final ParameterizedCommandExceptionType ERROR_TOO_SMALL = new ParameterizedCommandExceptionType("argument.integer.low", "Integer must not be less than ${minimum}, found ${found}", "found", "minimum");
|
||||
public static final ParameterizedCommandExceptionType ERROR_TOO_BIG = new ParameterizedCommandExceptionType("argument.integer.big", "Integer must not be more than ${maximum}, found ${found}", "found", "maximum");
|
||||
private static final Collection<String> EXAMPLES = Arrays.asList("0", "123", "-123");
|
||||
|
||||
private final int minimum;
|
||||
|
@ -47,16 +46,16 @@ 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) {
|
||||
reader.setCursor(start);
|
||||
throw ERROR_TOO_SMALL.createWithContext(reader, result, minimum);
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.integerTooLow().createWithContext(reader, result, minimum);
|
||||
}
|
||||
if (result > maximum) {
|
||||
reader.setCursor(start);
|
||||
throw ERROR_TOO_BIG.createWithContext(reader, result, maximum);
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.integerTooHigh().createWithContext(reader, result, maximum);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -85,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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
public interface BuiltInExceptionProvider {
|
||||
Dynamic2CommandExceptionType doubleTooLow();
|
||||
|
||||
Dynamic2CommandExceptionType doubleTooHigh();
|
||||
|
||||
Dynamic2CommandExceptionType floatTooLow();
|
||||
|
||||
Dynamic2CommandExceptionType floatTooHigh();
|
||||
|
||||
Dynamic2CommandExceptionType integerTooLow();
|
||||
|
||||
Dynamic2CommandExceptionType integerTooHigh();
|
||||
|
||||
Dynamic2CommandExceptionType longTooLow();
|
||||
|
||||
Dynamic2CommandExceptionType longTooHigh();
|
||||
|
||||
DynamicCommandExceptionType literalIncorrect();
|
||||
|
||||
SimpleCommandExceptionType readerExpectedStartOfQuote();
|
||||
|
||||
SimpleCommandExceptionType readerExpectedEndOfQuote();
|
||||
|
||||
DynamicCommandExceptionType readerInvalidEscape();
|
||||
|
||||
DynamicCommandExceptionType readerInvalidBool();
|
||||
|
||||
DynamicCommandExceptionType readerInvalidInt();
|
||||
|
||||
SimpleCommandExceptionType readerExpectedInt();
|
||||
|
||||
DynamicCommandExceptionType readerInvalidLong();
|
||||
|
||||
SimpleCommandExceptionType readerExpectedLong();
|
||||
|
||||
DynamicCommandExceptionType readerInvalidDouble();
|
||||
|
||||
SimpleCommandExceptionType readerExpectedDouble();
|
||||
|
||||
DynamicCommandExceptionType readerInvalidFloat();
|
||||
|
||||
SimpleCommandExceptionType readerExpectedFloat();
|
||||
|
||||
SimpleCommandExceptionType readerExpectedBool();
|
||||
|
||||
DynamicCommandExceptionType readerExpectedSymbol();
|
||||
|
||||
SimpleCommandExceptionType dispatcherUnknownCommand();
|
||||
|
||||
SimpleCommandExceptionType dispatcherUnknownArgument();
|
||||
|
||||
SimpleCommandExceptionType dispatcherExpectedArgumentSeparator();
|
||||
|
||||
DynamicCommandExceptionType dispatcherParseException();
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import com.mojang.brigadier.LiteralMessage;
|
||||
|
||||
public class BuiltInExceptions implements BuiltInExceptionProvider {
|
||||
private static final Dynamic2CommandExceptionType DOUBLE_TOO_SMALL = new Dynamic2CommandExceptionType((found, min) -> new LiteralMessage("Double must not be less than " + min + ", found " + found));
|
||||
private static final Dynamic2CommandExceptionType DOUBLE_TOO_BIG = new Dynamic2CommandExceptionType((found, max) -> new LiteralMessage("Double must not be more than " + max + ", found " + found));
|
||||
|
||||
private static final Dynamic2CommandExceptionType FLOAT_TOO_SMALL = new Dynamic2CommandExceptionType((found, min) -> new LiteralMessage("Float must not be less than " + min + ", found " + found));
|
||||
private static final Dynamic2CommandExceptionType FLOAT_TOO_BIG = new Dynamic2CommandExceptionType((found, max) -> new LiteralMessage("Float must not be more than " + max + ", found " + found));
|
||||
|
||||
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"));
|
||||
private static final SimpleCommandExceptionType READER_EXPECTED_END_OF_QUOTE = new SimpleCommandExceptionType(new LiteralMessage("Unclosed quoted string"));
|
||||
private static final DynamicCommandExceptionType READER_INVALID_ESCAPE = new DynamicCommandExceptionType(character -> new LiteralMessage("Invalid escape sequence '" + character + "' in quoted string"));
|
||||
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 + "'"));
|
||||
private static final SimpleCommandExceptionType READER_EXPECTED_FLOAT = new SimpleCommandExceptionType(new LiteralMessage("Expected float"));
|
||||
private static final SimpleCommandExceptionType READER_EXPECTED_BOOL = new SimpleCommandExceptionType(new LiteralMessage("Expected bool"));
|
||||
private static final DynamicCommandExceptionType READER_EXPECTED_SYMBOL = new DynamicCommandExceptionType(symbol -> new LiteralMessage("Expected '" + symbol + "'"));
|
||||
|
||||
private static final SimpleCommandExceptionType DISPATCHER_UNKNOWN_COMMAND = new SimpleCommandExceptionType(new LiteralMessage("Unknown command"));
|
||||
private static final SimpleCommandExceptionType DISPATCHER_UNKNOWN_ARGUMENT = new SimpleCommandExceptionType(new LiteralMessage("Incorrect argument for command"));
|
||||
private static final SimpleCommandExceptionType DISPATCHER_EXPECTED_ARGUMENT_SEPARATOR = new SimpleCommandExceptionType(new LiteralMessage("Expected whitespace to end one argument, but found trailing data"));
|
||||
private static final DynamicCommandExceptionType DISPATCHER_PARSE_EXCEPTION = new DynamicCommandExceptionType(message -> new LiteralMessage("Could not parse command: " + message));
|
||||
|
||||
@Override
|
||||
public Dynamic2CommandExceptionType doubleTooLow() {
|
||||
return DOUBLE_TOO_SMALL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic2CommandExceptionType doubleTooHigh() {
|
||||
return DOUBLE_TOO_BIG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic2CommandExceptionType floatTooLow() {
|
||||
return FLOAT_TOO_SMALL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic2CommandExceptionType floatTooHigh() {
|
||||
return FLOAT_TOO_BIG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic2CommandExceptionType integerTooLow() {
|
||||
return INTEGER_TOO_SMALL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic2CommandExceptionType integerTooHigh() {
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleCommandExceptionType readerExpectedStartOfQuote() {
|
||||
return READER_EXPECTED_START_OF_QUOTE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleCommandExceptionType readerExpectedEndOfQuote() {
|
||||
return READER_EXPECTED_END_OF_QUOTE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicCommandExceptionType readerInvalidEscape() {
|
||||
return READER_INVALID_ESCAPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicCommandExceptionType readerInvalidBool() {
|
||||
return READER_INVALID_BOOL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicCommandExceptionType readerInvalidInt() {
|
||||
return READER_INVALID_INT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleCommandExceptionType readerExpectedInt() {
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleCommandExceptionType readerExpectedDouble() {
|
||||
return READER_EXPECTED_DOUBLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicCommandExceptionType readerInvalidFloat() {
|
||||
return READER_INVALID_FLOAT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleCommandExceptionType readerExpectedFloat() {
|
||||
return READER_EXPECTED_FLOAT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleCommandExceptionType readerExpectedBool() {
|
||||
return READER_EXPECTED_BOOL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicCommandExceptionType readerExpectedSymbol() {
|
||||
return READER_EXPECTED_SYMBOL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleCommandExceptionType dispatcherUnknownCommand() {
|
||||
return DISPATCHER_UNKNOWN_COMMAND;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleCommandExceptionType dispatcherUnknownArgument() {
|
||||
return DISPATCHER_UNKNOWN_ARGUMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleCommandExceptionType dispatcherExpectedArgumentSeparator() {
|
||||
return DISPATCHER_EXPECTED_ARGUMENT_SEPARATOR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicCommandExceptionType dispatcherParseException() {
|
||||
return DISPATCHER_PARSE_EXCEPTION;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface CommandExceptionType {
|
||||
String getTypeName();
|
||||
|
||||
String getErrorMessage(Map<String, String> data);
|
||||
}
|
||||
|
|
|
@ -1,35 +1,39 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import java.util.Map;
|
||||
import com.mojang.brigadier.Message;
|
||||
|
||||
public class CommandSyntaxException extends Exception {
|
||||
public static final int CONTEXT_AMOUNT = 10;
|
||||
public static boolean ENABLE_COMMAND_STACK_TRACES = true;
|
||||
public static BuiltInExceptionProvider BUILT_IN_EXCEPTIONS = new BuiltInExceptions();
|
||||
|
||||
private final CommandExceptionType type;
|
||||
private final Map<String, String> data;
|
||||
private final Message message;
|
||||
private final String input;
|
||||
private final int cursor;
|
||||
|
||||
public CommandSyntaxException(final CommandExceptionType type, final Map<String, String> data) {
|
||||
super(type.getTypeName(), null, ENABLE_COMMAND_STACK_TRACES, ENABLE_COMMAND_STACK_TRACES);
|
||||
public CommandSyntaxException(final CommandExceptionType type, final Message message) {
|
||||
super(message.getString(), null, ENABLE_COMMAND_STACK_TRACES, ENABLE_COMMAND_STACK_TRACES);
|
||||
this.type = type;
|
||||
this.data = data;
|
||||
this.message = message;
|
||||
this.input = null;
|
||||
this.cursor = -1;
|
||||
}
|
||||
|
||||
public CommandSyntaxException(final CommandExceptionType type, final Map<String, String> data, final String input, final int cursor) {
|
||||
super(type.getTypeName(), null, ENABLE_COMMAND_STACK_TRACES, ENABLE_COMMAND_STACK_TRACES);
|
||||
public CommandSyntaxException(final CommandExceptionType type, final Message message, final String input, final int cursor) {
|
||||
super(message.getString(), null, ENABLE_COMMAND_STACK_TRACES, ENABLE_COMMAND_STACK_TRACES);
|
||||
this.type = type;
|
||||
this.data = data;
|
||||
this.message = message;
|
||||
this.input = input;
|
||||
this.cursor = cursor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
String message = type.getErrorMessage(data);
|
||||
String message = this.message.getString();
|
||||
final String context = getContext();
|
||||
if (context != null) {
|
||||
message += " at position " + cursor + ": " + context;
|
||||
|
@ -37,6 +41,10 @@ public class CommandSyntaxException extends Exception {
|
|||
return message;
|
||||
}
|
||||
|
||||
public Message getRawMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public String getContext() {
|
||||
if (input == null || cursor < 0) {
|
||||
return null;
|
||||
|
@ -58,10 +66,6 @@ public class CommandSyntaxException extends Exception {
|
|||
return type;
|
||||
}
|
||||
|
||||
public Map<String, String> getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public String getInput() {
|
||||
return input;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import com.mojang.brigadier.ImmutableStringReader;
|
||||
import com.mojang.brigadier.Message;
|
||||
|
||||
public class Dynamic2CommandExceptionType implements CommandExceptionType {
|
||||
private final Function function;
|
||||
|
||||
public Dynamic2CommandExceptionType(final Function function) {
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
public CommandSyntaxException create(final Object a, final Object b) {
|
||||
return new CommandSyntaxException(this, function.apply(a, b));
|
||||
}
|
||||
|
||||
public CommandSyntaxException createWithContext(final ImmutableStringReader reader, final Object a, final Object b) {
|
||||
return new CommandSyntaxException(this, function.apply(a, b), reader.getString(), reader.getCursor());
|
||||
}
|
||||
|
||||
public interface Function {
|
||||
Message apply(Object a, Object b);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import com.mojang.brigadier.ImmutableStringReader;
|
||||
import com.mojang.brigadier.Message;
|
||||
|
||||
public class Dynamic3CommandExceptionType implements CommandExceptionType {
|
||||
private final Function function;
|
||||
|
||||
public Dynamic3CommandExceptionType(final Function function) {
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
public CommandSyntaxException create(final Object a, final Object b, final Object c) {
|
||||
return new CommandSyntaxException(this, function.apply(a, b, c));
|
||||
}
|
||||
|
||||
public CommandSyntaxException createWithContext(final ImmutableStringReader reader, final Object a, final Object b, final Object c) {
|
||||
return new CommandSyntaxException(this, function.apply(a, b, c), reader.getString(), reader.getCursor());
|
||||
}
|
||||
|
||||
public interface Function {
|
||||
Message apply(Object a, Object b, Object c);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import com.mojang.brigadier.ImmutableStringReader;
|
||||
import com.mojang.brigadier.Message;
|
||||
|
||||
public class Dynamic4CommandExceptionType implements CommandExceptionType {
|
||||
private final Function function;
|
||||
|
||||
public Dynamic4CommandExceptionType(final Function function) {
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
public CommandSyntaxException create(final Object a, final Object b, final Object c, final Object d) {
|
||||
return new CommandSyntaxException(this, function.apply(a, b, c, d));
|
||||
}
|
||||
|
||||
public CommandSyntaxException createWithContext(final ImmutableStringReader reader, final Object a, final Object b, final Object c, final Object d) {
|
||||
return new CommandSyntaxException(this, function.apply(a, b, c, d), reader.getString(), reader.getCursor());
|
||||
}
|
||||
|
||||
public interface Function {
|
||||
Message apply(Object a, Object b, Object c, Object d);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import com.mojang.brigadier.ImmutableStringReader;
|
||||
import com.mojang.brigadier.Message;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class DynamicCommandExceptionType implements CommandExceptionType {
|
||||
private final Function<Object, Message> function;
|
||||
|
||||
public DynamicCommandExceptionType(final Function<Object, Message> function) {
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
public CommandSyntaxException create(final Object arg) {
|
||||
return new CommandSyntaxException(this, function.apply(arg));
|
||||
}
|
||||
|
||||
public CommandSyntaxException createWithContext(final ImmutableStringReader reader, final Object arg) {
|
||||
return new CommandSyntaxException(this, function.apply(arg), reader.getString(), reader.getCursor());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import com.mojang.brigadier.ImmutableStringReader;
|
||||
import com.mojang.brigadier.Message;
|
||||
|
||||
public class DynamicNCommandExceptionType implements CommandExceptionType {
|
||||
private final Function function;
|
||||
|
||||
public DynamicNCommandExceptionType(final Function function) {
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
public CommandSyntaxException create(final Object a, final Object... args) {
|
||||
return new CommandSyntaxException(this, function.apply(args));
|
||||
}
|
||||
|
||||
public CommandSyntaxException createWithContext(final ImmutableStringReader reader, final Object... args) {
|
||||
return new CommandSyntaxException(this, function.apply(args), reader.getString(), reader.getCursor());
|
||||
}
|
||||
|
||||
public interface Function {
|
||||
Message apply(Object[] args);
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mojang.brigadier.ImmutableStringReader;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ParameterizedCommandExceptionType implements CommandExceptionType {
|
||||
private static final Pattern PATTERN = Pattern.compile("\\$\\{(\\w+)}");
|
||||
private static final Joiner JOINER = Joiner.on(", ");
|
||||
|
||||
private final String name;
|
||||
private final String message;
|
||||
private final String[] keys;
|
||||
|
||||
public ParameterizedCommandExceptionType(final String name, final String message, final String... keys) {
|
||||
this.name = name;
|
||||
this.message = message;
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorMessage(final Map<String, String> data) {
|
||||
final Matcher matcher = PATTERN.matcher(message);
|
||||
final StringBuffer result = new StringBuffer();
|
||||
while (matcher.find()) {
|
||||
matcher.appendReplacement(result, Matcher.quoteReplacement(data.get(matcher.group(1))));
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public CommandSyntaxException create(final Object... values) {
|
||||
return new CommandSyntaxException(this, createMap(values));
|
||||
}
|
||||
|
||||
public CommandSyntaxException createWithContext(final ImmutableStringReader reader, final Object... values) {
|
||||
return new CommandSyntaxException(this, createMap(values), reader.getString(), reader.getCursor());
|
||||
}
|
||||
|
||||
public Map<String, String> createMap(final Object... values) {
|
||||
if (values.length != keys.length) {
|
||||
throw new IllegalArgumentException("Invalid values! (Expected: " + JOINER.join(keys) + ")");
|
||||
}
|
||||
|
||||
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
|
||||
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
builder = builder.put(keys[i], String.valueOf(values[i]));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof CommandExceptionType)) return false;
|
||||
|
||||
final CommandExceptionType that = (CommandExceptionType) o;
|
||||
|
||||
return getTypeName().equals(that.getTypeName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getTypeName().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return message;
|
||||
}
|
||||
}
|
|
@ -1,54 +1,28 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mojang.brigadier.ImmutableStringReader;
|
||||
|
||||
import java.util.Map;
|
||||
import com.mojang.brigadier.Message;
|
||||
|
||||
public class SimpleCommandExceptionType implements CommandExceptionType {
|
||||
private final String name;
|
||||
private final String message;
|
||||
private final Message message;
|
||||
|
||||
public SimpleCommandExceptionType(final String name, final String message) {
|
||||
this.name = name;
|
||||
public SimpleCommandExceptionType(final Message message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorMessage(final Map<String, String> data) {
|
||||
return message;
|
||||
}
|
||||
|
||||
public CommandSyntaxException create() {
|
||||
return new CommandSyntaxException(this, ImmutableMap.of());
|
||||
return new CommandSyntaxException(this, message);
|
||||
}
|
||||
|
||||
public CommandSyntaxException createWithContext(final ImmutableStringReader reader) {
|
||||
return new CommandSyntaxException(this, ImmutableMap.of(), reader.getString(), reader.getCursor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof CommandExceptionType)) return false;
|
||||
|
||||
final CommandExceptionType that = (CommandExceptionType) o;
|
||||
|
||||
return getTypeName().equals(that.getTypeName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getTypeName().hashCode();
|
||||
return new CommandSyntaxException(this, message, reader.getString(), reader.getCursor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return message;
|
||||
return message.getString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.suggestion;
|
||||
|
||||
import com.mojang.brigadier.Message;
|
||||
import com.mojang.brigadier.context.StringRange;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class IntegerSuggestion extends Suggestion {
|
||||
private int value;
|
||||
|
||||
public IntegerSuggestion(final StringRange range, final int value) {
|
||||
this(range, value, null);
|
||||
}
|
||||
|
||||
public IntegerSuggestion(final StringRange range, final int value, final Message tooltip) {
|
||||
super(range, Integer.toString(value), tooltip);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof IntegerSuggestion)) {
|
||||
return false;
|
||||
}
|
||||
final IntegerSuggestion that = (IntegerSuggestion) o;
|
||||
return value == that.value && super.equals(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "IntegerSuggestion{" +
|
||||
"value=" + value +
|
||||
", range=" + getRange() +
|
||||
", text='" + getText() + '\'' +
|
||||
", tooltip='" + getTooltip() + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final Suggestion o) {
|
||||
if (o instanceof IntegerSuggestion) {
|
||||
return Integer.compare(value, ((IntegerSuggestion) o).value);
|
||||
}
|
||||
return super.compareTo(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareToIgnoreCase(final Suggestion b) {
|
||||
return compareTo(b);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
@ -78,6 +81,10 @@ public class Suggestion implements Comparable<Suggestion> {
|
|||
return text.compareTo(o.text);
|
||||
}
|
||||
|
||||
public int compareToIgnoreCase(final Suggestion b) {
|
||||
return text.compareToIgnoreCase(b.text);
|
||||
}
|
||||
|
||||
public Suggestion expand(final String command, final StringRange range) {
|
||||
if (range.equals(this.range)) {
|
||||
return this;
|
||||
|
|
|
@ -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,12 +91,12 @@ 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);
|
||||
sorted.sort((a, b) -> a.getText().compareToIgnoreCase(b.getText()));
|
||||
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);
|
||||
}
|
||||
|
@ -55,16 +71,26 @@ public class SuggestionsBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
public SuggestionsBuilder suggest(final int value) {
|
||||
result.add(new IntegerSuggestion(StringRange.between(start, input.length()), value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public SuggestionsBuilder suggest(final int value, final Message tooltip) {
|
||||
result.add(new IntegerSuggestion(StringRange.between(start, input.length()), value, tooltip));
|
||||
return this;
|
||||
}
|
||||
|
||||
public SuggestionsBuilder add(final SuggestionsBuilder other) {
|
||||
result.addAll(other.result);
|
||||
return this;
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -120,4 +128,9 @@ public class ArgumentCommandNode<S, T> extends CommandNode<S> {
|
|||
public Collection<String> getExamples() {
|
||||
return type.getExamples();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "<argument " + name + ":" + type +">";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -8,23 +12,27 @@ import com.mojang.brigadier.context.CommandContext;
|
|||
import com.mojang.brigadier.context.CommandContextBuilder;
|
||||
import com.mojang.brigadier.context.StringRange;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.ParameterizedCommandExceptionType;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
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> {
|
||||
public static final ParameterizedCommandExceptionType ERROR_INCORRECT_LITERAL = new ParameterizedCommandExceptionType("argument.literal.incorrect", "Expected literal ${expected}", "expected");
|
||||
|
||||
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() {
|
||||
|
@ -45,7 +53,7 @@ public class LiteralCommandNode<S> extends CommandNode<S> {
|
|||
return;
|
||||
}
|
||||
|
||||
throw ERROR_INCORRECT_LITERAL.createWithContext(reader, literal);
|
||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.literalIncorrect().createWithContext(reader, literal);
|
||||
}
|
||||
|
||||
private int parse(final StringReader reader) {
|
||||
|
@ -66,7 +74,7 @@ public class LiteralCommandNode<S> extends CommandNode<S> {
|
|||
|
||||
@Override
|
||||
public CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) {
|
||||
if (literal.toLowerCase().startsWith(builder.getRemaining().toLowerCase())) {
|
||||
if (literalLowerCase.startsWith(builder.getRemainingLowerCase())) {
|
||||
return builder.suggest(literal).buildFuture();
|
||||
} else {
|
||||
return Suggestions.empty();
|
||||
|
@ -121,4 +129,9 @@ public class LiteralCommandNode<S> extends CommandNode<S> {
|
|||
public Collection<String> getExamples() {
|
||||
return Collections.singleton(literal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "<literal " + literal + ">";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -62,4 +65,9 @@ public class RootCommandNode<S> extends CommandNode<S> {
|
|||
public Collection<String> getExamples() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "<root>";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -12,6 +16,7 @@ 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;
|
||||
|
@ -45,6 +50,12 @@ public class CommandDispatcherTest {
|
|||
when(command.run(any())).thenReturn(42);
|
||||
}
|
||||
|
||||
private static StringReader inputWithOffset(final String input, final int offset) {
|
||||
final StringReader result = new StringReader(input);
|
||||
result.setCursor(offset);
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testCreateAndExecuteCommand() throws Exception {
|
||||
|
@ -54,6 +65,15 @@ public class CommandDispatcherTest {
|
|||
verify(command).run(any(CommandContext.class));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testCreateAndExecuteOffsetCommand() throws Exception {
|
||||
subject.register(literal("foo").executes(command));
|
||||
|
||||
assertThat(subject.execute(inputWithOffset("/foo", 1), source), is(42));
|
||||
verify(command).run(any(CommandContext.class));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testCreateAndMergeCommands() throws Exception {
|
||||
|
@ -74,8 +94,7 @@ public class CommandDispatcherTest {
|
|||
subject.execute("foo", source);
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(CommandDispatcher.ERROR_UNKNOWN_COMMAND));
|
||||
assertThat(ex.getData(), is(Collections.<String, Object>emptyMap()));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
@ -88,8 +107,7 @@ public class CommandDispatcherTest {
|
|||
subject.execute("foo", source);
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(CommandDispatcher.ERROR_UNKNOWN_COMMAND));
|
||||
assertThat(ex.getData(), is(Collections.<String, Object>emptyMap()));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
@ -102,8 +120,7 @@ public class CommandDispatcherTest {
|
|||
subject.execute("", source);
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(CommandDispatcher.ERROR_UNKNOWN_COMMAND));
|
||||
assertThat(ex.getData(), is(Collections.<String, Object>emptyMap()));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
@ -116,8 +133,7 @@ public class CommandDispatcherTest {
|
|||
subject.execute("foo bar", source);
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(CommandDispatcher.ERROR_UNKNOWN_ARGUMENT));
|
||||
assertThat(ex.getData(), is(Collections.emptyMap()));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument()));
|
||||
assertThat(ex.getCursor(), is(4));
|
||||
}
|
||||
}
|
||||
|
@ -130,8 +146,7 @@ public class CommandDispatcherTest {
|
|||
subject.execute("foo baz", source);
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(CommandDispatcher.ERROR_UNKNOWN_ARGUMENT));
|
||||
assertThat(ex.getData(), is(Collections.emptyMap()));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument()));
|
||||
assertThat(ex.getCursor(), is(4));
|
||||
}
|
||||
}
|
||||
|
@ -148,8 +163,7 @@ public class CommandDispatcherTest {
|
|||
subject.execute("foo unknown", source);
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(CommandDispatcher.ERROR_UNKNOWN_ARGUMENT));
|
||||
assertThat(ex.getData(), is(Collections.emptyMap()));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument()));
|
||||
assertThat(ex.getCursor(), is(4));
|
||||
}
|
||||
}
|
||||
|
@ -249,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));
|
||||
|
@ -280,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));
|
||||
|
@ -300,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(
|
||||
|
@ -310,8 +371,7 @@ public class CommandDispatcherTest {
|
|||
subject.execute("foo 5", source);
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(CommandDispatcher.ERROR_UNKNOWN_COMMAND));
|
||||
assertThat(ex.getData(), is(Collections.emptyMap()));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand()));
|
||||
assertThat(ex.getCursor(), is(5));
|
||||
}
|
||||
}
|
||||
|
@ -335,8 +395,7 @@ public class CommandDispatcherTest {
|
|||
subject.execute("foo$", source);
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(CommandDispatcher.ERROR_UNKNOWN_COMMAND));
|
||||
assertThat(ex.getData(), is(Collections.emptyMap()));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
@ -351,8 +410,7 @@ public class CommandDispatcherTest {
|
|||
subject.execute("foo bar", source);
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_INT));
|
||||
assertThat(ex.getData(), is(Collections.emptyMap()));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedInt()));
|
||||
assertThat(ex.getCursor(), is(4));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,7 +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 Iterables.getLast(subject.parse(command, source).getContext().getNodes()).getNode();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -174,4 +182,18 @@ public class CommandDispatcherUsagesTest {
|
|||
.build()
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmartUsage_offsetH() throws Exception {
|
||||
final StringReader offsetH = new StringReader("/|/|/h");
|
||||
offsetH.setCursor(5);
|
||||
|
||||
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()
|
||||
));
|
||||
}
|
||||
}
|
|
@ -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,24 @@ 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);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCompletionSuggestions_rootCommands() throws Exception {
|
||||
subject.register(literal("foo"));
|
||||
|
@ -42,6 +65,18 @@ public class CommandSuggestionsTest {
|
|||
assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(StringRange.at(0), "bar"), new Suggestion(StringRange.at(0), "baz"), new Suggestion(StringRange.at(0), "foo"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCompletionSuggestions_rootCommands_withInputOffset() throws Exception {
|
||||
subject.register(literal("foo"));
|
||||
subject.register(literal("bar"));
|
||||
subject.register(literal("baz"));
|
||||
|
||||
final Suggestions result = subject.getCompletionSuggestions(subject.parse(inputWithOffset("OOO", 3), source)).join();
|
||||
|
||||
assertThat(result.getRange(), equalTo(StringRange.at(3)));
|
||||
assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(StringRange.at(3), "bar"), new Suggestion(StringRange.at(3), "baz"), new Suggestion(StringRange.at(3), "foo"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCompletionSuggestions_rootCommands_partial() throws Exception {
|
||||
subject.register(literal("foo"));
|
||||
|
@ -54,6 +89,18 @@ public class CommandSuggestionsTest {
|
|||
assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(StringRange.between(0, 1), "bar"), new Suggestion(StringRange.between(0, 1), "baz"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCompletionSuggestions_rootCommands_partial_withInputOffset() throws Exception {
|
||||
subject.register(literal("foo"));
|
||||
subject.register(literal("bar"));
|
||||
subject.register(literal("baz"));
|
||||
|
||||
final Suggestions result = subject.getCompletionSuggestions(subject.parse(inputWithOffset("Zb", 1), source)).join();
|
||||
|
||||
assertThat(result.getRange(), equalTo(StringRange.between(1, 2)));
|
||||
assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(StringRange.between(1, 2), "bar"), new Suggestion(StringRange.between(1, 2), "baz"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCompletionSuggestions_subCommands() throws Exception {
|
||||
subject.register(
|
||||
|
@ -69,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(
|
||||
|
@ -85,6 +157,22 @@ public class CommandSuggestionsTest {
|
|||
assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(StringRange.between(7, 8), "bar"), new Suggestion(StringRange.between(7, 8), "baz"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCompletionSuggestions_subCommands_partial_withInputOffset() throws Exception {
|
||||
subject.register(
|
||||
literal("parent")
|
||||
.then(literal("foo"))
|
||||
.then(literal("bar"))
|
||||
.then(literal("baz"))
|
||||
);
|
||||
|
||||
final ParseResults<Object> parse = subject.parse(inputWithOffset("junk parent b", 5), source);
|
||||
final Suggestions result = subject.getCompletionSuggestions(parse).join();
|
||||
|
||||
assertThat(result.getRange(), equalTo(StringRange.between(12, 13)));
|
||||
assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(StringRange.between(12, 13), "bar"), new Suggestion(StringRange.between(12, 13), "baz"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCompletionSuggestions_redirect() throws Exception {
|
||||
final LiteralCommandNode<Object> actual = subject.register(literal("actual").then(literal("sub")));
|
||||
|
@ -109,6 +197,42 @@ 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 {
|
||||
final LiteralCommandNode<Object> actual = subject.register(literal("actual").then(literal("sub")));
|
||||
subject.register(literal("redirect").redirect(actual));
|
||||
|
||||
final ParseResults<Object> parse = subject.parse(inputWithOffset("/redirect s", 1), source);
|
||||
final Suggestions result = subject.getCompletionSuggestions(parse).join();
|
||||
|
||||
assertThat(result.getRange(), equalTo(StringRange.between(10, 11)));
|
||||
assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(StringRange.between(10, 11), "sub"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCompletionSuggestions_redirect_lots() throws Exception {
|
||||
final LiteralCommandNode<Object> loop = subject.register(literal("redirect"));
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
@ -156,6 +156,30 @@ public class StringReaderTest {
|
|||
assertThat(reader.getRemaining(), equalTo(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readSingleQuotedString() throws Exception {
|
||||
final StringReader reader = new StringReader("'hello world'");
|
||||
assertThat(reader.readQuotedString(), equalTo("hello world"));
|
||||
assertThat(reader.getRead(), equalTo("'hello world'"));
|
||||
assertThat(reader.getRemaining(), equalTo(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readMixedQuotedString_doubleInsideSingle() throws Exception {
|
||||
final StringReader reader = new StringReader("'hello \"world\"'");
|
||||
assertThat(reader.readQuotedString(), equalTo("hello \"world\""));
|
||||
assertThat(reader.getRead(), equalTo("'hello \"world\"'"));
|
||||
assertThat(reader.getRemaining(), equalTo(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readMixedQuotedString_singleInsideDouble() throws Exception {
|
||||
final StringReader reader = new StringReader("\"hello 'world'\"");
|
||||
assertThat(reader.readQuotedString(), equalTo("hello 'world'"));
|
||||
assertThat(reader.getRead(), equalTo("\"hello 'world'\""));
|
||||
assertThat(reader.getRemaining(), equalTo(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readQuotedString_empty() throws Exception {
|
||||
final StringReader reader = new StringReader("");
|
||||
|
@ -217,8 +241,7 @@ public class StringReaderTest {
|
|||
try {
|
||||
new StringReader("hello world\"").readQuotedString();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_START_OF_QUOTE));
|
||||
assertThat(ex.getData(), equalTo(Collections.emptyMap()));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedStartOfQuote()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
@ -228,8 +251,7 @@ public class StringReaderTest {
|
|||
try {
|
||||
new StringReader("\"hello world").readQuotedString();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_END_OF_QUOTE));
|
||||
assertThat(ex.getData(), equalTo(Collections.emptyMap()));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedEndOfQuote()));
|
||||
assertThat(ex.getCursor(), is(12));
|
||||
}
|
||||
}
|
||||
|
@ -239,12 +261,45 @@ public class StringReaderTest {
|
|||
try {
|
||||
new StringReader("\"hello\\nworld\"").readQuotedString();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(StringReader.ERROR_INVALID_ESCAPE));
|
||||
assertThat(ex.getData(), equalTo(ImmutableMap.of("character", "n")));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidEscape()));
|
||||
assertThat(ex.getCursor(), is(7));
|
||||
}
|
||||
}
|
||||
|
||||
@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");
|
||||
|
@ -266,8 +321,7 @@ public class StringReaderTest {
|
|||
try {
|
||||
new StringReader("12.34").readInt();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(StringReader.ERROR_INVALID_INT));
|
||||
assertThat(ex.getData(), equalTo(ImmutableMap.of("value", "12.34")));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidInt()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
@ -277,8 +331,7 @@ public class StringReaderTest {
|
|||
try {
|
||||
new StringReader("").readInt();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_INT));
|
||||
assertThat(ex.getData(), equalTo(Collections.emptyMap()));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedInt()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
@ -299,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");
|
||||
|
@ -328,8 +433,7 @@ public class StringReaderTest {
|
|||
try {
|
||||
new StringReader("12.34.56").readDouble();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(StringReader.ERROR_INVALID_DOUBLE));
|
||||
assertThat(ex.getData(), equalTo(ImmutableMap.of("value", "12.34.56")));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidDouble()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
@ -339,8 +443,7 @@ public class StringReaderTest {
|
|||
try {
|
||||
new StringReader("").readDouble();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_DOUBLE));
|
||||
assertThat(ex.getData(), equalTo(Collections.emptyMap()));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedDouble()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
@ -390,8 +493,7 @@ public class StringReaderTest {
|
|||
try {
|
||||
new StringReader("12.34.56").readFloat();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(StringReader.ERROR_INVALID_FLOAT));
|
||||
assertThat(ex.getData(), equalTo(ImmutableMap.of("value", "12.34.56")));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidFloat()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
@ -401,8 +503,7 @@ public class StringReaderTest {
|
|||
try {
|
||||
new StringReader("").readFloat();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_FLOAT));
|
||||
assertThat(ex.getData(), equalTo(Collections.emptyMap()));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedFloat()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
@ -437,8 +538,7 @@ public class StringReaderTest {
|
|||
reader.expect('a');
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_SYMBOL));
|
||||
assertThat(ex.getData(), equalTo(ImmutableMap.of("symbol", "a")));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedSymbol()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
@ -450,8 +550,7 @@ public class StringReaderTest {
|
|||
reader.expect('a');
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_SYMBOL));
|
||||
assertThat(ex.getData(), equalTo(ImmutableMap.of("symbol", "a")));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedSymbol()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
@ -470,8 +569,7 @@ public class StringReaderTest {
|
|||
reader.readBoolean();
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(StringReader.ERROR_INVALID_BOOL));
|
||||
assertThat(ex.getData(), equalTo(ImmutableMap.of("value", "tuesday")));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidBool()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
@ -483,8 +581,7 @@ public class StringReaderTest {
|
|||
reader.readBoolean();
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_BOOL));
|
||||
assertThat(ex.getData(), equalTo(Collections.emptyMap()));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedBool()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,6 +1,8 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.arguments;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.testing.EqualsTester;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.context.CommandContextBuilder;
|
||||
|
@ -12,7 +14,6 @@ import org.mockito.Mock;
|
|||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import static com.mojang.brigadier.arguments.DoubleArgumentType.doubleArg;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasToString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
@ -43,8 +44,7 @@ public class DoubleArgumentTypeTest {
|
|||
doubleArg(0, 100).parse(reader);
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(DoubleArgumentType.ERROR_TOO_SMALL));
|
||||
assertThat(ex.getData(), equalTo(ImmutableMap.<String, Object>of("found", "-5.0", "minimum", "0.0")));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.doubleTooLow()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
@ -56,8 +56,7 @@ public class DoubleArgumentTypeTest {
|
|||
doubleArg(-100, 0).parse(reader);
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(DoubleArgumentType.ERROR_TOO_BIG));
|
||||
assertThat(ex.getData(), equalTo(ImmutableMap.<String, Object>of("found", "5.0", "maximum", "0.0")));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.doubleTooHigh()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.arguments;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.testing.EqualsTester;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.context.CommandContextBuilder;
|
||||
|
@ -12,7 +14,6 @@ import org.mockito.Mock;
|
|||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import static com.mojang.brigadier.arguments.FloatArgumentType.floatArg;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasToString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
@ -43,8 +44,7 @@ public class FloatArgumentTypeTest {
|
|||
floatArg(0, 100).parse(reader);
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(FloatArgumentType.ERROR_TOO_SMALL));
|
||||
assertThat(ex.getData(), equalTo(ImmutableMap.<String, Object>of("found", "-5.0", "minimum", "0.0")));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.floatTooLow()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
@ -56,8 +56,7 @@ public class FloatArgumentTypeTest {
|
|||
floatArg(-100, 0).parse(reader);
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(FloatArgumentType.ERROR_TOO_BIG));
|
||||
assertThat(ex.getData(), equalTo(ImmutableMap.<String, Object>of("found", "5.0", "maximum", "0.0")));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.floatTooHigh()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.arguments;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.testing.EqualsTester;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.context.CommandContextBuilder;
|
||||
|
@ -12,7 +14,6 @@ import org.mockito.Mock;
|
|||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasToString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
@ -43,8 +44,7 @@ public class IntegerArgumentTypeTest {
|
|||
integer(0, 100).parse(reader);
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(IntegerArgumentType.ERROR_TOO_SMALL));
|
||||
assertThat(ex.getData(), equalTo(ImmutableMap.<String, Object>of("found", "-5", "minimum", "0")));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.integerTooLow()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
@ -56,8 +56,7 @@ public class IntegerArgumentTypeTest {
|
|||
integer(-100, 0).parse(reader);
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(IntegerArgumentType.ERROR_TOO_BIG));
|
||||
assertThat(ex.getData(), equalTo(ImmutableMap.<String, Object>of("found", "5", "maximum", "0")));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.integerTooHigh()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import com.mojang.brigadier.LiteralMessage;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
|
||||
public class DynamicCommandSyntaxExceptionTypeTest {
|
||||
private DynamicCommandExceptionType type;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
type = new DynamicCommandExceptionType(name -> new LiteralMessage("Hello, " + name + "!"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWithContext() throws Exception {
|
||||
final StringReader reader = new StringReader("Foo bar");
|
||||
reader.setCursor(5);
|
||||
final CommandSyntaxException exception = type.createWithContext(reader, "World");
|
||||
assertThat(exception.getType(), is(type));
|
||||
assertThat(exception.getInput(), is("Foo bar"));
|
||||
assertThat(exception.getCursor(), is(5));
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.testing.EqualsTester;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
|
||||
public class ParameterizedCommandSyntaxExceptionTypeTest {
|
||||
private ParameterizedCommandExceptionType type;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
type = new ParameterizedCommandExceptionType("foo", "Hello, ${name}!", "name");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void createMap_TooManyArguments() throws Exception {
|
||||
type.createMap("World", "Universe");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWithContext() throws Exception {
|
||||
final StringReader reader = new StringReader("Foo bar");
|
||||
reader.setCursor(5);
|
||||
final CommandSyntaxException exception = type.createWithContext(reader, "World");
|
||||
assertThat(exception.getType(), is(type));
|
||||
assertThat(exception.getData(), is(ImmutableMap.<String, Object>of("name", "World")));
|
||||
assertThat(exception.getInput(), is("Foo bar"));
|
||||
assertThat(exception.getCursor(), is(5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals() throws Exception {
|
||||
new EqualsTester()
|
||||
.addEqualityGroup(new ParameterizedCommandExceptionType("foo", "Hello, world!"), new ParameterizedCommandExceptionType("foo", "Hello, universe!"), new ParameterizedCommandExceptionType("foo", "Hello, world!", "bar"))
|
||||
.addEqualityGroup(new ParameterizedCommandExceptionType("bar", "Hello, world!"), new ParameterizedCommandExceptionType("bar", "Hello, universe!"), new ParameterizedCommandExceptionType("bar", "Hello, world!", "bar"))
|
||||
.testEquals();
|
||||
}
|
||||
}
|
|
@ -1,11 +1,12 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.exceptions;
|
||||
|
||||
import com.google.common.testing.EqualsTester;
|
||||
import com.mojang.brigadier.LiteralMessage;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
|
@ -16,39 +17,31 @@ import static org.mockito.Mockito.mock;
|
|||
public class SimpleCommandSyntaxExceptionTypeTest {
|
||||
@Test
|
||||
public void createWithContext() throws Exception {
|
||||
final SimpleCommandExceptionType type = new SimpleCommandExceptionType("foo", "bar");
|
||||
final SimpleCommandExceptionType type = new SimpleCommandExceptionType(new LiteralMessage("error"));
|
||||
final StringReader reader = new StringReader("Foo bar");
|
||||
reader.setCursor(5);
|
||||
final CommandSyntaxException exception = type.createWithContext(reader);
|
||||
assertThat(exception.getType(), is(type));
|
||||
assertThat(exception.getData(), is(Collections.emptyMap()));
|
||||
assertThat(exception.getInput(), is("Foo bar"));
|
||||
assertThat(exception.getCursor(), is(5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals() throws Exception {
|
||||
new EqualsTester()
|
||||
.addEqualityGroup(new SimpleCommandExceptionType("foo", "Hello, world!"), new SimpleCommandExceptionType("foo", "Hello, universe!"))
|
||||
.addEqualityGroup(new SimpleCommandExceptionType("bar", "Hello, world!"), new SimpleCommandExceptionType("bar", "Hello, universe!"))
|
||||
.testEquals();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getContext_none() throws Exception {
|
||||
final CommandSyntaxException exception = new CommandSyntaxException(mock(CommandExceptionType.class), Collections.emptyMap());
|
||||
final CommandSyntaxException exception = new CommandSyntaxException(mock(CommandExceptionType.class), new LiteralMessage("error"));
|
||||
assertThat(exception.getContext(), is(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getContext_short() throws Exception {
|
||||
final CommandSyntaxException exception = new CommandSyntaxException(mock(CommandExceptionType.class), Collections.emptyMap(), "Hello world!", 5);
|
||||
final CommandSyntaxException exception = new CommandSyntaxException(mock(CommandExceptionType.class), new LiteralMessage("error"), "Hello world!", 5);
|
||||
assertThat(exception.getContext(), equalTo("Hello<--[HERE]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getContext_long() throws Exception {
|
||||
final CommandSyntaxException exception = new CommandSyntaxException(mock(CommandExceptionType.class), Collections.emptyMap(), "Hello world! This has an error in it. Oh dear!", 20);
|
||||
final CommandSyntaxException exception = new CommandSyntaxException(mock(CommandExceptionType.class), new LiteralMessage("error"), "Hello world! This has an error in it. Oh dear!", 20);
|
||||
assertThat(exception.getContext(), equalTo("...d! This ha<--[HERE]"));
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
@ -5,6 +8,10 @@ import com.mojang.brigadier.context.StringRange;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
|
@ -58,4 +65,25 @@ public class SuggestionsBuilderTest {
|
|||
assertThat(other.getStart(), is(builder.getStart()));
|
||||
assertThat(other.getRemaining(), equalTo(builder.getRemaining()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sort_alphabetical() {
|
||||
Suggestions result = builder.suggest("2").suggest("4").suggest("6").suggest("8").suggest("30").suggest("32").build();
|
||||
List<String> actual = result.getList().stream().map(Suggestion::getText).collect(Collectors.toList());
|
||||
assertThat(actual, equalTo(Lists.newArrayList( "2", "30", "32", "4", "6", "8")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sort_numerical() {
|
||||
Suggestions result = builder.suggest(2).suggest(4).suggest(6).suggest(8).suggest(30).suggest(32).build();
|
||||
List<String> actual = result.getList().stream().map(Suggestion::getText).collect(Collectors.toList());
|
||||
assertThat(actual, equalTo(Lists.newArrayList( "2", "4", "6", "8", "30", "32")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sort_mixed() {
|
||||
Suggestions result = builder.suggest("11").suggest("22").suggest("33").suggest("a").suggest("b").suggest("c").suggest(2).suggest(4).suggest(6).suggest(8).suggest(30).suggest(32).suggest("3a").suggest("a3").build();
|
||||
List<String> actual = result.getList().stream().map(Suggestion::getText).collect(Collectors.toList());
|
||||
assertThat(actual, equalTo(Lists.newArrayList( "11", "2", "22", "33", "3a", "4", "6", "8", "30", "32", "a", "a3", "b", "c")));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,6 +1,8 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package com.mojang.brigadier.tree;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.testing.EqualsTester;
|
||||
import com.mojang.brigadier.Command;
|
||||
|
@ -35,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
|
||||
|
@ -59,8 +61,7 @@ public class LiteralCommandNodeTest extends AbstractCommandNodeTest {
|
|||
node.parse(reader, contextBuilder);
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(LiteralCommandNode.ERROR_INCORRECT_LITERAL));
|
||||
assertThat(ex.getData(), is(ImmutableMap.<String, Object>of("expected", "foo")));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.literalIncorrect()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
@ -72,8 +73,7 @@ public class LiteralCommandNodeTest extends AbstractCommandNodeTest {
|
|||
node.parse(reader, contextBuilder);
|
||||
fail();
|
||||
} catch (final CommandSyntaxException ex) {
|
||||
assertThat(ex.getType(), is(LiteralCommandNode.ERROR_INCORRECT_LITERAL));
|
||||
assertThat(ex.getData(), is(ImmutableMap.<String, Object>of("expected", "foo")));
|
||||
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.literalIncorrect()));
|
||||
assertThat(ex.getCursor(), is(0));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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