Index: modules/markdown/markdown.man ================================================================== --- modules/markdown/markdown.man +++ modules/markdown/markdown.man @@ -1,22 +1,53 @@ -[comment {-*- tcl -*- doctools manpage}] -[manpage_begin markdown n 1.0] -[moddesc {Markdown to HTML Converter}] -[titledesc {Converts Markdown text to HTML}] -[category {Text processing}] -[require Tcl 8.5] -[require textutil [opt 0.8]] -[description] - -The package [package Markdown] provides a command to convert -Markdown annotated text into HMTL. - -[list_begin definitions] -[call [cmd ::Markdown::convert] [arg "markdown"]] - -This command takes in a block of Markdown text, and returns a block -of HTML. -[list_end] - -[vset CATEGORY textutil] -[include ../doctools2base/include/feedback.inc] +[vset VERSION 1.1] +[comment {-*- tcl -*- doctools manpage}] +[manpage_begin markdown n [vset VERSION]] +[moddesc {Markdown to HTML Converter}] +[titledesc {Converts Markdown text to HTML}] +[category {Text processing}] +[require Tcl 8.5] +[require Markdown [vset VERSION]] +[require textutil [opt 0.8]] +[description] + +The package [package Markdown] provides a command to convert +Markdown annotated text into HMTL. + +[list_begin definitions] +[call [cmd ::Markdown::convert] [arg "markdown"]] + +This command takes in a block of Markdown text, and returns a block +of HTML. + +[para] The converter supports two types of syntax highlighting for +fenced code blocks: highlighting via a registered converter +(see [cmd ::Markdown::register]), or pure JavaScript highlighting, +e.g. via "highlight.js", where the language specifier used in the +markup is set as CSS class of the "code" element in the returned markup. + +[call [cmd ::Markdown::register] [arg "langspec"] [arg "converter"]] + +Register a language specific converter for prettifying a code block +(e.g. syntax highlighting). Markdown supports fenced code blocks with +an optional language specifier (e.g. "tcl"). When the markdown parser +processes such a code block and a converter for the specified langspec +is registered, the converter is called with the raw code block as +argument. The converter is supposed to return the markup of the code +block as result. The specified converter can be an arbitrary Tcl +command, the raw text block is added as last argument upon invocation. + +[call [cmd ::Markdown::get_lang_counter]] + +Return a dict of language specifier and number of occurrences in +fenced code blocks. This function can be used e.g. to detect, whether +some CSS or JavaScript headers should be included for rendering +without the need of postprocessing the rendered result. + +[call [cmd ::Markdown::reset_lang_counter]] + +Reset the language counters. + +[list_end] + +[vset CATEGORY textutil] +[include ../doctools2base/include/feedback.inc] [manpage_end] Index: modules/markdown/markdown.tcl ================================================================== --- modules/markdown/markdown.tcl +++ modules/markdown/markdown.tcl @@ -53,10 +53,34 @@ array set ::Markdown::_references [collect_references markdown] # PROCESS return [apply_templates markdown] } + + # + # Register a language specific converter. This converter can be + # used for fenced code blocks to transform the code block into a + # prettified HTML. + # + proc register {lang_specifier converter} { + set ::Markdown::converter($lang_specifier) $converter + } + + # + # Return a dict (attribute value pairs) of language specifiers and + # the number of occurrences as they were used in fenced code blocks. + # + proc get_lang_counter {} { + return [array get ::Markdown::lang_counter] + } + + # + # Reset the language counters of fenced code blocks. + # + proc reset_lang_counter {} { + array unset ::Markdown::lang_counter + } ## \private proc collect_references {markdown_var} { upvar $markdown_var markdown @@ -110,11 +134,11 @@ # PROCESS MARKDOWN while {$index < $no_lines} { set line [lindex $lines $index] - switch -regexp $line { + switch -regexp -matchvar matches -- $line { {^\s*$} { # EMPTY LINES if {![regexp {^\s*$} [lindex $lines [expr $index - 1]]]} { append result "\n\n" } @@ -229,19 +253,38 @@ } set code_result [join $code_result \n] append result
$code_result \n
}
- {^(?:(?:`{3,})|(?:~{3,}))(?:\{?\S+\}?)?\s*$} {
+ {^(?:(?:`{3,})|(?:~{3,}))\{?(\S+)?\}?\s*$} {
# FENCED CODE BLOCKS
set code_result {}
-
if {[string index $line 0] eq {`}} {
set end_match {^`{3,}\s*$}
} else {
set end_match {^~{3,}\s*$}
}
+ #
+ # A language specifier might be provided
+ # immediately after the leading delimiters.
+ #
+ # ```tcl
+ #
+ # The language specifier is used for two purposes:
+ # a) As a CSS class name
+ # (useful e.g. for highlight.js)
+ # b) As a name for a source code to HTML converter.
+ # When such a converter is registered,
+ # the codeblock will be sent through this converter.
+ #
+ set lang_specifier [string tolower [lindex $matches end]]
+ if {$lang_specifier ne ""} {
+ set code_CCS_class " class='$lang_specifier'"
+ incr ::Markdown::lang_counter($lang_specifier)
+ } else {
+ set code_CCS_class ""
+ }
while {$index < $no_lines} {
incr index
set line [lindex $lines $index]
@@ -249,16 +292,30 @@
if {[regexp $end_match $line]} {
incr index
break
}
- lappend code_result [html_escape $line]
+ lappend code_result $line
}
set code_result [join $code_result \n]
- append result $code_result
+ #
+ # If there is a converter registered, apply it on
+ # the resulting snippet.
+ #
+ if {[info exists ::Markdown::converter($lang_specifier)]} {
+ set code_result [{*}$::Markdown::converter($lang_specifier) $code_result]
+ } else {
+ set code_result [html_escape $code_result]
+ }
+ append result \
+ "" \
+ \
+ $code_result \
+
}
+
{^[ ]{0,3}(?:\*|-|\+) |^[ ]{0,3}\d+\. } {
# LISTS
set list_result {}
# continue matching same list type
@@ -749,7 +806,7 @@
proc html_escape {text} {
return [string map {& & < < > > \" "} $text]
}
}
-package provide Markdown 1.0
+package provide Markdown 1.1
Index: modules/markdown/markdown.test
==================================================================
--- modules/markdown/markdown.test
+++ modules/markdown/markdown.test
@@ -410,10 +410,40 @@
Here comes a generic example:
+ +set x 1
+}
+
+test mdtest-1.11 {fenced code block with language specifier} -body {
+
+ convert {
+ Here comes a Tcl example:
+
+ ```tcl
+ set x 1
+ ```
+ }
+} -result {
+ Here comes a Tcl example:
+ +set x 1
+}
#-------------------------------------------------------------------------
# Cleanup
testsuiteCleanup
Index: modules/markdown/pkgIndex.tcl
==================================================================
--- modules/markdown/pkgIndex.tcl
+++ modules/markdown/pkgIndex.tcl
@@ -1,11 +1,1 @@
-# Tcl package index file, version 1.1
-# This file is generated by the "pkg_mkIndex" command
-# and sourced either when an application starts up or
-# by a "package unknown" script. It invokes the
-# "package ifneeded" command to set up package-related
-# information so that packages will be loaded automatically
-# in response to "package require" commands. When this
-# script is sourced, the variable $dir must contain the
-# full path name of this file's directory.
-
-package ifneeded Markdown 1.0 [list source [file join $dir markdown.tcl]]
+package ifneeded Markdown 1.1 [list source [file join $dir markdown.tcl]]