nitdoc: update ModelView
[nit.git] / src / doc / doc_phases / doc_readme.nit
index 605ddac..f0e2c31 100644 (file)
@@ -20,6 +20,7 @@ intrude import markdown::wikilinks
 import doc_commands
 import doc_down
 import doc_intros_redefs
+import model::model_index
 
 # Generate content of `ReadmePage`.
 #
@@ -37,7 +38,8 @@ class ReadmePhase
        fun warning(location: nullable MDLocation, page: ReadmePage, message: String) do
                var loc = null
                if location != null then
-                       loc = location.to_location(page.mentity.mdoc.location.file)
+                       var mdoc = page.mentity.mdoc
+                       if mdoc != null then loc = location.to_location(mdoc.location.file)
                end
                ctx.warning(loc, "readme-warning", message)
        end
@@ -50,14 +52,15 @@ end
 
 redef class ReadmePage
        redef fun build_content(v, doc) do
-               if mentity.mdoc == null then
+               var mdoc = mentity.mdoc
+               if mdoc == null then
                        v.warning(null, self, "Empty README for group `{mentity}`")
                        return
                end
                var proc = new MarkdownProcessor
                proc.emitter = new ReadmeMdEmitter(proc, self, v)
                proc.emitter.decorator = new ReadmeDecorator
-               var md = mentity.mdoc.content.join("\n")
+               var md = mdoc.content.join("\n")
                proc.process(md)
        end
 end
@@ -90,6 +93,7 @@ class ReadmeMdEmitter
        # Called from `add_headline`.
        private fun open_section(lvl: Int, title: String) do
                var section = new ReadmeSection(title.escape_to_c, title, lvl, processor)
+               var current_section = self.current_section
                if current_section == null then
                        page.root.add_child(section)
                else
@@ -118,6 +122,7 @@ class ReadmeMdEmitter
        # This closes the current article, inserts `article` then opens a new article.
        private fun add_article(article: DocArticle) do
                close_article
+               var current_section = self.current_section
                if current_section == null then
                        page.root.add_child(article)
                else
@@ -146,21 +151,55 @@ class ReadmeMdEmitter
                context.pop
                pop_buffer
        end
+
+       # Find mentities matching `query`.
+       fun find_mentities(query: String): Array[MEntity] do
+               # search MEntities by full_name
+               var mentity = phase.doc.mentity_by_full_name(query)
+               if mentity != null then return [mentity]
+               # search MEntities by name
+               return phase.doc.mentities_by_name(query)
+       end
+
+       # Suggest mentities based on `query`.
+       fun suggest_mentities(query: String): Array[MEntity] do
+               return phase.doc.find(query, 3)
+       end
+
+       # Display a warning message with suggestions.
+       fun warn(token: TokenWikiLink, message: String, suggest: nullable Array[MEntity]) do
+               var msg = new Buffer
+               msg.append message
+               if suggest != null and suggest.not_empty then
+                       msg.append " (suggestions: "
+                       var i = 0
+                       for s in suggest do
+                               msg.append "`{s.full_name}`"
+                               if i < suggest.length - 1 then msg.append ", "
+                               i += 1
+                       end
+                       msg.append ")"
+               end
+               phase.warning(token.location, page, msg.write_to_string)
+       end
 end
 
 # MarkdownDecorator used to decorated the Readme file with links between doc entities.
 class ReadmeDecorator
        super MdDecorator
 
+       # Parser used to process doc commands
+       var parser = new DocCommandParser
+
        redef type EMITTER: ReadmeMdEmitter
 
        redef fun add_headline(v, block) do
-               var txt = block.block.first_line.value
+               var txt = block.block.first_line.as(not null).value
                var lvl = block.depth
                if not v.context.is_empty then
                        v.close_article
                        while v.current_section != null do
-                               if v.current_section.depth < lvl then break
+                               if v.current_section.as(not null).depth < lvl then break
                                v.close_section
                        end
                end
@@ -169,14 +208,14 @@ class ReadmeDecorator
        end
 
        redef fun add_wikilink(v, token) do
