src/doc_commands: merge Article and Comment commands
[nit.git] / src / doc / doc_commands.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 # Parsing of commands understood by documentation tools.
16 #
17 # This can be through:
18 # * `nitx` commands like `code: MEntity::name`
19 # * `nitdoc` wikilinks like `[[doc: MEntity::name]]`
20 module doc_commands
21
22 #
23 class DocCommandParser
24
25 # List of allowed command names for this parser
26 var allowed_commands: Array[String] = [ "doc", "list", "param", "return",
27 "new", "call", "code", "graph"] is writable
28
29 # Parse `string` as a DocCommand
30 #
31 # Returns `null` if the string cannot be parsed.
32 #
33 # ~~~
34 # var parser = new DocCommandParser
35 #
36 # var command = parser.parse("doc: core::Array")
37 # assert command isa CommentCommand
38 # assert command.arg == "core::Array"
39 #
40 # command = parser.parse(":") # syntax error
41 # assert command == null
42 # assert parser.errors.not_empty
43 # ~~~
44 fun parse(string: String): nullable DocCommand do
45 var pos = 0
46 var tmp = new FlatBuffer
47 errors.clear
48
49 # Parse command name
50 pos = string.read_until(tmp, pos, ':')
51 var name = tmp.write_to_string.trim
52
53 # Check allowed commands
54 if name.is_empty then
55 error("empty command name", 0)
56 return null
57 end
58 if not allowed_commands.has(name) then
59 error("unknown command name", 0)
60 return null
61 end
62
63 # Build the command
64 var command = new_command(name, string)
65 if command == null then
66 error("unknown command name", 0)
67 return null
68 end
69
70 # Parse the argument
71 tmp.clear
72 pos = string.read_until(tmp, pos + 1, '|')
73 var arg = tmp.write_to_string.trim
74 if arg.is_empty then
75 error("empty command arg", pos)
76 return null
77 end
78 command.arg = arg
79
80 # Parse command options
81 while pos < string.length do
82 # Parse option name
83 tmp.clear
84 pos = string.read_until(tmp, pos + 1, ':', ',')
85 var oname = tmp.write_to_string.trim
86 var oval = ""
87 if oname.is_empty then break
88 # Parse option value
89 if pos < string.length and string[pos] == ':' then
90 tmp.clear
91 pos = string.read_until(tmp, pos + 1, ',')
92 oval = tmp.write_to_string.trim
93 end
94 command.opts[oname] = oval
95 # TODO Check options
96 end
97
98 return command
99 end
100
101 # Init a new DocCommand from its `name`
102 #
103 # You must redefine this method to add new custom commands.
104 fun new_command(name, string: String): nullable DocCommand do
105 if name == "doc" then return new CommentCommand(string)
106 if name == "list" then return new ListCommand(string)
107 if name == "param" then return new ParamCommand(string)
108 if name == "return" then return new ReturnCommand(string)
109 if name == "new" then return new NewCommand(string)
110 if name == "call" then return new CallCommand(string)
111 if name == "code" then return new CodeCommand(string)
112 if name == "graph" then return new GraphCommand(string)
113 return null
114 end
115
116 # Errors and warnings from last call to `parse`
117 var errors = new Array[DocMessage]
118
119 # Generate an error
120 fun error(message: String, col: nullable Int) do
121 errors.add new DocMessage(1, message, col)
122 end
123
124 # Generate a warning
125 fun warning(message: String, col: nullable Int) do
126 errors.add new DocMessage(2, message, col)
127 end
128 end
129
130 # A message generated by the DocCommandParser
131 class DocMessage
132
133 # Message severity
134 #
135 # 1- Error
136 # 2- Warning
137 var level: Int
138
139 # Message explanatory string
140 var message: String
141
142 # Related column in original string if any
143 var col: nullable Int
144
145 redef fun to_s do
146 var str = new FlatBuffer
147 if level == 1 then
148 str.append "Error: "
149 else
150 str.append "Warning: "
151 end
152 str.append message
153 var col = self.col
154 if col != null then
155 str.append " (col: {col})"
156 end
157 return str.write_to_string
158 end
159 end
160
161 redef class Text
162 # Read `self` as raw text until `nend` and append it to the `out` buffer.
163 private fun read_until(out: FlatBuffer, start: Int, nend: Char...): Int do
164 var pos = start
165 while pos < length do
166 var c = self[pos]
167 var end_reached = false
168 for n in nend do
169 if c == n then
170 end_reached = true
171 break
172 end
173 end
174 if end_reached then break
175 out.add c
176 pos += 1
177 end
178 return pos
179 end
180 end
181
182 # A command aimed at a documentation tool like `nitdoc` or `nitx`.
183 #
184 # `DocCommand` are generally of the form `command: arg | opt1: val1, opt2: val2`.
185 abstract class DocCommand
186
187 # Original command string.
188 var string: String
189
190 # Command name.
191 var name: String is noinit
192
193 # Command arguments.
194 var arg: String is noinit, writable
195
196 # Command options.
197 var opts = new HashMap[String, String] is writable
198
199 redef fun to_s do
200 if opts.is_empty then
201 return "{name}: {arg}"
202 end
203 return "{name}: {arg} | {opts.join(", ", ": ")}"
204 end
205 end
206
207 # A `DocCommand` that includes the documentation article of a `MEntity`.
208 #
209 # Syntax: `doc: MEntity::name`.
210 class CommentCommand
211 super DocCommand
212
213 redef var name = "doc"
214 end
215
216 # A `DocCommand` that includes a list of something.
217 #
218 # Syntax: `list:kind: <arg>`.
219 class ListCommand
220 super DocCommand
221
222 redef var name = "list"
223 end
224
225 # A `DocCommand` that includes the list of methods tanking a `MType` as parameter.
226 #
227 # Syntax: `param: MType`.
228 class ParamCommand
229 super DocCommand
230
231 redef var name = "param"
232 end
233
234 # A `DocCommand` that includes the list of methods returning a `MType` as parameter.
235 #
236 # Syntax: `return: MType`.
237 class ReturnCommand
238 super DocCommand
239
240 redef var name = "return"
241 end
242
243 # A `DocCommand` that includes the list of methods creating new instances of a specific `MType`
244 #
245 # Syntax: `new: MType`.
246 class NewCommand
247 super DocCommand
248
249 redef var name = "new"
250 end
251
252 # A `DocCommand` that includes the list of methods calling a specific `MProperty`.
253 #
254 # Syntax: `call: MEntity::name`.
255 class CallCommand
256 super DocCommand
257
258 redef var name = "call"
259 end
260
261 # A `DocCommand` that includes the source code of a `MEntity`.
262 #
263 # Syntax:
264 # * `code: MEntity::name`
265 # * `./src/file.nit` to include source code from a file.
266 # * `./src/file.nit:1,2--3,4` to select code between positions.
267 class CodeCommand
268 super DocCommand
269
270 redef var name = "code"
271 end
272
273 # A `DocCommand` that display an graph for a `MEntity`.
274 #
275 # Syntax:
276 # * `graph: MEntity::name`
277 class GraphCommand
278 super DocCommand
279
280 redef var name = "graph"
281 end