changeset 2428:88f534ba35ba

Add tab completions for parent command options to subcommands Reviewed-by: jerboaa Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-August/020646.html
author Andrew Azores <aazores@redhat.com>
date Wed, 24 Aug 2016 08:57:17 -0400
parents bc83ef2d7047
children 01d03f3c18cd
files launcher/src/main/java/com/redhat/thermostat/launcher/internal/TabCompletion.java launcher/src/test/java/com/redhat/thermostat/launcher/internal/TabCompletionTest.java
diffstat 2 files changed, 83 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/TabCompletion.java	Tue Aug 23 18:47:09 2016 +0200
+++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/TabCompletion.java	Wed Aug 24 08:57:17 2016 -0400
@@ -67,17 +67,21 @@
 
     private TreeCompleter treeCompleter;
     private Map<String, TreeCompleter.Node> commandMap;
+    private Map<TreeCompleter.Node, Set<TreeCompleter.Node>> subcommandMap;
 
     public TabCompletion() {
-        this(new TreeCompleter(), new HashMap<String, TreeCompleter.Node>());
+        this(new TreeCompleter(), new HashMap<String, TreeCompleter.Node>(),
+                new HashMap<TreeCompleter.Node, Set<TreeCompleter.Node>>());
     }
 
     /*
      * Testing only
      */
-    TabCompletion(TreeCompleter treeCompleter, Map<String, TreeCompleter.Node> commandMap) {
+    TabCompletion(TreeCompleter treeCompleter, Map<String, TreeCompleter.Node> commandMap,
+                  Map<TreeCompleter.Node, Set<TreeCompleter.Node>> subcommandMap) {
         this.treeCompleter = treeCompleter;
         this.commandMap = commandMap;
+        this.subcommandMap = subcommandMap;
 
         treeCompleter.setAlphabeticalCompletions(true);
     }
@@ -160,6 +164,9 @@
     }
 
     private void addSubCommandCompletionsIfRequired(TreeCompleter.Node commandNode, CompleterService service) {
+        for (TreeCompleter.Node subcommand : getSubcommands(commandNode)) {
+            addTopLevelCommandOptionCompletions(subcommand, service);
+        }
         Map<String, Map<CliCommandOption, ? extends TabCompleter>> subcommandCompleters = service.getSubcommandCompleters();
         if (subcommandCompleters == null || subcommandCompleters.isEmpty()) {
             return;
@@ -175,11 +182,17 @@
                 TreeCompleter.Node completionNode = new TreeCompleter.Node(subcommand + " completer", completer);
                 completionNode.setRestartNode(commandNode);
                 addNodeByOption(subcommandNode, cliCommandOption, completionNode);
-                addTopLevelCommandOptionCompletions(subcommandNode, service);
             }
         }
     }
 
