markdown: add a toolcontext attribute
[nit.git] / src / markdown.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Transform Nit verbatim documentation into HTML
16 module markdown
17
18 import parser
19 import html
20
21 # The class that does the convertion from a `ADoc` to HTML
22 private class Doc2Mdwn
23 var toolcontext: ToolContext
24
25 # The lines of the current code block, empty is no current code block
26 var curblock = new Array[String]
27
28 fun work(ndoc: ADoc): HTMLTag
29 do
30 var root = new HTMLTag("div")
31 root.add_class("nitdoc")
32
33 # Indent level of the previous line
34 var lastindent = 0
35
36 # Indent level of the current line
37 var indent = 0
38
39 # The current element (p, li, etc.) if any
40 var n: nullable HTMLTag = null
41
42 # The current ul element (if any)
43 var ul: nullable HTMLTag = null
44
45 # Local variable to benefit adaptive typing
46 for c in ndoc.n_comment do
47 # Remove the starting `#`
48 var text = c.text.substring_from(1)
49
50 # Count the number of spaces
51 lastindent = indent
52 indent = 0
53 while text.length > indent and text[indent] == ' ' do indent += 1
54
55 # Is codeblock? Then just collect them
56 if indent > 4 then
57 var part = text.substring_from(4)
58 curblock.add(part)
59 continue
60 end
61
62 # Was a codblock just before the current line ?
63 close_codeblock(n or else root)
64
65 # Cleanup the string
66 text = text.trim
67
68 # The HTML node of the last line, so we know if we continue the same block
69 var old = n
70
71 # No line or loss of indentation: reset
72 if text.is_empty or indent < lastindent then
73 n = null
74 ul = null
75 if text.is_empty then continue
76 end
77
78 # Special first word: new paragraph
79 if text.has_prefix("TODO") or text.has_prefix("FIXME") then
80 n = new HTMLTag("p")
81 root.add n
82 n.add_class("todo")
83 ul = null
84 else if text.has_prefix("REQUIRE") or text.has_prefix("Require") or text.has_prefix("ENSURE") or text.has_prefix("Ensure") then
85 n = new HTMLTag("p")
86 root.add n
87 n.add_class("contract")
88 ul = null
89 end
90
91 # List
92 if text.has_prefix("* ") or text.has_prefix("- ") then
93 text = text.substring_from(1).trim
94 if ul == null then
95 ul = new HTMLTag("ul")
96 root.add ul
97 end
98 n = new HTMLTag("li")
99 ul.add(n)
100 end
101
102 # Nothing? then paragraph
103 if n == null then
104 n = new HTMLTag("p")
105 root.add n
106 ul = null
107 end
108
109 if old == n then
110 # Because spaces and `\n` where trimmed
111 n.append("\n")
112 end
113
114 process_line(n, text)
115
116 # Special case, the fist line is the synopsys and is in its own paragraph
117 if c == ndoc.n_comment.first then
118 n.add_class("synopsys")
119 n = null
120 end
121 end
122
123 # If the codeblock was the last code sequence
124 close_codeblock(n or else root)
125
126 return root
127 end
128
129 fun short_work(ndoc: ADoc): HTMLTag
130 do
131 var text = ndoc.n_comment.first.text.substring_from(1)
132 var n = new HTMLTag("span")
133 n.add_class("synopsys")
134 n.add_class("nitdoc")
135 process_line(n, text)
136 return n
137 end
138
139 fun process_line(n: HTMLTag, text: String)
140 do
141 # Loosly detect code parts
142 var parts = text.split("`")
143
144 # Process each code parts, thus alternate between text and code
145 var is_text = true
146 for part in parts do
147 if is_text then
148 # Text part
149 n.append part
150 else
151 # Code part
152 var n2 = new HTMLTag("code")
153 n.add(n2)
154 process_code(n2, part)
155 end
156 is_text = not is_text
157 end
158 end
159
160 fun close_codeblock(root: HTMLTag)
161 do
162 # Is there a codeblock to manage?
163 if not curblock.is_empty then
164 var n = new HTMLTag("pre")
165 root.add(n)
166 var btext = curblock.to_s
167 process_code(n, btext)
168 curblock.clear
169 end
170 end
171
172 fun process_code(n: HTMLTag, text: String)
173 do
174 n.append text
175 end
176 end
177
178 redef class ADoc
179 # Build a `<div>` element that contains the full documentation in HTML
180 fun full_markdown: HTMLTag
181 do
182 var tc = new ToolContext
183 var d2m = new Doc2Mdwn(tc)
184 return d2m.work(self)
185 end
186
187 # Build a `<span>` element that contains the synopsys in HTML
188 fun short_markdown: HTMLTag
189 do
190 var tc = new ToolContext
191 var d2m = new Doc2Mdwn(tc)
192 return d2m.short_work(self)
193 end
194 end