1 # This file is part of NIT ( http://www.nitlanguage.org ).
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
15 # Nitdoc page generation
20 private import json
::static
22 redef class ToolContext
23 private var opt_dir
= new OptionString("output directory", "-d", "--dir")
24 private var opt_source
= new OptionString("link for source (%f for filename, %l for first line, %L for last line)", "--source")
25 private var opt_sharedir
= new OptionString("directory containing nitdoc assets", "--sharedir")
26 private var opt_shareurl
= new OptionString("use shareurl instead of copy shared files", "--shareurl")
27 private var opt_custom_title
= new OptionString("custom title for homepage", "--custom-title")
28 private var opt_custom_brand
= new OptionString("custom link to external site", "--custom-brand")
29 private var opt_custom_intro
= new OptionString("custom intro text for homepage", "--custom-overview-text")
30 private var opt_custom_footer
= new OptionString("custom footer text", "--custom-footer-text")
32 private var opt_github_upstream
= new OptionString("Git branch where edited commits will be pulled into (ex: user:repo:branch)", "--github-upstream")
33 private var opt_github_base_sha1
= new OptionString("Git sha1 of base commit used to create pull request", "--github-base-sha1")
34 private var opt_github_gitdir
= new OptionString("Git working directory used to resolve path name (ex: /home/me/myproject/)", "--github-gitdir")
36 private var opt_piwik_tracker
= new OptionString("Piwik tracker URL (ex: nitlanguage.org/piwik/)", "--piwik-tracker")
37 private var opt_piwik_site_id
= new OptionString("Piwik site ID", "--piwik-site-id")
39 private var output_dir
: String
40 private var min_visibility
: MVisibility
45 var opts
= option_context
46 opts
.add_option
(opt_dir
, opt_source
, opt_sharedir
, opt_shareurl
)
47 opts
.add_option
(opt_custom_title
, opt_custom_footer
, opt_custom_intro
, opt_custom_brand
)
48 opts
.add_option
(opt_github_upstream
, opt_github_base_sha1
, opt_github_gitdir
)
49 opts
.add_option
(opt_piwik_tracker
, opt_piwik_site_id
)
52 redef fun process_options
(args
) do
56 var output_dir
= opt_dir
.value
57 if output_dir
== null then
60 self.output_dir
= output_dir
62 var gh_upstream
= opt_github_upstream
.value
63 var gh_base_sha
= opt_github_base_sha1
.value
64 var gh_gitdir
= opt_github_gitdir
.value
65 if not gh_upstream
== null or not gh_base_sha
== null or not gh_gitdir
== null then
66 if gh_upstream
== null or gh_base_sha
== null or gh_gitdir
== null then
67 print
"Error: Options {opt_github_upstream.names.first}, {opt_github_base_sha1.names.first} and {opt_github_gitdir.names.first} are required to enable the GitHub plugin"
74 # The Nitdoc class explores the model and generate pages for each mentities found
78 var mainmodule
: MModule
80 private fun init_output_dir
do
81 # create destination dir if it's necessary
82 var output_dir
= ctx
.output_dir
83 if not output_dir
.file_exists
then output_dir
.mkdir
85 var sharedir
= ctx
.opt_sharedir
.value
86 if sharedir
== null then
88 sharedir
= dir
/"share/nitdoc"
89 if not sharedir
.file_exists
then
90 print
"Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
95 if ctx
.opt_shareurl
.value
== null then
96 sys
.system
("cp -r -- {sharedir.to_s.escape_to_sh}/* {output_dir.to_s.escape_to_sh}/")
98 sys
.system
("cp -r -- {sharedir.to_s.escape_to_sh}/resources/ {output_dir.to_s.escape_to_sh}/resources/")
104 # Nitdoc QuickSearch list generator
106 # Create a JSON object containing links to:
110 # All entities are grouped by name to make the research easier.
113 private var table
= new QuickSearchTable
119 for mmodule
in model
.mmodules
do
120 if mmodule
.is_fictive
or mmodule
.is_test_suite
then continue
121 add_result_for
(mmodule
.name
, mmodule
.full_name
, mmodule
.nitdoc_url
)
123 for mclass
in model
.mclasses
do
124 if not ctx
.filter_mclass
(mclass
) then continue
125 add_result_for
(mclass
.name
, mclass
.full_name
, mclass
.nitdoc_url
)
127 for mproperty
in model
.mproperties
do
128 if not ctx
.filter_mproperty
(mproperty
) then continue
129 for mpropdef
in mproperty
.mpropdefs
do
130 var full_name
= mpropdef
.mclassdef
.mclass
.full_name
131 var cls_url
= mpropdef
.mclassdef
.mclass
.nitdoc_url
132 var def_url
= "{cls_url}#{mpropdef.mproperty.nitdoc_id}"
133 add_result_for
(mproperty
.name
, full_name
, def_url
)
138 private fun add_result_for
(query
: String, txt
: String, url
: String) do
139 table
[query
].add
new QuickSearchResult(txt
, url
)
142 fun render
: Template do
143 var tpl
= new Template
144 var buffer
= new RopeBuffer
146 buffer
.append
"var nitdocQuickSearchRawList="
147 table
.append_json buffer
153 # The result map for QuickSearch.
154 private class QuickSearchTable
155 super JsonMapRead[String, QuickSearchResultList]
156 super HashMap[String, QuickSearchResultList]
158 redef fun provide_default_value
(key
) do
159 var v
= new QuickSearchResultList
165 # A QuickSearch result list.
166 private class QuickSearchResultList
167 super JsonSequenceRead[QuickSearchResult]
168 super Array[QuickSearchResult]
171 # A QuickSearch result.
172 private class QuickSearchResult
175 # The text of the link.
178 # The destination of the link.
182 return "\{\"txt\":{txt.to_json},\"url\
":{url.to_json}\}"
187 # Define page structure and properties
188 abstract class NitdocPage
190 private var ctx
: ToolContext
191 private var model
: Model
192 private var mainmodule
: MModule
193 private var name_sorter
= new MEntityNameSorter
195 # Render the page as a html template
196 fun render
: Template do
198 if ctx
.opt_shareurl
.value
!= null then
199 shareurl
= ctx
.opt_shareurl
.value
.as(not null)
204 tpl
.title
= tpl_title
206 tpl
.shareurl
= shareurl
207 tpl
.topmenu
= tpl_topmenu
209 tpl
.footer
= ctx
.opt_custom_footer
.value
210 tpl
.body_attrs
.add
(new TagAttribute("data-bootstrap-share", shareurl
))
211 tpl
.sidebar
= tpl_sidebar
214 var tracker_url
= ctx
.opt_piwik_tracker
.value
215 var site_id
= ctx
.opt_piwik_site_id
.value
216 if tracker_url
!= null and site_id
!= null then
217 tpl
.scripts
.add
new TplPiwikScript(tracker_url
, site_id
)
223 fun page_url
: String is abstract
225 # Build page template
226 fun tpl_page
: TplPage is abstract
228 # Build page sidebar if any
229 fun tpl_sidebar
: nullable TplSidebar do return null
231 # Build page title string
232 fun tpl_title
: String do
233 if ctx
.opt_custom_title
.value
!= null then
234 return ctx
.opt_custom_title
.value
.to_s
239 # Build top menu template
240 fun tpl_topmenu
: TplTopMenu do
241 var topmenu
= new TplTopMenu(page_url
)
242 var brand
= ctx
.opt_custom_brand
.value
243 if brand
!= null then
244 var tpl
= new Template
245 tpl
.add
"<span class='navbar-brand'>"
250 topmenu
.add_link
new TplLink("index.html", "Overview")
251 topmenu
.add_link
new TplLink("search.html", "Index")
255 # Build page content template
256 fun tpl_content
is abstract
258 # A (source) link template for a given location
259 fun tpl_showsource
(location
: nullable Location): nullable String
261 if location
== null then return null
262 var source
= ctx
.opt_source
.value
263 if source
== null then
264 var url
= location
.file
.filename
.simplify_path
265 return "<a target='_blank' title='Show source' href=\"{url.html_escape}\
">View Source</a>"
267 # THIS IS JUST UGLY ! (but there is no replace yet)
268 var x
= source
.split_with
("%f")
269 source
= x
.join
(location
.file
.filename
.simplify_path
)
270 x
= source
.split_with
("%l")
271 source
= x
.join
(location
.line_start
.to_s
)
272 x
= source
.split_with
("%L")
273 source
= x
.join
(location
.line_end
.to_s
)
274 source
= source
.simplify_path
275 return "<a target='_blank' title='Show source' href=\"{source.to_s.html_escape}\
">View Source</a>"
278 # MProject description template
279 fun tpl_mproject_article
(mproject
: MProject): TplArticle do
280 var article
= mproject
.tpl_article
281 article
.subtitle
= mproject
.tpl_declaration
282 article
.content
= mproject
.tpl_definition
283 var mdoc
= mproject
.mdoc_or_fallback
285 article
.content
= mdoc
.tpl_short_comment
290 # MGroup description template
291 fun tpl_mgroup_article
(mgroup
: MGroup): TplArticle do
292 var article
= mgroup
.tpl_article
293 article
.subtitle
= mgroup
.tpl_declaration
294 article
.content
= mgroup
.tpl_definition
298 # MModule description template
299 fun tpl_mmodule_article
(mmodule
: MModule): TplArticle do
300 var article
= mmodule
.tpl_article
301 article
.subtitle
= mmodule
.tpl_declaration
302 article
.content
= mmodule
.tpl_definition
306 # MClassDef description template
307 fun tpl_mclass_article
(mclass
: MClass, mclassdefs
: Array[MClassDef]): TplArticle do
308 var article
= mclass
.tpl_article
312 # MClassDef description template
313 fun tpl_mclassdef_article
(mclassdef
: MClassDef): TplArticle do
314 var article
= mclassdef
.tpl_article
315 if mclassdef
.is_intro
then article
.content
= null
316 article
.source_link
= tpl_showsource
(mclassdef
.location
)
320 # MProp description template
322 # `main_mpropdef`: The most important mpropdef to display
323 # `local_mpropdefs`: List of other locally defined mpropdefs to display
324 # `lin`: full linearization from local_mpropdefs to intro (displayed in redef tree)
325 fun tpl_mprop_article
(main_mpropdef
: MPropDef, local_mpropdefs
: Array[MPropDef],
326 lin
: Array[MPropDef]): TplArticle do
327 var mprop
= main_mpropdef
.mproperty
328 var article
= new TplArticle(mprop
.nitdoc_id
)
329 var title
= new Template
330 title
.add mprop
.tpl_icon
331 title
.add
"<span id='{main_mpropdef.nitdoc_id}'></span>"
332 if main_mpropdef
.is_intro
then
333 title
.add mprop
.tpl_link
334 title
.add mprop
.intro
.tpl_signature
336 var cls_url
= mprop
.intro
.mclassdef
.mclass
.nitdoc_url
337 var def_url
= "{cls_url}#{mprop.nitdoc_id}"
338 var lnk
= new TplLink.with_title
(def_url
, mprop
.nitdoc_name
,
339 "Go to introduction")
343 article
.title
= title
344 article
.title_classes
.add
"signature"
345 article
.summary_title
= "{mprop.nitdoc_name}"
346 article
.subtitle
= main_mpropdef
.tpl_namespace
347 if main_mpropdef
.mdoc_or_fallback
!= null then
348 article
.content
= main_mpropdef
.mdoc_or_fallback
.tpl_comment
350 var subarticle
= new TplArticle("{main_mpropdef.nitdoc_id}.redefs")
352 if lin
.length
> 1 then
353 var lin_article
= new TplArticle("{main_mpropdef.nitdoc_id}.lin")
354 lin_article
.title
= "Inheritance"
355 var lst
= new TplList.with_classes
(["list-unstyled", "list-labeled"])
356 for mpropdef
in lin
do
357 lst
.add_li mpropdef
.tpl_inheritance_item
359 lin_article
.content
= lst
360 subarticle
.add_child lin_article
362 article
.add_child subarticle
368 # Display a list of modules contained in program
372 private var page
= new TplPage
373 redef fun tpl_page
do return page
375 private var sidebar
= new TplSidebar
376 redef fun tpl_sidebar
do return sidebar
378 redef fun tpl_title
do
379 if ctx
.opt_custom_title
.value
!= null then
380 return ctx
.opt_custom_title
.value
.to_s
386 redef fun page_url
do return "index.html"
390 # Display a list of modules, classes and properties
394 private var page
= new TplPage
395 redef fun tpl_page
do return page
397 redef fun tpl_title
do return "Index"
399 redef fun page_url
do return "search.html"
403 # Display a flattened view of the group
407 private var mgroup
: MGroup
409 private var page
= new TplPage
410 redef fun tpl_page
do return page
412 private var sidebar
= new TplSidebar
413 redef fun tpl_sidebar
do return sidebar
415 redef fun tpl_title
do return mgroup
.nitdoc_name
417 redef fun page_url
do return mgroup
.nitdoc_url
419 redef fun tpl_topmenu
do
421 var mproject
= mgroup
.mproject
422 if not mgroup
.is_root
then
423 topmenu
.add_link
new TplLink(mproject
.nitdoc_url
, mproject
.nitdoc_name
)
425 topmenu
.add_link
new TplLink(page_url
, mproject
.nitdoc_name
)
429 # Class list to display in sidebar
430 fun tpl_sidebar_mclasses
do
431 var mclasses
= new HashSet[MClass]
432 mclasses
.add_all intros
433 mclasses
.add_all redefs
434 if mclasses
.is_empty
then return
435 var list
= new TplList.with_classes
(["list-unstyled", "list-labeled"])
437 var sorted
= mclasses
.to_a
438 name_sorter
.sort
(sorted
)
439 for mclass
in sorted
do
440 list
.add_li tpl_sidebar_item
(mclass
)
442 tpl_sidebar
.boxes
.add
new TplSideBox.with_content
("All classes", list
)
445 private fun tpl_sidebar_item
(def
: MClass): TplListItem do
446 var classes
= def
.intro
.tpl_css_classes
.to_a
447 if intros
.has
(def
) then
452 var lnk
= new Template
453 lnk
.add
new TplLabel.with_classes
(classes
)
455 return new TplListItem.with_content
(lnk
)
460 # Display the list of introduced and redefined classes in module
464 private var mmodule
: MModule
466 private var page
= new TplPage
467 redef fun tpl_page
do return page
469 private var sidebar
= new TplSidebar
470 redef fun tpl_sidebar
do return sidebar
472 redef fun tpl_title
do return mmodule
.nitdoc_name
473 redef fun page_url
do return mmodule
.nitdoc_url
475 redef fun tpl_topmenu
do
477 var mproject
= mmodule
.mgroup
.mproject
478 topmenu
.add_link
new TplLink(mproject
.nitdoc_url
, mproject
.nitdoc_name
)
479 topmenu
.add_link
new TplLink(page_url
, mmodule
.nitdoc_name
)
483 # Class list to display in sidebar
484 fun tpl_sidebar_mclasses
do
485 var mclasses
= new HashSet[MClass]
486 mclasses
.add_all mmodule
.filter_intro_mclasses
(ctx
.min_visibility
)
487 mclasses
.add_all mmodule
.filter_redef_mclasses
(ctx
.min_visibility
)
488 if mclasses
.is_empty
then return
489 var list
= new TplList.with_classes
(["list-unstyled", "list-labeled"])
491 var sorted
= mclasses
.to_a
492 name_sorter
.sort
(sorted
)
493 for mclass
in sorted
do
494 list
.add_li tpl_sidebar_item
(mclass
)
496 tpl_sidebar
.boxes
.add
new TplSideBox.with_content
("All classes", list
)
499 private fun tpl_sidebar_item
(def
: MClass): TplListItem do
500 var classes
= def
.intro
.tpl_css_classes
.to_a
501 if def
.intro_mmodule
== mmodule
then
506 var lnk
= new Template
507 lnk
.add
new TplLabel.with_classes
(classes
)
509 return new TplListItem.with_content
(lnk
)
514 # Display a list properties defined or redefined for this class
518 private var mclass
: MClass
520 private var page
= new TplPage
521 redef fun tpl_page
do return page
523 private var sidebar
= new TplSidebar
524 redef fun tpl_sidebar
do return sidebar
526 redef fun tpl_title
do return "{mclass.nitdoc_name}{mclass.tpl_signature.write_to_string}"
527 redef fun page_url
do return mclass
.nitdoc_url
529 redef fun tpl_topmenu
do
531 var mproject
= mclass
.intro_mmodule
.mgroup
.mproject
532 topmenu
.add_link
new TplLink("{mproject.nitdoc_url}", "{mproject.nitdoc_name}")
533 topmenu
.add_link
new TplLink(page_url
, mclass
.nitdoc_name
)
537 # Property list to display in sidebar
538 fun tpl_sidebar_properties
do
539 var by_kind
= new PropertiesByKind.with_elements
(mclass_inherited_mprops
)
540 var summary
= new TplList.with_classes
(["list-unstyled"])
542 by_kind
.sort_groups
(name_sorter
)
543 for g
in by_kind
.groups
do tpl_sidebar_list
(g
, summary
)
544 tpl_sidebar
.boxes
.add
new TplSideBox.with_content
("All properties", summary
)
547 private fun tpl_sidebar_list
(mprops
: PropertyGroup[MProperty], summary
: TplList) do
548 if mprops
.is_empty
then return
549 var entry
= new TplListItem.with_content
(mprops
.title
)
550 var list
= new TplList.with_classes
(["list-unstyled", "list-labeled"])
551 for mprop
in mprops
do
552 list
.add_li tpl_sidebar_item
(mprop
)
555 summary
.elts
.add entry
558 private fun tpl_sidebar_item
(mprop
: MProperty): TplListItem do
559 var classes
= mprop
.intro
.tpl_css_classes
.to_a
560 if not mprops2mdefs
.has_key
(mprop
) then
561 classes
.add
"inherit"
562 var cls_url
= mprop
.intro
.mclassdef
.mclass
.nitdoc_url
563 var def_url
= "{cls_url}#{mprop.nitdoc_id}"
564 var lnk
= new TplLink(def_url
, mprop
.nitdoc_name
)
565 var mdoc
= mprop
.intro
.mdoc_or_fallback
566 if mdoc
!= null then lnk
.title
= mdoc
.short_comment
567 var item
= new Template
568 item
.add
new TplLabel.with_classes
(classes
)
570 return new TplListItem.with_content
(item
)
572 var defs
= mprops2mdefs
[mprop
]
573 if defs
.has
(mprop
.intro
) then
578 var lnk
= new Template
579 lnk
.add
new TplLabel.with_classes
(classes
)
580 lnk
.add mprop
.tpl_anchor
581 return new TplListItem.with_content
(lnk
)
589 private var mproperty
: MProperty
591 private var page
= new TplPage
592 redef fun tpl_page
do return page
594 private var sidebar
= new TplSidebar
595 redef fun tpl_sidebar
do return sidebar
597 redef fun tpl_title
do
598 return "{mproperty.nitdoc_name}{mproperty.tpl_signature.write_to_string}"
601 redef fun page_url
do return mproperty
.nitdoc_url
603 redef fun tpl_topmenu
do
605 var mmodule
= mproperty
.intro_mclassdef
.mmodule
606 var mproject
= mmodule
.mgroup
.mproject
607 var mclass
= mproperty
.intro_mclassdef
.mclass
608 topmenu
.add_link
new TplLink("{mproject.nitdoc_url}", "{mproject.nitdoc_name}")
609 topmenu
.add_link
new TplLink("{mclass.nitdoc_url}", "{mclass.nitdoc_name}")
610 topmenu
.add_link
new TplLink(page_url
, mproperty
.nitdoc_name
)