Header menu logo issie

Markdown Module

A small markdown renderer for Issie's in-app help. Issie's longer help text - the Info window, the waveform simulator's help panels, the bodies of confirmation popups - used to be written as React element trees: `str` and `bSpan` and `li` interleaved with the words, several hundred lines of it. Read as prose it was unreadable, which is a problem for text whose whole job is to read well. It is now written as markdown in AppMessages and rendered here. The subset is not a guess: it is what that text was already doing, counted. Headings, bold, italic, inline code, links, bullet and numbered lists, and tables - which is core GitHub markdown with nothing left over. There are no images and no nested lists, so there is no support for them; a line that tries will render as its own text rather than silently vanish. TWO THINGS THIS DELIBERATELY DOES NOT DO. It does not produce an HTML string. Rendering markdown by handing HTML to `dangerouslySetInnerHTML` is the usual shortcut, and in a renderer with node integration, for messages that interpolate sheet names the user chose, it is a bad trade. `render` builds React elements, so there is no path from message text to executed markup. It does not open links itself. A markdown link becomes an anchor with an `onClick` supplied by the caller, because in Electron an ordinary `href` navigates the application window away from Issie. Keeping the handler out here also keeps this module free of Electron and of anything above it in compile order. `parse` is pure and returns data, so the tests can read every message in AppMessages under plain .NET without a browser. `render` is the only part that needs one.

Types

Type Description

Block

Inline

Text with emphasis, within one paragraph, heading, list item or table cell.

Functions and values

Function or value Description

parse text

Full Usage: parse text

Parameters:
    text : string

Returns: Block list

Markdown to blocks. Paragraphs are separated by blank lines and a wrapped paragraph is joined back into one line, so the source can be wrapped to the column width the rest of the codebase uses without that wrapping reaching the screen.

text : string
Returns: Block list

parseInlines line

Full Usage: parseInlines line

Parameters:
    line : string

Returns: Inline list

Split a line into text and emphasis. Written as a scan rather than with a regular expression per mark so that the marks cannot interleave wrongly: at each position at most one of them can start, and each runs to its own closing mark. An unclosed mark is not an error - it is left as literal text, which is what a reader of the source would expect and what stops one stray asterisk swallowing a paragraph.

line : string
Returns: Inline list

render onLink text

Full Usage: render onLink text

Parameters:
    onLink : string -> unit
    text : string

Returns: ReactElement

Markdown text, rendered. The usual entry point.

onLink : string -> unit
text : string
Returns: ReactElement

renderBlocks onLink blocks

Full Usage: renderBlocks onLink blocks

Parameters:
    onLink : string -> unit
    blocks : Block list

Returns: ReactElement

Rendered markdown, as one element ready to drop into a popup body.

onLink : string -> unit
blocks : Block list
Returns: ReactElement

renderInlines onLink inlines

Full Usage: renderInlines onLink inlines

Parameters:
    onLink : string -> unit
    inlines : Inline list

Returns: ReactElement list

One line's worth of inlines. `onLink` is given a link's url when it is clicked - see the note at the top about why this module does not open it itself.

onLink : string -> unit
inlines : Inline list
Returns: ReactElement list

Type something to start searching.