Archived
0

Streamline command help a little bit

This commit is contained in:
Dico
2018-09-27 07:53:58 +01:00
parent 842e52bd92
commit f28e4393db
14 changed files with 148 additions and 59 deletions

View File

@@ -5,6 +5,7 @@ import io.dico.dicore.command.parameter.ArgumentBuffer;
import io.dico.dicore.command.predef.PredefinedCommand; import io.dico.dicore.command.predef.PredefinedCommand;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -109,6 +110,23 @@ public interface ICommandAddress {
*/ */
boolean isDepthLargerThan(int depth); boolean isDepthLargerThan(int depth);
/**
* @return true if this address has any children.
*/
boolean hasChildren();
/**
* @return total number of children, not considering any aliases
*/
int getNumberOfRealChildren();
/**
* Get an unmodifiable view of all main keys of the children of this address.
*
* @return the main keys
*/
Collection<String> getChildrenMainKeys();
/** /**
* Get an unmodifiable view of the children of this address. * Get an unmodifiable view of the children of this address.
* Values might be duplicated for aliases. * Values might be duplicated for aliases.

View File

@@ -10,10 +10,13 @@ import java.util.*;
public abstract class ModifiableCommandAddress implements ICommandAddress { public abstract class ModifiableCommandAddress implements ICommandAddress {
Map<String, ChildCommandAddress> children; Map<String, ChildCommandAddress> children;
Collection<String> childrenMainKeys = Collections.emptyList();
// the chat controller as configured by the programmer // the chat controller as configured by the programmer
IChatController chatController; IChatController chatController;
// cache for the algorithm that finds the first chat controller going up the tree // cache for the algorithm that finds the first chat controller going up the tree
transient IChatController chatControllerCache; transient IChatController chatControllerCache;
ModifiableCommandAddress helpChild; ModifiableCommandAddress helpChild;
public ModifiableCommandAddress() { public ModifiableCommandAddress() {
@@ -110,6 +113,21 @@ public abstract class ModifiableCommandAddress implements ICommandAddress {
return false; return false;
} }
@Override
public boolean hasChildren() {
return !children.isEmpty();
}
@Override
public int getNumberOfRealChildren() {
return childrenMainKeys.size();
}
@Override
public Collection<String> getChildrenMainKeys() {
return Collections.unmodifiableCollection(childrenMainKeys);
}
@Override @Override
public Map<String, ? extends ModifiableCommandAddress> getChildren() { public Map<String, ? extends ModifiableCommandAddress> getChildren() {
return Collections.unmodifiableMap(children); return Collections.unmodifiableMap(children);
@@ -140,7 +158,16 @@ public abstract class ModifiableCommandAddress implements ICommandAddress {
} }
Iterator<String> names = mChild.modifiableNamesIterator(); Iterator<String> names = mChild.modifiableNamesIterator();
children.put(names.next(), mChild); String mainKey = names.next();
if (!childrenMainKeys.contains(mainKey)) {
if (!(childrenMainKeys instanceof ArrayList)) {
childrenMainKeys = new ArrayList<>();
}
childrenMainKeys.add(mainKey);
}
children.put(mainKey, mChild);
while (names.hasNext()) { while (names.hasNext()) {
String name = names.next(); String name = names.next();
@@ -168,19 +195,29 @@ public abstract class ModifiableCommandAddress implements ICommandAddress {
} }
if (removeAliases) { if (removeAliases) {
for (Iterator<String> iterator = keyTarget.namesModifiable.iterator(); iterator.hasNext(); ) { Iterator<String> iterator = keyTarget.namesModifiable.iterator();
boolean first = true;
while (iterator.hasNext()) {
String alias = iterator.next(); String alias = iterator.next();
ChildCommandAddress aliasTarget = getChild(key); ChildCommandAddress aliasTarget = getChild(key);
if (aliasTarget == keyTarget) { if (aliasTarget == keyTarget) {
if (first) {
childrenMainKeys.remove(alias);
}
children.remove(alias); children.remove(alias);
iterator.remove();
} }
iterator.remove();
first = false;
} }
continue;
}
children.remove(key); } else {
keyTarget.namesModifiable.remove(key); if (key.equals(keyTarget.getMainKey())) {
childrenMainKeys.remove(key);
}
children.remove(key);
keyTarget.namesModifiable.remove(key);
}
} }
} }

View File

@@ -217,9 +217,14 @@ public class RootCommandAddress extends ModifiableCommandAddress implements ICom
try { try {
ICommandAddress target = getCommandTarget(context, buffer); ICommandAddress target = getCommandTarget(context, buffer);
List<String> out = target.hasCommand()
? target.getCommand().tabComplete(sender, target, location, buffer.getUnaffectingCopy()) List<String> out;
: Collections.emptyList(); if (target.hasCommand()) {
context.targetAcquired(target, target.getCommand(), buffer);
out = target.getCommand().tabCompleteWithContext(context, location);
} else {
out = Collections.emptyList();
}
int cursor = buffer.getCursor(); int cursor = buffer.getCursor();
String input; String input;
@@ -230,7 +235,7 @@ public class RootCommandAddress extends ModifiableCommandAddress implements ICom
} }
boolean wrapped = false; boolean wrapped = false;
for (String child : target.getChildren().keySet()) { for (String child : target.getChildrenMainKeys()) {
if (child.toLowerCase().startsWith(input)) { if (child.toLowerCase().startsWith(input)) {
if (!wrapped) { if (!wrapped) {
out = new ArrayList<>(out); out = new ArrayList<>(out);

View File

@@ -88,7 +88,7 @@ public class AbstractChatController implements IChatController {
case DESCRIPTION: case DESCRIPTION:
return Formatting.GREEN; return Formatting.GREEN;
case SYNTAX: case SYNTAX:
return Formatting.BLUE; return Formatting.AQUA;
case HIGHLIGHT: case HIGHLIGHT:
return Formatting.RED; return Formatting.RED;
case SUBCOMMAND: case SUBCOMMAND:

View File

@@ -82,7 +82,7 @@ public class HelpPages {
} }
public @NotNull String getSyntax(Permissible viewer, ExecutionContext context, ICommandAddress address) { public @NotNull String getSyntax(Permissible viewer, ExecutionContext context, ICommandAddress address) {
List<IHelpComponent> components = syntaxTopic.getComponents(address, viewer, context); List<IHelpComponent> components = syntaxTopic.getComponents(address, viewer, context, false);
if (components.isEmpty()) { if (components.isEmpty()) {
return getHelpPage(viewer, context, address, 1); return getHelpPage(viewer, context, address, 1);
} }

View File

@@ -15,8 +15,8 @@ public abstract class HelpTopicModifier implements IHelpTopic {
} }
@Override @Override
public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context) { public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
return modify(delegate.getComponents(target, viewer, context), target, viewer, context); return modify(delegate.getComponents(target, viewer, context, true), target, viewer, context);
} }
protected abstract List<IHelpComponent> modify(List<IHelpComponent> components, ICommandAddress target, Permissible viewer, ExecutionContext context); protected abstract List<IHelpComponent> modify(List<IHelpComponent> components, ICommandAddress target, Permissible viewer, ExecutionContext context);

View File

@@ -11,12 +11,13 @@ public interface IHelpTopic {
/** /**
* Get the components of this help topic * Get the components of this help topic
* *
* @param target The address of the command to provide help about * @param target The address of the command to provide help about
* @param viewer The permissible that the page will be shown to (null -> choose a default set). * @param viewer The permissible that the page will be shown to (null -> choose a default set).
* @param context Context of the command execution * @param context Context of the command execution
* @param isForPage A boolean indicating if the components are to be used in a page (for help)
* @return a mutable list of components to include in the help pages * @return a mutable list of components to include in the help pages
*/ */
List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context); List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage);
} }

