HTML rendering of Markdown documents

Introduced classes

class HtmlRenderer

markdown2 :: HtmlRenderer

Markdown document renderer to HTML

Redefined classes

redef class MdBlockQuote

markdown2 :: markdown_html_rendering $ MdBlockQuote

A block quote
redef class MdCode

markdown2 :: markdown_html_rendering $ MdCode

An inline code string
redef abstract class MdCodeBlock

markdown2 :: markdown_html_rendering $ MdCodeBlock

A block of code (indented or fenced)
redef class MdEmphasis

markdown2 :: markdown_html_rendering $ MdEmphasis

An emphasis
redef class MdHardLineBreak

markdown2 :: markdown_html_rendering $ MdHardLineBreak

A hardline break (\\n or \n)
redef class MdHeading

markdown2 :: markdown_html_rendering $ MdHeading

A heading
redef class MdHtmlBlock

markdown2 :: markdown_html_rendering $ MdHtmlBlock

An html block
redef class MdHtmlInline

markdown2 :: markdown_html_rendering $ MdHtmlInline

An inlined html string
redef class MdImage

markdown2 :: markdown_html_rendering $ MdImage

An image
redef class MdListItem

markdown2 :: markdown_html_rendering $ MdListItem

An ordered or unordered list item block
redef abstract class MdNode

markdown2 :: markdown_html_rendering $ MdNode

An abstract node
redef class MdOrderedList

markdown2 :: markdown_html_rendering $ MdOrderedList

An ordered list block
redef class MdParagraph

markdown2 :: markdown_html_rendering $ MdParagraph

A paragraph block
redef class MdSoftLineBreak

markdown2 :: markdown_html_rendering $ MdSoftLineBreak

A soft line breack (\r or \n)
redef class MdStrike

markdown2 :: markdown_html_rendering $ MdStrike

Striked text
redef class MdStrongEmphasis

markdown2 :: markdown_html_rendering $ MdStrongEmphasis

A strong emphasis token
redef class MdSuper

markdown2 :: markdown_html_rendering $ MdSuper

Super text
redef class MdText

markdown2 :: markdown_html_rendering $ MdText

A raw text token
redef class MdUnorderedList

markdown2 :: markdown_html_rendering $ MdUnorderedList

An unordered list

All class definitions

class HtmlRenderer

markdown2 $ HtmlRenderer

Markdown document renderer to HTML
redef class MdBlockQuote

markdown2 :: markdown_html_rendering $ MdBlockQuote

A block quote
redef class MdCode

markdown2 :: markdown_html_rendering $ MdCode

An inline code string
redef abstract class MdCodeBlock

markdown2 :: markdown_html_rendering $ MdCodeBlock

A block of code (indented or fenced)
redef class MdEmphasis

markdown2 :: markdown_html_rendering $ MdEmphasis

An emphasis
redef class MdHardLineBreak

markdown2 :: markdown_html_rendering $ MdHardLineBreak

A hardline break (\\n or \n)
redef class MdHeading

markdown2 :: markdown_html_rendering $ MdHeading

A heading
redef class MdHtmlBlock

markdown2 :: markdown_html_rendering $ MdHtmlBlock

An html block
redef class MdHtmlInline

markdown2 :: markdown_html_rendering $ MdHtmlInline

An inlined html string
redef class MdImage

markdown2 :: markdown_html_rendering $ MdImage

An image
redef class MdListItem

markdown2 :: markdown_html_rendering $ MdListItem

An ordered or unordered list item block
redef abstract class MdNode

markdown2 :: markdown_html_rendering $ MdNode

An abstract node
redef class MdOrderedList

markdown2 :: markdown_html_rendering $ MdOrderedList

An ordered list block
redef class MdParagraph

markdown2 :: markdown_html_rendering $ MdParagraph

A paragraph block
redef class MdSoftLineBreak

markdown2 :: markdown_html_rendering $ MdSoftLineBreak

A soft line breack (\r or \n)
redef class MdStrike

