Skip to content

Commit b2e63b3

Browse files
committed
markdown: create XetodocChatper/Heading class to unify logic for how markdown chapter headings are parsed
1 parent 216b15d commit b2e63b3

1 file changed

Lines changed: 128 additions & 0 deletions

File tree

src/markdown/fan/ext/xetodoc/Xetodoc.fan

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,134 @@ class Xetodoc
122122
// }
123123
}
124124

125+
**************************************************************************
126+
** XetodocChapter
127+
**************************************************************************
128+
129+
**
130+
** XetodocChapter is a simple DOM for the chapter meta and headings tree
131+
** that can be used b/w compilerDoc and xetodoc
132+
**
133+
@NoDoc
134+
class XetodocChapter
135+
{
136+
** Parse a markdown file as a chapter into its meta and headings
137+
static XetodocChapter parse(Str file)
138+
{
139+
meta := Str:Str[:] { ordered = true }
140+
list := XetodocHeading[,]
141+
map := Str:XetodocHeading[:]
142+
143+
// read lines
144+
lines := file.splitLines
145+
146+
// check leading comment for title: xxxx
147+
if (lines.first.trim == "<!--")
148+
{
149+
lines.eachWhile |line|
150+
{
151+
line = line.trim
152+
if (line == "-->") return "break"
153+
colon := line.index(":")
154+
if (colon == null) return null
155+
n := line[0..<colon].trim
156+
v := line[colon+1..-1].trim
157+
meta[n] = v
158+
return null
159+
}
160+
}
161+
162+
// lazily parse just heading lines
163+
proc := HeadingProcessor()
164+
l := XetodocHeading[,]
165+
lines.each |line|
166+
{
167+
if (!line.startsWith("#")) return
168+
169+
// compute level
170+
level := 0
171+
while (level+1 < line.size && line[level] == '#') level++
172+
173+
// create heading instance
174+
text := line[level..-1].trim
175+
anchor := proc.toAnchor(text)
176+
h := XetodocHeading(level, text, anchor)
177+
178+
// add to accumulator collections
179+
list.add(h)
180+
map[h.anchor] = h
181+
}
182+
183+
// now organize into a tree
184+
top := XetodocHeading[,]
185+
list.each |h, i|
186+
{
187+
if (i == 1) { top.add(h); return }
188+
for (j := i-1; j >= 0; --j)
189+
{
190+
if (list[j].level < h.level)
191+
{
192+
parent := list[j]
193+
parent.children.add(h)
194+
return
195+
}
196+
}
197+
top.add(h)
198+
}
199+
200+
return make {
201+
it.meta = meta
202+
it.top = top
203+
it.byId = map
204+
}
205+
}
206+
207+
private new make(|This| f) { f(this) }
208+
209+
Str:Str meta
210+
211+
XetodocHeading[] top
212+
213+
Str:XetodocHeading byId
214+
215+
Void dump()
216+
{
217+
echo("#####")
218+
echo(meta.join("\n"))
219+
echo("---")
220+
top.each |x| { x.dump(0) }
221+
}
222+
223+
static Void main(Str[] args)
224+
{
225+
parse(args[0].toUri.toFile.readAllStr).dump
226+
}
227+
}
228+
229+
@NoDoc
230+
class XetodocHeading
231+
{
232+
new make(Int level, Str text, Str anchor)
233+
{
234+
this.level = level
235+
this.text = text
236+
this.anchor = anchor
237+
}
238+
239+
const Int level
240+
const Str text
241+
const Str anchor
242+
XetodocHeading[] children := [,]
243+
244+
override Str toStr() { "$level | $text.toCode [#$anchor]" }
245+
246+
Void dump(Int indent)
247+
{
248+
echo(Str.spaces(indent) + this)
249+
children.each |kid| { kid.dump(indent+2) }
250+
}
251+
}
252+
125253
**************************************************************************
126254
** WarnProcessor
127255
**************************************************************************

0 commit comments

Comments
 (0)