Skip to content

Commit a628cf6

Browse files
committed
GROOVY-12002: add MarkdownSlurper support to groovysh
1 parent b6169df commit a628cf6

5 files changed

Lines changed: 91 additions & 2 deletions

File tree

subprojects/groovy-groovysh/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ dependencies {
3535
implementation projects.groovyNio
3636
testImplementation projects.groovyTest
3737
testImplementation projects.groovyCsv // for /slurp .csv default coverage
38+
testImplementation projects.groovyMarkdown // for /slurp .md default coverage
3839
testImplementation projects.groovyTestJunit6 // for @ForkedJvm in CSV fallback tests
3940
implementation "net.java.dev.jna:jna:${versions.jna}"
4041
implementation "org.jline:jansi:${versions.jline}"

subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyCommands.groovy

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ class GroovyCommands extends JlineCommandRegistry implements CommandRegistry {
8787
'YAML' : 'groovy.yaml.YamlSlurper',
8888
'XML' : 'groovy.xml.XmlParser',
8989
'TOML' : 'groovy.toml.TomlSlurper',
90+
'MARKDOWN' : 'groovy.markdown.MarkdownSlurper',
9091
]
9192

9293
/**
@@ -463,6 +464,8 @@ class GroovyCommands extends JlineCommandRegistry implements CommandRegistry {
463464
format = 'PROPERTIES'
464465
} else if (ext.equalsIgnoreCase('csv')) {
465466
format = 'CSV'
467+
} else if (ext.equalsIgnoreCase('md') || ext.equalsIgnoreCase('markdown')) {
468+
format = 'MARKDOWN'
466469
} else if (ext.equalsIgnoreCase('txt') || ext.equalsIgnoreCase('text')) {
467470
format = 'TEXT'
468471
}
@@ -947,7 +950,13 @@ class GroovyCommands extends JlineCommandRegistry implements CommandRegistry {
947950
private List<Completer> slurpCompleter(String command) {
948951
for (OptDesc o in compileOptDescs(command)) {
949952
if (o.shortOption()?.equals('-f')) {
950-
o.valueCompleter = new StringsCompleter('JSON', 'GROOVY', 'NONE', 'TEXT', 'YAML', 'TOML', 'XML')
953+
// Keep completion in sync with the formats /slurp actually
954+
// accepts: parser-style ones come from `slurpers`; the rest
955+
// are handled directly in slurpcmd's extension branches.
956+
// Deriving from `slurpers.keySet()` prevents new entries
957+
// (e.g. MARKDOWN, TOML) from drifting out of the completer.
958+
def formats = (['CSV', 'GROOVY', 'JSON', 'NONE', 'PROPERTIES', 'TEXT'] + slurpers.keySet()).toSet().sort()
959+
o.valueCompleter = new StringsCompleter(formats)
951960
break
952961
}
953962
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one or more
2+
# contributor license agreements. See the NOTICE file distributed with
3+
# this work for additional information regarding copyright ownership.
4+
# The ASF licenses this file to You under the Apache License, Version 2.0
5+
# (the "License"); you may not use this file except in compliance with
6+
# the License. You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
syntax "Markdown" "\.(md|markdown)$"
17+
18+
# Blockquotes
19+
COMMENT: "^>.*$"
20+
21+
# Lists: -, *, + (unordered); 1. 2. ... (ordered)
22+
OPERATOR: "^[[:space:]]*[\*\+\-][[:space:]]" "^[[:space:]]*[0-9]+\.[[:space:]]"
23+
24+
# Horizontal rules: --- *** ___ (with optional spaces)
25+
SECTION: "^[[:space:]]*([\*\-_][[:space:]]?){3,}[[:space:]]*$"
26+
27+
# Setext-style heading underlines (=== or --- on their own line)
28+
SECTION: "^[=\-]+[[:space:]]*$"
29+
30+
# ATX headings: # H1 through ###### H6
31+
TITLE: "^#{1,6}[[:space:]].*$"
32+
33+
# Emphasis (italic): *text* or _text_
34+
VARIABLE: "(\*[^*]+\*)" "(_[^_]+_)"
35+
36+
# Strong emphasis (bold): **text** or __text__ (overrides italic on overlap)
37+
KEYWORD: "(\*\*[^*]+\*\*)" "(__[^_]+__)"
38+
39+
# Strike-through: ~~text~~
40+
WARNING: "~~[^~]+~~"
41+
42+
# Inline code: `code`
43+
STRING: "`[^`]+`"
44+
45+
# Links: [text](url)
46+
LINK: "\[[^\]]+\]\([^)]+\)"
47+
# Images: ![alt](url)
48+
LINK: "!\[[^\]]*\]\([^)]+\)"
49+
# Bare URLs
50+
LINK: "\<https?://[^[:space:])>]+"
51+
52+
# GFM tables (lines bracketed by |)
53+
ATTRIBUTE: "^[[:space:]]*\|.*\|[[:space:]]*$"
54+
55+
# Fenced code blocks (triple backticks)
56+
$BLOCK_COMMENT: "```, ```"

subprojects/groovy-groovysh/src/spec/doc/groovysh.adoc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1043,7 +1043,7 @@ image:{reldir_groovysh}/assets/img/repl_show.png[Usage of the /show command, wid
10431043
Slurp files to shared variables. Groovy has a bunch of slurpers for
10441044
various formats like XML, JSON, YAML, etc. You can use those in your code
10451045
if you like, but the `/slurp` command can be a convenience shortcut.
1046-
It supports most of the common formats, including JSON, XML, YAML, CSV, TOML and property files.
1046+
It supports most of the common formats, including JSON, XML, YAML, CSV, TOML, Markdown and property files.
10471047
10481048
image:{reldir_groovysh}/assets/img/repl_slurp.png[Usage of the /slurp command, width=80%]
10491049
@@ -1070,6 +1070,9 @@ detail which may change in the future, the current behavior is as follows:
10701070
| PROPERTIES | Returns a Properties file, which is a Map-like object.
10711071
| CSV | Uses Apache Commons CSV if on the classpath. Assumes a header row which is used to
10721072
create a list (the rows) of maps from column name to value.
1073+
| MARKDOWN | Uses MarkdownSlurper from `groovy-markdown` if on the classpath. Returns a
1074+
`MarkdownDocument` (an `Iterable<Map<String, Object>>` of structured elements). Triggered
1075+
by `.md` and `.markdown` extensions.
10731076
| TEXT | Reads the file as raw lines (or argument as a line).
10741077
| NONE | If you want the raw text rather than parsed content.
10751078
|===

subprojects/groovy-groovysh/src/test/groovy/org/apache/groovy/groovysh/commands/SlurpTest.groovy

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,26 @@ class SlurpTest extends SystemTestSupport {
5858
assert value.version == '4.x'
5959
}
6060

61+
@Test
62+
void slurpMarkdownProducesIterableOfElements() {
63+
// Requires groovy.markdown.MarkdownSlurper; supplied via the
64+
// testImplementation projects.groovyMarkdown dependency.
65+
Path file = Files.writeString(tmp.resolve('notes.md'),
66+
"# Title\n\nA paragraph.\n\n- item one\n- item two\n")
67+
system.execute("doc = /slurp ${forwardSlashes(file)}")
68+
def doc = console.getVariable('doc')
69+
assert doc != null
70+
// MarkdownDocument is Iterable<Map<String,Object>>; toList lets us
71+
// assert without pinning to the exact element-type names (those
72+
// belong to groovy-markdown's API and may evolve).
73+
def elements = doc.toList()
74+
assert elements.size() >= 2
75+
def joined = elements.collect { it.values().toString() }.join(' ')
76+
assert joined.contains('Title')
77+
assert joined.contains('A paragraph.')
78+
assert joined.contains('item one')
79+
}
80+
6181
@Test
6282
void slurpCsvProducesListOfMaps() {
6383
// Requires groovy.csv.CsvSlurper on the classpath; supplied here via

0 commit comments

Comments
 (0)