X-Git-Url: http://nitlanguage.org diff --git a/src/doc/doc_phases/doc_readme.nit b/src/doc/doc_phases/doc_readme.nit index 605ddac..b7d2ac8 100644 --- a/src/doc/doc_phases/doc_readme.nit +++ b/src/doc/doc_phases/doc_readme.nit @@ -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,21 +52,21 @@ 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 proc = new ReadmeMdProcessor(self, v) + proc.decorator = new ReadmeDecorator + var md = mdoc.content.join("\n") proc.process(md) end end # Markdown emitter used to produce the `ReadmeArticle`. -class ReadmeMdEmitter - super MarkdownEmitter +class ReadmeMdProcessor + super MarkdownProcessor # Readme page being decorated. var page: ReadmePage @@ -89,7 +91,8 @@ 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 section = new ReadmeSection(title.escape_to_c, title, lvl, self) + var current_section = self.current_section if current_section == null then page.root.add_child(section) else @@ -118,6 +121,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 @@ -132,7 +136,7 @@ class ReadmeMdEmitter private fun open_article do var section: DocComposite = page.root if current_section != null then section = current_section.as(not null) - var article = new ReadmeArticle("mdarticle-{section.children.length}", null, processor) + var article = new ReadmeArticle("mdarticle-{section.children.length}", null, self) section.add_child(article) context.add article push_article article @@ -146,21 +150,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 - redef type EMITTER: ReadmeMdEmitter + # Parser used to process doc commands + var parser = new DocCommandParser + + redef type PROCESSOR: ReadmeMdProcessor 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 +207,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) @@ -187,78 +225,56 @@ class ReadmeDecorator end # Renders a link to a mentity. - private fun add_mentity_link(v: EMITTER, mentity: MEntity, name, comment: nullable Text) do + private fun add_mentity_link(v: PROCESSOR, mentity: MEntity, name, comment: nullable Text) do # 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 + fun render(v: ReadmeMdProcessor, token: TokenWikiLink) is abstract 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)