Updated //help with colors and sub-command support.

This commit is contained in:
sk89q 2014-06-30 21:56:05 -07:00
parent d1f5beb961
commit 1e2523ddcb
3 changed files with 238 additions and 45 deletions

View File

@ -19,9 +19,19 @@
package com.sk89q.worldedit.command;
import com.sk89q.minecraft.util.commands.*;
import com.sk89q.worldedit.*;
import com.google.common.base.Joiner;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandContext;
import com.sk89q.minecraft.util.commands.CommandPermissions;
import com.sk89q.minecraft.util.commands.Logging;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.EntityType;
import com.sk89q.worldedit.LocalConfiguration;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.LocalWorld.KillFlags;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor;
@ -29,16 +39,23 @@ import com.sk89q.worldedit.patterns.Pattern;
import com.sk89q.worldedit.patterns.SingleBlockPattern;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.command.CommandCallable;
import com.sk89q.worldedit.util.command.CommandMapping;
import com.sk89q.worldedit.util.command.Description;
import com.sk89q.worldedit.util.command.Dispatcher;
import com.sk89q.worldedit.util.command.PrimaryAliasComparator;
import com.sk89q.worldedit.util.command.parametric.Optional;
import com.sk89q.worldedit.util.formatting.Cmd;
import com.sk89q.worldedit.util.formatting.ColorCodeBuilder;
import com.sk89q.worldedit.util.formatting.CommandListBox;
import com.sk89q.worldedit.util.formatting.Style;
import com.sk89q.worldedit.util.formatting.StyledFragment;
import com.sk89q.worldedit.world.World;
import java.util.Comparator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import static com.sk89q.minecraft.util.commands.Logging.LogMode.PLACEMENT;
@ -497,59 +514,158 @@ public class UtilityCommands {
help(args, we, actor);
}
public static void help(CommandContext args, WorldEdit we, Actor actor) {
final Dispatcher dispatcher = we.getPlatformManager().getCommandManager().getDispatcher();
private static CommandMapping detectCommand(Dispatcher dispatcher, String command, boolean isRootLevel) {
CommandMapping mapping;
if (args.argsLength() == 0) {
SortedSet<String> commands = new TreeSet<String>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
final int ret = o1.replaceAll("/", "").compareToIgnoreCase(o2.replaceAll("/", ""));
if (ret == 0) {
return o1.compareToIgnoreCase(o2);
}
return ret;
}
});
commands.addAll(dispatcher.getPrimaryAliases());
// First try the command as entered
mapping = dispatcher.get(command);
if (mapping != null) {
return mapping;
}
StringBuilder sb = new StringBuilder();
boolean first = true;
for (String command : commands) {
if (!first) {
sb.append(", ");
}
sb.append('/');
sb.append(command);
first = false;
// Then if we're looking at root commands and the user didn't use
// any slashes, let's try double slashes and then single slashes.
// However, be aware that there exists different single slash
// and double slash commands in WorldEdit
if (isRootLevel && !command.contains("/")) {
mapping = dispatcher.get("//" + command);
if (mapping != null) {
return mapping;
}
actor.print(sb.toString());
return;
mapping = dispatcher.get("/" + command);
if (mapping != null) {
return mapping;
}
}
String command = args.getJoinedStrings(0).toLowerCase().replaceAll("^/", "");
CommandMapping mapping = dispatcher.get(command);
return null;
}
if (mapping == null) {
actor.printError("Unknown command '" + command + "'.");
return;
public static void help(CommandContext args, WorldEdit we, Actor actor) {
CommandCallable callable = we.getPlatformManager().getCommandManager().getDispatcher();
int page = 0;
final int perPage = actor instanceof Player ? 8 : 20; // More pages for console
int effectiveLength = args.argsLength();
// Detect page from args
try {
if (args.argsLength() > 0) {
page = args.getInteger(args.argsLength() - 1);
if (page <= 0) {
page = 1;
} else {
page--;
}
effectiveLength--;
}
} catch (NumberFormatException ignored) {
}
Description description = mapping.getDescription();
boolean isRootLevel = true;
List<String> visited = new ArrayList<String>();
if (description.getUsage() != null) {
actor.printDebug("Usage: " + description.getUsage());
// Drill down to the command
for (int i = 0; i < effectiveLength; i++) {
String command = args.getString(i);
if (callable instanceof Dispatcher) {
// Chop off the beginning / if we're are the root level
if (isRootLevel && command.length() > 1 && command.charAt(0) == '/') {
command = command.substring(1);
}
CommandMapping mapping = detectCommand((Dispatcher) callable, command, isRootLevel);
if (mapping != null) {
callable = mapping.getCallable();
} else {
if (isRootLevel) {
actor.printError(String.format("The command '%s' could not be found.", args.getString(i)));
return;
} else {
actor.printError(String.format("The sub-command '%s' under '%s' could not be found.",
command, Joiner.on(" ").join(visited)));
return;
}
}
visited.add(args.getString(i));
isRootLevel = false;
} else {
actor.printError(String.format("'%s' has no sub-commands. (Maybe '%s' is for a parameter?)",
Joiner.on(" ").join(visited), command));
return;
}
}
if (description.getHelp() != null) {
actor.print(description.getHelp());
} else if (description.getShortDescription() != null) {
actor.print(description.getShortDescription());
// Create the message
if (callable instanceof Dispatcher) {
Dispatcher dispatcher = (Dispatcher) callable;
// Get a list of aliases
List<CommandMapping> aliases = new ArrayList<CommandMapping>(dispatcher.getCommands());
Collections.sort(aliases, PrimaryAliasComparator.INSTANCE);
// Calculate pagination
int offset = perPage * page;
int pageTotal = (int) Math.ceil(aliases.size() / (double) perPage);
// Box
CommandListBox box = new CommandListBox(String.format("Help: page %d/%d ", page + 1, pageTotal));
StyledFragment contents = box.getContents();
StyledFragment tip = contents.createFragment(Style.GRAY);
if (offset >= aliases.size()) {
tip.createFragment(Style.RED).append(String.format("There is no page %d (total number of pages is %d).", page + 1, pageTotal)).newLine();
} else {
List<CommandMapping> list = aliases.subList(offset, Math.min(offset + perPage, aliases.size()));
tip.append("Type ");
tip.append(new Cmd().append("//help ").append("<command> [<page>]"));
tip.append(" for more information.").newLine();
// Add each command
for (CommandMapping mapping : list) {
StringBuilder builder = new StringBuilder();
if (isRootLevel) {
builder.append("/");
}
if (!visited.isEmpty()) {
builder.append(Joiner.on(" ").join(visited));
builder.append(" ");
}
builder.append(mapping.getPrimaryAlias());
box.appendCommand(builder.toString(), mapping.getDescription().getShortDescription());
}
}
actor.printRaw(ColorCodeBuilder.asColorCodes(box));
} else {
actor.print("No further help is available.");
CommandListBox box = new CommandListBox(String.format("Help: %s", Joiner.on(" ").join(visited)));
StyledFragment contents = box.getContents();
Description description = callable.getDescription();
if (description.getUsage() != null) {
contents.createFragment(Style.YELLOW).append("Usage: ");
contents.append(description.getUsage());
} else {
contents.createFragment(Style.GRAY).append("Usage information is not available.");
}
contents.newLine();
if (description.getHelp() != null) {
contents.createFragment(Style.YELLOW_DARK).append(description.getHelp());
} else if (description.getShortDescription() != null) {
contents.createFragment(Style.YELLOW_DARK).append(description.getShortDescription());
} else {
contents.createFragment(Style.GRAY).append("No further help is available.");
}
actor.printRaw(ColorCodeBuilder.asColorCodes(box));
}
}
}

View File

@ -0,0 +1,43 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.util.command;
import java.util.Comparator;
/**
* Compares the primary aliases of two {@link CommandMapping} using
* {@link String#compareTo(String)}.
*/
public final class PrimaryAliasComparator implements Comparator<CommandMapping> {
/**
* An instance of this class.
*/
public static final PrimaryAliasComparator INSTANCE = new PrimaryAliasComparator();
private PrimaryAliasComparator() {
}
@Override
public int compare(CommandMapping o1, CommandMapping o2) {
return o1.getPrimaryAlias().compareTo(o2.getPrimaryAlias());
}
}

View File

@ -0,0 +1,34 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.util.formatting;
/**
* Represents a fragment representing a command that is to be typed.
*/
public class Cmd extends StyledFragment {
/**
* Create a new instance.
*/
public Cmd() {
super(Style.CYAN);
}
}