Prohlížeč zdrojového kódu

docs/specs/markdown-code-highlighting.md

Náhled Zdrojový kód

Spec: Markdown Code Block Syntax Highlighting

  • Status: active
  • Created: 2026-03-12
  • Related code: app/helpers/highlight_helper.rb, spec/helpers/highlight_helper_spec.rb

Overview

Fenced code blocks in Markdown content are syntax-highlighted using Rouge, consistent with the manually-highlighted source code blocks elsewhere in the app. This applies wherever Markdown is rendered: example detail pages and the source browser Markdown view.

Behaviour

  • A fenced code block with a recognised language hint (e.g. `ruby) is highlighted using the matching Rouge lexer.
  • A fenced code block with no language hint, or an unrecognised language, falls back to plain-text rendering without raising an error.
  • The rendered HTML uses the same markup structure as highlight_code so existing Rouge CSS applies without additional stylesheet changes.
  • Highlighting applies wherever render_markdown is called.
  • A fenced code block may include an optional label in the info string, separated from the language hint by a space (e.g. ruby app/models/log.rb ` or `ruby log_header.rb, log_item.rb). The label is arbitrary text extending to the end of the fence line. When present, the label is displayed above the code block. A label without a language hint is not supported.

Implementation Notes

  • SourceAwareMarkdownRenderer#block_code delegates to HighlightHelper.rouge_highlight, a module-level method extracted from the old highlight_code instance method.
  • HighlightHelper.rouge_highlight returns a plain HTML string (no raw wrapping); callers that need an ActiveSupport::SafeBuffer call raw themselves (highlight_code helper method, render_markdown via Redcarpet).
  • block_code receives nil for language when no hint is given; Rouge::Lexer.find(nil) returns nil, so the fallback to PlainText handles both the no-hint and unknown-language cases.
  • No CSS changes needed for highlighting — rouge.scss already covers .highlight for both light and dark themes.
  • Label pre-processing workaround: Redcarpet stops reading the fence info string at the first space, causing fenced blocks with labels to not be recognised as code blocks at all. render_markdown pre-processes the Markdown with a gsub that replaces all spaces inside fence info lines (lines starting with or~~~) with\x1F(ASCII Unit Separator) before handing the text to Redcarpet.block_codethen decodes\x1Fback to spaces and splits on the first space to separate the language from the label. Labels are rendered as raw HTML (not escaped) in a

    element before the

    `.

Tests

  • spec/helpers/highlight_helper_spec.rb — fenced block with language renders Rouge HTML; fenced block without language renders plain; unknown language does not raise
  • spec/helpers/highlight_helper_spec.rb — fenced block with ruby path/to/file.rb renders .code-block__label containing the path; fenced block with only a language hint renders no label; label text is HTML-escaped

See Also

  • Rich Example Content — example detail pages render Markdown via render_markdown, which is the primary consumer.
  • Source Browser — source browser Markdown view also uses render_markdown.
  • Styling and CSS — Rouge CSS (rouge.scss) provides the colour rules consumed by the highlighted output.

Change Log

  • 2026-03-12: Initial prospective spec.
  • 2026-03-23: Added optional label in fence info string (space-separated, arbitrary text, raw HTML).