doc/commands: introduce docdown related commands
[nit.git] / src / doc / commands / commands_docdown.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 # Doc down related queries
16 module commands_docdown
17
18 import commands::commands_parser
19 import commands::commands_html
20
21 intrude import doc_down
22 intrude import markdown::wikilinks
23
24 # Retrieve the MDoc summary
25 #
26 # List all MarkdownHeading found and their ids.
27 class CmdSummary
28 super CmdComment
29
30 # Markdown processor used to parse the headlines
31 var markdown_processor: nullable MarkdownProcessor = null is optional, writable
32
33 # Resulting summary
34 #
35 # Associates each headline to its id.
36 var summary: nullable ArrayMap[String, HeadLine] = null is optional, writable
37
38 redef fun init_command do
39 var res = super
40 if not res isa CmdSuccess then return res
41 var mentity = self.mentity.as(not null)
42
43 var markdown_processor = self.markdown_processor
44 if markdown_processor == null then
45 markdown_processor = new MarkdownProcessor
46 self.markdown_processor = markdown_processor
47 end
48
49 var mdoc = self.mdoc
50 if mdoc == null then
51 mdoc = if fallback then mentity.mdoc_or_fallback else mentity.mdoc
52 self.mdoc = mdoc
53 end
54 if mdoc == null then return new WarningNoMDoc(mentity)
55
56 markdown_processor.process(mdoc.md_documentation.write_to_string)
57
58 var summary = new ArrayMap[String, HeadLine]
59 summary.add_all markdown_processor.decorator.headlines
60 self.summary = summary
61 return res
62 end
63 end
64
65 # Custom Markdown processor able to process doc commands
66 class CmdDecorator
67 super NitdocDecorator
68
69 redef type PROCESSOR: CmdMarkdownProcessor
70
71 # View used by wikilink commands to find model entities
72 var view: ModelView
73
74 redef fun add_span_code(v, buffer, from, to) do
75 var text = new FlatBuffer
76 buffer.read(text, from, to)
77 var name = text.write_to_string
78 name = name.replace("nullable ", "")
79 var mentity = try_find_mentity(view, name)
80 if mentity == null then
81 super
82 else
83 v.add "<code>"
84 v.emit_text mentity.html_link.write_to_string
85 v.add "</code>"
86 end
87 end
88
89 private fun try_find_mentity(view: ModelView, text: String): nullable MEntity do
90 var mentity = view.mentity_by_full_name(text)
91 if mentity != null then return mentity
92
93 var mentities = view.mentities_by_name(text)
94 if mentities.is_empty then
95 return null
96 else if mentities.length > 1 then
97 # TODO smart resolve conflicts
98 end
99 return mentities.first
100 end
101
102 redef fun add_wikilink(v, token) do
103 v.render_wikilink(token, view)
104 end
105 end
106
107 # Same as `InlineDecorator` but with wikilink commands handling
108 class CmdInlineDecorator
109 super InlineDecorator
110
111 redef type PROCESSOR: CmdMarkdownProcessor
112
113 # View used by wikilink commands to find model entities
114 var view: ModelView
115
116 redef fun add_wikilink(v, token) do
117 v.render_wikilink(token, view)
118 end
119 end
120
121 # Custom MarkdownEmitter for commands
122 class CmdMarkdownProcessor
123 super MarkdownProcessor
124
125 # Parser used to process doc commands
126 var parser: CommandParser
127
128 # Render a wikilink
129 fun render_wikilink(token: TokenWikiLink, model: ModelView) do
130 var link = token.link
131 if link == null then return
132 var name = token.name
133 if name != null then link = "{name} | {link}"
134
135 var cmd = link.write_to_string
136 if cmd.is_empty then
137 var error = new CmdParserError("Empty wikilink")
138 emit_text error.to_html.write_to_string
139 return
140 end
141
142 var command = parser.parse(cmd)
143 var error = parser.error
144
145 # If not a command, try a comment command
146 if command == null and error isa CmdParserError then
147 error = null
148 command = new CmdEntity(parser.view, mentity_name = cmd)
149 var status = command.parser_init(cmd, new HashMap[String, String])
150 if not status isa CmdSuccess then error = status
151 end
152
153 if error isa CmdError then
154 emit_text error.to_html.write_to_string
155 return
156 end
157 if error isa CmdWarning then
158 emit_text error.to_html.write_to_string
159 end
160 add command.as(not null).to_html
161 end
162 end
163
164 redef class Text
165 # Read `self` between `nstart` and `nend` (excluded) and writte chars to `out`.
166 private fun read(out: FlatBuffer, nstart, nend: Int): Int do
167 var pos = nstart
168 while pos < length and pos < nend do
169 out.add self[pos]
170 pos += 1
171 end
172 if pos == length then return -1
173 return pos
174 end
175 end