+    private Set<TreeCompleter.Node> getSubcommands(TreeCompleter.Node commandNode) {
+        if (!subcommandMap.containsKey(commandNode)) {
+            return Collections.emptySet();
+        }
+        return subcommandMap.get(commandNode);
+    }
+
     public void removeCompleterService(CompleterService service) {
         for (String commandName : getCommandsForService(service)) {
             TreeCompleter.Node command = commandMap.get(commandName);
@@ -213,6 +226,9 @@
     }
 
     private void removeSubCommandCompletionsIfRequired(TreeCompleter.Node commandNode, CompleterService service) {
+        for (TreeCompleter.Node subcommand : getSubcommands(commandNode)) {
+            removeTopLevelCommandOptionCompletions(subcommand, service);
+        }
         Map<String, Map<CliCommandOption, ? extends TabCompleter>> subcommandCompleters = service.getSubcommandCompleters();
         if (subcommandCompleters == null || subcommandCompleters.isEmpty()) {
             return;
@@ -244,7 +260,7 @@
                 String commandName = info.getName();
                 TreeCompleter.Node command = getCommandByName(commandName);
 
-                setupSubcommandCompletion(command, info.getSubcommands());
+                setupSubcommandCompletion(command, info);
 
                 for (Option option : (Collection<Option>) info.getOptions().getOptions()) {
                     setupDefaultCompletion(command, option);
@@ -257,16 +273,21 @@
         }
     }
 
-    private void setupSubcommandCompletion(TreeCompleter.Node commandNode, List<PluginConfiguration.Subcommand> subcommands) {
+    private void setupSubcommandCompletion(TreeCompleter.Node commandNode, CommandInfo commandInfo) {
+        List<PluginConfiguration.Subcommand> subcommands = commandInfo.getSubcommands();
         for (PluginConfiguration.Subcommand subcommand : subcommands) {
-            TreeCompleter.Node node = createStringNode(subcommand.getName());
-            node.setRestartNode(commandNode);
+            TreeCompleter.Node subcommandNode = createStringNode(subcommand.getName());
+            subcommandNode.setRestartNode(commandNode);
+            for (Option option : (Collection<Option>) commandInfo.getOptions().getOptions()) {
+                setupDefaultCompletion(subcommandNode, option);
+            }
             if (subcommand.getOptions() != null) {
                 for (Option option : (Collection<Option>) subcommand.getOptions().getOptions()) {
-                    setupDefaultCompletion(node, option);
+                    setupDefaultCompletion(subcommandNode, option);
                 }
             }
-            commandNode.addBranch(node);
+            commandNode.addBranch(subcommandNode);
+            registerSubcommand(commandNode, subcommandNode);
         }
     }
 
@@ -284,6 +305,13 @@
         }
     }
 
+    private void registerSubcommand(TreeCompleter.Node commandNode, TreeCompleter.Node subcommandNode) {
+        if (!subcommandMap.containsKey(commandNode)) {
+            subcommandMap.put(commandNode, new HashSet<TreeCompleter.Node>());
+        }
+        subcommandMap.get(commandNode).add(subcommandNode);
+    }
+
     private void addHelpOptionIfRequired(TreeCompleter.Node command) {
         if (HelpCommand.COMMAND_NAME.equals(command.getTag())) {
             return;
--- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/TabCompletionTest.java	Tue Aug 23 18:47:09 2016 +0200
+++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/TabCompletionTest.java	Wed Aug 24 08:57:17 2016 -0400
@@ -46,6 +46,7 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.EnumSet;
@@ -72,13 +73,15 @@
     private TabCompletion tabCompletion;
     private TreeCompleter treeCompleter;
     private Map<String, TreeCompleter.Node> commandMap;
+    private Map<TreeCompleter.Node, Set<TreeCompleter.Node>> subcommandMap;
 
     @Before
     @SuppressWarnings("unchecked")
     public void setup() {
         treeCompleter = mock(TreeCompleter.class);
         commandMap = new HashMap<>();
-        tabCompletion = new TabCompletion(treeCompleter, commandMap);
+        subcommandMap = new HashMap<>();
+        tabCompletion = new TabCompletion(treeCompleter, commandMap, subcommandMap);
     }
 
     @Test
@@ -300,4 +303,46 @@
         verify(treeCompleter, times(2)).addBranch(isA(TreeCompleter.Node.class));
     }
 
+    @Test
+    public void testSubcommandsReceiveParentOptionCompletions() {
+        Options parentOptions = new Options();
+        parentOptions.addOption("p", "parent", true, "parent option");
+
+        Options subcommandOptions = new Options();
+        subcommandOptions.addOption("s", "subcommand", true, "subcommand option");
+
+        PluginConfiguration.Subcommand subcommand = mock(PluginConfiguration.Subcommand.class);
+        when(subcommand.getName()).thenReturn("sub");
+        when(subcommand.getDescription()).thenReturn("sub desc");
+        when(subcommand.getOptions()).thenReturn(subcommandOptions);
+
+        CommandInfo parent = mock(CommandInfo.class);
+        when(parent.getName()).thenReturn("parent");
+        when(parent.getBundles()).thenReturn(Collections.<BundleInformation>emptyList());
+        when(parent.getDescription()).thenReturn("parent desc");
+        when(parent.getSubcommands()).thenReturn(Collections.singletonList(subcommand));
+        when(parent.getEnvironments()).thenReturn(EnumSet.of(Environment.CLI, Environment.SHELL));
+        when(parent.getOptions()).thenReturn(parentOptions);
+
+        CommandInfoSource infoSource = mock(CommandInfoSource.class);
+        when(infoSource.getCommandInfos()).thenReturn(Collections.singletonList(parent));
+        when(infoSource.getCommandInfo(parent.getName())).thenReturn(parent);
+
+        tabCompletion.setupTabCompletion(infoSource);
+
+        assertThat(commandMap.keySet(), is(equalTo(Collections.singleton(parent.getName()))));
+        assertThat(subcommandMap.size(), is(1));
+        TreeCompleter.Node parentNode = new ArrayList<>(subcommandMap.keySet()).get(0);
+        assertThat(parentNode.getTag(), is("parent"));
+        TreeCompleter.Node subcommandNode = new ArrayList<>(new ArrayList<>(subcommandMap.values()).get(0)).get(0);
+        assertThat(subcommandNode.getTag(), is("sub"));
+
+        List<TreeCompleter.Node> subcommandChildren = subcommandNode.getBranches();
+        HashSet<String> childTags = new HashSet<>();
+        for (TreeCompleter.Node node : subcommandChildren) {
+            childTags.add(node.getTag());
+        }
+        assertThat(childTags, is(equalTo(new HashSet<>(Arrays.asList("-s", "--subcommand", "-p", "--parent")))));
+    }
+
 }