From d7d0d99882f0736d35760ca1ad20fe4c45b1ea61 Mon Sep 17 00:00:00 2001 From: kanthi subramanian Date: Thu, 26 Feb 2026 12:36:16 -0600 Subject: [PATCH 1/3] Add shell mode for autocompletion of commands. --- ice/pom.xml | 10 ++ .../main/java/com/altinity/ice/cli/Main.java | 95 +++++++++++++++++++ pom.xml | 1 + 3 files changed, 106 insertions(+) diff --git a/ice/pom.xml b/ice/pom.xml index 2556c560..b200694b 100644 --- a/ice/pom.xml +++ b/ice/pom.xml @@ -512,6 +512,16 @@ picocli ${picocli.version} + + info.picocli + picocli-shell-jline3 + ${picocli.version} + + + org.jline + jline + ${jline.version} + com.fasterxml.jackson.core diff --git a/ice/src/main/java/com/altinity/ice/cli/Main.java b/ice/src/main/java/com/altinity/ice/cli/Main.java index a10e1da0..043fd904 100644 --- a/ice/src/main/java/com/altinity/ice/cli/Main.java +++ b/ice/src/main/java/com/altinity/ice/cli/Main.java @@ -34,12 +34,14 @@ import io.prometheus.metrics.instrumentation.jvm.JvmMetrics; import java.io.IOException; import java.net.URISyntaxException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.HashSet; import java.util.List; import java.util.Scanner; +import java.util.function.Supplier; import java.util.stream.Collectors; import org.apache.curator.shaded.com.google.common.net.HostAndPort; import org.apache.iceberg.CatalogProperties; @@ -47,10 +49,21 @@ import org.apache.iceberg.catalog.TableIdentifier; import org.apache.iceberg.rest.RESTCatalog; import org.eclipse.jetty.server.Server; +import org.jline.console.SystemRegistry; +import org.jline.console.impl.SystemRegistryImpl; +import org.jline.reader.EndOfFileException; +import org.jline.reader.LineReader; +import org.jline.reader.LineReaderBuilder; +import org.jline.reader.Parser; +import org.jline.reader.UserInterruptException; +import org.jline.reader.impl.DefaultParser; +import org.jline.terminal.Terminal; +import org.jline.terminal.TerminalBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import picocli.AutoComplete; import picocli.CommandLine; +import picocli.shell.jline3.PicocliCommands; @CommandLine.Command( name = "ice", @@ -63,6 +76,8 @@ public final class Main { private static final Logger logger = LoggerFactory.getLogger(Main.class); + private boolean inShellMode = false; + @CommandLine.Option( names = {"-c", "--config"}, description = "/path/to/config.yaml ($CWD/.ice.yaml by default)", @@ -764,6 +779,83 @@ private RESTCatalog loadCatalog(String configFile) throws IOException { return catalog; } + @CommandLine.Command( + name = "shell", + description = "Start interactive shell with tab completion.", + mixinStandardHelpOptions = true) + void shell() throws IOException { + if (inShellMode) { + logger.warn("Already in shell mode"); + return; + } + inShellMode = true; + + final String savedConfigFile = this.configFile; + final String savedLogLevel = this.logLevel; + final boolean savedInsecure = this.insecure; + + Supplier workDir = () -> Path.of(System.getProperty("user.dir")); + + CommandLine cmd = new CommandLine(this); + cmd.getSubcommands().remove("shell"); + + cmd.setExecutionStrategy( + parseResult -> { + Main main = (Main) parseResult.commandSpec().root().userObject(); + if (!parseResult.hasMatchedOption("--config")) { + main.configFile = savedConfigFile; + } + if (!parseResult.hasMatchedOption("--log-level")) { + main.logLevel = savedLogLevel; + } + if (!parseResult.hasMatchedOption("--insecure")) { + main.insecure = savedInsecure; + } + ch.qos.logback.classic.Logger rootLogger = + (ch.qos.logback.classic.Logger) + LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME); + rootLogger.setLevel(Level.toLevel(main.logLevel.toUpperCase(), Level.INFO)); + return new CommandLine.RunLast().execute(parseResult); + }); + cmd.setExecutionExceptionHandler( + (Exception ex, CommandLine self, CommandLine.ParseResult res) -> { + logger.error("Error", ex); + return 1; + }); + + PicocliCommands picocliCommands = new PicocliCommands(cmd); + + try (Terminal terminal = TerminalBuilder.builder().build()) { + Parser parser = new DefaultParser(); + SystemRegistry systemRegistry = new SystemRegistryImpl(parser, terminal, workDir, null); + systemRegistry.setCommandRegistries(picocliCommands); + + LineReader reader = + LineReaderBuilder.builder() + .terminal(terminal) + .completer(systemRegistry.completer()) + .parser(parser) + .variable(LineReader.LIST_MAX, 50) + .build(); + + String prompt = "ice> "; + + while (true) { + try { + systemRegistry.cleanUp(); + String line = reader.readLine(prompt); + systemRegistry.execute(line); + } catch (UserInterruptException e) { + // Ctrl+C - ignore + } catch (EndOfFileException e) { + return; + } catch (Exception e) { + systemRegistry.trace(e); + } + } + } + } + public static void main(String[] args) { CommandLine cmd = new CommandLine(new Main()); CommandLine.IExecutionStrategy defaultExecutionStrategy = cmd.getExecutionStrategy(); @@ -783,6 +875,9 @@ public static void main(String[] args) { logger.error("Fatal", ex); return 1; }); + if (args.length == 0) { + args = new String[] {"shell"}; + } int exitCode = cmd.execute(args); System.exit(exitCode); } diff --git a/pom.xml b/pom.xml index cbcab051..e84e6369 100644 --- a/pom.xml +++ b/pom.xml @@ -30,6 +30,7 @@ 3.27.2 3.2.0 + 3.26.1 From 6af15a2f34a678e9af23a89625ff3372cab7ff44 Mon Sep 17 00:00:00 2001 From: kanthi subramanian Date: Mon, 16 Mar 2026 11:01:04 -0400 Subject: [PATCH 2/3] Removed logic of default mode to shell --- ice/src/main/java/com/altinity/ice/cli/Main.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/ice/src/main/java/com/altinity/ice/cli/Main.java b/ice/src/main/java/com/altinity/ice/cli/Main.java index 043fd904..f720ab6a 100644 --- a/ice/src/main/java/com/altinity/ice/cli/Main.java +++ b/ice/src/main/java/com/altinity/ice/cli/Main.java @@ -875,9 +875,6 @@ public static void main(String[] args) { logger.error("Fatal", ex); return 1; }); - if (args.length == 0) { - args = new String[] {"shell"}; - } int exitCode = cmd.execute(args); System.exit(exitCode); } From 663e7efabfcbe9102a5df6e2fca86f2277ad8fed Mon Sep 17 00:00:00 2001 From: kanthi subramanian Date: Thu, 19 Mar 2026 18:12:33 -0400 Subject: [PATCH 3/3] Added System.exit on user interruption --- ice/src/main/java/com/altinity/ice/cli/Main.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ice/src/main/java/com/altinity/ice/cli/Main.java b/ice/src/main/java/com/altinity/ice/cli/Main.java index 6e8e7dee..7856c98b 100644 --- a/ice/src/main/java/com/altinity/ice/cli/Main.java +++ b/ice/src/main/java/com/altinity/ice/cli/Main.java @@ -917,7 +917,7 @@ void shell() throws IOException { String line = reader.readLine(prompt); systemRegistry.execute(line); } catch (UserInterruptException e) { - // Ctrl+C - ignore + System.exit(0); } catch (EndOfFileException e) { return; } catch (Exception e) {