markdown2 :: markdown_html_rendering $ MdStrike

Striked text
redef class MdStrongEmphasis

markdown2 :: markdown_html_rendering $ MdStrongEmphasis

A strong emphasis token
redef class MdSuper

markdown2 :: markdown_html_rendering $ MdSuper

Super text
redef class MdText

markdown2 :: markdown_html_rendering $ MdText

A raw text token
redef class MdUnorderedList

markdown2 :: markdown_html_rendering $ MdUnorderedList

An unordered list
package_diagram markdown2::markdown_html_rendering markdown_html_rendering markdown2::markdown_rendering markdown_rendering markdown2::markdown_html_rendering->markdown2::markdown_rendering markdown2::markdown_github markdown_github markdown2::markdown_html_rendering->markdown2::markdown_github markdown2::markdown_wikilinks markdown_wikilinks markdown2::markdown_html_rendering->markdown2::markdown_wikilinks markdown2::markdown_ast markdown_ast markdown2::markdown_rendering->markdown2::markdown_ast markdown2::markdown_block_parsing markdown_block_parsing markdown2::markdown_github->markdown2::markdown_block_parsing markdown2::markdown_wikilinks->markdown2::markdown_block_parsing ...markdown2::markdown_ast ... ...markdown2::markdown_ast->markdown2::markdown_ast ...markdown2::markdown_block_parsing ... ...markdown2::markdown_block_parsing->markdown2::markdown_block_parsing markdown2::markdown2 markdown2 markdown2::markdown2->markdown2::markdown_html_rendering markdown2::nitmd nitmd markdown2::nitmd->markdown2::markdown_html_rendering markdown2::test_markdown test_markdown markdown2::test_markdown->markdown2::markdown_html_rendering nitc::readme_metrics readme_metrics nitc::readme_metrics->markdown2::markdown2 nitc::testing_doc testing_doc nitc::testing_doc->markdown2::markdown2 nitc::readme_metrics... ... nitc::readme_metrics...->nitc::readme_metrics nitc::testing_doc... ... nitc::testing_doc...->nitc::testing_doc a_star-m a_star-m a_star-m->markdown2::nitmd a_star-m... ... a_star-m...->a_star-m

Ancestors

module abstract_collection

core :: abstract_collection

Abstract collection classes and services.
module abstract_text

core :: abstract_text

Abstract class for manipulation of sequences of characters
module array

core :: array

This module introduces the standard array structure.
module bitset

core :: bitset

Services to handle BitSet
module bytes

core :: bytes

Services for byte streams and arrays
module circular_array

core :: circular_array

Efficient data structure to access both end of the sequence.
module codec_base

core :: codec_base

Base for codecs to use with streams
module codecs

core :: codecs

Group module for all codec-related manipulations
module collection

core :: collection

This module define several collection classes.
module core

core :: core

Standard classes and methods used by default by Nit programs and libraries.
module environ

core :: environ

Access to the environment variables of the process
module error

core :: error

Standard error-management infrastructure.
module exec

core :: exec

Invocation and management of operating system sub-processes.
module file

core :: file

File manipulations (create, read, write, etc.)
module fixed_ints

core :: fixed_ints

Basic integers of fixed-precision
module fixed_ints_text

core :: fixed_ints_text

Text services to complement fixed_ints
module flat

core :: flat

All the array-based text representations
module gc

core :: gc

Access to the Nit internal garbage collection mechanism
module hash_collection

core :: hash_collection

Introduce HashMap and HashSet.
module iso8859_1

core :: iso8859_1

Codec for ISO8859-1 I/O
module kernel

core :: kernel

Most basic classes and methods.
module list

core :: list

This module handle double linked lists
module markdown_ast

markdown2 :: markdown_ast

Markdown AST representation
module markdown_block_parsing

markdown2 :: markdown_block_parsing

Markdown blocks parsing
module markdown_inline_parsing

markdown2 :: markdown_inline_parsing

Parser for inline markdown
module math

core :: math

