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 a628cf6918 GROOVY-12002: add MarkdownSlurper support to groovysh
a628cf6918 is described below
commit a628cf6918863e484da57e55e62cf52d9e8e9eeb
Author: Paul King <[email protected]>
AuthorDate: Sat May 9 11:39:17 2026 +1000
GROOVY-12002: add MarkdownSlurper support to groovysh
---
subprojects/groovy-groovysh/build.gradle | 1 +
.../groovy/groovysh/jline/GroovyCommands.groovy | 11 ++++-
.../src/main/resources/nanorc/markdown.nanorc | 56 ++++++++++++++++++++++
.../groovy-groovysh/src/spec/doc/groovysh.adoc | 5 +-
.../groovy/groovysh/commands/SlurpTest.groovy | 20 ++++++++
5 files changed, 91 insertions(+), 2 deletions(-)
diff --git a/subprojects/groovy-groovysh/build.gradle
b/subprojects/groovy-groovysh/build.gradle
index 9469ea0a60..fa0e7b06fc 100644
--- a/subprojects/groovy-groovysh/build.gradle
+++ b/subprojects/groovy-groovysh/build.gradle
@@ -35,6 +35,7 @@ dependencies {
implementation projects.groovyNio
testImplementation projects.groovyTest
testImplementation projects.groovyCsv // for /slurp .csv default
coverage
+ testImplementation projects.groovyMarkdown // for /slurp .md default
coverage
testImplementation projects.groovyTestJunit6 // for @ForkedJvm in CSV
fallback tests
implementation "net.java.dev.jna:jna:${versions.jna}"
implementation "org.jline:jansi:${versions.jline}"
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 7030a7685e..2a9e633089 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
@@ -87,6 +87,7 @@ class GroovyCommands extends JlineCommandRegistry implements
CommandRegistry {
'YAML' : 'groovy.yaml.YamlSlurper',
'XML' : 'groovy.xml.XmlParser',
'TOML' : 'groovy.toml.TomlSlurper',
+ 'MARKDOWN' : 'groovy.markdown.MarkdownSlurper',
]
/**
@@ -463,6 +464,8 @@ class GroovyCommands extends JlineCommandRegistry
implements CommandRegistry {
format = 'PROPERTIES'
} else if (ext.equalsIgnoreCase('csv')) {
format = 'CSV'
+ } else if (ext.equalsIgnoreCase('md') ||
ext.equalsIgnoreCase('markdown')) {
+ format = 'MARKDOWN'
} else if (ext.equalsIgnoreCase('txt') ||
ext.equalsIgnoreCase('text')) {
format = 'TEXT'
}
@@ -947,7 +950,13 @@ class GroovyCommands extends JlineCommandRegistry
implements CommandRegistry {
private List<Completer> slurpCompleter(String command) {
for (OptDesc o in compileOptDescs(command)) {
if (o.shortOption()?.equals('-f')) {
- o.valueCompleter = new StringsCompleter('JSON', 'GROOVY',
'NONE', 'TEXT', 'YAML', 'TOML', 'XML')
+ // Keep completion in sync with the formats /slurp actually
+ // accepts: parser-style ones come from `slurpers`; the rest
+ // are handled directly in slurpcmd's extension branches.
+ // Deriving from `slurpers.keySet()` prevents new entries
+ // (e.g. MARKDOWN, TOML) from drifting out of the completer.
+ def formats = (['CSV', 'GROOVY', 'JSON', 'NONE', 'PROPERTIES',
'TEXT'] + slurpers.keySet()).toSet().sort()
+ o.valueCompleter = new StringsCompleter(formats)
break
}
}
diff --git
a/subprojects/groovy-groovysh/src/main/resources/nanorc/markdown.nanorc
b/subprojects/groovy-groovysh/src/main/resources/nanorc/markdown.nanorc
new file mode 100644
index 0000000000..7e87c94333
--- /dev/null
+++ b/subprojects/groovy-groovysh/src/main/resources/nanorc/markdown.nanorc
@@ -0,0 +1,56 @@
+# 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.
+
+syntax "Markdown" "\.(md|markdown)$"
+
+# Blockquotes
+COMMENT: "^>.*$"
+
+# Lists: -, *, + (unordered); 1. 2. ... (ordered)
+OPERATOR: "^[[:space:]]*[\*\+\-][[:space:]]"
"^[[:space:]]*[0-9]+\.[[:space:]]"
+
+# Horizontal rules: --- *** ___ (with optional spaces)
+SECTION: "^[[:space:]]*([\*\-_][[:space:]]?){3,}[[:space:]]*$"
+
+# Setext-style heading underlines (=== or --- on their own line)
+SECTION: "^[=\-]+[[:space:]]*$"
+
+# ATX headings: # H1 through ###### H6
+TITLE: "^#{1,6}[[:space:]].*$"
+
+# Emphasis (italic): *text* or _text_
+VARIABLE: "(\*[^*]+\*)" "(_[^_]+_)"
+
+# Strong emphasis (bold): **text** or __text__ (overrides italic on overlap)
+KEYWORD: "(\*\*[^*]+\*\*)" "(__[^_]+__)"
+
+# Strike-through: ~~text~~
+WARNING: "~~[^~]+~~"
+
+# Inline code: `code`
+STRING: "`[^`]+`"
+
+# Links: [text](url)
+LINK: "\[[^\]]+\]\([^)]+\)"
+# Images: 
+LINK: "!\[[^\]]*\]\([^)]+\)"
+# Bare URLs
+LINK: "\<https?://[^[:space:])>]+"
+
+# GFM tables (lines bracketed by |)
+ATTRIBUTE: "^[[:space:]]*\|.*\|[[:space:]]*$"
+
+# Fenced code blocks (triple backticks)
+$BLOCK_COMMENT: "```, ```"
diff --git a/subprojects/groovy-groovysh/src/spec/doc/groovysh.adoc
b/subprojects/groovy-groovysh/src/spec/doc/groovysh.adoc
index e53a5522c4..6b0bb47621 100644
--- a/subprojects/groovy-groovysh/src/spec/doc/groovysh.adoc
+++ b/subprojects/groovy-groovysh/src/spec/doc/groovysh.adoc
@@ -1043,7 +1043,7 @@ image:{reldir_groovysh}/assets/img/repl_show.png[Usage of
the /show command, wid
Slurp files to shared variables. Groovy has a bunch of slurpers for
various formats like XML, JSON, YAML, etc. You can use those in your code
if you like, but the `/slurp` command can be a convenience shortcut.
-It supports most of the common formats, including JSON, XML, YAML, CSV, TOML
and property files.
+It supports most of the common formats, including JSON, XML, YAML, CSV, TOML,
Markdown and property files.
image:{reldir_groovysh}/assets/img/repl_slurp.png[Usage of the /slurp command,
width=80%]
@@ -1070,6 +1070,9 @@ detail which may change in the future, the current
behavior is as follows:
| PROPERTIES | Returns a Properties file, which is a Map-like object.
| CSV | Uses Apache Commons CSV if on the classpath. Assumes a header row
which is used to
create a list (the rows) of maps from column name to value.
+| MARKDOWN | Uses MarkdownSlurper from `groovy-markdown` if on the classpath.
Returns a
+`MarkdownDocument` (an `Iterable<Map<String, Object>>` of structured
elements). Triggered
+by `.md` and `.markdown` extensions.
| TEXT | Reads the file as raw lines (or argument as a line).
| NONE | If you want the raw text rather than parsed content.
|===
diff --git
a/subprojects/groovy-groovysh/src/test/groovy/org/apache/groovy/groovysh/commands/SlurpTest.groovy
b/subprojects/groovy-groovysh/src/test/groovy/org/apache/groovy/groovysh/commands/SlurpTest.groovy
index 27f0f89a6a..2d29dbcc1a 100644
---
a/subprojects/groovy-groovysh/src/test/groovy/org/apache/groovy/groovysh/commands/SlurpTest.groovy
+++
b/subprojects/groovy-groovysh/src/test/groovy/org/apache/groovy/groovysh/commands/SlurpTest.groovy
@@ -58,6 +58,26 @@ class SlurpTest extends SystemTestSupport {
assert value.version == '4.x'
}
+ @Test
+ void slurpMarkdownProducesIterableOfElements() {
+ // Requires groovy.markdown.MarkdownSlurper; supplied via the
+ // testImplementation projects.groovyMarkdown dependency.
+ Path file = Files.writeString(tmp.resolve('notes.md'),
+ "# Title\n\nA paragraph.\n\n- item one\n- item two\n")
+ system.execute("doc = /slurp ${forwardSlashes(file)}")
+ def doc = console.getVariable('doc')
+ assert doc != null
+ // MarkdownDocument is Iterable<Map<String,Object>>; toList lets us
+ // assert without pinning to the exact element-type names (those
+ // belong to groovy-markdown's API and may evolve).
+ def elements = doc.toList()
+ assert elements.size() >= 2
+ def joined = elements.collect { it.values().toString() }.join(' ')
+ assert joined.contains('Title')
+ assert joined.contains('A paragraph.')
+ assert joined.contains('item one')
+ }
+
@Test
void slurpCsvProducesListOfMaps() {
// Requires groovy.csv.CsvSlurper on the classpath; supplied here via