tags: api/space-lua maturity/experimental references:

  • libraries/Library/Std/APIs/Syntax.md
  • client/markdown_parser/custom_syntax.ts
  • client/codemirror/custom_syntax_widget.ts

Enables defining simple custom syntax extensions to Markdown. Custom syntax lets you define new delimiter-based regions (both inline and block) that are recognized by the parser and can be styled or rendered as [Space Lua/Widget|Widgets](Space Lua/Widget|Widgets).

API

syntax.define(spec)

Registers a custom syntax extension. The extension will be active after the next editor state rebuild (e.g. on page reload or config change).

spec is a table that can contain:

  • name (required): AST node name (e.g. "LatexInline")
  • startMarker (required): Regex string for start delimiter (e.g. "\\$")
  • endMarker (required): Regex string for end delimiter
  • mode (required): "block" or "inline"
  • CSS styling used when not [Live Preview|Live Previewing](Live Preview|Live Previewing):
    • startMarkerClass: CSS class for the start marker
    • bodyClass: CSS class for the body content between markers
    • endMarkerClass: CSS class for the end marker
  • CSS styling applied when Live Previewing:
    • renderClass: CSS class applied to the rendered widget
  • renderWidget: Callback function(body, pageName) returning API/widget|widget content for Live Preview.
  • renderHtml: Callback function(body, pageName) returning an HTML string or HTMLElement (e.g. via dom.*) for use when rendering to HTML.

Escaping

Within the body of an inline syntax extension, backslash (\) acts as an escape character. Use \ before the end marker to include it literally in the body. The render callback receives the raw body text with escape sequences intact; the renderer is responsible for unescaping if needed.

Example

Define an inline LaTeX syntax using $ delimiters:

syntax.define {
  name = "LatexInline",
  startMarker = "\\$(?!\\{)",
  endMarker = "\\$(?!\\{)",
  mode = "inline",
  --startMarkerClass = "sb-latex-mark",
  --bodyClass = "sb-latex-body",
  --endMarkerClass = "sb-latex-mark",
  renderClass = "sb-latex-inline-preview",
  renderWidget = function(body, pageName)
    return widget.html(dom.i { body })
  end,
  renderHtml = function(body, pageName)
    return dom.i { body }
  end
}

This will recognize $E=mc^2$ in your text and render the body using the provided callback (in this case it just makes it italic): $E=mc^2$.

note Note The (?!\{) negative lookahead prevents $ from clashing with the ${expr} Lua expression syntax. Without it, $math ${expr} more$ would prematurely close at the $ in ${expr}.

A block-level variant using $$ fences:

syntax.define {
  name = "LatexBlock",
  startMarker = "^\\|\\|$",
  endMarker = "^\\|\\|$",
  mode = "block",
  startMarkerClass = "sb-latex-mark",
  bodyClass = "sb-latex-body",
  endMarkerClass = "sb-latex-mark",
  renderWidget = function(body, pageName)
    return widget.htmlBlock(dom.marquee { body })
  end,
  renderHtml = function(body, pageName)
    return dom.marquee { body }
  end
}

This recognizes fenced blocks:

|| E = mc^2 ||

You can style these with [Space Style](Space Style):

.sb-latex-mark {
  color: cyan;
}
.sb-latex-body {
  color: gray;
}