import doc_commands
import doc_down
import doc_intros_redefs
+import model::model_index
# Generate content of `ReadmePage`.
#
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
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
#
# 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
# 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
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
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
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)
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 MProject 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", mentity.mclassdefs)
+ v.add_article new MEntitiesListArticle("Classes", null, mentity.mclassdefs)
else if mentity isa MClass then
- var mprops = mentity.collect_intro_mproperties(public_visibility)
- v.add_article new MEntitiesListArticle("Methods", mprops.to_a)
+ 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", mentity.mpropdefs)
+ v.add_article new MEntitiesListArticle("Methods", null, mentity.mpropdefs)
end
end
end