aboutsummaryrefslogtreecommitdiff
path: root/spark-common/src/main/java
diff options
context:
space:
mode:
authorLuck <git@lucko.me>2018-10-15 22:00:05 +0100
committerLuck <git@lucko.me>2018-10-15 22:00:05 +0100
commit8e25dac340a07f7a57a13bdde53b0605779ea920 (patch)
tree3f034154fe8630f6cbf5648c9d2cb6b1426b9efe /spark-common/src/main/java
parent91775dd2ecc3f3e70dd422f68cf6d06e74db5d49 (diff)
downloadspark-8e25dac340a07f7a57a13bdde53b0605779ea920.tar.gz
spark-8e25dac340a07f7a57a13bdde53b0605779ea920.tar.bz2
spark-8e25dac340a07f7a57a13bdde53b0605779ea920.zip
Implement tab completion, update readme
Diffstat (limited to 'spark-common/src/main/java')
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java55
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/Command.java49
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/HeapModule.java3
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/MonitoringModule.java13
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java28
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/tabcomplete/CompletionSupplier.java56
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/tabcomplete/TabCompleter.java100
7 files changed, 266 insertions, 38 deletions
diff --git a/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java b/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java
index 57c205f..1de0ec9 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java
@@ -27,10 +27,16 @@ import me.lucko.spark.common.command.Command;
import me.lucko.spark.common.command.modules.HeapModule;
import me.lucko.spark.common.command.modules.MonitoringModule;
import me.lucko.spark.common.command.modules.SamplerModule;
+import me.lucko.spark.common.command.tabcomplete.CompletionSupplier;
+import me.lucko.spark.common.command.tabcomplete.TabCompleter;
import me.lucko.spark.sampler.ThreadDumper;
import me.lucko.spark.sampler.TickCounter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
+import java.util.stream.Collectors;
/**
* Abstract command handling class used by all platforms.
@@ -55,7 +61,6 @@ public abstract class SparkPlatform<S> {
private final List<Command<S>> commands = prepareCommands();
// abstract methods implemented by each platform
-
public abstract String getVersion();
public abstract String getLabel();
public abstract void sendMessage(S sender, String message);
@@ -75,7 +80,7 @@ public abstract class SparkPlatform<S> {
public void executeCommand(S sender, String[] args) {
if (args.length == 0) {
- sendInfo(sender);
+ sendUsage(sender);
return;
}
@@ -93,23 +98,41 @@ public abstract class SparkPlatform<S> {
}
}
- sendInfo(sender);
+ sendUsage(sender);
}
- private void sendInfo(S sender) {
- // todo automagically generate this
+ public List<String> tabCompleteCommand(S sender, String[] args) {
+ List<String> arguments = new ArrayList<>(Arrays.asList(args));
+
+ if (args.length <= 1) {
+ List<String> mainCommands = this.commands.stream().map(c -> c.aliases().get(0)).collect(Collectors.toList());
+ return TabCompleter.create()
+ .at(0, CompletionSupplier.startsWith(mainCommands))
+ .complete(arguments);
+ }
+
+ String alias = arguments.remove(0);
+ for (Command<S> command : this.commands) {
+ if (command.aliases().contains(alias)) {
+ return command.tabCompleter().completions(this, sender, arguments);
+ }
+ }
+
+ return Collections.emptyList();
+ }
+
+ private void sendUsage(S sender) {
sendPrefixedMessage(sender, "&fspark &7v" + getVersion());
- sendMessage(sender, "&b&l> &7/spark start");
- sendMessage(sender, " &8[&7--timeout&8 <timeout seconds>]");
- sendMessage(sender, " &8[&7--thread&8 <thread name>]");
- sendMessage(sender, " &8[&7--not-combined]");
- sendMessage(sender, " &8[&7--interval&8 <interval millis>]");
- sendMessage(sender, " &8[&7--only-ticks-over&8 <tick length millis>]");
- sendMessage(sender, "&b&l> &7/spark info");
- sendMessage(sender, "&b&l> &7/spark stop");
- sendMessage(sender, "&b&l> &7/spark cancel");
- sendMessage(sender, "&b&l> &7/spark monitoring");
- sendMessage(sender, " &8[&7--threshold&8 <percentage increase>]");
+ for (Command<S> command : this.commands) {
+ sendMessage(sender, "&b&l> &7/" + getLabel() + " " + command.aliases().get(0));
+ for (Command.ArgumentInfo arg : command.arguments()) {
+ if (arg.requiresParameter()) {
+ sendMessage(sender, " &8[&7--" + arg.argumentName() + "&8 <" + arg.parameterDescription() + ">]");
+ } else {
+ sendMessage(sender, " &8[&7--" + arg.argumentName() + "]");
+ }
+ }
+ }
}
}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/Command.java b/spark-common/src/main/java/me/lucko/spark/common/command/Command.java
index 70dc7e8..a28320b 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/command/Command.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/command/Command.java
@@ -20,14 +20,13 @@
package me.lucko.spark.common.command;
-import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableList;
import me.lucko.spark.common.SparkPlatform;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
public class Command<S> {
@@ -35,20 +34,26 @@ public class Command<S> {
return new Builder<>();
}
- private final Set<String> aliases;
+ private final List<String> aliases;
+ private final List<ArgumentInfo> arguments;
private final Executor<S> executor;
private final TabCompleter<S> tabCompleter;
- private Command(Set<String> aliases, Executor<S> executor, TabCompleter<S> tabCompleter) {
+ private Command(List<String> aliases, List<ArgumentInfo> arguments, Executor<S> executor, TabCompleter<S> tabCompleter) {
this.aliases = aliases;
+ this.arguments = arguments;
this.executor = executor;
this.tabCompleter = tabCompleter;
}
- public Set<String> aliases() {
+ public List<String> aliases() {
return this.aliases;
}
+ public List<ArgumentInfo> arguments() {
+ return this.arguments;
+ }
+
public Executor<S> executor() {
return this.executor;
}
@@ -58,7 +63,8 @@ public class Command<S> {
}
public static final class Builder<S> {
- private ImmutableSet.Builder<String> aliases = ImmutableSet.builder();
+ private ImmutableList.Builder<String> aliases = ImmutableList.builder();
+ private ImmutableList.Builder<ArgumentInfo> arguments = ImmutableList.builder();
private Executor<S> executor = null;
private TabCompleter<S> tabCompleter = null;
@@ -71,6 +77,11 @@ public class Command<S> {
return this;
}
+ public Builder<S> argumentUsage(String argumentName, String parameterDescription) {
+ this.arguments.add(new ArgumentInfo(argumentName, parameterDescription));
+ return this;
+ }
+
public Builder<S> executor(Executor<S> executor) {
this.executor = Objects.requireNonNull(executor, "executor");
return this;
@@ -82,7 +93,7 @@ public class Command<S> {
}
public Command<S> build() {
- Set<String> aliases = this.aliases.build();
+ List<String> aliases = this.aliases.build();
if (aliases.isEmpty()) {
throw new IllegalStateException("No aliases defined");
}
@@ -92,7 +103,7 @@ public class Command<S> {
if (this.tabCompleter == null) {
this.tabCompleter = TabCompleter.empty();
}
- return new Command<>(aliases, this.executor, this.tabCompleter);
+ return new Command<>(aliases, this.arguments.build(), this.executor, this.tabCompleter);
}
}
@@ -110,4 +121,26 @@ public class Command<S> {
List<String> completions(SparkPlatform<S> platform, S sender, List<String> arguments);
}
+ public static final class ArgumentInfo {
+ private final String argumentName;
+ private final String parameterDescription;
+
+ public ArgumentInfo(String argumentName, String parameterDescription) {
+ this.argumentName = argumentName;
+ this.parameterDescription = parameterDescription;
+ }
+
+ public String argumentName() {
+ return this.argumentName;
+ }
+
+ public String parameterDescription() {
+ return this.parameterDescription;
+ }
+
+ public boolean requiresParameter() {
+ return this.parameterDescription != null;
+ }
+ }
+
}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/HeapModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/HeapModule.java
index e586971..8752443 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/command/modules/HeapModule.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/HeapModule.java
@@ -59,9 +59,6 @@ public class HeapModule<S> implements CommandModule<S> {
}
});
})
- .tabCompleter((platform, sender, arguments) -> {
- return null;
- })
.build()
);
}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/MonitoringModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/MonitoringModule.java
index eafc567..a6a227f 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/command/modules/MonitoringModule.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/MonitoringModule.java
@@ -23,9 +23,14 @@ package me.lucko.spark.common.command.modules;
import me.lucko.spark.common.SparkPlatform;
import me.lucko.spark.common.command.Command;
import me.lucko.spark.common.command.CommandModule;
+import me.lucko.spark.common.command.tabcomplete.CompletionSupplier;
+import me.lucko.spark.common.command.tabcomplete.TabCompleter;
import me.lucko.spark.monitor.TickMonitor;
import me.lucko.spark.sampler.TickCounter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.function.Consumer;
public class MonitoringModule<S> implements CommandModule<S> {
@@ -37,6 +42,7 @@ public class MonitoringModule<S> implements CommandModule<S> {
public void registerCommands(Consumer<Command<S>> consumer) {
consumer.accept(Command.<S>builder()
.aliases("monitoring")
+ .argumentUsage("threshold", "percentage increase")
.executor((platform, sender, arguments) -> {
if (this.activeTickMonitor == null) {
@@ -58,7 +64,12 @@ public class MonitoringModule<S> implements CommandModule<S> {
}
})
.tabCompleter((platform, sender, arguments) -> {
- return null;
+ List<String> opts = new ArrayList<>(Collections.singletonList("--threshold"));
+ opts.removeAll(arguments);
+
+ return TabCompleter.create()
+ .from(0, CompletionSupplier.startsWith(opts))
+ .complete(arguments);
})
.build()
);
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java
index 853aa5d..2b814e3 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java
@@ -23,6 +23,8 @@ package me.lucko.spark.common.command.modules;
import me.lucko.spark.common.SparkPlatform;
import me.lucko.spark.common.command.Command;
import me.lucko.spark.common.command.CommandModule;
+import me.lucko.spark.common.command.tabcomplete.CompletionSupplier;
+import me.lucko.spark.common.command.tabcomplete.TabCompleter;
import me.lucko.spark.common.http.Bytebin;
import me.lucko.spark.sampler.Sampler;
import me.lucko.spark.sampler.SamplerBuilder;
@@ -31,6 +33,9 @@ import me.lucko.spark.sampler.ThreadGrouper;
import me.lucko.spark.sampler.TickCounter;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@@ -47,6 +52,11 @@ public class SamplerModule<S> implements CommandModule<S> {
public void registerCommands(Consumer<Command<S>> consumer) {
consumer.accept(Command.<S>builder()
.aliases("start")
+ .argumentUsage("timeout", "timeout seconds")
+ .argumentUsage("thread", "thread name")
+ .argumentUsage("not-combined", null)
+ .argumentUsage("interval", "interval millis")
+ .argumentUsage("only-ticks-over", "tick length millis")
.executor((platform, sender, arguments) -> {
int timeoutSeconds = arguments.intFlag("timeout");
if (timeoutSeconds != -1 && timeoutSeconds <= 10) {
@@ -149,7 +159,14 @@ public class SamplerModule<S> implements CommandModule<S> {
}
})
.tabCompleter((platform, sender, arguments) -> {
- return null;
+ List<String> opts = new ArrayList<>(Arrays.asList("--timeout", "--interval",
+ "--not-combined", "--only-ticks-over"));
+ opts.removeAll(arguments);
+ opts.add("--thread"); // allowed multiple times
+
+ return TabCompleter.create()
+ .from(0, CompletionSupplier.startsWith(opts))
+ .complete(arguments);
})
.build()
);
@@ -174,9 +191,6 @@ public class SamplerModule<S> implements CommandModule<S> {
}
}
})
- .tabCompleter((platform, sender, arguments) -> {
- return null;
- })
.build()
);
@@ -194,9 +208,6 @@ public class SamplerModule<S> implements CommandModule<S> {
}
}
})
- .tabCompleter((platform, sender, arguments) -> {
- return null;
- })
.build()
);
@@ -213,9 +224,6 @@ public class SamplerModule<S> implements CommandModule<S> {
}
}
})
- .tabCompleter((platform, sender, arguments) -> {
- return null;
- })
.build()
);
}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/tabcomplete/CompletionSupplier.java b/spark-common/src/main/java/me/lucko/spark/common/command/tabcomplete/CompletionSupplier.java
new file mode 100644
index 0000000..f1a6d10
--- /dev/null
+++ b/spark-common/src/main/java/me/lucko/spark/common/command/tabcomplete/CompletionSupplier.java
@@ -0,0 +1,56 @@
+/*
+ * This file is part of LuckPerms, licensed under the MIT License.
+ *
+ * Copyright (c) lucko (Luck) <luck@lucko.me>
+ * Copyright (c) contributors
+ *
+ * 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.
+ */
+
+package me.lucko.spark.common.command.tabcomplete;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+public interface CompletionSupplier {
+
+ CompletionSupplier EMPTY = partial -> Collections.emptyList();
+
+ static CompletionSupplier startsWith(Collection<String> strings) {
+ if (strings.isEmpty()) {
+ return EMPTY;
+ }
+ return partial -> strings.stream().filter(startsWithIgnoreCasePredicate(partial)).collect(Collectors.toList());
+ }
+
+ static Predicate<String> startsWithIgnoreCasePredicate(String prefix) {
+ return string -> {
+ if (string.length() < prefix.length()) {
+ return false;
+ }
+ return string.regionMatches(true, 0, prefix, 0, prefix.length());
+ };
+ }
+
+ List<String> supplyCompletions(String partial);
+
+} \ No newline at end of file
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/tabcomplete/TabCompleter.java b/spark-common/src/main/java/me/lucko/spark/common/command/tabcomplete/TabCompleter.java
new file mode 100644
index 0000000..f8774b2
--- /dev/null
+++ b/spark-common/src/main/java/me/lucko/spark/common/command/tabcomplete/TabCompleter.java
@@ -0,0 +1,100 @@
+/*
+ * This file is part of LuckPerms, licensed under the MIT License.
+ *
+ * Copyright (c) lucko (Luck) <luck@lucko.me>
+ * Copyright (c) contributors
+ *
+ * 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.
+ */
+
+package me.lucko.spark.common.command.tabcomplete;
+
+import com.google.common.base.Preconditions;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Utility for computing tab completion results
+ */
+public class TabCompleter {
+
+ public static TabCompleter create() {
+ return new TabCompleter();
+ }
+
+ private final Map<Integer, CompletionSupplier> suppliers = new HashMap<>();
+ private int from = Integer.MAX_VALUE;
+
+ private TabCompleter() {
+
+ }
+
+ /**
+ * Marks that the given completion supplier should be used to compute tab
+ * completions at the given index.
+ *
+ * @param position the position
+ * @param supplier the supplier
+ * @return this
+ */
+ public TabCompleter at(int position, CompletionSupplier supplier) {
+ Preconditions.checkState(position < this.from);
+ this.suppliers.put(position, supplier);
+ return this;
+ }
+
+ /**
+ * Marks that the given completion supplier should be used to compute tab
+ * completions at the given index and at all subsequent indexes infinitely.
+ *
+ * @param position the position
+ * @param supplier the supplier
+ * @return this
+ */
+ public TabCompleter from(int position, CompletionSupplier supplier) {
+ Preconditions.checkState(this.from == Integer.MAX_VALUE);
+ this.suppliers.put(position, supplier);
+ this.from = position;
+ return this;
+ }
+
+ public List<String> complete(List<String> args) {
+ int lastIndex = 0;
+ String partial;
+
+ // nothing entered yet
+ if (args.isEmpty() || (partial = args.get((lastIndex = args.size() - 1))).trim().isEmpty()) {
+ return getCompletions(lastIndex, "");
+ }
+
+ // started typing something
+ return getCompletions(lastIndex, partial);
+ }
+
+ private List<String> getCompletions(int position, String partial) {
+ if (position >= this.from) {
+ return this.suppliers.get(this.from).supplyCompletions(partial);
+ }
+
+ return this.suppliers.getOrDefault(position, CompletionSupplier.EMPTY).supplyCompletions(partial);
+ }
+
+} \ No newline at end of file