You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ap...@apache.org on 2024/09/04 13:28:42 UTC

(ignite-3) branch main updated: IGNITE-23108 Improve HOCON highlighting in CLI (#4323)

This is an automated email from the ASF dual-hosted git repository.

apkhmv 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 118c7fb488 IGNITE-23108 Improve HOCON highlighting in CLI (#4323)
118c7fb488 is described below

commit 118c7fb488687adfc56e3cc2183f607281277c3d
Author: Vadim Pakhnushev <86...@users.noreply.github.com>
AuthorDate: Wed Sep 4 16:28:36 2024 +0300

    IGNITE-23108 Improve HOCON highlighting in CLI (#4323)
---
 gradle/libs.versions.toml                          |  8 ++-
 modules/cli/build.gradle                           |  1 +
 .../java/org/apache/ignite/internal/cli/Main.java  |  1 +
 .../ignite/internal/cli/commands/FormatMixin.java  | 68 ++++++++++++++++++
 .../ignite/internal/cli/commands/Options.java      |  4 ++
 .../cluster/config/ClusterConfigShowCommand.java   | 16 ++---
 .../config/ClusterConfigShowReplCommand.java       | 14 ++--
 .../node/config/NodeConfigShowCommand.java         | 14 ++--
 .../node/config/NodeConfigShowReplCommand.java     | 14 ++--
 ...iHighlighter.java => HoconAnsiHighlighter.java} | 48 +++++++------
 .../highlighter/JsonAnsiHighlighter.java           |  1 -
 .../commands/treesitter/parser/HoconTokenType.java | 83 +++++++++++++++++++++
 .../cli/commands/treesitter/parser/Indexer.java    | 75 +++++++++++--------
 .../cli/commands/treesitter/parser/Parser.java     | 31 ++++++--
 .../cli/core/repl/executor/ReplExecutorImpl.java   |  1 +
 .../{JsonDecorator.java => HoconDecorator.java}    | 10 +--
 .../internal/cli/decorators/JsonDecorator.java     | 18 ++---
 ...siHighlighterTest.java => HighlighterTest.java} | 28 +++++---
 .../treesitter/parser/HoconIndexerTest.java        | 84 ++++++++++++++++++++++
 19 files changed, 392 insertions(+), 127 deletions(-)

diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 03ceb99766..da05f8a7e1 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -79,9 +79,10 @@ awaitility = "4.2.2"
 progressBar = "0.9.4"
 guava = "33.2.1-jre"
 jna = "5.14.0"
-tree-sitter = "0.22.5"
-tree-sitter-json = "0.21.0"
-tree-sitter-sql = "gh-pages"
+tree-sitter = "0.23.0"
+tree-sitter-json = "0.21.0a"
+tree-sitter-sql = "gh-pages-a"
+tree-sitter-hocon = "master-a"
 
 #Tools
 pmdTool = "6.55.0"
@@ -265,3 +266,4 @@ jna = { module = "net.java.dev.jna:jna", version.ref = "jna"}
 tree-sitter = { module = "io.github.bonede:tree-sitter", version.ref = "tree-sitter" }
 tree-sitter-json = { module = "io.github.bonede:tree-sitter-json", version.ref = "tree-sitter-json" }
 tree-sitter-sql = { module = "io.github.bonede:tree-sitter-sql", version.ref = "tree-sitter-sql" }
+tree-sitter-hocon = { module = "io.github.bonede:tree-sitter-hocon", version.ref = "tree-sitter-hocon" }
diff --git a/modules/cli/build.gradle b/modules/cli/build.gradle
index f48bd28f01..08f96420f6 100644
--- a/modules/cli/build.gradle
+++ b/modules/cli/build.gradle
@@ -47,6 +47,7 @@ dependencies {
     implementation libs.tree.sitter
     implementation libs.tree.sitter.sql
     implementation libs.tree.sitter.json
+    implementation libs.tree.sitter.hocon
 
     implementation libs.fliptables
     implementation libs.jline.core
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/Main.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/Main.java
index dbb6f2947f..80ec29669a 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/Main.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/Main.java
@@ -97,6 +97,7 @@ public class Main {
         cmd.setExecutionExceptionHandler(new PicocliExecutionExceptionHandler());
         cmd.setDefaultValueProvider(micronautFactory.create(ConfigDefaultValueProvider.class));
         cmd.setTrimQuotes(true);
+        cmd.setCaseInsensitiveEnumValuesAllowed(true);
         return cmd.execute(args);
     }
 
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/FormatMixin.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/FormatMixin.java
new file mode 100644
index 0000000000..d6f965aec0
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/FormatMixin.java
@@ -0,0 +1,68 @@
+/*
+ * 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 static org.apache.ignite.internal.cli.commands.Options.Constants.CONFIG_FORMAT_OPTION;
+import static org.apache.ignite.internal.cli.commands.Options.Constants.CONFIG_FORMAT_OPTION_DESC;
+import static picocli.CommandLine.Help.Visibility.ALWAYS;
+
+import jakarta.inject.Inject;
+import org.apache.ignite.internal.cli.call.configuration.JsonString;
+import org.apache.ignite.internal.cli.config.CliConfigKeys;
+import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
+import org.apache.ignite.internal.cli.core.decorator.Decorator;
+import org.apache.ignite.internal.cli.core.decorator.TerminalOutput;
+import org.apache.ignite.internal.cli.decorators.HoconDecorator;
+import org.apache.ignite.internal.cli.decorators.JsonDecorator;
+import picocli.CommandLine.Option;
+
+/** Mixin for config format option. */
+public class FormatMixin {
+    private enum Format {
+        JSON,
+        HOCON
+    }
+
+    @Option(
+            names = CONFIG_FORMAT_OPTION,
+            description = CONFIG_FORMAT_OPTION_DESC,
+            defaultValue = "HOCON",
+            showDefaultValue = ALWAYS
+    )
+    private Format format;
+
+    @Inject
+    private ConfigManagerProvider configManagerProvider;
+
+    /**
+     * Gets the decorator corresponding to the format.
+     *
+     * @return Decorator.
+     */
+    public Decorator<JsonString, TerminalOutput> decorator() {
+        if (format == Format.JSON) {
+            return new JsonDecorator(isHighlightEnabled());
+        }
+        // HOCON is default
+        return new HoconDecorator(isHighlightEnabled());
+    }
+
+    private boolean isHighlightEnabled() {
+        return Boolean.parseBoolean(configManagerProvider.get().getCurrentProperty(CliConfigKeys.SYNTAX_HIGHLIGHTING.value()));
+    }
+}
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
index 94afff0d6b..c3d1c61025 100644
--- 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
@@ -291,5 +291,9 @@ public enum Options {
 
         public static final String RECOVERY_NODE_NAMES_OPTION_DESC = "Names specifying nodes to get partition states from. "
                 + "Case-sensitive, without quotes, all nodes if not set";
+
+        public static final String CONFIG_FORMAT_OPTION = "--format";
+
+        public static final String CONFIG_FORMAT_OPTION_DESC = "Output format. Valid values: ${COMPLETION-CANDIDATES}";
     }
 }
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/config/ClusterConfigShowCommand.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/config/ClusterConfigShowCommand.java
index 0b8f1334af..4ba949887d 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/config/ClusterConfigShowCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/config/ClusterConfigShowCommand.java
@@ -22,12 +22,10 @@ import java.util.concurrent.Callable;
 import org.apache.ignite.internal.cli.call.configuration.ClusterConfigShowCall;
 import org.apache.ignite.internal.cli.call.configuration.ClusterConfigShowCallInput;
 import org.apache.ignite.internal.cli.commands.BaseCommand;
+import org.apache.ignite.internal.cli.commands.FormatMixin;
 import org.apache.ignite.internal.cli.commands.cluster.ClusterUrlProfileMixin;
-import org.apache.ignite.internal.cli.config.CliConfigKeys;
-import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
 import org.apache.ignite.internal.cli.core.call.CallExecutionPipeline;
 import org.apache.ignite.internal.cli.core.exception.handler.ClusterNotInitializedExceptionHandler;
-import org.apache.ignite.internal.cli.decorators.JsonDecorator;
 import picocli.CommandLine.Command;
 import picocli.CommandLine.Mixin;
 import picocli.CommandLine.Parameters;
@@ -47,11 +45,11 @@ public class ClusterConfigShowCommand extends BaseCommand implements Callable<In
     @Parameters(arity = "0..1", description = "Configuration path selector")
     private String selector;
 
-    @Inject
-    private ClusterConfigShowCall call;
+    @Mixin
+    private FormatMixin format;
 
     @Inject
-    private ConfigManagerProvider configManagerProvider;
+    private ClusterConfigShowCall call;
 
     /** {@inheritDoc} */
     @Override
@@ -60,17 +58,13 @@ public class ClusterConfigShowCommand extends BaseCommand implements Callable<In
                 .inputProvider(this::buildCallInput)
                 .output(spec.commandLine().getOut())
                 .errOutput(spec.commandLine().getErr())
-                .decorator(new JsonDecorator(isHighlightEnabled()))
+                .decorator(format.decorator())
                 .exceptionHandler(ClusterNotInitializedExceptionHandler.createHandler("Cannot show cluster config"))
                 .verbose(verbose)
                 .build()
                 .runPipeline();
     }
 
