You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2025/08/20 11:50:41 UTC
(groovy) 01/02: GROOVY-8162: Update Groovysh to JLine3 (further /doc improvements)
This is an automated email from the ASF dual-hosted git repository.
paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit a94a90e3c39b36573dc01da83c3c41d28bab3594
Author: Paul King <pa...@asert.com.au>
AuthorDate: Wed Aug 20 21:15:07 2025 +1000
GROOVY-8162: Update Groovysh to JLine3 (further /doc improvements)
---
.../groovy/org/apache/groovy/groovysh/Main.groovy | 8 +-
.../groovy/groovysh/jline/GroovyCommands.groovy | 107 +++++++++++++++++++++
.../groovysh/jline/GroovyConsoleEngine.groovy | 2 +-
.../apache/groovy/groovysh/util/DocFinder.groovy | 25 ++++-
4 files changed, 135 insertions(+), 7 deletions(-)
diff --git a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/Main.groovy b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/Main.groovy
index 3ca95a99fd..ffaa9a217c 100644
--- a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/Main.groovy
+++ b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/Main.groovy
@@ -363,7 +363,9 @@ class Main {
// ScriptEngine and command registries
GroovyEngine scriptEngine = new GroovyEngine()
scriptEngine.put('ROOT', rootURL.toString())
- scriptEngine.put('CONSOLE_OPTIONS', [:])
+ if (!scriptEngine.hasVariable('CONSOLE_OPTIONS')) {
+ scriptEngine.put('CONSOLE_OPTIONS', [:])
+ }
def interpreterMode = Boolean.parseBoolean(System.getProperty("groovysh.interpreterMode", "true"))
scriptEngine.put('GROOVYSH_OPTIONS', [interpreterMode: interpreterMode])
Printer printer = new DefaultPrinter(scriptEngine, configPath)
@@ -436,9 +438,9 @@ class Main {
KeyMap<Binding> keyMap = reader.keyMaps.get("main")
keyMap.bind(new Reference(Widgets.TAILTIP_TOGGLE), KeyMap.alt("s"))
keyMap.bind(new Reference(Widgets.AUTOSUGGEST_TOGGLE), KeyMap.alt("v"))
- def init = configPath.getUserConfig('groovysh_init')
+ def init = configPath.getUserConfig('groovysh_init.groovy')
if (init) {
- systemRegistry.setConsoleOption() // initialize(configPath.getUserConfig('groovysh_init').toFile())
+ systemRegistry.initialize(init.toFile())
}
if (options.q) {
diff --git a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyCommands.groovy b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyCommands.groovy
index a4f23ca4a6..1a6f630c22 100644
--- a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyCommands.groovy
+++ b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyCommands.groovy
@@ -43,6 +43,7 @@ import org.jline.reader.impl.completer.NullCompleter
import org.jline.reader.impl.completer.StringsCompleter
import org.jline.utils.AttributedString
+import java.awt.Desktop
import java.awt.event.ActionListener
import java.lang.reflect.Method
import java.nio.charset.Charset
@@ -64,6 +65,7 @@ class GroovyCommands extends JlineCommandRegistry implements CommandRegistry {
private final Map<String, Tuple4<Function, Function, Function, List<String>>> commands = [
'/inspect' : new Tuple4<>(this::inspect, this::inspectCompleter, this::inspectCmdDesc, ['display/browse object info on terminal/object browser']),
'/console' : new Tuple4<>(this::console, this::defCompleter, this::defCmdDesc, ['launch Groovy console']),
+ '/doc' : new Tuple4<>(this::doc, this::defCompleter, this::defCmdDesc, ['display documentation']),
'/grab' : new Tuple4<>(this::grab, this::grabCompleter, this::grabCmdDesc, ['add maven repository dependencies to classpath']),
'/classloader' : new Tuple4<>(this::classLoader, this::classloaderCompleter, this::classLoaderCmdDesc, ['display/manage Groovy classLoader data']),
'/imports' : new Tuple4<>(this::importsCommand, this::importsCompleter, this::nameDeleteCmdDesc, ['show/delete import statements']),
@@ -231,6 +233,111 @@ class GroovyCommands extends JlineCommandRegistry implements CommandRegistry {
loadFile(engine, workDir.get().resolve(arg).toFile(), merge)
}
+ private static final String VAR_CONSOLE_OPTIONS = "CONSOLE_OPTIONS"
+
+ private Map<String, Object> consoleOption(String key) {
+ def opts = engine.hasVariable(VAR_CONSOLE_OPTIONS)
+ ? (Map<String, Object>) engine.get(VAR_CONSOLE_OPTIONS)
+ : new HashMap<>()
+ opts[key]
+ }
+
+ def doc(CommandInput input) {
+ def usage = new String[]{
+ "/doc - open document on browser",
+ "Usage: /doc [OBJECT]",
+ " -? --help Displays command help"
+ }
+ try {
+ parseOptions(usage, input.xargs());
+ if (input.xargs().length == 0) {
+ return null
+ }
+ if (!Desktop.isDesktopSupported()) {
+ throw new IllegalStateException("Desktop is not supported!")
+ }
+ Map<String, Object> docs
+ try {
+ docs = consoleOption("docs")
+ } catch (Exception e) {
+ Exception exception = new IllegalStateException("Bad documents configuration!")
+ exception.addSuppressed(e)
+ throw exception
+ }
+ if (docs == null) {
+ throw new IllegalStateException("No documents configuration!")
+ }
+ boolean done = false
+ Object arg = input.xargs()[0]
+ if (arg instanceof String) {
+ def addresses = []
+ addresses += docs.get(input.args()[0])
+ addresses.each { address ->
+ if (address != null) {
+ done = true
+ if (urlExists(address)) {
+ Desktop.getDesktop().browse(new URI(address))
+ } else {
+ throw new IllegalArgumentException("Document not found: " + address)
+ }
+ }
+ }
+ }
+ if (!done) {
+ String name
+ if (arg instanceof String && ((String) arg).matches("([a-z]+\\.)+[A-Z][a-zA-Z]+")) {
+ name = (String) arg
+ } else {
+ name = arg.getClass().getCanonicalName()
+ }
+ name = name.replaceAll("\\.", "/") + ".html"
+ Object doc = null
+ for (Map.Entry<String, Object> entry : docs.entrySet()) {
+ if (name.matches(entry.getKey())) {
+ doc = entry.getValue()
+ break
+ }
+ }
+ if (doc == null) {
+ throw new IllegalArgumentException("No document configuration for " + name)
+ }
+ String url = name
+ if (doc instanceof Collection) {
+ for (Object o : (Collection<?>) doc) {
+ url = o + name
+ if (urlExists(url)) {
+ Desktop.getDesktop().browse(new URI(url))
+ done = true
+ }
+ }
+ } else {
+ url = doc + name
+ if (urlExists(url)) {
+ Desktop.getDesktop().browse(new URI(url))
+ done = true
+ }
+ }
+ if (!done) {
+ throw new IllegalArgumentException("Document not found: " + url)
+ }
+ }
+ } catch (Exception e) {
+ saveException(e)
+ }
+ return null
+ }
+
+ private boolean urlExists(String weburl) {
+ try {
+ URL url = URI.create(weburl).toURL()
+ HttpURLConnection huc = (HttpURLConnection) url.openConnection()
+ huc.setRequestMethod("HEAD")
+ return huc.getResponseCode() == HttpURLConnection.HTTP_OK
+ } catch (Exception ignore) {
+ return false
+ }
+ }
+
def slurpcmd(CommandInput input) {
checkArgCount(input, [0, 1, 2, 3, 4])
if (maybePrintHelp(input, '/slurp')) return
diff --git a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyConsoleEngine.groovy b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyConsoleEngine.groovy
index 15cad9ebb1..4c1f938888 100644
--- a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyConsoleEngine.groovy
+++ b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyConsoleEngine.groovy
@@ -31,7 +31,7 @@ class GroovyConsoleEngine extends ConsoleEngineImpl {
private final Printer printer
GroovyConsoleEngine(ScriptEngine engine, Printer printer, Supplier<Path> workDir, ConfigurationPath configPath, LineReader reader) {
- super(Command.values().toSet() - Command.SLURP, engine, printer, workDir, configPath)
+ super(Command.values().toSet() - Command.SLURP - Command.DOC, engine, printer, workDir, configPath)
this.printer = printer
setLineReader(reader)
commandNames().each{ name -> rename(Command."${name.toUpperCase()}", "/$name") }
diff --git a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/util/DocFinder.groovy b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/util/DocFinder.groovy
index 83fb135a9d..c77a560a41 100644
--- a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/util/DocFinder.groovy
+++ b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/util/DocFinder.groovy
@@ -18,11 +18,25 @@
*/
package org.apache.groovy.groovysh.util
-// TODO previous version had browseWithAWT as potential fallback if no native browser found.
-// It also provided a list of URLs including GDK links. It would be nice to give those as options (or fire them all off).
class DocFinder extends HashMap<String, Object> {
+ private static boolean exists(String url) {
+ try {
+ (url.toURL().openConnection() as HttpURLConnection).with {
+ requestMethod = 'HEAD'
+ connectTimeout = 5000
+ readTimeout = 5000
+ return responseCode in 200..399
+ }
+ } catch (Exception e) {
+ return false
+ }
+ }
+
@Override
Object get(Object key) {
+ if (containsKey(key)) {
+ return super.get(key)
+ }
def groovyVersion = GroovySystem.getVersion()
Class clazz = Eval.me("${key}")
def name = clazz.name
@@ -32,6 +46,11 @@ class DocFinder extends HashMap<String, Object> {
}
def majorVersion = System.getProperty('java.version').tokenize('.')[0]
def module = clazz.module?.name ?: 'java.base'
- "https://docs.oracle.com/en/java/javase/${majorVersion}/docs/api/${module}/${path}".toString()
+ def result = ["https://docs.oracle.com/en/java/javase/${majorVersion}/docs/api/${module}/${path}".toString()]
+ def gdk = "https://docs.groovy-lang.org/latest/html/groovy-jdk/${path}"
+ if (exists(gdk)) {
+ result << gdk.toString()
+ }
+ result
}
}