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

paulk-asert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/master by this push:
     new 620aa3171b groovysh additional tests (cont'd)
620aa3171b is described below

commit 620aa3171b174329c8c5ec6bd04893e496d7e7e3
Author: Paul King <[email protected]>
AuthorDate: Sat May 9 11:30:17 2026 +1000

    groovysh additional tests (cont'd)
---
 .agents/skills/groovysh/SKILL.md                   | 11 +++
 .../groovy/groovysh/jline/GroovyBuiltins.groovy    |  2 +-
 .../groovy/groovysh/jline/GroovyPosixCommands.java |  6 +-
 .../groovysh/jline/GroovyPosixContext.groovy       | 13 +++-
 .../groovy-groovysh/src/spec/doc/groovysh.adoc     | 43 +++++++++++
 .../groovysh/jline/GroovyPosixContextTest.groovy   | 90 ++++++++++++++++++++++
 6 files changed, 162 insertions(+), 3 deletions(-)

diff --git a/.agents/skills/groovysh/SKILL.md b/.agents/skills/groovysh/SKILL.md
index 831e8b19a0..534146ad34 100644
--- a/.agents/skills/groovysh/SKILL.md
+++ b/.agents/skills/groovysh/SKILL.md
@@ -123,6 +123,17 @@ demonstrates the property under test:
    The no-arg `/save` form is a separate path that JSON-serialises
    `engine.sharedData` and *does* include variables. Round-trip tests
    for the file-form should exercise definitions, not bare variables.
+10. **`Less` and other highlight-aware JLine constructors silently
+    no-op highlighting if `ConfigurationPath` is omitted.** `Less` has
+    both 3-arg `(terminal, dir, opt)` and 4-arg
+    `(terminal, dir, opt, configPath)` constructors; the shorter form
+    compiles without warning but produces plain-text output. When
+    forking or wrapping these commands, plumb `configPath` through and
+    verify with a round-trip test (e.g. `GroovyPosixContextTest`). User-
+    visible symptom: plain text where coloured tokens are expected. We
+    hit this with `/less` after forking `PosixCommands`; `/nano` stayed
+    correct because it routes through `Commands.nano(...)`, which takes
+    `configPath` as a required argument.
 
 (Locale/platform/format brittleness is covered by the project-wide
 [`groovy-tests`](../groovy-tests/SKILL.md) skill — it's not unique to
diff --git 
a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyBuiltins.groovy
 
b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyBuiltins.groovy
index 93c99d57ba..9f200f73a6 100644
--- 
a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyBuiltins.groovy
+++ 
b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyBuiltins.groovy
@@ -82,7 +82,7 @@ class GroovyBuiltins extends Builtins {
                 temp.text = engine.buffer
                 input = new CommandInput(input.command(), [*input.args(), 
temp.absolutePath] as String[], input.terminal(), input.in(), input.out(), 
input.err())
             }
-            GroovyPosixCommands.less(new GroovyPosixContext(input.in(), new 
PrintStream(input.out()), new PrintStream(input.err()), workDir.get(), 
input.terminal(), engine::get), ['/less', *input.args()] as String[])
+            GroovyPosixCommands.less(new GroovyPosixContext(input.in(), new 
PrintStream(input.out()), new PrintStream(input.err()), workDir.get(), 
input.terminal(), engine::get, configPath), ['/less', *input.args()] as 
String[])
         } catch (Exception e) {
             saveException(e)
         }
diff --git 
a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyPosixCommands.java
 
b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyPosixCommands.java
index a8442c8528..1aa005dd6b 100644
--- 
a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyPosixCommands.java
+++ 
b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyPosixCommands.java
@@ -27,6 +27,7 @@
 package org.apache.groovy.groovysh.jline;
 
 import org.codehaus.groovy.runtime.ArrayGroovyMethods;
+import org.jline.builtins.ConfigurationPath;
 import org.jline.builtins.Less;
 import org.jline.builtins.Options;
 import org.jline.builtins.PosixCommands.Context;
@@ -1136,7 +1137,10 @@ public class GroovyPosixCommands {
             return;
         }
 
-        Less less = new Less(context.terminal(), context.currentDir(), opt);
+        ConfigurationPath cfg = (context instanceof GroovyPosixContext)
+                ? ((GroovyPosixContext) context).getConfigPath()
+                : null;
+        Less less = new Less(context.terminal(), context.currentDir(), opt, 
cfg);
         less.run(sources);
     }
 
diff --git 
a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyPosixContext.groovy
 
b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyPosixContext.groovy
index b2a2279425..a59a8717da 100644
--- 
a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyPosixContext.groovy
+++ 
b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyPosixContext.groovy
@@ -18,6 +18,7 @@
  */
 package org.apache.groovy.groovysh.jline
 
+import org.jline.builtins.ConfigurationPath
 import org.jline.builtins.PosixCommands
 import org.jline.terminal.Terminal
 
@@ -30,6 +31,12 @@ import java.util.function.Function
 class GroovyPosixContext extends PosixCommands.Context {
     /** Current working directory exposed to groovysh POSIX commands. */
     Path currentDir
+    /**
+     * Configuration path used to locate {@code jnanorc} and theme files for
+     * commands that integrate with the JLine syntax-highlighter (e.g. {@code 
/less}).
+     * Optional; null disables highlighting.
+     */
+    ConfigurationPath configPath
 
     /**
      * Creates a POSIX command context backed by groovysh state.
@@ -40,10 +47,14 @@ class GroovyPosixContext extends PosixCommands.Context {
      * @param currentDir current working directory
      * @param terminal active terminal
      * @param variables variable lookup function
+     * @param configPath configuration path used by syntax-highlighting 
commands; null disables highlighting
      */
-    GroovyPosixContext(InputStream inputStream, PrintStream out, PrintStream 
err, Path currentDir, Terminal terminal, Function<String, Object> variables) {
+    GroovyPosixContext(InputStream inputStream, PrintStream out, PrintStream 
err, Path currentDir,
+                       Terminal terminal, Function<String, Object> variables,
+                       ConfigurationPath configPath = null) {
         super(inputStream, out, err, null, terminal, variables)
         this.currentDir = currentDir
+        this.configPath = configPath
     }
 
     /**
diff --git a/subprojects/groovy-groovysh/src/spec/doc/groovysh.adoc 
b/subprojects/groovy-groovysh/src/spec/doc/groovysh.adoc
index b222f49047..e53a5522c4 100644
--- a/subprojects/groovy-groovysh/src/spec/doc/groovysh.adoc
+++ b/subprojects/groovy-groovysh/src/spec/doc/groovysh.adoc
@@ -586,6 +586,49 @@ apple
 banana
 ----
 
+[[GroovyShell-highlighter]]
+==== `/highlighter`
+
+The `/highlighter` command manages the nanorc theme system. Useful options
+include `--list` (show available themes), `--view=THEME` (preview a theme),
+`--switch=THEME` (change the active theme by rewriting your user-config
+`jnanorc`), and `--refresh` (reapply highlighting after a switch).
+
+Note: `--view` and `--switch` take *full filenames including the
+`.nanorctheme` extension*, not bare theme names. `/highlighter --list`
+shows the canonical form:
+
+[source,jshell]
+----
+groovy> /highlighter --list
+/Users/<you>/.groovy:
+dark.nanorctheme
+light.nanorctheme
+groovy> /highlighter --switch=light.nanorctheme
+groovy> /highlighter --refresh
+----
+
+Theme switching operates on user-config copies of `jnanorc` (used by the
+line reader and most commands) *and* `jlessrc` (used by `/less`); if a
+file is missing, that path silently no-ops on `--switch`. So a partial
+setup with only `jnanorc` will switch the prompt highlighting but leave
+`/less` permanently on whichever theme the classpath copy of `jlessrc`
+ships with.
+
+Once you create user-config copies, JLine resolves both the `theme`
+directive and the `include *.nanorc` entries *relative to the user-config
+directory* — the classpath copies are no longer consulted. So you also
+need the per-language syntax files (`groovy.nanorc`, `java.nanorc`,
+`json.nanorc`, `xml.nanorc`, `yaml.nanorc`, etc.) in the same directory,
+or you'll see the theme palette loaded but no actual highlighting (every
+file rendered as plain text). Easiest setup is to copy the entire
+`nanorc/` resource directory in one go:
+
+[source,bash]
+----
+cp <groovysh-src>/src/main/resources/nanorc/* ~/.groovy/
+----
+
 [[GroovyShell-history]]
 ==== `/history`
 
diff --git 
a/subprojects/groovy-groovysh/src/test/groovy/org/apache/groovy/groovysh/jline/GroovyPosixContextTest.groovy
 
b/subprojects/groovy-groovysh/src/test/groovy/org/apache/groovy/groovysh/jline/GroovyPosixContextTest.groovy
new file mode 100644
index 0000000000..a32d59a868
--- /dev/null
+++ 
b/subprojects/groovy-groovysh/src/test/groovy/org/apache/groovy/groovysh/jline/GroovyPosixContextTest.groovy
@@ -0,0 +1,90 @@
+/*
+ *  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.groovy.groovysh.jline
+
+import org.jline.builtins.ConfigurationPath
+import org.jline.terminal.Terminal
+import org.jline.terminal.TerminalBuilder
+import org.junit.jupiter.api.AfterEach
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+
+import java.nio.charset.StandardCharsets
+import java.nio.file.Files
+import java.nio.file.Path
+import java.util.function.Function
+
+/**
+ * Tests for {@link GroovyPosixContext}'s {@code configPath} plumbing — the
+ * field that {@link GroovyPosixCommands#less} reads to wire JLine's
+ * syntax-highlighter into {@code /less}. A regression here surfaces as
+ * {@code /less} losing colour rendering, which is silent (no error, just
+ * plain text).
+ */
+class GroovyPosixContextTest {
+
+    private Terminal terminal
+    private Path tmpDir
+
+    @BeforeEach
+    void setUp() {
+        terminal = TerminalBuilder.builder()
+                .dumb(true)
+                .streams(new ByteArrayInputStream(new byte[0]), new 
ByteArrayOutputStream())
+                .encoding(StandardCharsets.UTF_8)
+                .name('groovysh-test')
+                .build()
+        tmpDir = Files.createTempDirectory('groovysh-context-')
+    }
+
+    @AfterEach
+    void tearDown() {
+        terminal?.close()
+        tmpDir?.toFile()?.deleteDir()
+    }
+
+    private GroovyPosixContext context(ConfigurationPath cfg = null) {
+        new GroovyPosixContext(
+                new ByteArrayInputStream(new byte[0]),
+                new PrintStream(new ByteArrayOutputStream(), true, 
StandardCharsets.UTF_8),
+                new PrintStream(new ByteArrayOutputStream(), true, 
StandardCharsets.UTF_8),
+                tmpDir,
+                terminal,
+                { name -> null } as Function<String, Object>,
+                cfg)
+    }
+
+    @Test
+    void configPathDefaultsToNull() {
+        // Backward-compatible 6-arg constructor: callers that don't need
+        // syntax highlighting (most posix commands) keep working.
+        def ctx = context()
+        assert ctx.configPath == null
+    }
+
+    @Test
+    void configPathRoundTrips() {
+        // The 7-arg constructor is the path /less now uses; verify the
+        // ConfigurationPath survives construction so GroovyPosixCommands.less
+        // can hand it to the JLine Less constructor for highlighting.
+        ConfigurationPath cfg = new ConfigurationPath(tmpDir, tmpDir)
+        def ctx = context(cfg)
+        assert ctx.configPath.is(cfg)
+    }
+}

Reply via email to