Mathematical operations
module native

core :: native

Native structures for text and bytes
module numeric

core :: numeric

Advanced services for Numeric types
module protocol

core :: protocol

module queue

core :: queue

Queuing data structures and wrappers
module range

core :: range

Module for range of discrete objects.
module re

core :: re

Regular expression support for all services based on Pattern
module ropes

core :: ropes

Tree-based representation of a String.
module sorter

core :: sorter

This module contains classes used to compare things and sorts arrays.
module stream

core :: stream

Input and output streams of characters
module text

core :: text

All the classes and methods related to the manipulation of text entities
module time

core :: time

Management of time and dates
module union_find

core :: union_find

union–find algorithm using an efficient disjoint-set data structure
module utf8

core :: utf8

Codec for UTF-8 I/O

Parents

module markdown_github

markdown2 :: markdown_github

Markdown Github mode
module markdown_rendering

markdown2 :: markdown_rendering

Markdown document rendering

Children

module nitmd

markdown2 :: nitmd

A Markdown parser for Nit.
module test_markdown

markdown2 :: test_markdown

Test suites for module markdown

Descendants

module a_star-m

a_star-m

module api

nitc :: api

Components required to build a web server about the nit model.
module metrics

nitc :: metrics

Various statistics about Nit models and programs
module nitmetrics

nitc :: nitmetrics

A program that collects various metrics on nit programs and libraries
module nitunit

nitc :: nitunit

Testing tool.
module nitweb

nitc :: nitweb

Runs a webserver based on nitcorn that render things from model.
module readme_metrics

nitc :: readme_metrics

Collect common metrics about README files
module testing

nitc :: testing

Test unit generation and execution for Nit.
module testing_doc

nitc :: testing_doc

Testing from code comments.
# HTML rendering of Markdown documents
module markdown_html_rendering

import markdown_rendering
import markdown_github
import markdown_wikilinks