View File

@@ -20,7 +20,7 @@ public class DefaultPageBuilder implements IPageBuilder {
List<IHelpComponent> components = new LinkedList<>(); List<IHelpComponent> components = new LinkedList<>();
for (IHelpTopic topic : helpTopics) { for (IHelpTopic topic : helpTopics) {
components.addAll(topic.getComponents(target, viewer, context)); components.addAll(topic.getComponents(target, viewer, context, true));
} }
PageBorders pageBorders = null; PageBorders pageBorders = null;

View File

@@ -16,7 +16,7 @@ import java.util.List;
public class DescriptionHelpTopic implements IHelpTopic { public class DescriptionHelpTopic implements IHelpTopic {
@Override @Override
public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context) { public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
List<IHelpComponent> out = new ArrayList<>(); List<IHelpComponent> out = new ArrayList<>();
Formatting format = context.getFormat(EMessageType.DESCRIPTION); Formatting format = context.getFormat(EMessageType.DESCRIPTION);

View File

@@ -11,29 +11,31 @@ import io.dico.dicore.command.predef.PredefinedCommand;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.permissions.Permissible; import org.bukkit.permissions.Permissible;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import java.util.Map;
public class SubcommandsHelpTopic implements IHelpTopic { public class SubcommandsHelpTopic implements IHelpTopic {
@Override @Override
public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context) { public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
List<IHelpComponent> out = new ArrayList<>(); Collection<String> mainKeys = target.getChildrenMainKeys();
Map<String, ? extends ICommandAddress> children = target.getChildren(); if (mainKeys.isEmpty()) {
if (children.isEmpty()) { return Collections.emptyList();
//System.out.println("No subcommands");
return out;
} }
CommandSender sender = viewer instanceof CommandSender ? (CommandSender) viewer : context.getSender(); List<IHelpComponent> result = new ArrayList<>();
children.values().stream().distinct().forEach(child -> {
if ((!child.hasCommand() || child.getCommand().isVisibleTo(sender)) && !(child instanceof PredefinedCommand)) {
out.add(getComponent(child, viewer, context));
}
});
return out; mainKeys = new ArrayList<>(target.getChildrenMainKeys());
((ArrayList<String>) mainKeys).sort(null);
CommandSender sender = viewer instanceof CommandSender ? (CommandSender) viewer : context.getSender();
for (String key : mainKeys) {
ICommandAddress child = target.getChild(key);
if ((child.hasChildren() || child.hasUserDeclaredCommand()) && child.getCommand().isVisibleTo(sender)) {
result.add(getComponent(child, viewer, context));
}
}
return result;
} }
public IHelpComponent getComponent(ICommandAddress child, Permissible viewer, ExecutionContext context) { public IHelpComponent getComponent(ICommandAddress child, Permissible viewer, ExecutionContext context) {

View File

@@ -19,56 +19,74 @@ import java.util.Map;
public class SyntaxHelpTopic implements IHelpTopic { public class SyntaxHelpTopic implements IHelpTopic {
@Override @Override
public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context) { public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
if (!target.hasCommand()) { if (!target.hasCommand()) {
return Collections.emptyList(); return Collections.emptyList();
} }
String line = context.getFormat(EMessageType.SYNTAX) + "Syntax: " if (target.hasChildren()) {
+ context.getFormat(EMessageType.INSTRUCTION) + target.getAddress() if (!isForPage) {
+ ' ' + getShortSyntax(target, context); // HelpPages will send help instead of syntax, which might in turn include syntax as well.
return Collections.emptyList();
}
return Collections.singletonList(new SimpleHelpComponent(line)); if (!target.hasUserDeclaredCommand() && !target.getCommand().getParameterList().hasAnyParameters()) {
// no point adding syntax at all
return Collections.emptyList();
}
}
StringBuilder line = new StringBuilder();
if (isForPage)
line.append(context.getFormat(EMessageType.SYNTAX))
.append("Syntax: ");
line.append('/')
.append(context.getFormat(EMessageType.INSTRUCTION))
.append(target.getAddress())
.append(' ');
addShortSyntax(line, target, context);
return Collections.singletonList(new SimpleHelpComponent(line.toString()));
} }
private static String getShortSyntax(ICommandAddress target, ExecutionContext ctx) { private static void addShortSyntax(StringBuilder builder, ICommandAddress address, ExecutionContext ctx) {
StringBuilder syntax = new StringBuilder(); if (address.hasCommand()) {
if (target.hasCommand()) {
Formatting syntaxColor = ctx.getFormat(EMessageType.SYNTAX); Formatting syntaxColor = ctx.getFormat(EMessageType.SYNTAX);
Formatting highlight = ctx.getFormat(EMessageType.HIGHLIGHT); Formatting highlight = ctx.getFormat(EMessageType.HIGHLIGHT);
syntax.append(syntaxColor); builder.append(syntaxColor);
Command command = target.getCommand(); Command command = address.getCommand();
ParameterList list = command.getParameterList(); ParameterList list = command.getParameterList();
Parameter<?, ?> repeated = list.getRepeatedParameter(); Parameter<?, ?> repeated = list.getRepeatedParameter();
int requiredCount = list.getRequiredCount(); int requiredCount = list.getRequiredCount();
List<Parameter<?, ?>> indexedParameters = list.getIndexedParameters(); List<Parameter<?, ?>> indexedParameters = list.getIndexedParameters();
for (int i = 0, n = indexedParameters.size(); i < n; i++) { for (int i = 0, n = indexedParameters.size(); i < n; i++) {
syntax.append(i < requiredCount ? " <" : " ["); builder.append(i < requiredCount ? " <" : " [");
Parameter<?, ?> param = indexedParameters.get(i); Parameter<?, ?> param = indexedParameters.get(i);
syntax.append(param.getName()); builder.append(param.getName());
if (param == repeated) { if (param == repeated) {
syntax.append(highlight).append("...").append(syntaxColor); builder.append(highlight).append("...").append(syntaxColor);
} }
syntax.append(i < requiredCount ? '>' : ']'); builder.append(i < requiredCount ? '>' : ']');
} }
Map<String, Parameter<?, ?>> parametersByName = list.getParametersByName(); Map<String, Parameter<?, ?>> parametersByName = list.getParametersByName();
for (Parameter<?, ?> param : parametersByName.values()) { for (Parameter<?, ?> param : parametersByName.values()) {
if (param.isFlag()) { if (param.isFlag()) {
syntax.append(" [").append(param.getName()); builder.append(" [").append(param.getName());
if (param.expectsInput()) { if (param.expectsInput()) {
syntax.append(" <").append(param.getName()).append(">"); builder.append(" <").append(param.getName()).append(">");
} }
syntax.append(']'); builder.append(']');
} }
} }
} else { } else {
syntax.append(' '); builder.append(' ');
} }
return syntax.toString();
} }
} }

View File

@@ -24,7 +24,7 @@ public class HelpComponentInserter extends HelpTopicModifier {
for (int i = insertions.size() - 1; i >= 0; i--) { for (int i = insertions.size() - 1; i >= 0; i--) {
IInsertion insertion = insertions.get(i); IInsertion insertion = insertions.get(i);
int idx = insertion.insertionIndex(components, target, viewer, context); int idx = insertion.insertionIndex(components, target, viewer, context);
List<IHelpComponent> inserted = insertion.getComponents(target, viewer, context); List<IHelpComponent> inserted = insertion.getComponents(target, viewer, context, true);
components.addAll(idx, inserted); components.addAll(idx, inserted);
} }

View File

@@ -17,8 +17,8 @@ public class Insertions {
public static IInsertion combine(IHelpTopic topic, IInsertionFunction function) { public static IInsertion combine(IHelpTopic topic, IInsertionFunction function) {
return new IInsertion() { return new IInsertion() {
@Override @Override
public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context) { public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
return topic.getComponents(target, viewer, context); return topic.getComponents(target, viewer, context, true);
} }
@Override @Override

View File

@@ -60,6 +60,14 @@ public class ParameterList {
return this; return this;
} }
public boolean hasAnyParameters() {
return !byName.isEmpty();
}
public int getIndexedParameterCount() {
return indexedParameters.size();
}
public List<Parameter<?, ?>> getIndexedParameters() { public List<Parameter<?, ?>> getIndexedParameters() {
return Collections.unmodifiableList(indexedParameters); return Collections.unmodifiableList(indexedParameters);
} }