[
https://issues.apache.org/jira/browse/GROOVY-12003?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18079687#comment-18079687
]
ASF GitHub Bot commented on GROOVY-12003:
-----------------------------------------
Copilot commented on code in PR #2521:
URL: https://github.com/apache/groovy/pull/2521#discussion_r3213166174
##########
subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyCommands.groovy:
##########
@@ -214,6 +236,200 @@ class GroovyCommands extends JlineCommandRegistry
implements CommandRegistry {
return null
}
+ /**
+ * Displays an image inline using JLine's terminal-graphics support
+ * (Sixel, Kitty, iTerm2). Falls back to a summary line when the
+ * terminal doesn't speak any supported protocol; the {@code --gui}
+ * flag opens a Swing window instead.
+ *
+ * @param input parsed command input
+ * @return always {@code null}
+ */
+ def img(CommandInput input) {
+ checkArgCount(input, [0, 1, 2, 3, 4, 5])
+ if (maybePrintHelp(input, '/img')) return
+ try {
+ Integer width = null
+ Integer height = null
+ boolean preserveAspect = true
+ boolean gui = false
+ Object positional = null
+ String positionalLabel = null
+ for (int i = 0; i < input.args().length; i++) {
+ String a = input.args()[i]
+ if (a == null) {
+ // JLine puts null into args() when a $var reference
resolves
+ // to null — usually because the variable isn't defined
yet.
+ throw new IllegalArgumentException(
+ '/img: variable reference resolved to null ' +
+ '(undefined or not yet assigned) — define
it first, e.g. ' +
+ "'panel =
ScatterPlot.of(...).canvas().panel()'")
+ }
+ if (a == '-w' || a == '--width') {
+ width = Integer.parseInt(input.args()[++i])
+ } else if (a.startsWith('--width=')) {
+ width = Integer.parseInt(a.substring('--width='.length()))
Review Comment:
The `-w/--width` branch unconditionally consumes the next token via
`input.args()[++i]`. If the user passes `--width` without a value (or as the
last argument), this will throw an
`ArrayIndexOutOfBoundsException`/`NullPointerException` and end up as a less
helpful saved exception. Please add an explicit bounds/value check and throw a
targeted `IllegalArgumentException` explaining the missing width value.
##########
subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyCommands.groovy:
##########
@@ -214,6 +236,200 @@ class GroovyCommands extends JlineCommandRegistry
implements CommandRegistry {
return null
}
+ /**
+ * Displays an image inline using JLine's terminal-graphics support
+ * (Sixel, Kitty, iTerm2). Falls back to a summary line when the
+ * terminal doesn't speak any supported protocol; the {@code --gui}
+ * flag opens a Swing window instead.
+ *
+ * @param input parsed command input
+ * @return always {@code null}
+ */
+ def img(CommandInput input) {
+ checkArgCount(input, [0, 1, 2, 3, 4, 5])
Review Comment:
`/img` currently restricts the argument count to 0..5 via `checkArgCount`,
but valid invocations can exceed this when using the space-separated forms of
`--width`/`--height` together with flags (e.g. `--width 64 --height 32 --gui
$img` => 6+ args). Consider removing the fixed arg-count check for this
command, or increasing it and/or validating based on parsed options + exactly
one positional argument instead of raw token count.
##########
subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyCommands.groovy:
##########
@@ -214,6 +236,200 @@ class GroovyCommands extends JlineCommandRegistry
implements CommandRegistry {
return null
}
+ /**
+ * Displays an image inline using JLine's terminal-graphics support
+ * (Sixel, Kitty, iTerm2). Falls back to a summary line when the
+ * terminal doesn't speak any supported protocol; the {@code --gui}
+ * flag opens a Swing window instead.
+ *
+ * @param input parsed command input
+ * @return always {@code null}
+ */
+ def img(CommandInput input) {
+ checkArgCount(input, [0, 1, 2, 3, 4, 5])
+ if (maybePrintHelp(input, '/img')) return
+ try {
+ Integer width = null
+ Integer height = null
+ boolean preserveAspect = true
+ boolean gui = false
+ Object positional = null
+ String positionalLabel = null
+ for (int i = 0; i < input.args().length; i++) {
+ String a = input.args()[i]
+ if (a == null) {
+ // JLine puts null into args() when a $var reference
resolves
+ // to null — usually because the variable isn't defined
yet.
+ throw new IllegalArgumentException(
+ '/img: variable reference resolved to null ' +
+ '(undefined or not yet assigned) — define
it first, e.g. ' +
+ "'panel =
ScatterPlot.of(...).canvas().panel()'")
+ }
+ if (a == '-w' || a == '--width') {
+ width = Integer.parseInt(input.args()[++i])
+ } else if (a.startsWith('--width=')) {
+ width = Integer.parseInt(a.substring('--width='.length()))
+ } else if (a == '--height') {
+ height = Integer.parseInt(input.args()[++i])
+ } else if (a.startsWith('--height=')) {
+ height =
Integer.parseInt(a.substring('--height='.length()))
Review Comment:
The `--height` branch unconditionally consumes the next token via
`input.args()[++i]`. If the user passes `--height` without a value (or as the
last argument), this will throw and surface as an internal exception. Please
add an explicit bounds/value check and throw a clear `IllegalArgumentException`
for the missing height value.
##########
subprojects/groovy-groovysh/src/test/groovy/org/apache/groovy/groovysh/commands/ImgTest.groovy:
##########
@@ -0,0 +1,151 @@
+/*
+ * 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.commands
+
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.io.TempDir
+
+import javax.imageio.ImageIO
+import javax.swing.JLabel
+import java.awt.image.BufferedImage
+import java.nio.file.Files
Review Comment:
Unused import: `java.nio.file.Files` is imported but not referenced anywhere
in this test file.
##########
subprojects/groovy-groovysh/src/spec/doc/groovysh.adoc:
##########
@@ -720,6 +720,75 @@ groovy> /imports
import java.util.concurrent.BlockingQueue
--------------
+[[GroovyShell-img]]
+==== `/img`
+
+The `/img` command displays an image inline using JLine's terminal-graphics
+support (Sixel, Kitty, or iTerm2 protocols, auto-detected). The argument is
+either a local file path, an `http(s)://` URL, or a Groovy variable
+reference using the standard `$` syntax.
+
+[source,jshell]
+--------------
+groovy> /img chart.png
+groovy> /img --width=80 https://example.com/diagram.png
+groovy> img = new java.awt.image.BufferedImage(200, 100, 1) // some
BufferedImage
+groovy> /img $img
+--------------
+
+When the argument resolves to a Groovy value (rather than a path), `/img`
+will accept any of:
+
+* a `java.awt.image.BufferedImage` — used as-is
+* a `java.awt.image.RenderedImage` — drawn into a `BufferedImage`
+* anything with a `createBufferedImage(int, int)` method (e.g.
+ `org.jfree.chart.JFreeChart`) — duck-typed, no compile-time dependency
+* anything with a `toBufferedImage(int, int)` method (e.g. Smile's
+ `smile.plot.swing.Figure`) — duck-typed sibling for libraries that
+ follow the `to…` rather than `create…` naming convention
+* a `javax.swing.JComponent` (e.g. Smile's `Canvas` and `MultiFigurePane`) —
+ laid out and painted to a `BufferedImage` at the requested size
+
+So once you've grabbed JFreeChart you can render charts in the REPL
+without saving to a file:
+
+image:assets/img/repl_img_jfreechart.png[Using /img with JFreeChart]
+
+Smile is similar — `Figure.toBufferedImage(int, int)` matches the second
+duck-type, so a fresh `Figure` can be handed straight to `/img`. The
+following draws the classic Iris scatter plot:
+
+image:assets/img/repl_img_smile.png[Using /img with Smile]
Review Comment:
These new images use `image:assets/img/...` while the rest of this document
uses `image:{reldir_groovysh}/assets/img/...`. Using the consistent
`{reldir_groovysh}` prefix here will avoid broken image links when the docs are
built from different base directories.
##########
subprojects/groovy-groovysh/src/spec/doc/groovysh.adoc:
##########
@@ -720,6 +720,75 @@ groovy> /imports
import java.util.concurrent.BlockingQueue
--------------
+[[GroovyShell-img]]
+==== `/img`
+
+The `/img` command displays an image inline using JLine's terminal-graphics
+support (Sixel, Kitty, or iTerm2 protocols, auto-detected). The argument is
+either a local file path, an `http(s)://` URL, or a Groovy variable
+reference using the standard `$` syntax.
+
+[source,jshell]
+--------------
+groovy> /img chart.png
+groovy> /img --width=80 https://example.com/diagram.png
+groovy> img = new java.awt.image.BufferedImage(200, 100, 1) // some
BufferedImage
+groovy> /img $img
+--------------
+
+When the argument resolves to a Groovy value (rather than a path), `/img`
+will accept any of:
+
+* a `java.awt.image.BufferedImage` — used as-is
+* a `java.awt.image.RenderedImage` — drawn into a `BufferedImage`
+* anything with a `createBufferedImage(int, int)` method (e.g.
+ `org.jfree.chart.JFreeChart`) — duck-typed, no compile-time dependency
+* anything with a `toBufferedImage(int, int)` method (e.g. Smile's
+ `smile.plot.swing.Figure`) — duck-typed sibling for libraries that
+ follow the `to…` rather than `create…` naming convention
+* a `javax.swing.JComponent` (e.g. Smile's `Canvas` and `MultiFigurePane`) —
+ laid out and painted to a `BufferedImage` at the requested size
+
+So once you've grabbed JFreeChart you can render charts in the REPL
+without saving to a file:
+
+image:assets/img/repl_img_jfreechart.png[Using /img with JFreeChart]
+
+Smile is similar — `Figure.toBufferedImage(int, int)` matches the second
+duck-type, so a fresh `Figure` can be handed straight to `/img`. The
+following draws the classic Iris scatter plot:
+
+image:assets/img/repl_img_smile.png[Using /img with Smile]
Review Comment:
These new images use `image:assets/img/...` while the rest of this document
uses `image:{reldir_groovysh}/assets/img/...`. Using the consistent
`{reldir_groovysh}` prefix here will avoid broken image links when the docs are
built from different base directories.
##########
subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyCommands.groovy:
##########
@@ -871,6 +1087,18 @@ class GroovyCommands extends JlineCommandRegistry
implements CommandRegistry {
])
}
+ private CmdDesc imgCmdDesc(String name) {
+ new CmdDesc([
+ new AttributedString("$name [OPTIONS] FILE_OR_URL"),
Review Comment:
The `/img` help/usage line says `FILE_OR_URL`, but the implementation and
docs also accept a Groovy variable reference (e.g. `/img $imgVar`) and other
non-string values resolved via `xargs`. Updating the usage synopsis to reflect
the supported inputs would make `--help` less misleading.
##########
subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyCommands.groovy:
##########
@@ -871,6 +1087,18 @@ class GroovyCommands extends JlineCommandRegistry
implements CommandRegistry {
])
}
+ private CmdDesc imgCmdDesc(String name) {
+ new CmdDesc([
+ new AttributedString("$name [OPTIONS] FILE_OR_URL"),
+ ], [], [
+ '-? --help' : doDescription('Displays
command help'),
+ '-w --width=COLS' : doDescription('Image width in
character columns'),
+ ' --height=ROWS' : doDescription('Image height in
character rows'),
+ '-p --no-preserve-aspect-ratio' : doDescription("Don't preserve
aspect ratio"),
Review Comment:
`--width`/`--height` are described here as character columns/rows, but for
generated-image inputs (`createBufferedImage`/`toBufferedImage`/`JComponent`)
the same options are interpreted as source-image pixels (as described later in
the user docs and implied by `dimsConsumedByGeneration`). Consider clarifying
the option descriptions (or adding a note) so `--help` matches the actual
behavior.
> Add /img command to groovysh for inline image and chart display
> ---------------------------------------------------------------
>
> Key: GROOVY-12003
> URL: https://issues.apache.org/jira/browse/GROOVY-12003
> Project: Groovy
> Issue Type: Improvement
> Reporter: Paul King
> Assignee: Paul King
> Priority: Major
>
--
This message was sent by Atlassian Jira
(v8.20.10#820010)