@@ -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