This is an automated email from the ASF dual-hosted git repository.
sk0x50 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new d980d03bc1 IGNITE-18148 CLI Added more dynamic completers. Fixes #1363
d980d03bc1 is described below
commit d980d03bc1401f027748ecbca1aa4f1fc0fc5831
Author: Aleksandr Pakhomov <[email protected]>
AuthorDate: Wed Nov 30 15:59:16 2022 +0200
IGNITE-18148 CLI Added more dynamic completers. Fixes #1363
Signed-off-by: Slava Koptilin <[email protected]>
---
.../ignite/internal/cli/commands/BaseCommand.java | 13 +-
.../ignite/internal/cli/commands/Options.java | 215 +++++++++++++++++++
.../internal/cli/commands/OptionsConstants.java | 62 ------
.../ignite/internal/cli/commands/ProfileMixin.java | 6 +-
.../internal/cli/commands/TopLevelCliCommand.java | 5 +-
.../profile/CliConfigProfileCreateCommand.java | 11 +-
.../cli/commands/cluster/ClusterUrlMixin.java | 8 +-
.../commands/cluster/init/ClusterInitOptions.java | 26 ++-
.../cluster/topology/LogicalTopologyCommand.java | 5 +-
.../topology/LogicalTopologyReplCommand.java | 5 +-
.../cluster/topology/PhysicalTopologyCommand.java | 5 +-
.../topology/PhysicalTopologyReplCommand.java | 5 +-
.../cli/commands/connect/ConnectCommand.java | 4 +-
.../cli/commands/connect/ConnectReplCommand.java | 4 +-
.../internal/cli/commands/node/NodeUrlMixin.java | 15 +-
.../internal/cli/commands/sql/SqlCommand.java | 17 +-
.../internal/cli/commands/sql/SqlReplCommand.java | 16 +-
.../cli/core/repl/completer/CompleterConf.java | 145 +++++++++++++
.../{DynamicCompleter.java => DummyCompleter.java} | 16 +-
.../cli/core/repl/completer/DynamicCompleter.java | 11 +-
.../completer/DynamicCompleterActivationPoint.java | 71 +++++--
.../repl/completer/DynamicCompleterFactory.java | 90 +-------
.../repl/completer/DynamicCompleterFilter.java | 40 ++--
.../repl/completer/DynamicCompleterRegistry.java | 113 ++++++----
.../repl/completer/NodeNameDynamicCompleter.java | 61 ------
.../cli/core/repl/completer/NodeUrlProvider.java | 5 +-
.../ClusterConfigDynamicCompleterFactory.java | 71 +++++++
.../{ => hocon}/HoconDynamicCompleter.java | 30 +--
.../hocon/NodeConfigDynamicCompleterFactory.java | 70 +++++++
.../node/NodeNameDynamicCompleterFactory.java} | 34 ++-
.../completer/node/StringDynamicCompleter.java | 52 +++++
.../core/repl/executor/IgnitePicocliCommands.java | 12 +-
.../cli/core/repl/executor/ReplExecutor.java | 1 +
.../completer/DynamicCompleterRegistryTest.java | 233 ++++++++++++++++++---
.../repl/completer/HoconDynamicCompleterTest.java | 5 +-
...erTest.java => StringDynamicCompleterTest.java} | 42 ++--
36 files changed, 1088 insertions(+), 436 deletions(-)
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/BaseCommand.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/BaseCommand.java
index e3e919cef9..8d1ed5a7ae 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/BaseCommand.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/BaseCommand.java
@@ -17,6 +17,13 @@
package org.apache.ignite.internal.cli.commands;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.HELP_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.HELP_OPTION_DESC;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.HELP_OPTION_SHORT;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.VERBOSE_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.VERBOSE_OPTION_DESC;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.VERBOSE_OPTION_SHORT;
+
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.Option;
import picocli.CommandLine.Spec;
@@ -26,12 +33,14 @@ import picocli.CommandLine.Spec;
*/
public abstract class BaseCommand {
/** Help option specification. */
- @Option(names = {"-h", "--help"}, usageHelp = true, description = "Show
this help message and exit.")
+ @Option(names = {HELP_OPTION, HELP_OPTION_SHORT}, usageHelp = true,
description = HELP_OPTION_DESC)
protected boolean usageHelpRequested;
- @Option(names = {"-v", "--verbose"}, description = "Show additional
information.")
+ /** Verbose option specification. */
+ @Option(names = {VERBOSE_OPTION, VERBOSE_OPTION_SHORT}, description =
VERBOSE_OPTION_DESC)
protected boolean verbose;
+ /** Instance of picocli command specification. */
@Spec
protected CommandSpec spec;
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java
new file mode 100644
index 0000000000..1125a9b0cb
--- /dev/null
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.cli.commands;
+
+import org.apache.ignite.internal.cli.config.ConfigConstants;
+
+/**
+ * Constants to use in {@code Option} annotations for commands.
+ */
+public enum Options {
+ CLUSTER_URL(Constants.CLUSTER_URL_OPTION, Constants.URL_OPTION_SHORT,
Constants.CLUSTER_URL_OPTION_DESC),
+ NODE_URL(Constants.NODE_URL_OPTION, Constants.URL_OPTION_SHORT,
Constants.NODE_URL_OPTION_DESC),
+
+ CLUSTER_NAME(Constants.CLUSTER_NAME_OPTION,
Constants.CLUSTER_NAME_OPTION_SHORT, Constants.CLUSTER_NAME_OPTION_DESC),
+ NODE_NAME(Constants.NODE_NAME_OPTION, Constants.NODE_NAME_OPTION_SHORT,
Constants.NODE_NAME_OPTION_DESC),
+
+ CMG_NODE_NAME(Constants.CMG_NODE_NAME_OPTION,
Constants.CMG_NODE_NAME_OPTION_SHORT, Constants.CMG_NODE_NAME_OPTION_DESC),
+ META_STORAGE_NODE_NAME(
+ Constants.META_STORAGE_NODE_NAME_OPTION,
+ Constants.META_STORAGE_NODE_NAME_OPTION_SHORT,
+ Constants.META_STORAGE_NODE_NAME_OPTION_DESC
+ ),
+
+ PROFILE(Constants.PROFILE_OPTION, Constants.PROFILE_OPTION_SHORT,
Constants.PROFILE_OPTION_DESC),
+ PROFILE_COPY_FROM(
+ Constants.PROFILE_COPY_FROM_OPTION,
+ Constants.PROFILE_COPY_FROM_OPTION_SHORT,
+ Constants.PROFILE_COPY_FROM_OPTION_DESC
+ ),
+ PROFILE_ACTIVATE(Constants.PROFILE_ACTIVATE_OPTION,
Constants.PROFILE_ACTIVATE_OPTION_SHORT,
Constants.PROFILE_ACTIVATE_OPTION_DESC),
+
+ SCRIPT_FILE(Constants.SCRIPT_FILE_OPTION,
Constants.SCRIPT_FILE_OPTION_SHORT, Constants.SCRIPT_FILE_OPTION_DESC),
+ JDBC_URL(Constants.JDBC_URL_OPTION, Constants.JDBC_URL_OPTION_SHORT,
Constants.JDBC_URL_OPTION_DESC),
+
+ PLAIN(Constants.PLAIN_OPTION, Constants.PLAIN_OPTION,
Constants.PLAIN_OPTION_DESC),
+ VERBOSE(Constants.VERBOSE_OPTION, Constants.VERBOSE_OPTION_SHORT,
Constants.VERBOSE_OPTION_DESC),
+ HELP(Constants.HELP_OPTION, Constants.HELP_OPTION_SHORT,
Constants.HELP_OPTION_DESC),
+ VERSION(Constants.VERSION_OPTION, Constants.VERSION_OPTION,
Constants.VERSION_OPTION_DESC);
+
+ private final String fullName;
+ private final String shortName;
+ private final String description;
+
+ Options(String fullName, String shortName, String description) {
+ this.fullName = fullName;
+ this.shortName = shortName;
+ this.description = description;
+ }
+
+ public String fullName() {
+ return fullName;
+ }
+
+ public String shortName() {
+ return shortName;
+ }
+
+ public String description() {
+ return description;
+ }
+
+ /** Constants for all options. */
+ public static final class Constants {
+ /** Cluster endpoint URL option long name. */
+ public static final String CLUSTER_URL_OPTION =
"--cluster-endpoint-url";
+
+ /** Cluster endpoint URL option description. */
+ public static final String CLUSTER_URL_OPTION_DESC = "URL of cluster
endpoint";
+
+ /** Cluster endpoint URL option description key. */
+ public static final String CLUSTER_URL_KEY =
ConfigConstants.CLUSTER_URL;
+
+ /** Node URL option long name. */
+ public static final String NODE_URL_OPTION = "--node-url";
+
+ /** Node URL option description. */
+ public static final String NODE_URL_OPTION_DESC = "URL of ignite node";
+
+ /** Node URL or name option description. */
+ public static final String NODE_URL_OR_NAME_DESC = "URL or name of an
Ignite node";
+
+ /** Profile name option long name. */
+ public static final String PROFILE_OPTION = "--profile";
+
+ /** Profile name option short name. */
+ public static final String PROFILE_OPTION_SHORT = "-p";
+
+ /** Profile name option description. */
+ public static final String PROFILE_OPTION_DESC = "Profile name";
+
+ /** URL option short name. */
+ public static final String URL_OPTION_SHORT = "-u";
+
+ /** Node name option long name. */
+ public static final String NODE_NAME_OPTION = "--node-name";
+
+ /** Node name option short name. */
+ public static final String NODE_NAME_OPTION_SHORT = "-n";
+
+ /** Node name option description. */
+ public static final String NODE_NAME_OPTION_DESC = "Name of an Ignite
node";
+
+ /** Verbose option long name. */
+ public static final String VERBOSE_OPTION = "--verbose";
+
+ /** Verbose option short name. */
+ public static final String VERBOSE_OPTION_SHORT = "-v";
+
+ /** Verbose option description. */
+ public static final String VERBOSE_OPTION_DESC = "Show additional
information: logs, REST calls";
+
+ /** Help option long name. */
+ public static final String HELP_OPTION = "--help";
+
+ /** Help option short name. */
+ public static final String HELP_OPTION_SHORT = "-h";
+
+ /** Verbose option description. */
+ public static final String HELP_OPTION_DESC = "Show help for the
specified command";
+
+ /** Profile copy from option long name. */
+ public static final String PROFILE_COPY_FROM_OPTION = "--copy-from";
+
+ /** Profile copy from option short name. */
+ public static final String PROFILE_COPY_FROM_OPTION_SHORT = "-c";
+
+ /** Profile copy from option description. */
+ public static final String PROFILE_COPY_FROM_OPTION_DESC = "Profile
whose content will be copied to new one";
+
+ /** Profile activate option long name. */
+ public static final String PROFILE_ACTIVATE_OPTION = "--activate";
+
+ /** Profile activate option short name. */
+ public static final String PROFILE_ACTIVATE_OPTION_SHORT = "-a";
+
+ /** Profile activate option description. */
+ public static final String PROFILE_ACTIVATE_OPTION_DESC = "Activate
new profile as current or not";
+
+ /** Cluster management node name option long name. */
+ public static final String CMG_NODE_NAME_OPTION = "--cmg-node";
+
+ /** Cluster management node name option short name. */
+ public static final String CMG_NODE_NAME_OPTION_SHORT = "-c";
+
+ /** Cluster management node name option description. */
+ public static final String CMG_NODE_NAME_OPTION_DESC = "Name of the
node (repeat like '--cmg-node node1 --cmg-node node2' "
+ + "to specify more than one node) that will host the Cluster
Management Group."
+ + "If omitted, then --meta-storage-node values will also
supply the nodes for the Cluster Management Group.";
+
+ /** Meta storage management node name option long name. */
+ public static final String META_STORAGE_NODE_NAME_OPTION =
"--meta-storage-node";
+
+ /** Meta storage node name option short name. */
+ public static final String META_STORAGE_NODE_NAME_OPTION_SHORT = "-m";
+
+ /** Meta storage node name option description. */
+ public static final String META_STORAGE_NODE_NAME_OPTION_DESC = "Name
of the node (repeat like '--meta-storage-node node1 "
+ + "--meta-storage-node node2' to specify more than one node)
that will host the Meta Storage."
+ + "If the --cmg-node parameter is omitted, the same nodes will
also host the Cluster Management Group.";
+
+ /** Cluster name option long name. */
+ public static final String CLUSTER_NAME_OPTION = "--cluster-name";
+
+ /** Cluster name option short name. */
+ public static final String CLUSTER_NAME_OPTION_SHORT = "-n";
+
+ /** Cluster name option description. */
+ public static final String CLUSTER_NAME_OPTION_DESC = "Human-readable
name of the cluster";
+
+ /** Plain option long name. */
+ public static final String PLAIN_OPTION = "--plain";
+
+ /** Plain option description. */
+ public static final String PLAIN_OPTION_DESC = "Display output with
plain formatting";
+
+ /** JDBC URL option long name. */
+ public static final String JDBC_URL_OPTION = "--jdbc-url";
+
+ /** JDBC URL option short name. */
+ public static final String JDBC_URL_OPTION_SHORT = "-u";
+
+ /** JDBC URL option description. */
+ public static final String JDBC_URL_OPTION_DESC = "JDBC url to ignite
cluster";
+
+ /** SQL script file option long name. */
+ public static final String SCRIPT_FILE_OPTION = "--script-file";
+
+ /** SQL script file option short name. */
+ public static final String SCRIPT_FILE_OPTION_SHORT = "-f";
+
+ /** SQL script file option description. */
+ public static final String SCRIPT_FILE_OPTION_DESC = "Path to file
with SQL commands to execute";
+
+ /** Version option long name. */
+ public static final String VERSION_OPTION = "--version";
+
+ /** Version option description. */
+ public static final String VERSION_OPTION_DESC = "Print version
information and exit";
+ }
+}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/OptionsConstants.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/OptionsConstants.java
deleted file mode 100644
index 98c56bfc38..0000000000
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/OptionsConstants.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.internal.cli.commands;
-
-import org.apache.ignite.internal.cli.config.ConfigConstants;
-
-/**
- * Constants to use in {@code Option} annotations for commands.
- */
-public class OptionsConstants {
- /** Cluster endpoint URL option long name. */
- public static final String CLUSTER_URL_OPTION = "--cluster-endpoint-url";
-
- /** Cluster endpoint URL option description. */
- public static final String CLUSTER_URL_DESC = "URL of cluster endpoint";
-
- /** Cluster endpoint URL option description key. */
- public static final String CLUSTER_URL_KEY = ConfigConstants.CLUSTER_URL;
-
- /** Node URL option long name. */
- public static final String NODE_URL_OPTION = "--node-url";
-
- /** Node URL option description. */
- public static final String NODE_URL_DESC = "URL of ignite node";
-
- /** Node URL or name option description. */
- public static final String NODE_URL_OR_NAME_DESC = "URL or name of an
Ignite node";
-
- /** Profile name option names. */
- public static final String PROFILE_OPTION = "--profile";
- public static final String PROFILE_OPTION_SHORT = "-p";
-
- /** Profile name option description. */
- public static final String PROFILE_OPTION_DESC = "Profile name";
-
- /** URL option short name. */
- public static final String URL_OPTION_SHORT = "-u";
-
- /** Node name option long name. */
- public static final String NODE_NAME_OPTION = "--node-name";
-
- /** Node name option short name. */
- public static final String NODE_NAME_OPTION_SHORT = "-n";
-
- /** Node name option description. */
- public static final String NODE_NAME_DESC = "Name of an Ignite node";
-}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/ProfileMixin.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/ProfileMixin.java
index d0cf65817b..f82a6f0cca 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/ProfileMixin.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/ProfileMixin.java
@@ -17,9 +17,9 @@
package org.apache.ignite.internal.cli.commands;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.PROFILE_OPTION;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.PROFILE_OPTION_DESC;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.PROFILE_OPTION_SHORT;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.PROFILE_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.PROFILE_OPTION_DESC;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.PROFILE_OPTION_SHORT;
import picocli.CommandLine.Option;
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/TopLevelCliCommand.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/TopLevelCliCommand.java
index 444722a1c5..ee13d88282 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/TopLevelCliCommand.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/TopLevelCliCommand.java
@@ -17,6 +17,9 @@
package org.apache.ignite.internal.cli.commands;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.VERSION_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.VERSION_OPTION_DESC;
+
import org.apache.ignite.internal.cli.VersionProvider;
import org.apache.ignite.internal.cli.commands.cliconfig.CliCommand;
import org.apache.ignite.internal.cli.commands.cluster.ClusterCommand;
@@ -46,6 +49,6 @@ import picocli.CommandLine.Option;
})
public class TopLevelCliCommand extends BaseCommand {
@SuppressWarnings("PMD.UnusedPrivateField")
- @Option(names = {"--version"}, versionHelp = true, description = "Print
version information and exit")
+ @Option(names = VERSION_OPTION, versionHelp = true, description =
VERSION_OPTION_DESC)
private boolean versionRequested;
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cliconfig/profile/CliConfigProfileCreateCommand.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cliconfig/profile/CliConfigProfileCreateCommand.java
index 594120d149..cd87262fdc 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cliconfig/profile/CliConfigProfileCreateCommand.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cliconfig/profile/CliConfigProfileCreateCommand.java
@@ -17,6 +17,13 @@
package org.apache.ignite.internal.cli.commands.cliconfig.profile;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.PROFILE_ACTIVATE_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.PROFILE_ACTIVATE_OPTION_DESC;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.PROFILE_ACTIVATE_OPTION_SHORT;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.PROFILE_COPY_FROM_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.PROFILE_COPY_FROM_OPTION_DESC;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.PROFILE_COPY_FROM_OPTION_SHORT;
+
import jakarta.inject.Inject;
import java.util.concurrent.Callable;
import
org.apache.ignite.internal.cli.call.cliconfig.profile.CliConfigProfileCreateCall;
@@ -35,10 +42,10 @@ public class CliConfigProfileCreateCommand extends
BaseCommand implements Callab
@Parameters(arity = "1", description = "Name of new profile")
private String profileName;
- @Option(names = {"--copy-from", "-c"}, description = "Profile whose
content will be copied to new one")
+ @Option(names = {PROFILE_COPY_FROM_OPTION,
PROFILE_COPY_FROM_OPTION_SHORT}, description = PROFILE_COPY_FROM_OPTION_DESC)
private String copyFrom;
- @Option(names = {"--activate", "-a"}, description = "Activate new profile
as current or not")
+ @Option(names = {PROFILE_ACTIVATE_OPTION, PROFILE_ACTIVATE_OPTION_SHORT},
description = PROFILE_ACTIVATE_OPTION_DESC)
private boolean activate;
@Inject
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/ClusterUrlMixin.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/ClusterUrlMixin.java
index 685900ac3c..bbe1a3dcaa 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/ClusterUrlMixin.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/ClusterUrlMixin.java
@@ -17,9 +17,9 @@
package org.apache.ignite.internal.cli.commands.cluster;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.CLUSTER_URL_DESC;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.CLUSTER_URL_OPTION;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.URL_OPTION_SHORT;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.CLUSTER_URL_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.CLUSTER_URL_OPTION_DESC;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.URL_OPTION_SHORT;
import java.net.URL;
import org.apache.ignite.internal.cli.core.converters.UrlConverter;
@@ -30,7 +30,7 @@ import picocli.CommandLine.Option;
*/
public class ClusterUrlMixin {
/** Cluster endpoint URL option. */
- @Option(names = {URL_OPTION_SHORT, CLUSTER_URL_OPTION}, description =
CLUSTER_URL_DESC, converter = UrlConverter.class)
+ @Option(names = {URL_OPTION_SHORT, CLUSTER_URL_OPTION}, description =
CLUSTER_URL_OPTION_DESC, converter = UrlConverter.class)
private URL clusterUrl;
public String getClusterUrl() {
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitOptions.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitOptions.java
index 011512ab5e..75fd02f512 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitOptions.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitOptions.java
@@ -17,6 +17,16 @@
package org.apache.ignite.internal.cli.commands.cluster.init;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.CLUSTER_NAME_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.CLUSTER_NAME_OPTION_DESC;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.CLUSTER_NAME_OPTION_SHORT;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.CMG_NODE_NAME_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.CMG_NODE_NAME_OPTION_DESC;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.CMG_NODE_NAME_OPTION_SHORT;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.META_STORAGE_NODE_NAME_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.META_STORAGE_NODE_NAME_OPTION_DESC;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.META_STORAGE_NODE_NAME_OPTION_SHORT;
+
import java.util.ArrayList;
import java.util.List;
import picocli.CommandLine.Option;
@@ -29,25 +39,19 @@ public class ClusterInitOptions {
* List of names of the nodes (each represented by a separate command line
argument) that will host the Meta Storage. If the
* "--cmg-nodes" parameter is omitted, the same nodes will also host the
Cluster Management Group.
*/
- @Option(names = {"-m", "--meta-storage-node"}, required = true,
description = {
- "Name of the node (repeat like '--meta-storage-node node1
--meta-storage-node node2' to specify more than one node)",
- "that will host the Meta Storage.",
- "If the --cmg-node parameter is omitted, the same nodes will also
host the Cluster Management Group."
- })
+ @Option(names = {META_STORAGE_NODE_NAME_OPTION,
META_STORAGE_NODE_NAME_OPTION_SHORT},
+ required = true,
+ description = META_STORAGE_NODE_NAME_OPTION_DESC)
private List<String> metaStorageNodes;
/**
* List of names of the nodes (each represented by a separate command line
argument) that will host the Cluster Management Group.
*/
- @Option(names = {"-c", "--cmg-node"}, description = {
- "Name of the node (repeat like '--cmg-node node1 --cmg-node node2'
to specify more than one node)",
- "that will host the Cluster Management Group.",
- "If omitted, then --meta-storage-node values will also supply the
nodes for the Cluster Management Group."
- })
+ @Option(names = {CMG_NODE_NAME_OPTION, CMG_NODE_NAME_OPTION_SHORT},
description = CMG_NODE_NAME_OPTION_DESC)
private List<String> cmgNodes = new ArrayList<>();
/** Name of the cluster. */
- @Option(names = {"-n", "--cluster-name"}, required = true, description =
"Human-readable name of the cluster")
+ @Option(names = {CLUSTER_NAME_OPTION, CLUSTER_NAME_OPTION_SHORT}, required
= true, description = CLUSTER_NAME_OPTION_DESC)
private String clusterName;
public List<String> getMetaStorageNodes() {
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/topology/LogicalTopologyCommand.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/topology/LogicalTopologyCommand.java
index f4b994aeb6..24b9a2f0a9 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/topology/LogicalTopologyCommand.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/topology/LogicalTopologyCommand.java
@@ -17,6 +17,9 @@
package org.apache.ignite.internal.cli.commands.cluster.topology;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.PLAIN_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.PLAIN_OPTION_DESC;
+
import jakarta.inject.Inject;
import java.util.concurrent.Callable;
import
org.apache.ignite.internal.cli.call.cluster.topology.LogicalTopologyCall;
@@ -43,7 +46,7 @@ public class LogicalTopologyCommand extends BaseCommand
implements Callable<Inte
@Inject
private LogicalTopologyCall call;
- @Option(names = "--plain", description = "Display output with plain
formatting")
+ @Option(names = PLAIN_OPTION, description = PLAIN_OPTION_DESC)
private boolean plain;
/** {@inheritDoc} */
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/topology/LogicalTopologyReplCommand.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/topology/LogicalTopologyReplCommand.java
index 37c0f7f7a7..3069cab8cd 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/topology/LogicalTopologyReplCommand.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/topology/LogicalTopologyReplCommand.java
@@ -17,6 +17,9 @@
package org.apache.ignite.internal.cli.commands.cluster.topology;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.PLAIN_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.PLAIN_OPTION_DESC;
+
import jakarta.inject.Inject;
import
org.apache.ignite.internal.cli.call.cluster.topology.LogicalTopologyCall;
import org.apache.ignite.internal.cli.commands.BaseCommand;
@@ -46,7 +49,7 @@ public class LogicalTopologyReplCommand extends BaseCommand
implements Runnable
@Inject
private ConnectToClusterQuestion question;
- @Option(names = "--plain", description = "Display output with plain
formatting")
+ @Option(names = PLAIN_OPTION, description = PLAIN_OPTION_DESC)
private boolean plain;
/** {@inheritDoc} */
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/topology/PhysicalTopologyCommand.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/topology/PhysicalTopologyCommand.java
index a28f3fc274..b6c32e3cc4 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/topology/PhysicalTopologyCommand.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/topology/PhysicalTopologyCommand.java
@@ -17,6 +17,9 @@
package org.apache.ignite.internal.cli.commands.cluster.topology;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.PLAIN_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.PLAIN_OPTION_DESC;
+
import jakarta.inject.Inject;
import java.util.concurrent.Callable;
import
org.apache.ignite.internal.cli.call.cluster.topology.PhysicalTopologyCall;
@@ -42,7 +45,7 @@ public class PhysicalTopologyCommand extends BaseCommand
implements Callable<Int
@Inject
private PhysicalTopologyCall call;
- @Option(names = "--plain", description = "Display output with plain
formatting")
+ @Option(names = PLAIN_OPTION, description = PLAIN_OPTION_DESC)
private boolean plain;
/** {@inheritDoc} */
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/topology/PhysicalTopologyReplCommand.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/topology/PhysicalTopologyReplCommand.java
index b72f450f55..81e251234e 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/topology/PhysicalTopologyReplCommand.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/topology/PhysicalTopologyReplCommand.java
@@ -17,6 +17,9 @@
package org.apache.ignite.internal.cli.commands.cluster.topology;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.PLAIN_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.PLAIN_OPTION_DESC;
+
import jakarta.inject.Inject;
import
org.apache.ignite.internal.cli.call.cluster.topology.PhysicalTopologyCall;
import org.apache.ignite.internal.cli.commands.BaseCommand;
@@ -45,7 +48,7 @@ public class PhysicalTopologyReplCommand extends BaseCommand
implements Runnable
@Inject
private ConnectToClusterQuestion question;
- @Option(names = "--plain", description = "Display output with plain
formatting")
+ @Option(names = PLAIN_OPTION, description = PLAIN_OPTION_DESC)
private boolean plain;
/** {@inheritDoc} */
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectCommand.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectCommand.java
index 9457086550..987d5344be 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectCommand.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectCommand.java
@@ -17,8 +17,8 @@
package org.apache.ignite.internal.cli.commands.connect;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.CLUSTER_URL_KEY;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.NODE_URL_OR_NAME_DESC;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.CLUSTER_URL_KEY;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.NODE_URL_OR_NAME_DESC;
import jakarta.inject.Inject;
import org.apache.ignite.internal.cli.ReplManager;
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectReplCommand.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectReplCommand.java
index 6f73ea78b5..db12b83720 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectReplCommand.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectReplCommand.java
@@ -17,8 +17,8 @@
package org.apache.ignite.internal.cli.commands.connect;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.CLUSTER_URL_KEY;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.NODE_URL_OR_NAME_DESC;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.CLUSTER_URL_KEY;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.NODE_URL_OR_NAME_DESC;
import jakarta.inject.Inject;
import org.apache.ignite.internal.cli.call.connect.ConnectCall;
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeUrlMixin.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeUrlMixin.java
index 8106ef0698..c60f4544b9 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeUrlMixin.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeUrlMixin.java
@@ -17,12 +17,11 @@
package org.apache.ignite.internal.cli.commands.node;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.NODE_NAME_DESC;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.NODE_NAME_OPTION;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.NODE_NAME_OPTION_SHORT;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.NODE_URL_DESC;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.NODE_URL_OPTION;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.URL_OPTION_SHORT;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.NODE_NAME_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.NODE_NAME_OPTION_SHORT;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.NODE_URL_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.NODE_URL_OPTION_DESC;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.URL_OPTION_SHORT;
import jakarta.inject.Inject;
import java.net.URL;
@@ -49,13 +48,13 @@ public class NodeUrlMixin {
/**
* Node URL option.
*/
- @Option(names = {URL_OPTION_SHORT, NODE_URL_OPTION}, description =
NODE_URL_DESC, converter = UrlConverter.class)
+ @Option(names = {URL_OPTION_SHORT, NODE_URL_OPTION}, description =
NODE_URL_OPTION_DESC, converter = UrlConverter.class)
private URL nodeUrl;
/**
* Node name option.
*/
- @Option(names = {NODE_NAME_OPTION_SHORT, NODE_NAME_OPTION},
description = NODE_NAME_DESC)
+ @Option(names = {NODE_NAME_OPTION_SHORT, NODE_NAME_OPTION},
description = NODE_URL_OPTION_DESC)
private String nodeName;
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/sql/SqlCommand.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/sql/SqlCommand.java
index 7635404003..f36ff6e84e 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/sql/SqlCommand.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/sql/SqlCommand.java
@@ -17,6 +17,15 @@
package org.apache.ignite.internal.cli.commands.sql;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.JDBC_URL_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.JDBC_URL_OPTION_DESC;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.JDBC_URL_OPTION_SHORT;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.PLAIN_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.PLAIN_OPTION_DESC;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.SCRIPT_FILE_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.SCRIPT_FILE_OPTION_DESC;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.SCRIPT_FILE_OPTION_SHORT;
+
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@@ -44,11 +53,11 @@ import picocli.CommandLine.Parameters;
*/
@Command(name = "sql", description = "Executes SQL query")
public class SqlCommand extends BaseCommand implements Callable<Integer> {
- @Option(names = {"-u", "--jdbc-url"}, required = true,
- descriptionKey = "ignite.jdbc-url", description = "JDBC url to
ignite cluster")
+ @Option(names = {JDBC_URL_OPTION, JDBC_URL_OPTION_SHORT}, required = true,
+ descriptionKey = "ignite.jdbc-url", description =
JDBC_URL_OPTION_DESC)
private String jdbc;
- @Option(names = "--plain", description = "Display output with plain
formatting")
+ @Option(names = PLAIN_OPTION, description = PLAIN_OPTION_DESC)
private boolean plain;
@ArgGroup(multiplicity = "1")
@@ -58,7 +67,7 @@ public class SqlCommand extends BaseCommand implements
Callable<Integer> {
@Parameters(index = "0", description = "SQL query to execute")
private String command;
- @Option(names = {"-f", "--script-file"}, description = "Path to file
with SQL commands to execute")
+ @Option(names = {SCRIPT_FILE_OPTION, SCRIPT_FILE_OPTION_SHORT},
description = SCRIPT_FILE_OPTION_DESC)
private File file;
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/sql/SqlReplCommand.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/sql/SqlReplCommand.java
index 05ea375939..13d94523bd 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/sql/SqlReplCommand.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/sql/SqlReplCommand.java
@@ -17,6 +17,14 @@
package org.apache.ignite.internal.cli.commands.sql;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.JDBC_URL_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.JDBC_URL_OPTION_DESC;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.JDBC_URL_OPTION_SHORT;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.PLAIN_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.PLAIN_OPTION_DESC;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.SCRIPT_FILE_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.SCRIPT_FILE_OPTION_SHORT;
+
import jakarta.inject.Inject;
import java.io.File;
import java.io.IOException;
@@ -50,11 +58,11 @@ import picocli.CommandLine.Parameters;
*/
@Command(name = "sql", description = "Executes SQL query")
public class SqlReplCommand extends BaseCommand implements Runnable {
- @Option(names = {"-u", "--jdbc-url"}, required = true,
- descriptionKey = "ignite.jdbc-url", description = "JDBC url to
ignite cluster")
+ @Option(names = {JDBC_URL_OPTION, JDBC_URL_OPTION_SHORT}, required = true,
+ descriptionKey = "ignite.jdbc-url", description =
JDBC_URL_OPTION_DESC)
private String jdbc;
- @Option(names = "--plain", description = "Display output with plain
formatting")
+ @Option(names = PLAIN_OPTION, description = PLAIN_OPTION_DESC)
private boolean plain;
@ArgGroup
@@ -64,7 +72,7 @@ public class SqlReplCommand extends BaseCommand implements
Runnable {
@Parameters(index = "0", description = "SQL query to execute")
private String command;
- @Option(names = {"-f", "--script-file"}, description = "Path to file
with SQL commands to execute")
+ @Option(names = {SCRIPT_FILE_OPTION, SCRIPT_FILE_OPTION_SHORT},
description = SCRIPT_FILE_OPTION_SHORT)
private File file;
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/CompleterConf.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/CompleterConf.java
new file mode 100644
index 0000000000..4833e93393
--- /dev/null
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/CompleterConf.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.cli.core.repl.completer;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.ignite.internal.cli.commands.Options;
+
+/**
+ * Configuration for dynamic completer. It declares for what command and
option the completer could be applied.
+ * Also supports explicit declaration of options after which the completer
should not be applied.
+ */
+public class CompleterConf {
+
+ private final List<String[]> commands;
+
+ private final Set<String> enableOptions;
+
+ private final Set<String> disableOptions;
+
+ private final boolean exclusiveEnableOptions;
+
+ private CompleterConf(List<String[]> commands, Set<String> enableOptions,
Set<String> disableOptions, boolean exclusiveEnableOptions) {
+ if (commands == null) {
+ throw new IllegalArgumentException("commands must not be null");
+ }
+
+ this.commands = commands;
+ this.enableOptions = enableOptions;
+ this.disableOptions = disableOptions;
+ this.exclusiveEnableOptions = exclusiveEnableOptions;
+ }
+
+ public static CompleterConf everytime() {
+ return builder().build();
+ }
+
+ public static CompleterConf forCommand(String... words) {
+ return builder().command(words).build();
+ }
+
+ public static CompleterConfBuilder builder() {
+ return new CompleterConfBuilder();
+ }
+
+ public List<String[]> commands() {
+ return commands;
+ }
+
+ public Set<String> enableOptions() {
+ return enableOptions;
+ }
+
+ public Set<String> disableOptions() {
+ return disableOptions;
+ }
+
+ public boolean commandSpecific() {
+ return !commands.isEmpty();
+ }
+
+ public boolean hasEnableOptions() {
+ return enableOptions != null;
+ }
+
+ public boolean hasDisableOptions() {
+ return disableOptions != null;
+ }
+
+ public boolean isExclusiveEnableOptions() {
+ return exclusiveEnableOptions;
+ }
+
+ /** Builder for {@link CompleterConf}. */
+ public static class CompleterConfBuilder {
+ private final List<String[]> command = new ArrayList<>();
+
+ private Set<String> enableOptions;
+
+ private Set<String> disableOptions;
+
+ private boolean exclusiveEnableOptions;
+
+ private CompleterConfBuilder() {
+ }
+
+ /** Setup commands after those the completer should be called. */
+ public CompleterConfBuilder command(String... words) {
+ this.command.add(Arrays.copyOf(words, words.length));
+ return this;
+ }
+
+ /** Setup options after those the completer should be called. */
+ public CompleterConfBuilder enableOptions(Options... enableOptions) {
+ this.enableOptions = Stream.of(enableOptions)
+ .flatMap(opt -> Stream.of(opt.fullName(), opt.shortName()))
+ .collect(Collectors.toSet());
+ return this;
+ }
+
+ /** Setup options after those the completer should be called. */
+ public CompleterConfBuilder enableOptions(String... enableOptions) {
+ this.enableOptions = Set.of(enableOptions);
+ return this;
+ }
+
+ /** Setup options after those the completer should NOT be called. */
+ public CompleterConfBuilder disableOptions(String... disableOptions) {
+ this.disableOptions = Set.of(disableOptions);
+ return this;
+ }
+
+ /**
+ * If called than all enable options of current configuration will
become disable options for all other completers.
+ * For example, --node-name should be completed by only one completer.
+ */
+ public CompleterConfBuilder exclusiveEnableOptions() {
+ this.exclusiveEnableOptions = true;
+ return this;
+ }
+
+ public CompleterConf build() {
+ return new CompleterConf(command, enableOptions, disableOptions,
exclusiveEnableOptions);
+ }
+ }
+}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleter.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DummyCompleter.java
similarity index 74%
copy from
modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleter.java
copy to
modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DummyCompleter.java
index 933222f324..24ab34ca41 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleter.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DummyCompleter.java
@@ -19,14 +19,10 @@ package org.apache.ignite.internal.cli.core.repl.completer;
import java.util.List;
-/**
- * Dynamic completer returns completions that can be fetched in real time
- * from Ignite 3 node, file, or cached.
- */
-public interface DynamicCompleter {
-
- /**
- * Given typed words returns list of candidates that can be autocompleted.
- */
- List<String> complete(String[] words);
+/** Dummy completer that returns empty list on any input. */
+public class DummyCompleter implements DynamicCompleter {
+ @Override
+ public List<String> complete(String[] words) {
+ return List.of();
+ }
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleter.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleter.java
index 933222f324..df930c6fd2 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleter.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleter.java
@@ -20,13 +20,14 @@ package org.apache.ignite.internal.cli.core.repl.completer;
import java.util.List;
/**
- * Dynamic completer returns completions that can be fetched in real time
- * from Ignite 3 node, file, or cached.
+ * Dynamic completer returns completions that can be fetched in real time from
Ignite 3 node, file, or cached.
+ *
+ * <p>The lifecycle of {@link DynamicCompleter} is determined by {@link
DynamicCompleterFactory},
+ * use the factory to create an instance of {@link DynamicCompleter}.
*/
+@FunctionalInterface
public interface DynamicCompleter {
- /**
- * Given typed words returns list of candidates that can be autocompleted.
- */
+ /** Given typed words returns list of candidates that can be
autocompleted. */
List<String> complete(String[] words);
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterActivationPoint.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterActivationPoint.java
index 3d24ee4a1f..c1aaee0f1f 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterActivationPoint.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterActivationPoint.java
@@ -17,40 +17,65 @@
package org.apache.ignite.internal.cli.core.repl.completer;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.NODE_NAME_OPTION;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.NODE_NAME_OPTION_SHORT;
-
import jakarta.inject.Singleton;
+import org.apache.ignite.internal.cli.commands.Options;
+import
org.apache.ignite.internal.cli.core.repl.completer.hocon.ClusterConfigDynamicCompleterFactory;
+import
org.apache.ignite.internal.cli.core.repl.completer.hocon.NodeConfigDynamicCompleterFactory;
+import
org.apache.ignite.internal.cli.core.repl.completer.node.NodeNameDynamicCompleterFactory;
-/**
- * Activation point that links commands with dynamic completers.
- */
+/** Activation point that links commands with dynamic completers. */
@Singleton
public class DynamicCompleterActivationPoint {
- private final DynamicCompleterFactory factory;
+ private final NodeNameDynamicCompleterFactory
nodeNameDynamicCompleterFactory;
+ private final ClusterConfigDynamicCompleterFactory
clusterConfigDynamicCompleterFactory;
+ private final NodeConfigDynamicCompleterFactory
nodeConfigDynamicCompleterFactory;
- /** Default constructor. */
- public DynamicCompleterActivationPoint(DynamicCompleterFactory factory) {
- this.factory = factory;
+ /** Main constructor. */
+ public DynamicCompleterActivationPoint(
+ NodeNameDynamicCompleterFactory nodeNameDynamicCompleterFactory,
+ ClusterConfigDynamicCompleterFactory
clusterConfigDynamicCompleterFactory,
+ NodeConfigDynamicCompleterFactory nodeConfigDynamicCompleterFactory
+ ) {
+ this.nodeNameDynamicCompleterFactory = nodeNameDynamicCompleterFactory;
+ this.clusterConfigDynamicCompleterFactory =
clusterConfigDynamicCompleterFactory;
+ this.nodeConfigDynamicCompleterFactory =
nodeConfigDynamicCompleterFactory;
}
+
/**
* Registers all dynamic completers in given {@link
DynamicCompleterRegistry}.
*/
public void activateDynamicCompleter(DynamicCompleterRegistry registry) {
- registry.register(new String[]{"cluster", "config", "show"},
- new String[]{NODE_NAME_OPTION, NODE_NAME_OPTION_SHORT},
- factory.clusterConfigCompleter(""));
- registry.register(new String[]{"cluster", "config", "update"},
- new String[]{NODE_NAME_OPTION, NODE_NAME_OPTION_SHORT},
- factory.clusterConfigCompleter(""));
- registry.register(new String[]{"node", "config", "show"},
- new String[]{NODE_NAME_OPTION, NODE_NAME_OPTION_SHORT},
- factory.nodeConfigCompleter(""));
- registry.register(new String[]{"node", "config", "update"},
- new String[]{NODE_NAME_OPTION, NODE_NAME_OPTION_SHORT},
- factory.nodeConfigCompleter(""));
- registry.register(factory.nodeNameCompleter(NODE_NAME_OPTION,
NODE_NAME_OPTION_SHORT));
+ registry.register(
+ CompleterConf.builder()
+ .command("cluster", "config", "show")
+ .command("cluster", "config", "update").build(),
+ clusterConfigDynamicCompleterFactory
+ );
+ registry.register(
+ CompleterConf.builder()
+ .command("node", "config", "show")
+ .command("node", "config", "update").build(),
+ nodeConfigDynamicCompleterFactory
+ );
+ // exclusive option that disables other completers for node name
+ registry.register(
+ CompleterConf.builder()
+ .enableOptions(Options.NODE_NAME)
+ .exclusiveEnableOptions().build(),
+ nodeNameDynamicCompleterFactory
+ );
+ registry.register(
+ CompleterConf.forCommand("connect"),
+ nodeNameDynamicCompleterFactory
+ );
+ registry.register(
+ CompleterConf.builder()
+ .command("cluster", "init")
+ .enableOptions(Options.META_STORAGE_NODE_NAME,
Options.CMG_NODE_NAME)
+ .build(),
+ nodeNameDynamicCompleterFactory
+ );
}
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterFactory.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterFactory.java
index 8927f1695b..fb5f8842c0 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterFactory.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterFactory.java
@@ -17,89 +17,13 @@
package org.apache.ignite.internal.cli.core.repl.completer;
-import com.typesafe.config.Config;
-import com.typesafe.config.ConfigFactory;
-import io.micronaut.context.annotation.Bean;
-import java.util.Set;
-import org.apache.ignite.internal.cli.NodeNameRegistry;
-import org.apache.ignite.internal.cli.call.configuration.ClusterConfigShowCall;
-import
org.apache.ignite.internal.cli.call.configuration.ClusterConfigShowCallInput;
-import org.apache.ignite.internal.cli.call.configuration.NodeConfigShowCall;
-import
org.apache.ignite.internal.cli.call.configuration.NodeConfigShowCallInput;
-
/**
- * Factory that creates {@link DynamicCompleter}s.
+ * Factory that is responsible for defining the lifecycle of {@link
DynamicCompleter}.
+ * It is called each time the completer is needed. So, the factory should
cache the completer
+ * if there is no need to update it.
*/
-@Bean
-public class DynamicCompleterFactory {
- private final NodeConfigShowCall nodeConfigShowCall;
- private final ClusterConfigShowCall clusterConfigShowCall;
- private final NodeUrlProvider urlProvider;
- private final NodeNameRegistry nodeNameRegistry;
-
- /** Default constructor. */
- public DynamicCompleterFactory(
- NodeConfigShowCall nodeConfigShowCall,
- ClusterConfigShowCall clusterConfigShowCall,
- NodeUrlProvider urlProvider,
- NodeNameRegistry nodeNameRegistry) {
-
- this.nodeConfigShowCall = nodeConfigShowCall;
- this.clusterConfigShowCall = clusterConfigShowCall;
- this.urlProvider = urlProvider;
- this.nodeNameRegistry = nodeNameRegistry;
- }
-
- /** Creates node config completer with given activation prefix. */
- public LazyDynamicCompleter nodeConfigCompleter(String activationPrefix) {
- return nodeConfigCompleter(Set.of(activationPrefix));
- }
-
- /** Creates node config completer with given set of activation prefixes. */
- public LazyDynamicCompleter nodeConfigCompleter(Set<String>
activationPrefixes) {
- return new LazyDynamicCompleter(() -> {
- try {
- Config config = ConfigFactory.parseString(
- nodeConfigShowCall.execute(
- // todo
https://issues.apache.org/jira/browse/IGNITE-17416
-
NodeConfigShowCallInput.builder().nodeUrl(urlProvider.resolveUrl(new
String[]{""})).build()
- ).body().getValue()
- );
- return new HoconDynamicCompleter(activationPrefixes, config);
- } catch (Exception e) {
- return new HoconDynamicCompleter(activationPrefixes,
ConfigFactory.parseString(""));
- }
- });
- }
-
- /** Creates cluster config completer with given activation prefix. */
- public LazyDynamicCompleter clusterConfigCompleter(String
activationPrefix) {
- return clusterConfigCompleter(Set.of(activationPrefix));
- }
-
- /** Creates cluster config completer with given set of activation
prefixes. */
- public LazyDynamicCompleter clusterConfigCompleter(Set<String>
activationPrefixes) {
- return new LazyDynamicCompleter(() -> {
- try {
- Config config = ConfigFactory.parseString(
- clusterConfigShowCall.execute(
- // todo
https://issues.apache.org/jira/browse/IGNITE-17416
-
ClusterConfigShowCallInput.builder().clusterUrl(urlProvider.resolveUrl(new
String[]{""})).build()
- ).body().getValue()
- );
- return new HoconDynamicCompleter(activationPrefixes, config);
- } catch (Exception e) {
- return new HoconDynamicCompleter(activationPrefixes,
ConfigFactory.parseString(""));
- }
- });
- }
-
- public DynamicCompleter nodeNameCompleter(String... activationPrefixes) {
- return nodeNameCompleter(Set.of(activationPrefixes));
- }
-
- public DynamicCompleter nodeNameCompleter(Set<String> activationPrefixes) {
- return new NodeNameDynamicCompleter(activationPrefixes,
nodeNameRegistry);
- }
-
+@FunctionalInterface
+public interface DynamicCompleterFactory {
+ /** Return an instance of {@link DynamicCompleter}. */
+ DynamicCompleter getDynamicCompleter(String[] words);
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterFilter.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterFilter.java
index ef345f98d1..59f5459438 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterFilter.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterFilter.java
@@ -17,11 +17,17 @@
package org.apache.ignite.internal.cli.core.repl.completer;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.CLUSTER_URL_OPTION;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.NODE_URL_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.CLUSTER_URL_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.HELP_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.HELP_OPTION_SHORT;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.NODE_URL_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.VERBOSE_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.VERBOSE_OPTION_SHORT;
import jakarta.inject.Singleton;
import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
import org.apache.ignite.internal.cli.core.repl.Session;
/**
@@ -43,25 +49,31 @@ public class DynamicCompleterFilter implements
CompleterFilter {
@Override
public String[] filter(String[] words, String[] candidates) {
+ List<String> notOptionsCandidates = Arrays.stream(candidates)
+ .filter(candidate -> !candidate.startsWith("-"))
+ .collect(Collectors.toList());
+
+ if (!notOptionsCandidates.isEmpty()) {
+ return notOptionsCandidates.toArray(String[]::new);
+ }
+
return Arrays.stream(candidates)
.filter(candidate -> filterClusterUrl(words, candidate))
- .filter(candidate -> filterHelp(words, candidate))
+ .filter(candidate -> filterCommonOptions(words, candidate))
.toArray(String[]::new);
}
- private boolean filterHelp(String[] words, String candidate) {
- if (optionTyped(words)) {
- return true;
- }
-
- return !(candidate.equals("--help") || candidate.equals("-h"));
+ private boolean filterCommonOptions(String[] words, String candidate) {
+ return optionTyped(words)
+ || !(HELP_OPTION.equals(candidate)
+ || HELP_OPTION_SHORT.equals(candidate)
+ || VERBOSE_OPTION_SHORT.equals(candidate)
+ || VERBOSE_OPTION.equals(candidate));
}
private boolean filterClusterUrl(String[] words, String candidate) {
- if (optionTyped(words)) {
- return true;
- }
-
- return !session.isConnectedToNode() ||
(!candidate.equals(CLUSTER_URL_OPTION) && !candidate.equals(NODE_URL_OPTION));
+ return optionTyped(words)
+ || !session.isConnectedToNode()
+ || (!candidate.equals(CLUSTER_URL_OPTION) &&
!candidate.equals(NODE_URL_OPTION));
}
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterRegistry.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterRegistry.java
index db2348f759..3dc6da5d60 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterRegistry.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterRegistry.java
@@ -18,12 +18,14 @@
package org.apache.ignite.internal.cli.core.repl.completer;
import static
org.apache.ignite.internal.cli.util.ArrayUtils.findLastNotEmptyWord;
+import static
org.apache.ignite.internal.cli.util.ArrayUtils.findLastNotEmptyWordBeforeWordFromEnd;
import jakarta.inject.Singleton;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
-import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
@@ -38,60 +40,97 @@ public class DynamicCompleterRegistry {
public List<DynamicCompleter> findCompleters(String[] words) {
return completionStrategiesList.stream()
.filter(strategy -> strategy.canBeApplied(words))
- .map(CompletionStrategy::completer)
+ .map(strategy -> strategy.completer(words))
.collect(Collectors.toList());
}
- public void register(DynamicCompleter completer) {
- completionStrategiesList.add(new CompletionStrategy(ignored -> true,
completer));
- }
-
/** Registers dynamic completer that can be found by given predicate. */
- public void register(Predicate<String[]> predicate, DynamicCompleter
completer) {
- completionStrategiesList.add(new CompletionStrategy(predicate,
completer));
- }
+ public void register(CompleterConf conf, DynamicCompleterFactory factory) {
+ if (conf.isExclusiveEnableOptions()) {
+ // add disable option for all strategies because current
configuration has exclusive enable option
+ completionStrategiesList.forEach(strategy ->
strategy.exclusiveDisableOptions.addAll(conf.enableOptions()));
+ }
- /** Registers dynamic completer that can be found by given prefix. */
- public void register(String[] prefixWords, DynamicCompleter completer) {
- register((String[] words) -> samePrefix(words, prefixWords),
completer);
- }
+ Set<String> exclusiveDisableOptions = completionStrategiesList.stream()
+ .filter(strategy -> strategy.conf.isExclusiveEnableOptions())
+ .flatMap(strategy -> strategy.conf.enableOptions().stream())
+ .collect(Collectors.toSet());
- /** Registers dynamic completer that can be found by given prefix. */
- public void register(String[] prefixWords, String[] stopPostfixWords,
DynamicCompleter completer) {
- register((String[] words) -> samePrefix(words, prefixWords) &&
notSamePostfix(words, stopPostfixWords), completer);
+ CompletionStrategy strategy = new CompletionStrategy(conf, factory);
+ strategy.exclusiveDisableOptions.addAll(exclusiveDisableOptions);
+ completionStrategiesList.add(strategy);
}
- private boolean samePrefix(String[] words, String[] prefixWords) {
- if (words.length < prefixWords.length) {
- return false;
+ private static class CompletionStrategy {
+ private final CompleterConf conf;
+
+ private final DynamicCompleterFactory factory;
+
+ private final Set<String> exclusiveDisableOptions = new HashSet<>();
+
+ private CompletionStrategy(CompleterConf conf, DynamicCompleterFactory
factory) {
+ this.conf = conf;
+ this.factory = factory;
}
- for (int i = 0; i < prefixWords.length; i++) {
- if (!words[i].equals(prefixWords[i])) {
+
+ private static boolean samePrefix(String[] words, String[]
prefixWords) {
+ if (words.length < prefixWords.length) {
return false;
}
+ for (int i = 0; i < prefixWords.length; i++) {
+ if (!words[i].equals(prefixWords[i])) {
+ return false;
+ }
+ }
+ return true;
}
- return true;
- }
-
- private boolean notSamePostfix(String[] words, String[] stopPostfixWords) {
- return words.length > 0 &&
!Set.of(stopPostfixWords).contains(findLastNotEmptyWord(words));
- }
- private static class CompletionStrategy {
- private final Predicate<String[]> predicate;
- private final DynamicCompleter completer;
+ boolean canBeApplied(String[] words) {
+ // empty command means can be applied to all words
+ if (!conf.commandSpecific()) {
+ return canBeAppliedCommandMatch(words);
+ }
- private CompletionStrategy(Predicate<String[]> predicate,
DynamicCompleter completer) {
- this.predicate = predicate;
- this.completer = completer;
+ Optional<String[]> commandsMatch =
conf.commands().stream().filter(command -> samePrefix(words,
command)).findFirst();
+ return commandsMatch.isPresent() &&
canBeAppliedCommandMatch(words);
}
- boolean canBeApplied(String[] words) {
- return predicate.test(words);
+ private boolean canBeAppliedCommandMatch(String[] words) {
+ String cursorWord = words[words.length - 1];
+ String lastNotEmptyWord = findLastNotEmptyWord(words);
+ String preLastNotEmptyWord =
findLastNotEmptyWordBeforeWordFromEnd(words, lastNotEmptyWord);
+
+ if (cursorWord.equals(lastNotEmptyWord)) {
+ if (exclusiveDisableOptions.contains(lastNotEmptyWord) ||
exclusiveDisableOptions.contains(preLastNotEmptyWord)) {
+ return false;
+ }
+ } else if (exclusiveDisableOptions.contains(lastNotEmptyWord)) {
+ return false;
+ }
+
+ if (conf.hasEnableOptions()) {
+ if (cursorWord.equals(lastNotEmptyWord)) {
+ return conf.enableOptions().contains(lastNotEmptyWord) //
command subcommand --enable-option
+ ||
conf.enableOptions().contains(preLastNotEmptyWord); // command subcommand
--enable-option lastWord
+ } else {
+ return conf.enableOptions().contains(lastNotEmptyWord); //
command subcommand --enable-option <space>
+ }
+ }
+
+ if (conf.hasDisableOptions()) {
+ if (cursorWord.equals(lastNotEmptyWord)) {
+ return !conf.disableOptions().contains(lastNotEmptyWord)
+ &&
!conf.disableOptions().contains(preLastNotEmptyWord);
+ } else {
+ return !conf.disableOptions().contains(lastNotEmptyWord);
+ }
+ }
+
+ return true;
}
- DynamicCompleter completer() {
- return completer;
+ DynamicCompleter completer(String[] words) {
+ return factory.getDynamicCompleter(words);
}
}
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/NodeNameDynamicCompleter.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/NodeNameDynamicCompleter.java
deleted file mode 100644
index a4d5be7e8b..0000000000
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/NodeNameDynamicCompleter.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.internal.cli.core.repl.completer;
-
-import static
org.apache.ignite.internal.cli.util.ArrayUtils.findLastNotEmptyWord;
-import static
org.apache.ignite.internal.cli.util.ArrayUtils.findLastNotEmptyWordBeforeWordFromEnd;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-import org.apache.ignite.internal.cli.NodeNameRegistry;
-
-/**
- * Completes typed words with node names.
- */
-public class NodeNameDynamicCompleter implements DynamicCompleter {
-
- /** Words, after those the completer should have been activated. */
- private final Set<String> activationPostfixes;
-
- /** Node names registry. */
- private final NodeNameRegistry nodeNameRegistry;
-
- /** Default constructor that creates an instance from a given set of
postfixes that trigger the completion. */
- public NodeNameDynamicCompleter(Set<String> activationPostfixes,
NodeNameRegistry nodeNameRegistry) {
- this.activationPostfixes = activationPostfixes;
- this.nodeNameRegistry = nodeNameRegistry;
- }
-
- @Override
- public List<String> complete(String[] words) {
- String lastWord = findLastNotEmptyWord(words);
- String beforeLastWord = findLastNotEmptyWordBeforeWordFromEnd(words,
lastWord);
- if (activationPostfixes.contains(lastWord)) {
- return new ArrayList<>(nodeNameRegistry.getAllNames());
- } else if (activationPostfixes.contains(beforeLastWord)) {
- return nodeNameRegistry.getAllNames().stream()
- .filter(it -> it.startsWith(lastWord))
- .collect(Collectors.toList());
- } else {
- return Collections.emptyList();
- }
- }
-}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/NodeUrlProvider.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/NodeUrlProvider.java
index 89f407abec..90126810da 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/NodeUrlProvider.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/NodeUrlProvider.java
@@ -17,8 +17,9 @@
package org.apache.ignite.internal.cli.core.repl.completer;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.CLUSTER_URL_OPTION;
+
import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.commands.OptionsConstants;
import org.apache.ignite.internal.cli.config.ConfigConstants;
import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
import org.apache.ignite.internal.cli.core.repl.Session;
@@ -54,7 +55,7 @@ public class NodeUrlProvider {
private String findClusterUrlIn(String[] words) {
for (String word : words) {
- String prefix = OptionsConstants.CLUSTER_URL_OPTION + "=";
+ String prefix = CLUSTER_URL_OPTION + "=";
if (word.startsWith(prefix)) {
return word.substring(prefix.length());
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/hocon/ClusterConfigDynamicCompleterFactory.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/hocon/ClusterConfigDynamicCompleterFactory.java
new file mode 100644
index 0000000000..e745ae105a
--- /dev/null
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/hocon/ClusterConfigDynamicCompleterFactory.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.cli.core.repl.completer.hocon;
+
+import com.typesafe.config.Config;
+import com.typesafe.config.ConfigFactory;
+import jakarta.inject.Singleton;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.ignite.internal.cli.call.configuration.ClusterConfigShowCall;
+import
org.apache.ignite.internal.cli.call.configuration.ClusterConfigShowCallInput;
+import org.apache.ignite.internal.cli.core.repl.completer.DummyCompleter;
+import org.apache.ignite.internal.cli.core.repl.completer.DynamicCompleter;
+import
org.apache.ignite.internal.cli.core.repl.completer.DynamicCompleterFactory;
+import org.apache.ignite.internal.cli.core.repl.completer.NodeUrlProvider;
+
+/** Factory for cluster config show/update command. */
+@Singleton
+public class ClusterConfigDynamicCompleterFactory implements
DynamicCompleterFactory {
+
+ private final NodeUrlProvider urlProvider;
+
+ private final ClusterConfigShowCall clusterConfigShowCall;
+
+ private final AtomicReference<DynamicCompleter> cachedCompleter = new
AtomicReference<>(null);
+
+ public ClusterConfigDynamicCompleterFactory(NodeUrlProvider urlProvider,
ClusterConfigShowCall clusterConfigShowCall) {
+ this.urlProvider = urlProvider;
+ this.clusterConfigShowCall = clusterConfigShowCall;
+ }
+
+ @Override
+ public DynamicCompleter getDynamicCompleter(String[] words) {
+ if (cachedCompleter.getAcquire() != null) {
+ return cachedCompleter.getAcquire();
+ } else {
+ // call REST on the background in order not to freeze UI thread
+ CompletableFuture.runAsync(() -> {
+ try {
+ Config config = ConfigFactory.parseString(
+ clusterConfigShowCall.execute(
+ // todo
https://issues.apache.org/jira/browse/IGNITE-17416
+
ClusterConfigShowCallInput.builder().clusterUrl(urlProvider.resolveUrl(new
String[]{""})).build()
+ ).body().getValue()
+ );
+ cachedCompleter.compareAndExchangeRelease(null, new
HoconDynamicCompleter(config));
+ } catch (Exception ignored) {
+ // no-op
+ }
+ });
+ }
+
+ // return dummy completer this time, but hope the next call will
return cached completer
+ return new DummyCompleter();
+ }
+}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/HoconDynamicCompleter.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/hocon/HoconDynamicCompleter.java
similarity index 73%
rename from
modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/HoconDynamicCompleter.java
rename to
modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/hocon/HoconDynamicCompleter.java
index d02a20bb5e..ff71bf6afd 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/HoconDynamicCompleter.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/hocon/HoconDynamicCompleter.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.cli.core.repl.completer;
+package org.apache.ignite.internal.cli.core.repl.completer.hocon;
import static
org.apache.ignite.internal.cli.util.ArrayUtils.findLastNotEmptyWord;
@@ -25,10 +25,9 @@ import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
+import org.apache.ignite.internal.cli.core.repl.completer.DynamicCompleter;
-/**
- * Completes typed words with hocon schema keys.
- */
+/** Completes typed words with hocon schema keys. */
public class HoconDynamicCompleter implements DynamicCompleter {
private final Config config;
@@ -39,12 +38,8 @@ public class HoconDynamicCompleter implements
DynamicCompleter {
/** List of all possible completions. */
private final List<String> completions;
- /** Words, after those the completer should have been activated. */
- private final Set<String> activationPostfixes;
-
/** Default constructor that creates an instance from a given set of
postfixes that trigger the completion. */
- public HoconDynamicCompleter(Set<String> activationPostfixes, Config
config) {
- this.activationPostfixes = activationPostfixes;
+ public HoconDynamicCompleter(Config config) {
this.config = config;
this.leafs =
config.entrySet().stream().map(Entry::getKey).collect(Collectors.toSet());
this.completions = this.compile();
@@ -61,23 +56,21 @@ public class HoconDynamicCompleter implements
DynamicCompleter {
/**
* Completes the given typed words with the config keys that are in the
same level as the last typed words.
- * <p/>
- * Example: given typed words ["cluster", "config", "show", "a"], The last
word is "a", only root config values will be
- * suggested to autocomplete: "aimem", "aipersist". If user hits "aimem"
and types dot "." then only subkeys of "aimem." will be
- * suggested: "aimem.pageSize", "aimem.regions".
+ *
+ * <p>Example: given typed words ["cluster", "config", "show", "a"], The
last word is "a", only root config values will be suggested to
+ * autocomplete: "aimem", "aipersist". If user hits "aimem" and types dot
"." then only subkeys of "aimem." will be suggested:
+ * "aimem.pageSize", "aimem.regions".
*/
@Override
public List<String> complete(String[] words) {
- final String lastWord = findLastNotEmptyWord(words);
+ String lastWord = findLastNotEmptyWord(words);
- if (activationPostfixes.contains(lastWord)
- // activation profile contains empty string and the last typed
word is empty
- || (activationPostfixes.contains("") && words[words.length -
1].isEmpty())) {
+ if (words[words.length - 1].isEmpty()) {
// roots
return completions.stream().filter(s -> s.split("\\.").length ==
1).collect(Collectors.toList());
}
- final int deepLevel = lastWord.endsWith(".")
+ int deepLevel = lastWord.endsWith(".")
? lastWord.split("\\.").length + 1
: lastWord.split("\\.").length;
@@ -87,7 +80,6 @@ public class HoconDynamicCompleter implements
DynamicCompleter {
}
-
private void walkAndAdd(String keyPrefix, Set<String> keySet, List<String>
result) {
keySet.forEach(key -> {
if (!leafs.contains(keyPrefix + key)) {
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/hocon/NodeConfigDynamicCompleterFactory.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/hocon/NodeConfigDynamicCompleterFactory.java
new file mode 100644
index 0000000000..7de52d4c85
--- /dev/null
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/hocon/NodeConfigDynamicCompleterFactory.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.cli.core.repl.completer.hocon;
+
+import com.typesafe.config.Config;
+import com.typesafe.config.ConfigFactory;
+import jakarta.inject.Singleton;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.ignite.internal.cli.call.configuration.NodeConfigShowCall;
+import
org.apache.ignite.internal.cli.call.configuration.NodeConfigShowCallInput;
+import org.apache.ignite.internal.cli.core.repl.completer.DummyCompleter;
+import org.apache.ignite.internal.cli.core.repl.completer.DynamicCompleter;
+import
org.apache.ignite.internal.cli.core.repl.completer.DynamicCompleterFactory;
+import org.apache.ignite.internal.cli.core.repl.completer.NodeUrlProvider;
+
+/** Factory for node config show/update completer. */
+@Singleton
+public class NodeConfigDynamicCompleterFactory implements
DynamicCompleterFactory {
+ private final NodeUrlProvider urlProvider;
+
+ private final NodeConfigShowCall nodeConfigShowCall;
+
+ private final AtomicReference<DynamicCompleter> cachedCompleter = new
AtomicReference<>(null);
+
+ public NodeConfigDynamicCompleterFactory(NodeUrlProvider urlProvider,
NodeConfigShowCall nodeConfigShowCall) {
+ this.urlProvider = urlProvider;
+ this.nodeConfigShowCall = nodeConfigShowCall;
+ }
+
+ @Override
+ public DynamicCompleter getDynamicCompleter(String[] words) {
+ if (cachedCompleter.getAcquire() != null) {
+ return cachedCompleter.getAcquire();
+ } else {
+ // call REST on the background in order not to freeze UI thread
+ CompletableFuture.runAsync(() -> {
+ try {
+ Config config = ConfigFactory.parseString(
+ nodeConfigShowCall.execute(
+ // todo
https://issues.apache.org/jira/browse/IGNITE-17416
+
NodeConfigShowCallInput.builder().nodeUrl(urlProvider.resolveUrl(new
String[]{""})).build()
+ ).body().getValue()
+ );
+ cachedCompleter.compareAndExchangeRelease(null, new
HoconDynamicCompleter(config));
+ } catch (Exception ignored) {
+ // no-op
+ }
+ });
+ }
+
+ // return dummy completer this time, but hope the next call will
return cached completer
+ return new DummyCompleter();
+ }
+}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/ProfileMixin.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/node/NodeNameDynamicCompleterFactory.java
similarity index 50%
copy from
modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/ProfileMixin.java
copy to
modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/node/NodeNameDynamicCompleterFactory.java
index d0cf65817b..3d26caa1fc 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/ProfileMixin.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/node/NodeNameDynamicCompleterFactory.java
@@ -15,27 +15,25 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.cli.commands;
+package org.apache.ignite.internal.cli.core.repl.completer.node;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.PROFILE_OPTION;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.PROFILE_OPTION_DESC;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.PROFILE_OPTION_SHORT;
+import jakarta.inject.Singleton;
+import org.apache.ignite.internal.cli.NodeNameRegistry;
+import org.apache.ignite.internal.cli.core.repl.completer.DynamicCompleter;
+import
org.apache.ignite.internal.cli.core.repl.completer.DynamicCompleterFactory;
-import picocli.CommandLine.Option;
+/** Factory for --node-name option completer. */
+@Singleton
+public class NodeNameDynamicCompleterFactory implements
DynamicCompleterFactory {
-/**
- * Mixin for profile option.
- */
-public class ProfileMixin {
- @Option(names = {PROFILE_OPTION_SHORT, PROFILE_OPTION}, description =
PROFILE_OPTION_DESC)
- private String profileName;
+ private final NodeNameRegistry nodeNameRegistry;
+
+ public NodeNameDynamicCompleterFactory(NodeNameRegistry nodeNameRegistry) {
+ this.nodeNameRegistry = nodeNameRegistry;
+ }
- /**
- * Gets profile name.
- *
- * @return profile name
- */
- public String getProfileName() {
- return profileName;
+ @Override
+ public DynamicCompleter getDynamicCompleter(String[] words) {
+ return new StringDynamicCompleter(nodeNameRegistry.getAllNames());
}
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/node/StringDynamicCompleter.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/node/StringDynamicCompleter.java
new file mode 100644
index 0000000000..020c98224b
--- /dev/null
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/node/StringDynamicCompleter.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.cli.core.repl.completer.node;
+
+import static
org.apache.ignite.internal.cli.util.ArrayUtils.findLastNotEmptyWord;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.apache.ignite.internal.cli.core.repl.completer.DynamicCompleter;
+
+/**
+ * Completes typed words with provided list of strings.
+ */
+public class StringDynamicCompleter implements DynamicCompleter {
+
+ /** Values that will ve suggested. */
+ private final Set<String> values;
+
+ /** Default constructor. */
+ public StringDynamicCompleter(Set<String> values) {
+ this.values = values;
+ }
+
+ @Override
+ public List<String> complete(String[] words) {
+ if (words[words.length - 1].isBlank()) {
+ return new ArrayList<>(values);
+ }
+
+ String lastWord = findLastNotEmptyWord(words);
+ return values.stream()
+ .filter(it -> it.startsWith(lastWord))
+ .collect(Collectors.toList());
+ }
+}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/IgnitePicocliCommands.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/IgnitePicocliCommands.java
index db4d26ef35..704f547ab4 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/IgnitePicocliCommands.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/IgnitePicocliCommands.java
@@ -201,7 +201,7 @@ public class IgnitePicocliCommands implements
CommandRegistry {
}
List<DynamicCompleter> completers =
completerRegistry.findCompleters(words);
- if (completers.size() > 0) {
+ if (!completers.isEmpty()) {
try {
completers.stream()
.map(c -> c.complete(words))
@@ -213,13 +213,17 @@ public class IgnitePicocliCommands implements
CommandRegistry {
}
}
+ if (!candidates.isEmpty()) {
+ return;
+ }
+
String[] filteredCandidates =
completerFilters.get(0).filter(words, staticCandidates.toArray(String[]::new));
for (int i = 1; i < completerFilters.size(); i++) {
filteredCandidates = completerFilters.get(i).filter(words,
filteredCandidates);
}
for (String c : filteredCandidates) {
- candidates.add(new Candidate(c));
+ candidates.add(staticCandidate(c));
}
}
@@ -230,5 +234,9 @@ public class IgnitePicocliCommands implements
CommandRegistry {
return new Candidate(one, one, null, null, null, null, false,
sortingPriority);
}
+
+ private Candidate staticCandidate(String one) {
+ return new Candidate(one, one, null, null, null, null, true, 10);
+ }
}
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutor.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutor.java
index 1290263099..a8bfeabc8f 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutor.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutor.java
@@ -168,6 +168,7 @@ public class ReplExecutor {
CommandLineContextProvider.setCmd(cmd);
cmd.setExecutionExceptionHandler(new
PicocliExecutionExceptionHandler());
cmd.registerConverter(NodeNameOrUrl.class, new
NodeNameOrUrlConverter(nodeNameRegistry));
+
DynamicCompleterRegistry completerRegistry =
factory.create(DynamicCompleterRegistry.class);
DynamicCompleterActivationPoint activationPoint =
factory.create(DynamicCompleterActivationPoint.class);
activationPoint.activateDynamicCompleter(completerRegistry);
diff --git
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterRegistryTest.java
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterRegistryTest.java
index cf7f453f98..52b52206c2 100644
---
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterRegistryTest.java
+++
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterRegistryTest.java
@@ -18,12 +18,16 @@
package org.apache.ignite.internal.cli.core.repl.completer;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.both;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import java.util.List;
+import org.apache.ignite.internal.cli.commands.Options;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
class DynamicCompleterRegistryTest {
@@ -36,6 +40,11 @@ class DynamicCompleterRegistryTest {
DynamicCompleter completer3;
+ /** Makes reading easier. */
+ private static String[] words(String... words) {
+ return words;
+ }
+
@BeforeEach
void setUp() {
registry = new DynamicCompleterRegistry();
@@ -45,28 +54,30 @@ class DynamicCompleterRegistryTest {
}
@Test
- void findsCompletersRegisteredWithPredicate() {
+ void findsCompleterRegisteredWithMultyCommand() {
// Given
- registry.register(words -> words[0].equals("command1"), completer1);
- registry.register(words -> words[0].equals("command2"), completer2);
- registry.register(words -> words[0].equals("command2"), completer3);
-
- // When find completers for "command1"
- List<DynamicCompleter> completers = registry.findCompleters(new
String[]{"command1"});
+ registry.register(
+ CompleterConf.builder()
+ .command("command1")
+ .command("command2").build(),
+ words -> completer1
+ );
// Then
- assertThat(completers, containsInAnyOrder(completer1));
+ assertThat(registry.findCompleters(words("command1")),
containsInAnyOrder(completer1));
+ // And
+ assertThat(registry.findCompleters(words("command2")),
containsInAnyOrder(completer1));
}
@Test
void findsCompletersRegisteredWithStaticCommand() {
// Given
- registry.register(new String[]{"command1", "subcommand1"}, completer1);
- registry.register(new String[]{"command1", "subcommand1"}, completer2);
- registry.register(new String[]{"command2"}, completer3);
+ registry.register(CompleterConf.forCommand("command1", "subcommand1"),
words -> completer1);
+ registry.register(CompleterConf.forCommand("command1", "subcommand1"),
words -> completer2);
+ registry.register(CompleterConf.forCommand("command2"), words ->
completer3);
// When find completers for "command1"
- List<DynamicCompleter> completers = registry.findCompleters(new
String[]{"command1", "subcommand1"});
+ List<DynamicCompleter> completers =
registry.findCompleters(words("command1", "subcommand1"));
// Then
assertThat(completers, containsInAnyOrder(completer1, completer2));
@@ -75,33 +86,207 @@ class DynamicCompleterRegistryTest {
@Test
void returnsEmptyCollectionIfThereIsNoSuitableCompleter() {
// Given
- registry.register(new String[]{"command1", "subcommand1"}, completer1);
- registry.register(new String[]{"command1", "subcommand1"}, completer2);
- registry.register(new String[]{"command2"}, completer3);
+ registry.register(CompleterConf.forCommand("command1", "subcommand1"),
words -> completer1);
+ registry.register(CompleterConf.forCommand("command1", "subcommand1"),
words -> completer2);
+ registry.register(CompleterConf.forCommand("command2"), words ->
completer3);
// Then
- assertThat(registry.findCompleters(new String[]{"command1"}),
is(empty()));
- assertThat(registry.findCompleters(new String[]{"command3"}),
is(empty()));
+ assertThat(registry.findCompleters(words("command1")), is(empty()));
+ assertThat(registry.findCompleters(words("command3")), is(empty()));
// But if command start with one of the registered prefixes
assertThat(
- registry.findCompleters(new String[]{"command1",
"subcommand1", "subsubcommand1"}),
+ registry.findCompleters(words("command1", "subcommand1",
"subsubcommand1")),
containsInAnyOrder(completer1, completer2)
);
}
@Test
- void doesntReturnCompleterWithStopPostfixWords() {
+ void doesntReturnCompleterWithDisableOptions() {
// Given
- registry.register(new String[]{"command1", "subcommand1"}, new
String[]{"--stopWord"}, completer1);
- registry.register(new String[]{"command1", "subcommand1"}, new
String[]{"--stopWord"}, completer2);
+ registry.register(
+ CompleterConf.builder().command("command1",
"subcommand1").disableOptions("--stopWord").build(),
+ words -> completer1
+ );
// Then
assertThat(
- registry.findCompleters(new String[]{"command1",
"subcommand1", "subsubcommand1"}),
- containsInAnyOrder(completer1, completer2)
+ registry.findCompleters(words("command1", "subcommand1",
"subsub1")),
+ containsInAnyOrder(completer1)
+ );
+ assertThat(
+ registry.findCompleters(words("command1", "subcommand1",
"subsub1", "")),
+ containsInAnyOrder(completer1)
+ );
+ assertThat(
+ registry.findCompleters(words("command1", "subcommand1", "",
"")),
+ containsInAnyOrder(completer1)
);
// But if command ends with a stop word
- assertThat(registry.findCompleters(new String[]{"command1",
"subcommand1", "--stopWord"}), is(empty()));
+ assertThat(
+ registry.findCompleters(words("command1", "subcommand1",
"--stopWord")),
+ is(empty())
+ );
+ assertThat(
+ registry.findCompleters(words("command1", "subcommand1",
"--stopWord", "")),
+ is(empty())
+ );
+ assertThat(
+ registry.findCompleters(words("command1", "subcommand1",
"--stopWord", "do-not-complete-me")),
+ is(empty())
+ );
+ assertThat(
+ registry.findCompleters(words("command1", "subcommand1",
"--stopWord", "do-not-complete-me", "but-complete-me")),
+ containsInAnyOrder(completer1)
+ );
+ }
+
+ @Test
+ @DisplayName("If enable options are provided than they are taken into
account")
+ void enableOptions() {
+ // Given completer with enable option
+ registry.register(
+ CompleterConf.builder()
+ .command("command1")
+ .enableOptions("--complete-after-me", "-com")
+ .build(),
+ words -> completer1
+ );
+
+ // Then for words without those options there are no completers
+ assertThat(registry.findCompleters(words("command1")), is(empty()));
+ // And enable option should not be completed itself
+ assertThat(registry.findCompleters(words("command1", "-co")),
is(empty()));
+
+ // But if any of the enable options are provided then completer is
found
+ assertThat(registry.findCompleters(words("command1", "some",
"--complete-after-me", "")), containsInAnyOrder(completer1));
+ assertThat(registry.findCompleters(words("command1", "-com", "")),
containsInAnyOrder(completer1));
+ assertThat(registry.findCompleters(words("command1", "-com", " ")),
containsInAnyOrder(completer1));
+ assertThat(registry.findCompleters(words("command1", "-com",
"autocompleteme")), containsInAnyOrder(completer1));
+ }
+
+ @Test
+ @DisplayName("Combination of completers with the same enable/disable
options")
+ void combinationOfCompleters() {
+ // Given first completer with disable option
+ registry.register(
+ CompleterConf.builder()
+ .command("node", "config", "show")
+ .disableOptions("-n").build(),
+ words -> completer1
+ );
+ // And the second completer with the same enable option
+ registry.register(
+ CompleterConf.builder().enableOptions("-n").build(),
+ (words -> completer2)
+ );
+
+ // Then they do not intersect
+ assertThat(
+ registry.findCompleters(words("node", "config", "show",
"-n")), // "-n" is competed by completer2
+ both(hasSize(1)).and(containsInAnyOrder(completer2))
+ );
+ assertThat(
+ registry.findCompleters(words("node", "config", "show", "-n",
"nodeName")),
+ both(hasSize(1)).and(containsInAnyOrder(completer2))
+ );
+ assertThat(
+ registry.findCompleters(words("node", "config", "show", "-n",
"nodeName", " ")),
+ both(hasSize(1)).and(containsInAnyOrder(completer1))
+ );
+ assertThat(
+ registry.findCompleters(words("node", "config", "show", "-n",
"nodeName", "completeMe")),
+ both(hasSize(1)).and(containsInAnyOrder(completer1))
+ );
+ }
+
+ @Test
+ @DisplayName("If some completer has exclusive enable option set then all
other completers have this option as disable option")
+ void exclusiveEnableOption() {
+ // Given completer with exclusive enable option -n
+ registry.register(
+ CompleterConf.builder()
+ .command("node", "config", "show")
+ .enableOptions(Options.NODE_NAME)
+ .exclusiveEnableOptions().build(),
+ words -> completer1
+ );
+ // And completer for the same command
+ registry.register(
+ CompleterConf.forCommand("node", "config", "show"),
+ words -> completer2
+ );
+ // And common completer for other option
+ registry.register(
+ CompleterConf.builder().enableOptions("-l").build(),
+ words -> completer3
+ );
+
+ // Then exclusive option is on the priority and no other completer is
used for -n
+ assertThat(
+ registry.findCompleters(words("node", "config", "show", "-n")),
+ both(hasSize(1)).and(containsInAnyOrder(completer1))
+ );
+ assertThat(
+ registry.findCompleters(words("node", "config", "show", "-n",
"")),
+ both(hasSize(1)).and(containsInAnyOrder(completer1))
+ );
+ assertThat(
+ registry.findCompleters(words("node", "config", "show", "-n",
"nod")),
+ both(hasSize(1)).and(containsInAnyOrder(completer1))
+ );
+ // But other cases work well
+ assertThat(
+ registry.findCompleters(words("node", "config", "show", "-l",
"nod")),
+ both(hasSize(2)).and(containsInAnyOrder(completer2,
completer3))
+ );
+ assertThat(
+ registry.findCompleters(words("node", "config", "show", "-n",
"node", " ")),
+ both(hasSize(1)).and(containsInAnyOrder(completer2))
+ );
+ }
+
+ @Test
+ @DisplayName(
+ "If some completer has exclusive enable option set then all other
completers have this option as disable option, "
+ + "registration order changed"
+ )
+ void exclusiveEnableOptionRegisterInAnotherOrder() {
+ // Given completers without exclusive enable option registered first
+ registry.register(
+ CompleterConf.forCommand("node", "config", "show"),
+ words -> completer2
+ );
+ registry.register(
+ CompleterConf.builder().enableOptions("-l").build(),
+ words -> completer3
+ );
+ // And exclusiveEnableOption is registered last
+ registry.register(
+ CompleterConf.builder()
+ .command("node", "config", "show")
+ .enableOptions("-n")
+ .exclusiveEnableOptions().build(),
+ words -> completer1
+ );
+
+ // Then exclusive option is on the priority and no other completer is
used for -n
+ assertThat(
+ registry.findCompleters(words("node", "config", "show", "-n",
"nod")),
+ both(hasSize(1)).and(containsInAnyOrder(completer1))
+ );
+ assertThat(
+ registry.findCompleters(words("node", "config", "show", "-n")),
+ both(hasSize(1)).and(containsInAnyOrder(completer1))
+ );
+ // But other cases work well
+ assertThat(
+ registry.findCompleters(words("node", "config", "show", "-l",
"nod")),
+ both(hasSize(2)).and(containsInAnyOrder(completer2,
completer3))
+ );
+ assertThat(
+ registry.findCompleters(words("node", "config", "show", "-n",
"node", "")),
+ both(hasSize(1)).and(containsInAnyOrder(completer2))
+ );
}
}
diff --git
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/HoconDynamicCompleterTest.java
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/HoconDynamicCompleterTest.java
index b86356884f..6fa99a0496 100644
---
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/HoconDynamicCompleterTest.java
+++
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/HoconDynamicCompleterTest.java
@@ -23,7 +23,7 @@ import static org.hamcrest.Matchers.hasSize;
import com.typesafe.config.ConfigFactory;
import java.util.List;
-import java.util.Set;
+import
org.apache.ignite.internal.cli.core.repl.completer.hocon.HoconDynamicCompleter;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -31,8 +31,7 @@ class HoconDynamicCompleterTest {
HoconDynamicCompleter completer;
private static HoconDynamicCompleter completerFrom(String configString) {
- Set<String> activationPostfixes = Set.of("");
- return new HoconDynamicCompleter(activationPostfixes,
ConfigFactory.parseString(configString));
+ return new
HoconDynamicCompleter(ConfigFactory.parseString(configString));
}
@Test
diff --git
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/NodeNameDynamicCompleterTest.java
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/StringDynamicCompleterTest.java
similarity index 53%
rename from
modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/NodeNameDynamicCompleterTest.java
rename to
modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/StringDynamicCompleterTest.java
index d6b5f1ab5f..7445377329 100644
---
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/NodeNameDynamicCompleterTest.java
+++
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/StringDynamicCompleterTest.java
@@ -17,51 +17,41 @@
package org.apache.ignite.internal.cli.core.repl.completer;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.NODE_NAME_OPTION;
-import static
org.apache.ignite.internal.cli.commands.OptionsConstants.NODE_NAME_OPTION_SHORT;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
-import org.apache.ignite.internal.cli.NodeNameRegistry;
+import
org.apache.ignite.internal.cli.core.repl.completer.node.StringDynamicCompleter;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
-/** Tests for {@link NodeNameDynamicCompleter}. */
-class NodeNameDynamicCompleterTest {
-
- private static final Set<String> prefixes = Set.of(NODE_NAME_OPTION,
NODE_NAME_OPTION_SHORT);
- private static final List<String> nodeNames = List.of("node1", "node2",
"remoteNode");
- private NodeNameDynamicCompleter completer;
-
- @BeforeEach
- void setUp() {
- NodeNameRegistry nodeNameRegistry = mock(NodeNameRegistry.class);
- when(nodeNameRegistry.getAllNames()).thenReturn(new
HashSet<>(nodeNames));
- completer = new NodeNameDynamicCompleter(prefixes, nodeNameRegistry);
- }
+/** Tests for {@link StringDynamicCompleter}. */
+class StringDynamicCompleterTest {
+ private static final Set<String> candidates = Set.of("node1", "node2",
"remoteNode");
+ private StringDynamicCompleter completer;
private static Stream<Arguments> words() {
return Stream.of(
- Arguments.of(new String[]{"-n"}, nodeNames),
- Arguments.of(new String[]{"--node-name"}, nodeNames),
- Arguments.of(new String[]{"-n", "node"}, List.of("node1",
"node2")),
- Arguments.of(new String[]{"node", "config", "show"},
Collections.emptyList())
+ Arguments.of(new String[]{"n"}, Set.of("node1", "node2")),
+ Arguments.of(new String[]{""}, candidates),
+ Arguments.of(new String[]{" "}, candidates),
+ Arguments.of(new String[]{"node1", ""}, candidates),
+ Arguments.of(new String[]{"-n", "node"}, Set.of("node1",
"node2"))
);
}
+ @BeforeEach
+ void setUp() {
+ completer = new StringDynamicCompleter(candidates);
+ }
+
@ParameterizedTest
@MethodSource("words")
- void complete(String[] words, List<String> expectedCompletions) {
+ void complete(String[] words, Set<String> expectedCompletions) {
assertThat(completer.complete(words),
containsInAnyOrder(expectedCompletions.toArray(new String[0])));
}
}