View raw

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