X-Git-Url: http://nitlanguage.org diff --git a/src/web/api_docdown.nit b/src/web/api_docdown.nit index 3510b64..84dcc8c 100644 --- a/src/web/api_docdown.nit +++ b/src/web/api_docdown.nit @@ -19,23 +19,30 @@ import api_graph intrude import doc_down intrude import markdown::wikilinks import doc_commands +import model::model_index -# Docdown handler accept docdown as POST data and render it as HTML -class APIDocdown - super APIHandler - - # Modelbuilder used by the commands - var modelbuilder: ModelBuilder - +redef class NitwebConfig # Specific Markdown processor to use within Nitweb var md_processor: MarkdownProcessor is lazy do var proc = new MarkdownProcessor - proc.emitter.decorator = new NitwebDecorator(view, modelbuilder) + proc.decorator = new NitwebDecorator(view, modelbuilder) return proc end +end + +redef class APIRouter + redef init do + super + use("/docdown/", new APIDocdown(config)) + end +end + +# Docdown handler accept docdown as POST data and render it as HTML +class APIDocdown + super APIHandler redef fun post(req, res) do - res.html md_processor.process(req.body) + res.html config.md_processor.process(req.body) end end @@ -51,11 +58,36 @@ class NitwebDecorator # 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 "" + v.write_mentity_link(mentity, text.write_to_string) + v.add "" + 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 @@ -70,173 +102,198 @@ class NitwebInlineDecorator 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 "

Warning: {text}

" + fun write_warning(text: String) do + emit_text "

Warning: {text}

" end # Write an error in the output - fun write_error(v: MarkdownEmitter, text: String) do - v.emit_text "

Error: {text}

" + fun write_error(text: String) do + emit_text "

Error: {text}

" 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 "" 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 "

" - write_mentity_link(v, mentity) - v.add " - " - v.emit_text mdoc.synopsis - v.add "

" - 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 "

" + 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 "

" + 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 "" - 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 "
"
@@ -256,17 +313,27 @@ end
 
 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