nitiwiki: new module `markdown_highlight` to plug in an external highlighter
[nit.git] / contrib / nitiwiki / src / markdown_highlight.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Extends the wiki with an external highlighter (or any processor) for code blocks
16 module markdown_highlight
17 import wiki_links
18
19 redef class WikiConfig
20 # External highlighter command called to process block code.
21 #
22 # * key: `wiki.highlighter`
23 # * default: empty string, that means no external highlighter
24 # * example: `highlight --fragment -S "$1" --inline-css --enclose-pre`
25 #
26 # The external highlighter is a shell command invoked with `sh -c`.
27 # The meta information of the fence is passed as `$1`.
28 # *Important*: `$1` is given as is, thus is tainted. You SHOULD protect it with quotes in the command.
29 #
30 # By default, the highlighter is only called on fenced block code with a meta information.
31 # See `wiki.highlighter.default` to force the invocation of the highlighter on any code block.
32 #
33 # The output of the command will be inserted as is in the generated document.
34 # Therefore it is expected that the command returns a valid, complete and balanced HTML fragment.
35 # If the highlighter returns nothing (empty output), the internal rendering is used as a fall-back
36 # (as if the option was not set).
37 #
38 # Advanced usages can invoke a custom shell script instead of a standard command to
39 # check the argument, filter it, dispatch to various advanced commands, implement ad-hoc behaviors, etc.
40 var highlighter: String is lazy do
41 return value_or_default("wiki.highlighter", "")
42 end
43
44 # Default meta (i.e. language) to use to call the external highlighter.
45 #
46 # * key: `wiki.highlighter.default`
47 # * default: empty string, that means no default meta information.
48 # * example: `nit`
49 #
50 # When set, this configuration forces the external highlighter (see `wiki.highlighter`)
51 # to be called also on basic code block (with the indentation) and plain fenced code
52 # blocks (without meta information).
53 #
54 # The value is used as the `$1` argument of the configured highlighter command.
55 #
56 # Note: has no effect if `wiki.highlighter` is not set.
57 var highlighter_default: String is lazy do
58 return value_or_default("wiki.highlighter.default", "")
59 end
60 end
61
62 redef class NitiwikiDecorator
63 # Extends special cases for meta in fences
64 redef fun add_code(v, block) do
65 var highlighter = wiki.config.highlighter
66
67 # No highlighter, then defaults
68 if highlighter.is_empty then
69 super
70 return
71 end
72
73 var code = block.raw_content
74 var meta = block.meta or else wiki.config.highlighter_default
75
76 # No meta nor forced meta, then defaults
77 if meta.is_empty then
78 super
79 return
80 end
81
82 # Execute the command
83 wiki.message("Executing `{highlighter}` `{meta}` (in {context.src_path.as(not null)})", 2)
84 var proc = new ProcessDuplex("sh", "-c", highlighter, "", meta.to_s)
85 var res = proc.write_and_read(code)
86 if proc.status != 0 then
87 wiki.message("Warning: `{highlighter}` `{meta}` returned {proc.status} (in {context.src_path.as(not null)})", 0)
88 end
89
90 # Check the result
91 if res.is_empty then
92 # No result, then defaults
93 wiki.message(" `{highlighter}` produced nothing, process internally instead (in {context.src_path.as(not null)})", 2)
94 super
95 return
96 end
97 v.add(res)
98 end
99 end