-               var link = token.link.to_s
-               var cmd = new DocCommand(link)
-               if cmd isa UnknownCommand then
+               var link = token.link.as(not null).to_s
+               var cmd = parser.parse(link)
+               if cmd == null then
                        # search MEntities by name
-                       var res = v.phase.doc.mentities_by_name(link.to_s)
+                       var res = v.find_mentities(link.to_s)
                        # no match, print warning and display wikilink as is
                        if res.is_empty then
-                               v.phase.warning(token.location, v.page, "Link to unknown entity `{link}`")
+                               v.warn(token, "Link to unknown entity `{link}`", v.suggest_mentities(link.to_s))
                                super
                        else
                                add_mentity_link(v, res.first, token.name, token.comment)
@@ -191,74 +230,52 @@ class ReadmeDecorator
                # TODO real link
                var link = mentity.full_name
                if name == null then name = mentity.name
-               if comment == null and mentity.mdoc != null then
-                       comment = mentity.mdoc.synopsis
+               if comment == null then
+                       var mdoc = mentity.mdoc
+                       if mdoc != null then comment = mdoc.synopsis
                end
                add_link(v, link, name, comment)
        end
 end
 
-redef interface DocCommand
+redef class DocCommand
 
        # Render the content of the doc command.
        fun render(v: ReadmeMdEmitter, token: TokenWikiLink) is abstract
-
-       # Search `doc` model for mentities match `string`.
-       fun search_model(doc: DocModel, string: String): Array[MEntity] do
-               var res
-               if string.has("::") then
-                       res = doc.mentities_by_namespace(string).to_a
-               else
-                       res = doc.mentities_by_name(string).to_a
-               end
-               return res
-       end
 end
 
-redef class ArticleCommand
+redef class CommentCommand
        redef fun render(v, token) do
                var string = args.first
-               var res = search_model(v.phase.doc, string)
-               res = filter_results(res)
+               var res = v.find_mentities(string)
                if res.is_empty then
-                       v.phase.warning(
-                               token.location, v.page,
-                               "Try to include documentation of unknown entity `{args.first}`")
+                       v.warn(token,
+                               "Try to include documentation of unknown entity `{string}`",
+                               v.suggest_mentities(string))
                        return
                end
-               if res.length > 1 then
-                       v.phase.warning(token.location, v.page, "conflicting article for `{args.first}` (choices : {res.join(", ")})")
-               end
                v.add_article new DocumentationArticle("readme", "Readme", res.first)
        end
-
-       private fun filter_results(res: Array[MEntity]): Array[MEntity] do
-               var out = new Array[MEntity]
-               for e in res do
-                       if e isa MPackage then continue
-                       if e isa MGroup then continue
-                       out.add e
-               end
-               return out
-       end
 end
 
 redef class ListCommand
        redef fun render(v, token) do
                var string = args.first
-               var res = search_model(v.phase.doc, string)
+               var res = v.find_mentities(string)
                if res.is_empty then
-                       v.phase.warning(token.location, v.page, "include article for unknown entity `{args.first}`")
+                       v.warn(token,
+                               "Try to include article of unknown entity `{string}`",
+                               v.suggest_mentities(string))
                        return
                end
                if res.length > 1 then
-                       v.phase.warning(token.location, v.page, "conflicting article for `{args.first}` (choices : {res.join(", ")})")
+                       v.warn(token, "Conflicting article for `{args.first}`", res)
                end
                var mentity = res.first
                if mentity isa MModule then
                        v.add_article new MEntitiesListArticle("Classes", null, mentity.mclassdefs)
                else if mentity isa MClass then
-                       var mprops = mentity.collect_intro_mproperties(public_visibility)
+                       var mprops = mentity.collect_intro_mproperties(v.phase.doc)
                        v.add_article new MEntitiesListArticle("Methods", null, mprops.to_a)
                else if mentity isa MClassDef then
                        v.add_article new MEntitiesListArticle("Methods", null, mentity.mpropdefs)