Class SyntaxHighlighter

java.lang.Object
co.mindus.utils.SyntaxHighlighter

public class SyntaxHighlighter extends Object
Server-side syntax highlighting for code blocks in HTML documents.

This class provides an optional, lazy-loaded syntax highlighting engine that processes fenced code blocks (produced by the Markdown parser in TextUtils) and wraps language tokens in <span class="hljs-xxx"> elements. The resulting HTML can be styled with any highlight.js-compatible CSS theme — no client-side JavaScript is required.

Architecture

The highlighting library — iizi-html-highlighter.jar, a Java port of highlight.js — is referenced directly: the jar is on the application class path and on com.iizix.core's bundle class path, so Highlighter and its renderer are ordinary compile-time types. Each instance holds its own engine, which is inexpensive to create.

Until 2026-08-12 the library was located at run time, loaded into an isolated class loader and driven entirely by reflection. That layer was removed once the jar was on the class path in every environment. Consequence to be aware of: a missing jar is now a NoClassDefFoundError at class load rather than a contained initialization failure.

Lifecycle

Instances are created via create() and can process any number of documents and code blocks. dispose() releases the engine reference; there is no class loader to close, and it is retained for callers written against the previous design. For one-shot usage through the TextUtils.DocumentBuilder, the builder manages the lifecycle automatically.

Theme CSS

Theme CSS files are classpath resources alongside this class, independent of the highlighting library, so they can be loaded without creating an instance. Themes are organized into two sets:

  • Curated — a hand-picked selection of 16 high-quality themes suitable for most use cases, stored in hl/css/curated/.
  • Extended — a collection of 61 additional themes from highlight.js, stored in hl/css/extended/.

Thread Safety

A single instance is not thread-safe; each thread should create its own via create(), or access must be externally synchronized. Static methods (theme loading, CSS utilities) are always thread-safe.

Supported Languages

The bundled codehighlight 1.0.3 registers exactly these 25 identifiers (verified against the shipped jar — there are no aliases, so an unregistered name such as js, ts, html or properties falls back to escaped plain text):

apache, bash, cpp, cs, css, diff, go, groovy, http, ini, java, javascript, json, makefile, markdown, objectivec, perl, php, python, ruby, scala, shell, sql, xml, yaml.

(C) Copyright Mindus SARL, 2026. All rights reserved.