# Markdown document renderer to HTML
class HtmlRenderer
	super MdRenderer

	# HTML output under construction
	private var html: Buffer is noinit

	# Render `document` as HTML
	redef fun render(document) do
		reset
		enter_visit(document)
		return html.write_to_string
	end

	redef fun visit(node) do node.render_html(self)

	# Reset `headings` and internal state
	fun reset do
		html = new Buffer
		if enable_heading_ids then headings.clear
	end

	# Last char visited
	#
	# Used to avoid double blank lines.
	private var last_char: nullable Char = null

	# Add `string` to `html`
	private fun add(string: String) do
		html.append(string)
		if not html.is_empty then
			last_char = html.last
		end
	end

	# Add a raw `html` string to the output
	#
	# Raw means that the string will not be escaped.
	fun add_raw(html: String) do add html

	# Add `text` string to the output
	#
	# The string will be escaped.
	fun add_text(text: String) do add html_escape(text, true)

	# Add a blank line to the output
	fun add_line do
		if last_char != null and last_char != '\n' then
			add "\n"
		end
	end

	# Escape `string` to HTML
	#
	# When `keep_entities`, HTML entities will not be escaped.
	fun html_escape(string: String, keep_entities: Bool): String do
		var buf: nullable Buffer = null
		for i in [0..string.length[ do
			var c = string.chars[i]
			var sub = null
			if c == '&' and (not keep_entities or string.search_from(re_entity, i) == null) then
				sub = "&"
			else if c == '<' then
				sub = "&lt;"
			else if c == '>' then
				sub = "&gt;"
			else if c == '"' then
				sub = "&quot;"
			else
				if buf != null then buf.add c
				continue
			end
			if buf == null then
				buf = new Buffer
				for j in [0..i[ do buf.add string.chars[j]
			end
			buf.append sub
		end

		if buf == null then return string
		return buf.to_s
	end

	# HTML entity pattern
	private var re_entity: Regex = "^&(#x[a-f0-9]\{1,8\}|#[0-9]\{1,8\}|[a-z][a-z0-9]\{1,31\});".to_re

	# Encode the `uri` string
	fun encode_uri(uri: String): String do
		var buf = new Buffer

		var i = 0
		while i < uri.length do
			var c = uri.chars[i]
			if (c >= '0' and c <= '9') or
			   (c >= 'a' and c <= 'z') or
			   (c >= 'A' and c <= 'Z') or
			   c == ';' or c == ',' or c == '/' or c == '?' or
			   c == ':' or c == '@' or c == '=' or c == '+' or
			   c == '$' or c == '-' or c == '_' or c == '.' or
			   c == '!' or c == '~' or c == '*' or c == '(' or
			   c == ')' or c == '#' or c == '\''
			then
				buf.add c
			else if c == '&' then
				buf.append "&amp;"
			else if c == '%' and uri.search_from(re_uri_code, i) != null then
				buf.append uri.substring(i, 3)
				i += 2
			else
				var bytes = c.to_s.bytes
				for b in bytes do buf.append "%{b.to_i.to_hex}".to_upper
			end
			i += 1
		end

		return buf.to_s
	end

	# URI encode pattern
	private var re_uri_code: Regex = "^%[a-zA-Z0-9]\{2\}".to_re

	# Add `id` tags to headings
	var enable_heading_ids = false is optional, writable

	# Associate headings ids to blocks
	var headings = new ArrayMap[String, MdHeading]

	# Strip heading id
	fun strip_id(text: String): String do
		# strip id
		var b = new FlatBuffer
		for c in text do
			if c == ' ' then
				b.add '_'
			else
				if not c.is_letter and
				   not c.is_digit and
				   not allowed_id_chars.has(c) then continue
				b.add c
			end
		end
		var res = b.to_s
		if res.is_empty then res = "_"
		var key = res
		# check for multiple id definitions
		if headings.has_key(key) then
			var i = 1
			key = "{res}_{i}"
			while headings.has_key(key) do
				i += 1
				key = "{res}_{i}"
			end
		end
		return key
	end

	# Allowed characters in ids
	var allowed_id_chars: Array[Char] = ['-', '_', ':', '.']
end

redef class MdNode

	# Render `self` as HTML
	fun render_html(v: HtmlRenderer) do visit_all(v)
end

# Blocks

redef class MdBlockQuote
	redef fun render_html(v) do
		v.add_line
		v.add_raw "<blockquote>"
		v.add_line
		visit_all(v)
		v.add_line
		v.add_raw "</blockquote>"
		v.add_line
	end
end

redef class MdCodeBlock
	redef fun render_html(v) do
		var info = self.info
		v.add_line
		v.add_raw "<pre>"
		v.add_raw "<code"
		if info != null and not info.is_empty then
			v.add_raw " class=\"language-{info.split(" ").first}\""
		end
		v.add_raw ">"
		var literal = self.literal or else ""
		var lines = literal.split("\n")
		for i in [0..lines.length[ do
			var line = lines[i]
			v.add_raw v.html_escape(line, false)
			if i < lines.length - 1 then
				v.add_raw "\n"
			end
		end
		v.add_raw "</code>"
		v.add_raw "</pre>"
		v.add_line
	end
end

redef class MdHeading
	redef fun render_html(v) do
		v.add_line
		if v.enable_heading_ids then
			var id = self.id
			if id == null then
				id = v.strip_id(title)
				v.headings[id] = self
				self.id = id
			end
			v.add_raw "<h{level} id=\"{id}\">"
		else
			v.add_raw "<h{level}>"
		end
		visit_all(v)
		v.add_raw "</h{level}>"
		v.add_line
	end

	#
	var id: nullable String = null

	#
	fun title: String do
		var v = new RawTextVisitor
		return v.render(self)
	end
end

redef class MdUnorderedList
	redef fun render_html(v) do
		v.add_line
		v.add_raw "<ul>"
		v.add_line
		visit_all(v)
		v.add_line
		v.add_raw "</ul>"
		v.add_line
	end
end

redef class MdOrderedList
	redef fun render_html(v) do
		var start = self.start_number
		v.add_line
		v.add_raw "<ol"
		if start != 1 then
			v.add_raw " start=\"{start}\""
		end
		v.add_raw ">"
		v.add_line
		visit_all(v)
		v.add_line
		v.add_raw "</ol>"
		v.add_line
	end
end

redef class MdListItem
	redef fun render_html(v) do
		v.add_raw "<li>"
		visit_all(v)
		v.add_raw "</li>"
		v.add_line
	end
end

redef class MdParagraph
	redef fun render_html(v) do
		var is_tight = is_in_tight_list
		if not is_tight then
			v.add_line
			v.add_raw "<p>"
		end
		visit_all(v)
		if not is_tight then
			v.add_raw "</p>"
			v.add_line
		end
	end
end

redef class MdThematicBreak
	redef fun render_html(v) do
		v.add_line
		v.add_raw "<hr />"
		v.add_line
	end
end

redef class MdHtmlBlock
	redef fun render_html(v) do
		v.add_line
		var literal = self.literal or else ""
		var lines = literal.split("\n")
		for i in [0..lines.length[ do
			var line = lines[i]
			if not line.trim.is_empty then
				v.add_raw line
			end
			if i < lines.length - 1 then
				v.add_raw "\n"
			end
		end
		v.add_line
	end
end

# Inlines

redef class MdHardLineBreak
	redef fun render_html(v) do
		v.add_raw "<br />"
		v.add_line
	end
end

redef class MdSoftLineBreak
	redef fun render_html(v) do
		v.add_raw "\n"
	end
end

redef class MdCode
	redef fun render_html(v) do
		v.add_raw "<code>"
		v.add_raw v.html_escape(literal, false)
		v.add_raw "</code>"
	end
end

redef class MdEmphasis
	redef fun render_html(v) do
		v.add_raw "<em>"
		visit_all(v)
		v.add_raw "</em>"
	end
end

redef class MdStrongEmphasis
	redef fun render_html(v) do
		v.add_raw "<strong>"
		visit_all(v)
		v.add_raw "</strong>"
	end
end

redef class MdHtmlInline
	redef fun render_html(v) do
		v.add_raw literal
	end
end

redef class MdImage
	redef fun render_html(v) do
		var url = self.destination
		var title = self.title
		v.add_raw "<img"
		v.add_raw " src=\"{v.encode_uri(url)}\""

		var alt_text = self.alt_text
		v.add_raw " alt=\"{alt_text}\""

		if title != null and not title.is_empty then
			v.add_raw " title=\""
			v.add_text title
			v.add_raw "\""
		end

		v.add_raw " />"
	end

	private fun alt_text: String do
		var v = new RawTextVisitor
		return v.render(self)
	end
end

redef class MdLink
	redef fun render_html(v) do
		var url = self.destination
		var title = self.title
		v.add_raw "<a"
		v.add_raw " href=\"{v.encode_uri(url)}\""
		if title != null and not title.is_empty then
			v.add_raw " title=\""
			v.add_text title
			v.add_raw "\""
		end
		v.add_raw ">"
		visit_all(v)
		v.add_raw "</a>"
	end
end

redef class MdText
	redef fun render_html(v) do
		v.add_text literal
	end
end

# Github mode

redef class MdStrike
	redef fun render_html(v) do
		v.add_raw "<del>"
		visit_all(v)
		v.add_raw "</del>"
	end
end

redef class MdSuper
	redef fun render_html(v) do
		v.add_raw "<sup>"
		visit_all(v)
		v.add_raw "</sup>"
	end
end

# Wikilinks mode

redef class MdWikilink

	# Dummy rendering of wikilinks
	#
	# Clients should redefine this.
	redef fun render_html(v) do
		v.add_raw "<wiki link=\"{v.encode_uri(link)}\">"
		visit_all(v)
		v.add_raw "</wiki>"
	end
end
lib/markdown2/markdown_html_rendering.nit:15,1--458,3