[OCaml] Mobile-friendly clone of cgit.
feat add highlighting engine module
Highlight.highlight takes a language name and source code string, returns a list of lines where each line is a list of dream-html span nodes with CSS classes following hilite's convention. Falls back gracefully to plain escaped text when the language is unknown or highlighting fails.
lib/highlight.ml
@@ -0,0 +1,53 @@
1
Added:
(* -*- mode: tuareg; -*- *)
2
Added:
3
Added:
(** Server-side syntax highlighting engine.
4
Added:
5
Added:
Tokenizes source code using TextMate grammars (via hilite) and produces
6
Added:
{!Dream_html.node} spans ready for embedding in the page. Falls back
7
Added:
gracefully to plain text when no grammar is available for the requested
8
Added:
language. *)
9
Added:
10
Added:
open Dream_html
11
Added:
12
Added:
(** A single highlighted line: a list of HTML nodes (spans with classes). *)
13
Added:
type line = node list
14
Added:
15
Added:
(** Highlight source code for the given language.
16
Added:
17
Added:
Returns a list of lines, each line being a list of [<span>] nodes with
18
Added:
appropriate CSS classes. If [lang] is [None] or the language is not
19
Added:
supported, returns plain-text lines (no spans, just escaped text).
20
Added:
21
Added:
The CSS classes follow hilite's convention:
22
Added:
[{lang_scope}-{token_scope_segments}], e.g.
23
Added:
[source.python-storage-type-function]. *)
24
Added:
let highlight ~lang source : line list =
25
Added:
let plain_lines () =
26
Added:
String.split_on_char '\n' source
27
Added:
|> List.map (fun line -> [ txt "%s\n" line ])
28
Added:
in
29
Added:
match lang with
30
Added:
| None -> plain_lines ()
31
Added:
| Some lang_name -> (
32
Added:
let scope = Highlight_grammars.scope_of_lang lang_name in
33
Added:
match scope with
34
Added:
| None -> plain_lines ()
35
Added:
| Some scope_name -> (
36
Added:
let tm = Lazy.force Highlight_grammars.registry in
37
Added:
match
38
Added:
Hilite.src_code_to_pairs ~escape:true ~lookup_method:`Scope_name
39
Added:
~tm ~lang:scope_name source
40
Added:
with
41
Added:
| Error _ -> plain_lines ()
42
Added:
| Ok pairs ->
43
Added:
List.map
44
Added:
(fun line_pairs ->
45
Added:
List.map
46
Added:
(fun (css_class, content) ->
47
Added:
if css_class = "" then txt ~raw:true "%s" content
48
Added:
else
49
Added:
HTML.span
50
Added:
[ HTML.class_ "%s" css_class ]
51
Added:
[ txt ~raw:true "%s" content ])
52
Added:
line_pairs)
53
Added:
pairs))