ni_nitdoc: Properties list in modules page
[nit.git] / src / ni_nitdoc.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2008 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 module ni_nitdoc
18
19 import model_utils
20 import abstract_compiler
21 import html
22
23 class Nitdoc
24 private var toolcontext: ToolContext
25 private var model: Model
26 private var modelbuilder: ModelBuilder
27 private var mainmodule: MModule
28 private var arguments: Array[String]
29 private var destinationdir: nullable String
30 private var sharedir: nullable String
31
32 private var opt_dir = new OptionString("Directory where doc is generated", "-d", "--dir")
33 private var opt_source = new OptionString("What link for source (%f for filename, %l for first line, %L for last line)", "--source")
34 private var opt_sharedir = new OptionString("Directory containing the nitdoc files", "--sharedir")
35 private var opt_nodot = new OptionBool("Do not generate graphes with graphiviz", "--no-dot")
36
37 init(toolcontext: ToolContext) do
38 # We need a model to collect stufs
39 self.toolcontext = toolcontext
40 self.arguments = toolcontext.option_context.rest
41 toolcontext.option_context.add_option(opt_dir)
42 toolcontext.option_context.add_option(opt_source)
43 toolcontext.option_context.add_option(opt_sharedir)
44 toolcontext.option_context.add_option(opt_nodot)
45 process_options
46
47 if arguments.length < 1 then
48 toolcontext.option_context.usage
49 exit(1)
50 end
51
52 model = new Model
53 modelbuilder = new ModelBuilder(model, toolcontext)
54
55 # Here we load an process std modules
56 var mmodules = modelbuilder.parse_and_build([arguments.first])
57 if mmodules.is_empty then return
58 modelbuilder.full_propdef_semantic_analysis
59 assert mmodules.length == 1
60 self.mainmodule = mmodules.first
61 end
62
63 private fun process_options do
64 if not opt_dir.value is null then
65 destinationdir = opt_dir.value
66 else
67 destinationdir = "nitdoc_directory"
68 end
69 if not opt_sharedir.value is null then
70 sharedir = opt_sharedir.value
71 else
72 var dir = "NIT_DIR".environ
73 if dir.is_empty then
74 dir = "{sys.program_name.dirname}/../share/nitdoc"
75 else
76 dir = "{dir}/share/nitdoc"
77 end
78 sharedir = dir
79 if sharedir is null then
80 print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
81 abort
82 end
83 dir = "{sharedir.to_s}/scripts/js-facilities.js"
84 if sharedir is null then
85 print "Error: Invalid nitdoc share files. Check --sharedir or envvar NIT_DIR"
86 abort
87 end
88 end
89 end
90
91 fun start do
92 if arguments.length == 1 then
93 # Create destination dir if it's necessary
94 if not destinationdir.file_exists then destinationdir.mkdir
95 sys.system("cp -r {sharedir.to_s}/* {destinationdir.to_s}/")
96 overview
97 fullindex
98 modules
99 end
100 end
101
102 fun overview do
103 var overviewpage = new NitdocOverview.with(modelbuilder.nmodules, self.opt_nodot.value, destinationdir.to_s)
104 overviewpage.save("{destinationdir.to_s}/index.html")
105 end
106
107 fun fullindex do
108 var fullindex = new NitdocFullindex.with(model.mmodules)
109 fullindex.save("{destinationdir.to_s}/full-index.html")
110 end
111
112 fun modules do
113 for mod in modelbuilder.nmodules do
114 var modulepage = new NitdocModules.with(mod)
115 modulepage.save("{destinationdir.to_s}/{mod.mmodule.name}.html")
116 end
117 end
118
119 end
120
121 class NitdocOverview
122 super NitdocPage
123
124 var amodules: Array[AModule]
125
126 # Init with Array[AModule] to get all ifnormations about each MModule containt in a program
127 # opt_nodot to inform about the graph gen
128 # destination: to know where will be saved dot files
129 init with(modules: Array[AModule], opt_nodot: Bool, destination: String) do
130 self.amodules = modules
131 self.opt_nodot = opt_nodot
132 self.destinationdir = destination
133 end
134
135 redef fun head do
136 super
137 add("title").text("Overview | Nit Standard Library")
138 end
139
140 redef fun header do
141 open("header")
142 open("nav").add_class("main")
143 open("ul")
144 add("li").add_class("current").text("Overview")
145 open("li")
146 add_html("<a href=\"full-index.html\">Full Index</a>")
147 close("li")
148 open("li").attr("id", "liGitHub")
149 open("a").add_class("btn").attr("id", "logGitHub")
150 add("img").attr("id", "imgGitHub").attr("src", "resources/icons/github-icon.png")
151 close("a")
152 open("div").add_class("popover bottom")
153 add("div").add_class("arrow").text(" ")
154 open("div").add_class("githubTitle")
155 add("h3").text("Github Sign In")
156 close("div")
157 open("div")
158 add("label").attr("id", "lbloginGit").text("Username")
159 add("input").attr("id", "loginGit").attr("name", "login").attr("type", "text")
160 open("label").attr("id", "logginMessage").text("Hello ")
161 open("a").attr("id", "githubAccount")
162 add("strong").attr("id", "nickName").text(" ")
163 close("a")
164 close("label")
165 close("div")
166 open("div")
167 add("label").attr("id", "lbpasswordGit").text("Password")
168 add("input").attr("id", "passwordGit").attr("name", "password").attr("type", "password")
169 open("div").attr("id", "listBranches")
170 add("label").attr("id", "lbBranches").text("Branch")
171 add("select").add_class("dropdown").attr("id", "dropBranches").attr("name", "dropBranches").attr("tabindex", "1").text(" ")
172 close("div")
173 close("div")
174 open("div")
175 add("label").attr("id", "lbrepositoryGit").text("Repository")
176 add("input").attr("id", "repositoryGit").attr("name", "repository").attr("type", "text")
177 close("div")
178 open("div")
179 add("label").attr("id", "lbbranchGit").text("Branch")
180 add("input").attr("id", "branchGit").attr("name", "branch").attr("type", "text")
181 close("div")
182 open("div")
183 add("a").attr("id", "signIn").text("Sign In")
184 close("div")
185 close("div")
186 close("li")
187 close("ul")
188 close("nav")
189 close("header")
190 end
191
192 redef fun body do
193 super
194 open("div").add_class("page")
195 open("div").add_class("content fullpage")
196 add("h1").text("Nit Standard Library")
197 open("article").add_class("overview")
198 add_html("<p>Documentation for the standard library of Nit<br />Version jenkins-component=stdlib-19<br />Date: TODAY</p>")
199 close("article")
200 open("article").add_class("overview")
201 add("h2").text("Modules")
202 open("ul")
203 add_modules
204 close("ul")
205 process_generate_dot
206 close("article")
207 close("div")
208 close("div")
209 add("footer").text("Nit standard library. Version jenkins-component=stdlib-19.")
210 end
211
212 fun add_modules do
213 var ls = new List[nullable MModule]
214 for amodule in amodules do
215 var mmodule = amodule.mmodule.public_owner
216 if mmodule != null and not ls.has(mmodule) then
217 open("li")
218 add("a").attr("href", "{mmodule.name}.html").text("{mmodule.to_s} ")
219 add_html(amodule.comment)
220 close("li")
221 ls.add(mmodule)
222 end
223 end
224 end
225
226 fun process_generate_dot do
227 var op = new Buffer
228 op.append("digraph dep \{ rankdir=BT; node[shape=none,margin=0,width=0,height=0,fontsize=10]; edge[dir=none,color=gray]; ranksep=0.2; nodesep=0.1;\n")
229 for amodule in amodules do
230 op.append("\"{amodule.mmodule.name}\"[URL=\"{amodule.mmodule.name}.html\"];\n")
231 for mmodule2 in amodule.mmodule.in_importation.direct_greaters do
232 op.append("\"{amodule.mmodule.name}\"->\"{mmodule2.name}\";\n")
233 end
234 end
235 op.append("\}\n")
236 generate_dot(op.to_s, "dep", "Modules hierarchy")
237 end
238
239 end
240
241 class NitdocFullindex
242 super NitdocPage
243
244 var mmodules: Array[MModule]
245
246 init with(mmodules: Array[MModule]) do
247 self.mmodules = mmodules
248 opt_nodot = false
249 destinationdir = ""
250 end
251
252 redef fun head do
253 super
254 add("title").text("Full Index | Nit Standard Library")
255 end
256
257 redef fun header do
258 open("header")
259 open("nav").add_class("main")
260 open("ul")
261 open("li")
262 add_html("<a href=\"index.html\">Overview</a>")
263 close("li")
264 add("li").add_class("current").text("Full Index")
265 open("li").attr("id", "liGitHub")
266 open("a").add_class("btn").attr("id", "logGitHub")
267 add("img").attr("id", "imgGitHub").attr("src", "resources/icons/github-icon.png")
268 close("a")
269 open("div").add_class("popover bottom")
270 add("div").add_class("arrow").text(" ")
271 open("div").add_class("githubTitle")
272 add("h3").text("Github Sign In")
273 close("div")
274 open("div")
275 add("label").attr("id", "lbloginGit").text("Username")
276 add("input").attr("id", "loginGit").attr("name", "login").attr("type", "text")
277 open("label").attr("id", "logginMessage").text("Hello ")
278 open("a").attr("id", "githubAccount")
279 add("strong").attr("id", "nickName").text(" ")
280 close("a")
281 close("label")
282 close("div")
283 open("div")
284 add("label").attr("id", "lbpasswordGit").text("Password")
285 add("input").attr("id", "passwordGit").attr("name", "password").attr("type", "password")
286 open("div").attr("id", "listBranches")
287 add("label").attr("id", "lbBranches").text("Branch")
288 add("select").add_class("dropdown").attr("id", "dropBranches").attr("name", "dropBranches").attr("tabindex", "1").text(" ")
289 close("div")
290 close("div")
291 open("div")
292 add("label").attr("id", "lbrepositoryGit").text("Repository")
293 add("input").attr("id", "repositoryGit").attr("name", "repository").attr("type", "text")
294 close("div")
295 open("div")
296 add("label").attr("id", "lbbranchGit").text("Branch")
297 add("input").attr("id", "branchGit").attr("name", "branch").attr("type", "text")
298 close("div")
299 open("div")
300 add("a").attr("id", "signIn").text("Sign In")
301 close("div")
302 close("div")
303 close("li")
304 close("ul")
305 close("nav")
306 close("header")
307 end
308
309 redef fun body do
310 super
311 open("div").add_class("page")
312 open("div").add_class("content fullpage")
313 add("h1").text("Full Index")
314 add_content
315 close("div")
316 close("div")
317 add("footer").text("Nit standard library. Version jenkins-component=stdlib-19.")
318 end
319
320 fun add_content do
321 module_column
322 classes_column
323 properties_column
324 end
325
326 # Add to content modules column
327 fun module_column do
328 var ls = new List[nullable MModule]
329 open("article").add_class("modules filterable")
330 add("h2").text("Modules")
331 open("ul")
332 for mmodule in mmodules do
333 if mmodule.public_owner != null and not ls.has(mmodule.public_owner) then
334 ls.add(mmodule.public_owner)
335 open("li")
336 add("a").attr("href", "{mmodule.public_owner.name}.html").text(mmodule.public_owner.name)
337 close("li")
338 end
339 end
340 close("ul")
341 close("article")
342 end
343
344 # Add to content classes modules
345 fun classes_column do
346 open("article").add_class("classes filterable")
347 add("h2").text("Classes")
348 open("ul")
349
350 for mclass in mmodules.first.imported_mclasses do
351 open("li")
352 add("a").attr("href", "{mclass.name}.html").text(mclass.name)
353 close("li")
354 end
355
356 close("ul")
357 close("article")
358 end
359
360 # Insert the properties column of fullindex page
361 fun properties_column do
362 open("article").add_class("properties filterable")
363 add("h2").text("Properties")
364 open("ul")
365
366 for method in mmodules.first.imported_methods do
367 if method.visibility is none_visibility or method.visibility is intrude_visibility then continue
368 open("li").add_class("intro")
369 add("span").attr("title", "introduction").text("I")
370 add_html("&nbsp;")
371 add("a").attr("href", "{method.local_class.name}.html").attr("title", "").text("{method.name} ({method.local_class.name})")
372 close("li")
373 end
374
375 for method in mmodules.first.redef_methods do
376 if method.visibility is none_visibility or method.visibility is intrude_visibility then continue
377 open("li").add_class("redef")
378 add("span").attr("title", "redefinition").text("R")
379 add_html("&nbsp;")
380 add("a").attr("href", "{method.local_class.name}.html").attr("title", "").text("{method.name} ({method.local_class.name})")
381 close("li")
382 end
383
384 close("ul")
385 close("article")
386 end
387
388 end
389
390 class NitdocModules
391 super NitdocPage
392
393 var amodule: AModule
394 var modulename: String
395 init with(amodule: AModule) do
396 self.amodule = amodule
397 self.modulename = self.amodule.mmodule.name
398 opt_nodot = false
399 destinationdir = ""
400 end
401
402 redef fun head do
403 super
404 add("title").text("{modulename} module | {amodule.short_comment}")
405 end
406
407 redef fun header do
408 open("header")
409 open("nav").add_class("main")
410 open("ul")
411 open("li")
412 add_html("<a href=\"index.html\">Overview</a>")
413 close("li")
414 add("li").add_class("current").text(modulename)
415 open("li")
416 add_html("<a href=\"full-index.html\" >Full Index</a>")
417 close("li")
418 open("li").attr("id", "liGitHub")
419 open("a").add_class("btn").attr("id", "logGitHub")
420 add("img").attr("id", "imgGitHub").attr("src", "resources/icons/github-icon.png")
421 close("a")
422 open("div").add_class("popover bottom")
423 add("div").add_class("arrow").text(" ")
424 open("div").add_class("githubTitle")
425 add("h3").text("Github Sign In")
426 close("div")
427 open("div")
428 add("label").attr("id", "lbloginGit").text("Username")
429 add("input").attr("id", "loginGit").attr("name", "login").attr("type", "text")
430 open("label").attr("id", "logginMessage").text("Hello ")
431 open("a").attr("id", "githubAccount")
432 add("strong").attr("id", "nickName").text(" ")
433 close("a")
434 close("label")
435 close("div")
436 open("div")
437 add("label").attr("id", "lbpasswordGit").text("Password")
438 add("input").attr("id", "passwordGit").attr("name", "password").attr("type", "password")
439 open("div").attr("id", "listBranches")
440 add("label").attr("id", "lbBranches").text("Branch")
441 add("select").add_class("dropdown").attr("id", "dropBranches").attr("name", "dropBranches").attr("tabindex", "1").text(" ")
442 close("div")
443 close("div")
444 open("div")
445 add("label").attr("id", "lbrepositoryGit").text("Repository")
446 add("input").attr("id", "repositoryGit").attr("name", "repository").attr("type", "text")
447 close("div")
448 open("div")
449 add("label").attr("id", "lbbranchGit").text("Branch")
450 add("input").attr("id", "branchGit").attr("name", "branch").attr("type", "text")
451 close("div")
452 open("div")
453 add("a").attr("id", "signIn").text("Sign In")
454 close("div")
455 close("div")
456 close("li")
457 close("ul")
458 close("nav")
459 close("header")
460 end
461
462 redef fun body do
463 super
464 open("div").add_class("page")
465 menu
466 add_content
467 close("div")
468 add("footer").text("Nit standard library. Version jenkins-component=stdlib-19.")
469 end
470
471 # Insert all tags in content part
472 fun add_content do
473 open("div").add_class("content")
474 add("h1").text(modulename)
475 add("div").add_class("subtitle").text("module {modulename}")
476 module_comment
477 classes
478 properties
479 close("div")
480 end
481
482 # Insert module comment in the content
483 fun module_comment do
484 var doc = amodule.comment
485 open("div").attr("id", "description")
486 add("pre").add_class("text_label").text(doc)
487 add("textarea").add_class("edit").attr("rows", "1").attr("cols", "76").attr("id", "fileContent").text(" ")
488 add("a").attr("id", "cancelBtn").text("Cancel")
489 add("a").attr("id", "commitBtn").text("Commit")
490 add("pre").add_class("text_label").attr("id", "preSave").attr("type", "2")
491 close("div")
492 end
493
494 fun menu do
495 var mmodule = amodule.mmodule
496 open("div").add_class("menu")
497 open("nav")
498 add("h3").text("Module Hierarchy").attr("style","cursor: pointer;")
499 if mmodule.in_importation.direct_greaters.length > 0 then
500 add_html("<h4>All dependencies</h4><ul>")
501 for m in mmodule.in_importation.direct_greaters do
502 if m == mmodule or mmodule == m.public_owner then continue
503 open("li")
504 add("a").attr("href", "{m.name}.html").text(m.name)
505 close("li")
506 end
507 add_html("</ul>")
508 end
509 if mmodule.in_importation.greaters.length > 0 then
510 add_html("<h4>All clients</h4><ul>")
511 for m in mmodule.in_importation.greaters do
512 if m == mmodule then continue
513 open("li")
514 add("a").attr("href", "{m.name}.html").text(m.name)
515 close("li")
516 end
517 add_html("</ul>")
518 end
519 close("nav")
520 if mmodule.in_nesting.direct_greaters.length > 0 then
521 open("nav")
522 add("h3").text("Nested Modules").attr("style","cursor: pointer;")
523 open("ul")
524 for m in mmodule.in_nesting.direct_greaters do
525 open("li")
526 add("a").attr("href", "{m.name}.html").text(m.name)
527 close("li")
528 end
529 close("ul")
530
531 close("nav")
532 end
533 close("div")
534 end
535
536 fun classes do
537 open("div").add_class("module")
538 open("article").add_class("classes filterable")
539 add("h2").text("Classes")
540 open("ul")
541 for c, state in amodule.mmodule.mclasses do
542 var name = c.name
543 if state == c_is_intro or state == c_is_imported then
544 open("li").add_class("intro")
545 add("span").attr("title", "introduced in this module").text("I ")
546 else
547 open("li").add_class("redef")
548 add("span").attr("title", "refined in this module").text("R ")
549 end
550 add("a").attr("href", "{name}.html").text(name)
551 close("li")
552 end
553 close("ul")
554 close("article")
555 close("div")
556 end
557
558 fun properties do
559 open("article").add_class("properties filterable")
560 add_html("<h2>Properties</h2>")
561 open("ul")
562 for method in amodule.mmodule.imported_methods do
563 if method.visibility is none_visibility or method.visibility is intrude_visibility then continue
564 open("li").add_class("intro")
565 add("span").attr("title", "introduction").text("I")
566 add_html("&nbsp;")
567 add("a").attr("href", "{method.local_class.name}.html").attr("title", "").text("{method.name} ({method.local_class.name})")
568 close("li")
569 end
570
571 for method in amodule.mmodule.redef_methods do
572 if method.visibility is none_visibility or method.visibility is intrude_visibility then continue
573 open("li").add_class("redef")
574 add("span").attr("title", "redefinition").text("R")
575 add_html("&nbsp;")
576 add("a").attr("href", "{method.local_class.name}.html").attr("title", "").text("{method.name} ({method.local_class.name})")
577 close("li")
578 end
579
580 close("ul")
581 close("article")
582 end
583
584 end
585
586 class NitdocPage
587 super HTMLPage
588
589 var opt_nodot: Bool
590 var destinationdir : String
591
592 redef fun head do
593 add("meta").attr("charset", "utf-8")
594 add("script").attr("type", "text/javascript").attr("src", "scripts/jquery-1.7.1.min.js")
595 add("script").attr("type", "text/javascript").attr("src", "quicksearch-list.js")
596 add("script").attr("type", "text/javascript").attr("src", "scripts/js-facilities.js")
597 add("link").attr("rel", "stylesheet").attr("href", "styles/main.css").attr("type", "text/css").attr("media", "screen")
598 end
599
600 redef fun body do header
601 fun header do end
602
603 # Generate a clickable graphviz image using a dot content
604 fun generate_dot(dot: String, name: String, alt: String) do
605 if opt_nodot then return
606 var file = new OFStream.open("{self.destinationdir}/{name}.dot")
607 file.write(dot)
608 file.close
609 sys.system("\{ test -f {self.destinationdir}/{name}.png && test -f {self.destinationdir}/{name}.s.dot && diff {self.destinationdir}/{name}.dot {self.destinationdir}/{name}.s.dot >/dev/null 2>&1 ; \} || \{ cp {self.destinationdir}/{name}.dot {self.destinationdir}/{name}.s.dot && dot -Tpng -o{self.destinationdir}/{name}.png -Tcmapx -o{self.destinationdir}/{name}.map {self.destinationdir}/{name}.s.dot ; \}")
610 open("article").add_class("graph")
611 add("img").attr("src", "{name}.png").attr("usemap", "#{name}").attr("style", "margin:auto").attr("alt", "{alt}")
612 close("article")
613 var fmap = new IFStream.open("{self.destinationdir}/{name}.map")
614 add_html(fmap.read_all)
615 fmap.close
616 end
617
618 end
619
620 redef class AModule
621 private fun comment: String do
622 var ret = ""
623 if n_moduledecl is null or n_moduledecl.n_doc is null then ret
624 if n_moduledecl.n_doc is null then return ""
625 for t in n_moduledecl.n_doc.n_comment do
626 ret += "{t.text.replace("# ", "")}"
627 end
628 return ret
629 end
630
631 private fun short_comment: String do
632 var ret = ""
633 if n_moduledecl != null and n_moduledecl.n_doc != null then
634 var txt = n_moduledecl.n_doc.n_comment.first.text
635 txt = txt.replace("# ", "")
636 txt = txt.replace("\n", "")
637 ret += txt
638 end
639 return ret
640 end
641 end
642
643 redef class MModule
644
645 var amodule: nullable AModule
646
647 # Get the list of all methods in a module
648 fun imported_methods: Set[MMethod] do
649 var methods = new HashSet[MMethod]
650 for mclass in imported_mclasses do
651 for method in mclass.intro_methods do
652 methods.add(method)
653 end
654 end
655 return methods
656 end
657
658 # Get the list aof all refined methods in a module
659 fun redef_methods: Set[MMethod] do
660 var methods = new HashSet[MMethod]
661 for mclass in redef_mclasses do
662 for method in mclass.intro_methods do
663 methods.add(method)
664 end
665 end
666 return methods
667 end
668 end
669
670 redef class MProperty
671
672 var is_redef: Bool
673 var apropdef: nullable APropdef
674
675 redef init(intro_mclassdef: MClassDef, name: String, visibility: MVisibility)
676 do
677 super
678 is_redef = false
679 end
680
681 fun local_class: MClass do
682 var classdef = self.intro_mclassdef
683 return classdef.mclass
684 end
685
686 end
687
688 redef class MClass
689
690 # Associate all MMethods to each MModule concerns
691 fun all_methods: HashMap[MModule, Set[MMethod]] do
692 var hm = new HashMap[MModule, Set[MMethod]]
693 for mmodule, childs in concerns do
694 if not hm.has_key(mmodule) then hm[mmodule] = new HashSet[MMethod]
695 for prop in intro_methods do
696 if mmodule == prop.intro_mclassdef.mmodule then
697 prop.is_redef = false
698 hm[mmodule].add(prop)
699 end
700 end
701 for prop in redef_methods do
702 if mmodule == prop.intro_mclassdef.mmodule then
703 prop.is_redef = true
704 hm[mmodule].add(prop)
705 end
706 end
707
708 if childs != null then
709 for child in childs do
710 if not hm.has_key(child) then hm[child] = new HashSet[MMethod]
711 for prop in intro_methods do
712 if child == prop.intro_mclassdef.mmodule then
713 prop.is_redef = false
714 hm[child].add(prop)
715 end
716 end
717 for prop in redef_methods do
718 if child == prop.intro_mclassdef.mmodule then
719 prop.is_redef = true
720 hm[child].add(prop)
721 end
722 end
723 end
724 end
725 end
726 return hm
727 end
728
729 end
730
731 # Create a tool context to handle options and paths
732 var toolcontext = new ToolContext
733 toolcontext.process_options
734
735 # Here we launch the nit index
736 var nitdoc = new Nitdoc(toolcontext)
737 nitdoc.start