intrude import doc_down
intrude import markdown::wikilinks
import doc_commands
+import model::model_index
+
+redef class NitwebConfig
+ # Specific Markdown processor to use within Nitweb
+ var md_processor: MarkdownProcessor is lazy do
+ var proc = new MarkdownProcessor
+ proc.decorator = new NitwebDecorator(view, modelbuilder)
+ return proc
+ end
+end
redef class APIRouter
redef init do
class APIDocdown
super APIHandler
- # Specific Markdown processor to use within Nitweb
- var md_processor: MarkdownProcessor is lazy do
- var proc = new MarkdownProcessor
- proc.emitter.decorator = new NitwebDecorator(view, config.modelbuilder)
- return proc
- end
-
redef fun post(req, res) do
- res.html md_processor.process(req.body)
+ res.html config.md_processor.process(req.body)
end
end
# Modelbuilder used to access code
var modelbuilder: ModelBuilder
+ redef fun add_span_code(v, buffer, from, to) do
+ var text = new FlatBuffer
+ buffer.read(text, from, to)
+ var name = text.write_to_string
+ name = name.replace("nullable ", "")
+ var mentity = try_find_mentity(view, name)
+ if mentity == null then
+ super
+ else
+ v.add "<code>"
+ v.write_mentity_link(mentity, text.write_to_string)
+ v.add "</code>"
+ end
+ end
+
+ private fun try_find_mentity(view: ModelView, text: String): nullable MEntity do
+ var mentity = view.mentity_by_full_name(text)
+ if mentity != null then return mentity
+
+ var mentities = view.mentities_by_name(text)
+ if mentities.is_empty then
+ return null
+ else if mentities.length > 1 then
+ # TODO smart resolve conflicts
+ end
+ return mentities.first
+ end
+
redef fun add_wikilink(v, token) do
- var link = token.link
- if link == null then return
- var cmd = new DocCommand(link.write_to_string)
- cmd.render(v, token, view)
+ v.render_wikilink(token, view)
end
end
var modelbuilder: ModelBuilder
redef fun add_wikilink(v, token) do
- var link = token.link
- if link == null then return
- var cmd = new DocCommand(link.write_to_string)
- cmd.render(v, token, view)
+ v.render_wikilink(token, view)
end
end
-redef interface DocCommand
+redef class MarkdownProcessor
- # Emit the HTML related to the execution of this doc command
- fun render(v: MarkdownEmitter, token: TokenWikiLink, model: ModelView) do
- write_error(v, "Not yet implemented command `{token.link or else "null"}`")
+ # Parser used to process doc commands
+ var parser = new DocCommandParser
+
+ # Render a wikilink
+ fun render_wikilink(token: TokenWikiLink, model: ModelView) do
+ var link = token.link
+ if link == null then return
+ var name = token.name
+ if name != null then link = "{name} | {link}"
+ var cmd = parser.parse(link.write_to_string)
+ if cmd == null then
+ var full_name = if token.link != null then token.link.as(not null).write_to_string.trim else null
+ if full_name == null or full_name.is_empty then
+ write_error("empty wikilink")
+ return
+ end
+ var mentity = find_mentity(model, full_name)
+ if mentity == null then return
+ name = if token.name != null then token.name.as(not null).to_s else null
+ write_mentity_link(mentity, name)
+ return
+ else
+ for message in parser.errors do
+ if message.level == 1 then
+ write_error(message.message)
+ else if message.level > 1 then
+ write_warning(message.message)
+ end
+ end
+ end
+ cmd.render(self, token, model)
end
- # Find the MEntity ` with `full_name`.
- fun find_mentity(model: ModelView, full_name: nullable String): nullable MEntity do
- if full_name == null then return null
- return model.mentity_by_full_name(full_name.from_percent_encoding)
+ # Find the MEntity that matches `name`.
+ #
+ # Write an error if the entity is not found
+ fun find_mentity(model: ModelView, name: nullable String): nullable MEntity do
+ if name == null then
+ write_error("no MEntity found")
+ return null
+ end
+ # Lookup by full name
+ var mentity = model.mentity_by_full_name(name)
+ if mentity != null then return mentity
+
+ var mentities = model.mentities_by_name(name)
+ if mentities.is_empty then
+ var suggest = model.find(name, 3)
+ var msg = new Buffer
+ msg.append "no MEntity found for name `{name}`"
+ if 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
+ write_error(msg.write_to_string)
+ return null
+ else if mentities.length > 1 then
+ var msg = new Buffer
+ msg.append "conflicts for name `{name}`"
+ msg.append " (conflicts: "
+ var i = 0
+ for s in mentities do
+ msg.append "`{s.full_name}`"
+ if i < mentities.length - 1 then msg.append ", "
+ i += 1
+ end
+ msg.append ")"
+ write_warning(msg.write_to_string)
+ end
+ return mentities.first
end
# Write a warning in the output
- fun write_warning(v: MarkdownEmitter, text: String) do
- v.emit_text "<p class='text-warning'>Warning: {text}</p>"
+ fun write_warning(text: String) do
+ emit_text "<p class='text-warning'>Warning: {text}</p>"
end
# Write an error in the output
- fun write_error(v: MarkdownEmitter, text: String) do
- v.emit_text "<p class='text-danger'>Error: {text}</p>"
+ fun write_error(text: String) do
+ emit_text "<p class='text-danger'>Error: {text}</p>"
end
# Write a link to a mentity in the output
- fun write_mentity_link(v: MarkdownEmitter, mentity: MEntity) do
+ fun write_mentity_link(mentity: MEntity, text: nullable String) do
var link = mentity.web_url
- var name = mentity.name
+ var name = text or else mentity.name
var mdoc = mentity.mdoc_or_fallback
var comment = null
if mdoc != null then comment = mdoc.synopsis
- v.decorator.add_link(v, link, name, comment)
+ decorator.add_link(self, link, name, comment)
end
-end
-redef class UnknownCommand
- redef fun render(v, token, model) do
- var link = token.link
- if link == null then
- write_error(v, "Empty command")
- return
- end
- var full_name = link.write_to_string
- var mentity = find_mentity(model, full_name)
- if mentity == null then
- write_error(v, "Unknown command `{link}`")
- return
+ # Write a mentity list in the output
+ fun write_mentity_list(mentities: Collection[MEntity]) do
+ add "<ul>"
+ for mentity in mentities do
+ var mdoc = mentity.mdoc_or_fallback
+ add "<li>"
+ write_mentity_link(mentity)
+ if mdoc != null then
+ add " - "
+ emit_text mdoc.synopsis
+ end
+ add "</li>"
end
- write_mentity_link(v, mentity)
+ add "</ul>"
end
end
-redef class ArticleCommand
- redef fun render(v, token, model) do
- if args.is_empty then
- write_error(v, "Expected one arg: the MEntity name")
- return
- end
- var name = args.first
- var mentity = find_mentity(model, name)
- if mentity == null then
- write_error(v, "No MEntity found for name `{name}`")
- return
- end
- var mdoc = mentity.mdoc_or_fallback
- if mdoc == null then
- write_warning(v, "No MDoc for mentity `{name}`")
- return
- end
- v.add "<h3>"
- write_mentity_link(v, mentity)
- v.add " - "
- v.emit_text mdoc.synopsis
- v.add "</h3>"
- v.add v.processor.process(mdoc.comment).write_to_string
+redef class DocCommand
+
+ # Emit the HTML related to the execution of this doc command
+ fun render(v: MarkdownProcessor, token: TokenWikiLink, model: ModelView) do
+ v.write_error("not yet implemented command `{token.link or else "null"}`")
end
end
redef class CommentCommand
redef fun render(v, token, model) do
- if args.is_empty then
- write_error(v, "Expected one arg: the MEntity name")
- return
- end
- var name = args.first
- var mentity = find_mentity(model, name)
- if mentity == null then
- write_error(v, "No MEntity found for name `{name}`")
- return
- end
+ var name = arg
+ var mentity = v.find_mentity(model, name)
+ if mentity == null then return
var mdoc = mentity.mdoc_or_fallback
if mdoc == null then
- write_warning(v, "No MDoc for mentity `{name}`")
+ v.write_warning("no MDoc for mentity `{name}`")
return
end
- v.add v.processor.process(mdoc.comment).write_to_string
+ v.add "<h3>"
+ if not opts.has_key("no-link") then
+ v.write_mentity_link(mentity)
+ end
+ if not opts.has_key("no-link") and not opts.has_key("no-synopsis") then
+ v.add " - "
+ end
+ if not opts.has_key("no-synopsis") then
+ v.emit_text mdoc.html_synopsis.write_to_string
+ end
+ v.add "</h3>"
+ if not opts.has_key("no-comment") then
+ v.add v.process(mdoc.comment).write_to_string
+ end
end
end
redef class ListCommand
redef fun render(v, token, model) do
- if args.is_empty then
- write_error(v, "Expected one arg: the MEntity name")
- return
- end
- var name = args.first
- var mentity = find_mentity(model, name)
+ var name = arg
+ var mentity = v.find_mentity(model, name)
+ if mentity == null then return
if mentity isa MPackage then
- write_list(v, mentity.mgroups)
+ v.write_mentity_list(mentity.mgroups)
else if mentity isa MGroup then
var res = new Array[MEntity]
res.add_all mentity.in_nesting.smallers
res.add_all mentity.mmodules
- write_list(v, res)
+ v.write_mentity_list(res)
else if mentity isa MModule then
- write_list(v, mentity.mclassdefs)
+ v.write_mentity_list(mentity.mclassdefs)
else if mentity isa MClass then
- write_list(v, mentity.collect_intro_mproperties(model))
+ v.write_mentity_list(mentity.collect_intro_mproperties(model))
else if mentity isa MClassDef then
- write_list(v, mentity.mpropdefs)
+ v.write_mentity_list(mentity.mpropdefs)
else if mentity isa MProperty then
- write_list(v, mentity.mpropdefs)
+ v.write_mentity_list(mentity.mpropdefs)
else
- write_error(v, "No list found for name `{name}`")
+ v.write_error("no list found for name `{name}`")
end
end
-
- # Write a mentity list in the output
- fun write_list(v: MarkdownEmitter, mentities: Collection[MEntity]) do
- v.add "<ul>"
- for mentity in mentities do
- var mdoc = mentity.mdoc_or_fallback
- v.add "<li>"
- write_mentity_link(v, mentity)
- if mdoc != null then
- v.add " - "
- v.emit_text mdoc.synopsis
- end
- v.add "</li>"
- end
- v.add "</ul>"
- end
end
redef class CodeCommand
redef fun render(v, token, model) do
- if args.is_empty then
- write_error(v, "Expected one arg: the MEntity name")
- return
- end
- var name = args.first
- var mentity = find_mentity(model, name)
- if mentity == null then
- write_error(v, "No MEntity found for name `{name}`")
- return
- end
+ var name = arg
+ var mentity = v.find_mentity(model, name)
+ if mentity == null then return
if mentity isa MClass then mentity = mentity.intro
if mentity isa MProperty then mentity = mentity.intro
var source = render_source(mentity, v.decorator.as(NitwebDecorator).modelbuilder)
if source == null then
- write_error(v, "No source for MEntity `{name}`")
+ v.write_error("no source for MEntity `{name}`")
return
end
v.add "<pre>"
redef class GraphCommand
redef fun render(v, token, model) do
- if args.is_empty then
- write_error(v, "Expected one arg: the MEntity name")
- return
- end
- var name = args.first
- var mentity = find_mentity(model, name)
- if mentity == null then
- write_error(v, "No MEntity found for name `{name}`")
- return
- end
+ var name = arg
+ var mentity = v.find_mentity(model, name)
+ if mentity == null then return
var g = new InheritanceGraph(mentity, model)
- v.add g.draw(3, 3).to_svg
+ var pdepth = if opts.has_key("pdepth") and opts["pdepth"].is_int then
+ opts["pdepth"].to_i else 3
+ var cdepth = if opts.has_key("cdepth") and opts["cdepth"].is_int then
+ opts["cdepth"].to_i else 3
+ v.add g.draw(pdepth, cdepth).to_svg
+ end
+end
+
+redef class Text
+ # Read `self` between `nstart` and `nend` (excluded) and writte chars to `out`.
+ private fun read(out: FlatBuffer, nstart, nend: Int): Int do
+ var pos = nstart
+ while pos < length and pos < nend do
+ out.add self[pos]
+ pos += 1
+ end
+ if pos == length then return -1
+ return pos
end
end