-    private boolean isHighlightEnabled() {
-        return Boolean.parseBoolean(configManagerProvider.get().getCurrentProperty(CliConfigKeys.SYNTAX_HIGHLIGHTING.value()));
-    }
-
     private ClusterConfigShowCallInput buildCallInput() {
         return ClusterConfigShowCallInput.builder()
                 .clusterUrl(clusterUrl.getClusterUrl())
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/config/ClusterConfigShowReplCommand.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/config/ClusterConfigShowReplCommand.java
index c606ef1a07..f9eda6a7ac 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/config/ClusterConfigShowReplCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/config/ClusterConfigShowReplCommand.java
@@ -21,13 +21,11 @@ import jakarta.inject.Inject;
 import org.apache.ignite.internal.cli.call.configuration.ClusterConfigShowCall;
 import org.apache.ignite.internal.cli.call.configuration.ClusterConfigShowCallInput;
 import org.apache.ignite.internal.cli.commands.BaseCommand;
+import org.apache.ignite.internal.cli.commands.FormatMixin;
 import org.apache.ignite.internal.cli.commands.cluster.ClusterUrlMixin;
 import org.apache.ignite.internal.cli.commands.questions.ConnectToClusterQuestion;
-import org.apache.ignite.internal.cli.config.CliConfigKeys;
-import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
 import org.apache.ignite.internal.cli.core.exception.handler.ClusterNotInitializedExceptionHandler;
 import org.apache.ignite.internal.cli.core.flow.builder.Flows;
-import org.apache.ignite.internal.cli.decorators.JsonDecorator;
 import picocli.CommandLine.Command;
 import picocli.CommandLine.Mixin;
 import picocli.CommandLine.Parameters;
@@ -55,8 +53,8 @@ public class ClusterConfigShowReplCommand extends BaseCommand implements Runnabl
     @Inject
     private ConnectToClusterQuestion question;
 
-    @Inject
-    private ConfigManagerProvider configManagerProvider;
+    @Mixin
+    private FormatMixin format;
 
     @Override
     public void run() {
@@ -65,15 +63,11 @@ public class ClusterConfigShowReplCommand extends BaseCommand implements Runnabl
                 .then(Flows.fromCall(call))
                 .exceptionHandler(ClusterNotInitializedExceptionHandler.createReplHandler("Cannot show cluster config"))
                 .verbose(verbose)
-                .print(new JsonDecorator(isHighlightEnabled()))
+                .print(format.decorator())
                 .start();
     }
 
     private ClusterConfigShowCallInput configShowCallInput(String clusterUrl) {
         return ClusterConfigShowCallInput.builder().selector(selector).clusterUrl(clusterUrl).build();
     }
-
-    private boolean isHighlightEnabled() {
-        return Boolean.parseBoolean(configManagerProvider.get().getCurrentProperty(CliConfigKeys.SYNTAX_HIGHLIGHTING.value()));
-    }
 }
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/config/NodeConfigShowCommand.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/config/NodeConfigShowCommand.java
index 4031fd5f57..fae17ff388 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/config/NodeConfigShowCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/config/NodeConfigShowCommand.java
@@ -22,11 +22,9 @@ import java.util.concurrent.Callable;
 import org.apache.ignite.internal.cli.call.configuration.NodeConfigShowCall;
 import org.apache.ignite.internal.cli.call.configuration.NodeConfigShowCallInput;
 import org.apache.ignite.internal.cli.commands.BaseCommand;