Author:
Christopher Mindus
See Also:
  • Field Details

    • CSS_PREFIX

      public static final String CSS_PREFIX
      CSS class prefix used by the highlight engine for token spans.
      See Also:
    • AUTO_DETECT_MIN_RELEVANCE

      public static final int AUTO_DETECT_MIN_RELEVANCE
      Minimum relevance score required for auto-detected language highlighting to be applied to a bare code block (i.e. one without an explicit language-xxx class).

      When highlightCodeBlocks(String,boolean) encounters a bare <pre><code> block and auto-detection is enabled, the block is highlighted only if the detection's relevance score meets or exceeds this threshold. Below this value the code is left as plain escaped text, avoiding misleading or garbled output from low-confidence guesses.

      The highlight.js engine typically returns relevance values of 0–5 for plain prose or very short snippets, and 7+ for recognizable code. A threshold of 5 provides a reasonable balance between false positives and missed detections.

      See Also:
    • DEFAULT_LIGHT_THEME

      public static final SyntaxHighlighter.HighlightTheme DEFAULT_LIGHT_THEME
      Default highlight theme used when none is specified and the Markdown theme is light.
    • DEFAULT_DARK_THEME

      public static final SyntaxHighlighter.HighlightTheme DEFAULT_DARK_THEME
      Default highlight theme used when none is specified and the Markdown theme is dark.
  • Method Details

    • create

      public static SyntaxHighlighter create()
      Creates a new syntax highlighter instance.

      The highlighting library is referenced directly, so this simply builds an engine with the hljs- renderer factory. Instances are cheap; dispose() exists for callers written against the previous dynamic-loading design and releases only the engine reference.

      For one-shot usage through the TextUtils.DocumentBuilder, prefer .syntaxHighlight(true) which manages the lifecycle automatically.

      Returns:
      A new, ready-to-use highlighter instance.
      Throws:
      IllegalStateException - if the engine cannot be created.
    • highlight

      public String highlight(String language, String code)
      Highlights a code block for a specific language.

      The input code should be plain text (not HTML-escaped). The output contains <span class="hljs-xxx"> elements wrapping language tokens. HTML special characters in the source code are properly escaped in the output.

      The language identifier is case-insensitive; it is normalized to lower case before being passed to the underlying engine.

      Parameters:
      language - The language identifier (e.g. "java", "javascript", "css", "json", "sql", "bash"). Case-insensitive. If null or empty, falls back to highlightAuto(String).
      code - The source code text to highlight.
      Returns:
      The highlighted HTML, or the original code (HTML-escaped) if the language is not recognized.
    • highlightAuto

      public SyntaxHighlighter.HighlightResult highlightAuto(String code)
      Highlights a code block with automatic language detection.

      The highlighter analyzes the code and selects the best-matching language. Use the returned SyntaxHighlighter.HighlightResult to access the highlighted HTML, detected language name, and relevance score.

      Parameters:
      code - The source code text to highlight.
      Returns:
      A result containing the highlighted HTML and detection metadata.
    • getSupportedLanguages

      public String[] getSupportedLanguages()
      Returns the list of language identifiers supported by the highlighter.

      Always returns an empty array. The bundled Highlight.java exposes no public enumeration of its registered languages, so there is nothing to query; this was already the effective behaviour when the library was driven by reflection. See the class-level "Supported Languages" section for the verified list of the 25 registered identifiers.

      Returns:
      An empty array.
    • highlightCodeBlocks

      public String highlightCodeBlocks(String html)
      Processes an HTML fragment and highlights all fenced code blocks in-place, with automatic language detection enabled for bare code blocks.

      This convenience overload delegates to highlightCodeBlocks(html, true).

      Parameters:
      html - An HTML fragment containing zero or more code blocks.
      Returns:
      The HTML with all code blocks syntax-highlighted.
      See Also:
    • highlightCodeBlocks

      public String highlightCodeBlocks(String html, boolean autoDetect)
      Processes an HTML fragment and highlights all fenced code blocks in-place.

      This method performs up to two passes over the HTML:

      1. Explicit language — scans for <pre><code class="language-xxx"> blocks (as produced by commonmark) and highlights them using the declared language. This pass always runs.
      2. Auto-detection (optional) — if autoDetect is true, scans for bare <pre><code> blocks (no language-xxx class) and uses highlightAuto(String) to guess the language. Only results whose relevance score meets or exceeds AUTO_DETECT_MIN_RELEVANCE are applied; below the threshold the block is left as plain escaped text to avoid garbled output from low-confidence guesses.

      The same highlighter instance is used for all code blocks in the fragment, ensuring the engine is initialized exactly once regardless of how many code blocks are present.

      Parameters:
      html - An HTML fragment containing zero or more code blocks.
      autoDetect - true to enable automatic language detection for bare code blocks; false to leave them untouched.
      Returns:
      The HTML with code blocks syntax-highlighted.
    • dispose

      public void dispose()
      Releases this instance's highlighting engine.

      The library is loaded by the application class loader, so there is no class loader to close; this drops the engine reference and the instance must not be used afterwards. Retained because callers written against the previous dynamic-loading design call it.

      This method is idempotent — calling it multiple times has no effect.

    • loadThemeCss

      public static String loadThemeCss(SyntaxHighlighter.HighlightTheme theme)
      Loads the CSS text for a given highlight theme.

      Results are cached after first load. This method does not require a SyntaxHighlighter instance — theme CSS files are classpath resources that can be loaded at any time.

      Parameters:
      theme - The highlight theme to load.
      Returns:
      The CSS text, or null if the theme's CSS resource cannot be found.
    • clearThemeCache

      public static void clearThemeCache()
      Clears the theme CSS cache, freeing memory.

      Subsequent calls to loadThemeCss(HighlightTheme) will reload from classpath resources.

    • getConflictResolutionCss

      public static String getConflictResolutionCss()
      Returns a CSS snippet that resolves padding conflicts between Markdown document themes and highlight.js code block themes.

      When both a Markdown document theme (from TextUtils.Theme) and a syntax highlight theme are active in the same document, the document theme may apply its own padding and background to <pre> elements, which conflicts with the highlight theme's code.hljs styling. Injecting this snippet after both theme CSS blocks ensures the highlight theme controls code block appearance without double padding.

      Example usage:

      sb.append("<style>\n").append(docThemeCss).append("\n</style>\n");
      sb.append("<style>\n").append(hlThemeCss).append("\n</style>\n");
      sb.append("<style>\n").append(SyntaxHighlighter.getConflictResolutionCss())
        .append("\n</style>\n");
      
      Returns:
      A CSS fragment (two rules) to include after both theme CSS blocks.
    • getAvailableThemes

      public static SyntaxHighlighter.HighlightTheme[] getAvailableThemes()
      Returns all available themes.
      Returns:
      An array of all themes (curated and extended).
    • getThemes

      Returns themes matching the given type classification.
      Parameters:
      type - The desired theme type (LIGHT, DARK, or NEUTRAL).
      Returns:
      An array of matching themes.
    • getThemes

      Returns themes from the given set.
      Parameters:
      set - The desired theme set (CURATED or EXTENDED).
      Returns:
      An array of matching themes.
    • getThemes

      Returns themes matching both a type and a set.
      Parameters:
      type - The desired theme type.
      set - The desired theme set.
      Returns:
      An array of matching themes.
    • defaultThemeFor

      public static SyntaxHighlighter.HighlightTheme defaultThemeFor(TextUtils.Theme mdTheme)
      Selects an appropriate default highlight theme that matches a Markdown document theme's light/dark classification.

      If the Markdown theme is light, returns DEFAULT_LIGHT_THEME. If dark, returns DEFAULT_DARK_THEME. For NONE or unknown themes, defaults to the light theme.

      Parameters:
      mdTheme - The Markdown document theme (from TextUtils.Theme).
      Returns:
      A suitable highlight theme.