package org.metaborg.spoofax.shell.commands;

import java.awt.Color;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;

import org.apache.commons.lang3.StringUtils;
import org.metaborg.spoofax.shell.core.StyledText;
import org.metaborg.spoofax.shell.invoker.CommandNotFoundException;
import org.metaborg.spoofax.shell.invoker.ICommandInvoker;

import com.google.inject.Inject;
import com.google.inject.name.Named;

/**
 * Shows descriptions of all commands, or one command if given.
 */
public class HelpCommand implements IReplCommand {
    private ICommandInvoker invoker;
    private final Consumer<StyledText> successHook;
    private final Consumer<StyledText> errorHook;

    /**
     * Instantiates a new HelpCommand.
     *
     * @param invoker
     *            The {@link ICommandInvoker}.
     * @param successHook
     *            Called when a given command and its description was found.
     * @param errorHook
     *            Called when a given command was not found.
     */
    @Inject
    public HelpCommand(ICommandInvoker invoker,
                       @Named("onSuccess") Consumer<StyledText> successHook,
                       @Named("onError") Consumer<StyledText> errorHook) {
        this.invoker = invoker;
        this.successHook = successHook;
        this.errorHook = errorHook;
    }

    @Override
    public String description() {
        return "You just used it.";
    }

    @Override
    public void execute(String... args) {
        try {
            StringBuilder output = new StringBuilder();
            Map<String, IReplCommand> commands = invoker.getCommands();

            Set<String> commandNames;
            if (args.length > 0) {
                commandNames = Collections.singleton(args[0]);
                invoker.commandFromName(args[0]);
            } else {
                commandNames = commands.keySet();
            }

            int longestCommand = commandNames.stream()
                .max((a, b) -> Integer.compare(a.length(), b.length())).orElse("").length();

            // @formatter:off
            commandNames.forEach(name ->
                output.append(invoker.commandPrefix()).append(name)
                      .append(StringUtils.repeat(' ', longestCommand - name.length() + 1))
                      .append(commands.get(name).description()).append('\n'));
            // @formatter:on

            successHook.accept(new StyledText(output.toString()));
        } catch (CommandNotFoundException e) {
            errorHook.accept(new StyledText(Color.RED, e.getMessage()));
        }
    }

}

@@ -131,19 +131,33 @@
 va.util.
-Set
+function.Consumer
 ;%0Aimport
@@ -171,72 +171,62 @@
 til.
-function.Consumer;%0A%0Aimport org.apache.commons.lang3.StringUtils;
+stream.Collectors;%0Aimport java.util.stream.IntStream;%0A
 %0Aimp
@@ -1480,129 +1480,287 @@
     
-@Override%0A    public void execute(String... args) %7B%0A        try %7B%0A            StringBuilder output = new StringBuilder();
+private String formathelp(Map%3CString, IReplCommand%3E commands) %7B%0A        int longestCommand = commands.keySet().stream().mapToInt(a -%3E a.length()).max().orElse(0);%0A        String format = %22%25-%22 + longestCommand + %22s %25s%22;%0A%0A        return commands.keySet().stream().flatMap(name -%3E %7B
 %0A   
@@ -1772,69 +1772,44 @@
     
-Map%3C
 String
-, IReplCommand%3E commands = invoker.getCommands(
+%5B%5D sname = name.split(%22%5C%5CR%22
 );%0A
-%0A
     
@@ -1820,182 +1820,227 @@
     
-Set%3C
 String
-%3E commandNames;%0A            if (args.length %3E 0) %7B%0A                commandNames = Collections.singleton(args%5B0%5D);%0A                invoker.commandFromName(args%5B0%5D);%0A
+%5B%5D sdesc = commands.get(name).description().split(%22%5C%5CR%22);%0A%0A            return IntStream.range(0, Math.max(sname.length, sdesc.length))%0A                    .mapToObj(idx -%3E String.format(format,%0A                   
     
@@ -2051,17 +2051,12 @@
     
-%7D else %7B%0A
+    
     
@@ -2071,41 +2071,45 @@
     
-commandNames = commands.keySet();
+idx %3C sname.length ? sname%5Bidx%5D : %22%22,
 %0A   
@@ -2113,27 +2113,24 @@
             
-%7D%0A%0A
             
@@ -2133,482 +2133,509 @@
     
-int longestCommand = commandNames.stream()%0A                .max((a, b) -%3E Integer.compare(a.length(), b.length())).orElse(%22%22).length(
+                           idx %3C sdesc.length ? sdesc%5Bidx%5D : %22%22));%0A        %7D).collect(Collectors.joining(%22%5Cn%22)
 );%0A
-%0A
     
+%7D%0A%0A
     
-    // @formatter:off%0A            commandNames.forEach(name -%3E%0A                output.append(invoker.commandPrefix()).append(name)%0A                      .append(StringUtils.repeat(' ', longestCommand - name.length() + 1))%0A                      .append(commands.get(name).description()).append('%5Cn'));%0A            // @formatter:on
+@Override%0A    public void execute(String... args) %7B%0A        try %7B%0A            Map%3CString, IReplCommand%3E commands;%0A            if (args.length %3E 0) %7B%0A                IReplCommand command = invoker.commandFromName(args%5B0%5D);%0A                commands = Collections.singletonMap(args%5B0%5D, command);%0A            %7D else %7B%0A                commands = invoker.getCommands();%0A            %7D
 %0A%0A  
@@ -2682,24 +2682,27 @@
 ext(
-output.toString(
+formathelp(commands
 )));

Fix output of help command by allowing for multiline descriptions