+import org.apache.ignite.internal.cli.commands.FormatMixin;
 import org.apache.ignite.internal.cli.commands.node.NodeUrlProfileMixin;
-import org.apache.ignite.internal.cli.config.CliConfigKeys;
-import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
 import org.apache.ignite.internal.cli.core.call.CallExecutionPipeline;
-import org.apache.ignite.internal.cli.decorators.JsonDecorator;
 import picocli.CommandLine.Command;
 import picocli.CommandLine.Mixin;
 import picocli.CommandLine.Parameters;
@@ -47,8 +45,8 @@ public class NodeConfigShowCommand extends BaseCommand implements Callable<Integ
     @Inject
     private NodeConfigShowCall call;
 
-    @Inject
-    private ConfigManagerProvider configManagerProvider;
+    @Mixin
+    private FormatMixin format;
 
     /** {@inheritDoc} */
     @Override
@@ -57,16 +55,12 @@ public class NodeConfigShowCommand extends BaseCommand implements Callable<Integ
                 .inputProvider(this::buildCallInput)
                 .output(spec.commandLine().getOut())
                 .errOutput(spec.commandLine().getErr())
-                .decorator(new JsonDecorator(isHighlightEnabled()))
+                .decorator(format.decorator())
                 .verbose(verbose)
                 .build()
                 .runPipeline();
     }
 
