(** Server-side syntax highlighting engine. Tokenizes source code using TextMate grammars (via hilite) and produces {!Dream_html.node} spans ready for embedding in the page. Falls back gracefully to plain text when no grammar is available for the requested language. *) open Dream_html type line = node list (** A single highlighted line: a list of HTML nodes (spans with classes). *) (** Highlight source code for the given language. Returns a list of lines, each line being a list of [] nodes with appropriate CSS classes. If [lang] is [None] or the language is not supported, returns plain-text lines (no spans, just escaped text). The CSS classes follow hilite's convention: [{lang_scope}-{token_scope_segments}], e.g. [source.python-storage-type-function]. *) let highlight ~lang source : line list = let plain_lines () = String.split_on_char '\n' source |> List.map (fun line -> [ txt "%s\n" line ]) in match lang with | None -> plain_lines () | Some lang_name -> ( let scope = Grammars.scope_of_lang lang_name in match scope with | None -> plain_lines () | Some scope_name -> ( let tm = Lazy.force Grammars.registry in match Hilite.src_code_to_pairs ~escape:true ~lookup_method:`Scope_name ~tm ~lang:scope_name source with | Error _ -> plain_lines () | Ok pairs -> List.map (fun line_pairs -> List.map (fun (css_class, content) -> if css_class = "" then txt ~raw:true "%s" content else HTML.span [ HTML.class_ "%s" css_class ] [ txt ~raw:true "%s" content ]) line_pairs) pairs))