-    private boolean isHighlightEnabled() {
-        return Boolean.parseBoolean(configManagerProvider.get().getCurrentProperty(CliConfigKeys.SYNTAX_HIGHLIGHTING.value()));
-    }
-
     private NodeConfigShowCallInput buildCallInput() {
         return NodeConfigShowCallInput.builder()
                 .nodeUrl(nodeUrl.getNodeUrl())
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/config/NodeConfigShowReplCommand.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/config/NodeConfigShowReplCommand.java
index 365076bc73..90496bd2db 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/config/NodeConfigShowReplCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/config/NodeConfigShowReplCommand.java
@@ -21,12 +21,10 @@ import jakarta.inject.Inject;
 import org.apache.ignite.internal.cli.call.configuration.NodeConfigShowCall;
 import org.apache.ignite.internal.cli.call.configuration.NodeConfigShowCallInput;
 import org.apache.ignite.internal.cli.commands.BaseCommand;
+import org.apache.ignite.internal.cli.commands.FormatMixin;
 import org.apache.ignite.internal.cli.commands.node.NodeUrlMixin;
 import org.apache.ignite.internal.cli.commands.questions.ConnectToClusterQuestion;
-import org.apache.ignite.internal.cli.config.CliConfigKeys;
-import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
 import org.apache.ignite.internal.cli.core.flow.builder.Flows;
-import org.apache.ignite.internal.cli.decorators.JsonDecorator;
 import picocli.CommandLine.Command;
 import picocli.CommandLine.Mixin;
 import picocli.CommandLine.Parameters;
@@ -50,8 +48,8 @@ public class NodeConfigShowReplCommand extends BaseCommand implements Runnable {
     @Inject
     private ConnectToClusterQuestion question;
 
-    @Inject
-    private ConfigManagerProvider configManagerProvider;
+    @Mixin
+    private FormatMixin format;
 
     /** {@inheritDoc} */
     @Override
@@ -60,15 +58,11 @@ public class NodeConfigShowReplCommand extends BaseCommand implements Runnable {
                 .map(this::nodeConfigShowCallInput)
                 .then(Flows.fromCall(call))
                 .verbose(verbose)
-                .print(new JsonDecorator(isHighlightEnabled()))
+                .print(format.decorator())
                 .start();
     }
 
     private NodeConfigShowCallInput nodeConfigShowCallInput(String nodeUrl) {
         return NodeConfigShowCallInput.builder().selector(selector).nodeUrl(nodeUrl).build();
     }
-
-    private boolean isHighlightEnabled() {
-        return Boolean.parseBoolean(configManagerProvider.get().getCurrentProperty(CliConfigKeys.SYNTAX_HIGHLIGHTING.value()));
-    }
 }
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/highlighter/JsonAnsiHighlighter.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/highlighter/HoconAnsiHighlighter.java
similarity index 57%
copy from modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/highlighter/JsonAnsiHighlighter.java
copy to modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/highlighter/HoconAnsiHighlighter.java
index f22d834d88..51e351b110 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/highlighter/JsonAnsiHighlighter.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/highlighter/HoconAnsiHighlighter.java
@@ -19,45 +19,49 @@ package org.apache.ignite.internal.cli.commands.treesitter.highlighter;
 
 import static org.apache.ignite.internal.cli.core.style.AnsiStringSupport.ansi;
 
+import java.util.EnumMap;
 import java.util.Map;
+import org.apache.ignite.internal.cli.commands.treesitter.parser.HoconTokenType;
 import org.apache.ignite.internal.cli.commands.treesitter.parser.Indexer;
-import org.apache.ignite.internal.cli.commands.treesitter.parser.JsonTokenType;
 import org.apache.ignite.internal.cli.commands.treesitter.parser.Parser;
 import org.apache.ignite.internal.cli.core.style.AnsiStringSupport;
 import org.apache.ignite.internal.cli.core.style.AnsiStringSupport.Color;
 
 /**
- * Class for highlighting JSON text with ANSI colors.
+ * Class for highlighting HOCON text with ANSI colors.
  */
-public class JsonAnsiHighlighter {
+public class HoconAnsiHighlighter {
 
-    private static final Map<JsonTokenType, AnsiStringSupport.Color> colorMap = Map.of(
-            JsonTokenType.QUOTE, Color.GREEN,
-            JsonTokenType.STRING_CONTENT, Color.GREEN,
-            JsonTokenType.BRACKET, Color.WHITE,
-            JsonTokenType.COMMA, Color.WHITE,
-            JsonTokenType.COLON, Color.WHITE,
-            JsonTokenType.NUMBER, Color.BLUE,
-            JsonTokenType.BOOL, Color.YELLOW_DARK,
-            JsonTokenType.UNKNOWN, Color.GRAY,
-            JsonTokenType.SPACE, Color.GRAY
-    );
+    private static final Map<HoconTokenType, Color> COLOR_MAP = new EnumMap<>(HoconTokenType.class);
+
+    static {
+        COLOR_MAP.put(HoconTokenType.QUOTE, Color.GREEN);
+        COLOR_MAP.put(HoconTokenType.STRING, Color.GREEN);
+        COLOR_MAP.put(HoconTokenType.PATH, Color.WHITE);
+        COLOR_MAP.put(HoconTokenType.BRACKET, Color.GRAY);
+        COLOR_MAP.put(HoconTokenType.COMMA, Color.GRAY);
+        COLOR_MAP.put(HoconTokenType.COLON, Color.GRAY);
+        COLOR_MAP.put(HoconTokenType.EQUALS, Color.GRAY);
+        COLOR_MAP.put(HoconTokenType.NUMBER, Color.BLUE);
+        COLOR_MAP.put(HoconTokenType.BOOL, Color.YELLOW_DARK);
+        COLOR_MAP.put(HoconTokenType.UNKNOWN, Color.GRAY);
+        COLOR_MAP.put(HoconTokenType.SPACE, Color.GRAY);
+    }
 
     /**
-     * Highlights the input JSON text with ANSI colors.
+     * Highlights the input HOCON text with ANSI colors.
      *
-     * @param text The input JSON text.
-     * @return The highlighted JSON text.
+     * @param text The input HOCON text.
+     * @return The highlighted HOCON text.
      */
     public static String highlight(String text) {
-        // TODO Use tree-sitter-hocon parser here IGNITE-23108
-        var tree = Parser.parseJson(text);
-        JsonTokenType[] tokens = Indexer.indexJson(text, tree);
+        var tree = Parser.parseHocon(text);
+        HoconTokenType[] tokens = Indexer.indexHocon(text, tree);
         var sb = new StringBuilder();
 
         for (int i = 0; i < text.length(); i++) {
-            JsonTokenType token = tokens[i];
-            Color color = colorMap.get(token);
+            HoconTokenType token = tokens[i];
+            Color color = COLOR_MAP.get(token);
             sb.append(AnsiStringSupport.fg(color).mark(String.valueOf(text.charAt(i))));
         }
 
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/highlighter/JsonAnsiHighlighter.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/highlighter/JsonAnsiHighlighter.java
index f22d834d88..d9ad073c09 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/highlighter/JsonAnsiHighlighter.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/highlighter/JsonAnsiHighlighter.java
@@ -50,7 +50,6 @@ public class JsonAnsiHighlighter {
      * @return The highlighted JSON text.
      */
     public static String highlight(String text) {
-        // TODO Use tree-sitter-hocon parser here IGNITE-23108
         var tree = Parser.parseJson(text);
         JsonTokenType[] tokens = Indexer.indexJson(text, tree);
         var sb = new StringBuilder();
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/parser/HoconTokenType.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/parser/HoconTokenType.java
new file mode 100644
index 0000000000..611f6ea960
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/parser/HoconTokenType.java
@@ -0,0 +1,83 @@
+/*
+ * 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.treesitter.parser;
+
+import org.treesitter.TSNode;
+
+/**
+ * Enum representing the different types of tokens in a HOCON text.
+ */
+public enum HoconTokenType {
+    QUOTE,
+    STRING,
+    PATH,
+    NUMBER,
+    COLON,
+    EQUALS,
+    BRACKET,
+    COMMA,
+    UNKNOWN,
+    BOOL,
+    SPACE;
+
+    private static final String PATH_TYPE = "path";
+
+    static HoconTokenType fromNode(TSNode node) {
+        String nodeType = nodeType(node);
+        switch (nodeType) {
+            case "\"":
+                return QUOTE;
+            case PATH_TYPE:
+                return PATH;
+            case "string":
+            case "unquoted_string":
+                return STRING;
+            case "{":
+            case "}":
+            case "[":
+            case "]":
+                return BRACKET;
+            case ",":
+                return COMMA;
+            case ":":
+                return COLON;
+            case "=":
+                return EQUALS;
+            case "number":
+                return NUMBER;
+            case "true":
+            case "false":
+                return BOOL;
+            default:
+                return UNKNOWN;
+        }
+    }
+
+    private static String nodeType(TSNode node) {
+        TSNode parent = node;
+        // Both key and value nodes could have the same type - string - but we want to highlight the key ("path") differently.
+        // Proper fix would be to change the logic of tokenizing completely but this is enough for now.
+        while (!parent.isNull()) {
+            if (PATH_TYPE.equals(parent.getType())) {
+                return PATH_TYPE;
+            }
+            parent = parent.getParent();
+        }
+        return node.getType();
+    }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/parser/Indexer.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/parser/Indexer.java
index 0bd535bc97..6ca08cd83f 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/parser/Indexer.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/parser/Indexer.java
@@ -17,13 +17,14 @@
 
 package org.apache.ignite.internal.cli.commands.treesitter.parser;
 
+import java.util.function.Function;
 import org.treesitter.TSNode;
 import org.treesitter.TSTree;
 import org.treesitter.TSTreeCursor;
 
 /**
  * Builds an "index" for the provided text and abstract syntax tree.
- * The index is an array of tokens, where each token is a member of the SqlTokenType enum.
+ * The index is an array of tokens, where each token is a member of the corresponding enum.
  * It is used to map each character in the input text to a token.
  */
 public class Indexer {
@@ -36,21 +37,61 @@ public class Indexer {
      * @return The index.
      */
     public static SqlTokenType[] indexSql(String text, TSTree tree) {
-        SqlTokenType[] tokens = new SqlTokenType[text.length()];
+        return index(text, tree, SqlTokenType[]::new, SqlTokenType.SPACE, SqlTokenType::fromNode);
+    }
+
+    /**
+     * Builds an index for the provided JSON text and abstract syntax tree.
+     *
+     * @param text The input JSON text.
+     * @param tree The abstract syntax tree.
+     * @return The index.
+     */
+    public static JsonTokenType[] indexJson(String text, TSTree tree) {
+        return index(text, tree, JsonTokenType[]::new, JsonTokenType.SPACE, JsonTokenType::fromNode);
+    }
 
+    /**
+     * Builds an index for the provided HOCON text and abstract syntax tree.
+     *
+     * @param text The input HOCON text.
+     * @param tree The abstract syntax tree.
+     * @return The index.
+     */
+    public static HoconTokenType[] indexHocon(String text, TSTree tree) {
+        return index(text, tree, HoconTokenType[]::new, HoconTokenType.SPACE, HoconTokenType::fromNode);
+    }
+
+    /**
+     * Builds an index for the provided text and abstract syntax tree.
+     *
+     * @param text The input text.
+     * @param tree The abstract syntax tree.
+     * @param tokensArrayFactory Function to create an array of tokens.
+     * @param spaceToken Token representing space.
+     * @param nodeToToken Function to extract a token from the node of the tree.
+     * @return The index.
+     */
+    private static <T> T[] index(
+            String text,
+            TSTree tree,
+            Function<Integer, T[]> tokensArrayFactory,
+            T spaceToken,
+            Function<TSNode, T> nodeToToken
+    ) {
+        T[] tokens = tokensArrayFactory.apply(text.length());
         TSTreeCursor cursor = new TSTreeCursor(tree.getRootNode());
         for (int i = 0; i < text.length(); i++) {
             if (Character.isSpaceChar(text.charAt(i))) {
-                tokens[i] = SqlTokenType.SPACE;
+                tokens[i] = spaceToken;
                 continue;
             }
             findTerminalNode(i, cursor);
             TSNode node = cursor.currentNode();
-            tokens[i] = SqlTokenType.fromNode(node);
+            tokens[i] = nodeToToken.apply(node);
             cursor.reset(tree.getRootNode());
         }
         return tokens;
-
     }
 
     private static void findTerminalNode(int pos, TSTreeCursor cursor) {
@@ -71,28 +112,4 @@ public class Indexer {
     private static boolean inside(int pos, TSNode node) {
         return pos >= node.getStartByte() && pos < node.getEndByte();
     }
-
-    /**
-     * Builds an index for the provided JSON text and abstract syntax tree.
-     *
-     * @param text The input JSON text.
-     * @param tree The abstract syntax tree.
-     * @return The index.
-     */
-    public static JsonTokenType[] indexJson(String text, TSTree tree) {
-        JsonTokenType[] tokens = new JsonTokenType[text.length()];
-
-        TSTreeCursor cursor = new TSTreeCursor(tree.getRootNode());
-        for (int i = 0; i < text.length(); i++) {
-            if (Character.isSpaceChar(text.charAt(i))) {
-                tokens[i] = JsonTokenType.SPACE;
-                continue;
-            }
-            findTerminalNode(i, cursor);
-            TSNode node = cursor.currentNode();
-            tokens[i] = JsonTokenType.fromNode(node);
-            cursor.reset(tree.getRootNode());
-        }
-        return tokens;
-    }
 }
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/parser/Parser.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/parser/Parser.java
index d1ab8a821e..4fb6ef8856 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/parser/Parser.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/parser/Parser.java
@@ -17,8 +17,10 @@
 
 package org.apache.ignite.internal.cli.commands.treesitter.parser;
 
+import org.treesitter.TSLanguage;
 import org.treesitter.TSParser;
 import org.treesitter.TSTree;
+import org.treesitter.TreeSitterHocon;
 import org.treesitter.TreeSitterJson;
 import org.treesitter.TreeSitterSql;
 
@@ -36,9 +38,7 @@ public final class Parser {
      * @return The abstract syntax tree.
      */
     public static TSTree parseSql(String text) {
-        var parser = new TSParser();
-        parser.setLanguage(new TreeSitterSql());
-        return parser.parseString(null, text);
+        return parseLanguage(text, new TreeSitterSql());
     }
 
     /**
@@ -48,8 +48,29 @@ public final class Parser {
      * @return The abstract syntax tree.
      */
     public static TSTree parseJson(String text) {
-        var parser = new TSParser();
-        parser.setLanguage(new TreeSitterJson());
+        return parseLanguage(text, new TreeSitterJson());
+    }
+
+    /**
+     * Parses the input text as HOCON.
+     *
+     * @param text The input text.
+     * @return The abstract syntax tree.
+     */
+    public static TSTree parseHocon(String text) {
+        return parseLanguage(text, new TreeSitterHocon());
+    }
+
+    /**
+     * Parses the input text.
+     *
+     * @param text The input text.
+     * @param language The language to parse the text with.
+     * @return The abstract syntax tree.
+     */
+    private static TSTree parseLanguage(String text, TSLanguage language) {
+        TSParser parser = new TSParser();
+        parser.setLanguage(language);
         return parser.parseString(null, text);
     }
 }
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorImpl.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorImpl.java
index c65742f917..d960a0c018 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorImpl.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorImpl.java
@@ -194,6 +194,7 @@ public class ReplExecutorImpl implements ReplExecutor {
         CommandLineContextProvider.setCmd(cmd);
         cmd.setExecutionExceptionHandler(new PicocliExecutionExceptionHandler(exceptionHandlers));
         cmd.setTrimQuotes(true);
+        cmd.setCaseInsensitiveEnumValuesAllowed(true);
 
         DynamicCompleterRegistry completerRegistry = factory.create(DynamicCompleterRegistry.class);
         DynamicCompleterActivationPoint activationPoint = factory.create(DynamicCompleterActivationPoint.class);
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/JsonDecorator.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/HoconDecorator.java
similarity index 88%
copy from modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/JsonDecorator.java
copy to modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/HoconDecorator.java
index 57e7567000..e9fb50b965 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/JsonDecorator.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/HoconDecorator.java
@@ -22,18 +22,18 @@ import com.typesafe.config.ConfigException;
 import com.typesafe.config.ConfigFactory;
 import com.typesafe.config.ConfigRenderOptions;
 import org.apache.ignite.internal.cli.call.configuration.JsonString;
-import org.apache.ignite.internal.cli.commands.treesitter.highlighter.JsonAnsiHighlighter;
+import org.apache.ignite.internal.cli.commands.treesitter.highlighter.HoconAnsiHighlighter;
 import org.apache.ignite.internal.cli.core.decorator.Decorator;
 import org.apache.ignite.internal.cli.core.decorator.TerminalOutput;
 
 /**
- * Pretty json decorator.
+ * Pretty hocon decorator.
  */
-public class JsonDecorator implements Decorator<JsonString, TerminalOutput> {
+public class HoconDecorator implements Decorator<JsonString, TerminalOutput> {
 
     private final boolean highlight;
 
-    public JsonDecorator(boolean highlight) {
+    public HoconDecorator(boolean highlight) {
         this.highlight = highlight;
     }
 
@@ -49,7 +49,7 @@ public class JsonDecorator implements Decorator<JsonString, TerminalOutput> {
                     text = text.substring(0, text.length() - 1);
                 }
 
-                return highlight ? JsonAnsiHighlighter.highlight(text) : text;
+                return highlight ? HoconAnsiHighlighter.highlight(text) : text;
 
             } catch (ConfigException.Parse e) {
                 return json.getValue(); // no-op
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/JsonDecorator.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/JsonDecorator.java
index 57e7567000..65c96f8766 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/JsonDecorator.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/JsonDecorator.java
@@ -17,10 +17,9 @@
 
 package org.apache.ignite.internal.cli.decorators;
 
-import com.typesafe.config.Config;
-import com.typesafe.config.ConfigException;
-import com.typesafe.config.ConfigFactory;
-import com.typesafe.config.ConfigRenderOptions;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.ignite.internal.cli.call.configuration.JsonString;
 import org.apache.ignite.internal.cli.commands.treesitter.highlighter.JsonAnsiHighlighter;
 import org.apache.ignite.internal.cli.core.decorator.Decorator;
@@ -40,18 +39,15 @@ public class JsonDecorator implements Decorator<JsonString, TerminalOutput> {
     /** {@inheritDoc} */
     @Override
     public TerminalOutput decorate(JsonString json) {
+        ObjectMapper mapper = new ObjectMapper();
         return () -> {
             try {
-                Config config = ConfigFactory.parseString(json.getValue());
-                String text = config.root().render(ConfigRenderOptions.concise().setFormatted(true).setJson(false));
-                if (text.endsWith("\n")) {
-                    // Config renders with the trailing line separator, but we also add a separator in the handleResult.
-                    text = text.substring(0, text.length() - 1);
-                }
+                String text = mapper.writerWithDefaultPrettyPrinter()
+                        .writeValueAsString(mapper.readValue(json.getValue(), JsonNode.class));
 
                 return highlight ? JsonAnsiHighlighter.highlight(text) : text;
 
-            } catch (ConfigException.Parse e) {
+            } catch (JsonProcessingException e) {
                 return json.getValue(); // no-op
             }
         };
diff --git a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/treesitter/highlighter/JsonAnsiHighlighterTest.java b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/treesitter/highlighter/HighlighterTest.java
similarity index 69%
rename from modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/treesitter/highlighter/JsonAnsiHighlighterTest.java
rename to modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/treesitter/highlighter/HighlighterTest.java
index 3c7eea8f2b..729fa9b13a 100644
--- a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/treesitter/highlighter/JsonAnsiHighlighterTest.java
+++ b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/treesitter/highlighter/HighlighterTest.java
@@ -21,7 +21,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.params.provider.Arguments.of;
 
 import java.util.stream.Stream;
-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;
@@ -29,10 +28,9 @@ import org.junit.jupiter.params.provider.MethodSource;
 /**
  * Tests for the highlighting errors in JSON text. The colors itself are not tested.
  */
-class JsonAnsiHighlighterTest {
-    JsonAnsiHighlighter highlighter;
+class HighlighterTest {
 
-    private static Stream<Arguments> textInput() {
+    private static Stream<Arguments> jsonTextInput() {
         return Stream.of(
                 of("plain", "plain"),
                 of("{\"key\": \"val\"}", "{\"key\": \"val\"}"),
@@ -40,15 +38,25 @@ class JsonAnsiHighlighterTest {
         );
     }
 
-    @BeforeEach
-    void setUp() {
-        highlighter = new JsonAnsiHighlighter();
+    @ParameterizedTest
+    @MethodSource("jsonTextInput")
+    void jsonHighlight(String input, String expected) {
+        String result = JsonAnsiHighlighter.highlight(input);
+        assertEquals(expected, result);
+    }
+
+    private static Stream<Arguments> hoconTextInput() {
+        return Stream.of(
+                of("plain", "plain"),
+                of("{\"key\": \"val\"}", "{\"key\": \"val\"}"),
+                of("key=val", "key=val")
+        );
     }
 
     @ParameterizedTest
-    @MethodSource("textInput")
-    void testHighlight(String input, String expected) {
-        String result = highlighter.highlight(input);
+    @MethodSource("hoconTextInput")
+    void hoconHighlight(String input, String expected) {
+        String result = HoconAnsiHighlighter.highlight(input);
         assertEquals(expected, result);
     }
 }
diff --git a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/treesitter/parser/HoconIndexerTest.java b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/treesitter/parser/HoconIndexerTest.java
new file mode 100644
index 0000000000..f9838c34cf
--- /dev/null
+++ b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/treesitter/parser/HoconIndexerTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.treesitter.parser;
+
+import static org.apache.ignite.internal.cli.commands.treesitter.parser.HoconTokenType.BOOL;
+import static org.apache.ignite.internal.cli.commands.treesitter.parser.HoconTokenType.BRACKET;
+import static org.apache.ignite.internal.cli.commands.treesitter.parser.HoconTokenType.COLON;
+import static org.apache.ignite.internal.cli.commands.treesitter.parser.HoconTokenType.COMMA;
+import static org.apache.ignite.internal.cli.commands.treesitter.parser.HoconTokenType.EQUALS;
+import static org.apache.ignite.internal.cli.commands.treesitter.parser.HoconTokenType.NUMBER;
+import static org.apache.ignite.internal.cli.commands.treesitter.parser.HoconTokenType.PATH;
+import static org.apache.ignite.internal.cli.commands.treesitter.parser.HoconTokenType.QUOTE;
+import static org.apache.ignite.internal.cli.commands.treesitter.parser.HoconTokenType.SPACE;
+import static org.apache.ignite.internal.cli.commands.treesitter.parser.HoconTokenType.STRING;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+
+import java.util.stream.Stream;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * Tests for the indexing logic for HOCON.
+ */
+public class HoconIndexerTest {
+    private static Stream<Arguments> hoconProvider() {
+        return Stream.of(
+                Arguments.of(
+                        "{\"a\": 1, \"b\": \"t\", c=true}",
+                        new HoconTokenType[]{
+                                // {
+                                BRACKET,
+                                // "a"
+                                PATH, PATH, PATH,
+                                // :
+                                COLON,
+                                // 1
+                                SPACE, NUMBER,
+                                // ,
+                                COMMA,
+                                // "b"
+                                SPACE, PATH, PATH, PATH,
+                                // :
+                                COLON,
+                                // "t"
+                                SPACE, QUOTE, STRING, QUOTE,
+                                // ,
+                                COMMA,
+                                // c
+                                SPACE, PATH,
+                                // =
+                                EQUALS,
+                                // true
+                                BOOL, BOOL, BOOL, BOOL,
+                                // }
+                                BRACKET
+                        }
+                )
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("hoconProvider")
+    void indexHocon(String text, HoconTokenType[] expectedTokens) {
+        var tree = Parser.parseHocon(text);
+        HoconTokenType[] tokens = Indexer.indexHocon(text, tree);
+        assertArrayEquals(expectedTokens, tokens);
+    }
+}