Custom Markdown Rules - slugcat-dev/mark-ed GitHub Wiki

Grammar

Markdown is parsed with two different sets of rules ("grammar"): Line grammar for block types like headings and lists, and inline grammar for, well, inline formatting like emphasis or links.

Default line grammar:
ThematicBreak, ATXHeading, CodeBlock, BlockQuote, TaskList, UnorderedList, OrderedList

Default inline grammar:
Escape, Autolink, InlineCode, URL, Email, Emphasis, StrongEmphasis, Underline, Strikethrough

Disable Rules

You can disable any rule you don't like by using disableRule.

import { Editor, disableRule } from '@slugcat-dev/mark-ed'

const editor = new Editor('my-editor', {
  // Disable code blocks and inline code
  markdown: {
    lineGrammar: {
      CodeBlock: disableRule
    },
    inlineGrammar: {
      InlineCode: disableRule
    }
  }
})

Add Custom Rules

Caution

You must configure the markdown parser in a way that it always returns the same text that was fed into it. Text may be wrapped in HTML elements, but if you remove or add text, the editor will not correctly rerender and parts of the content will be lost.

Example

Input text:
- This is a list item

Output text:
<span class="md-list">• This is a list item</span>
<span class="md-list"><span class="md-mark">- </span>This is a list item</span>

If you want to render a in place of - , you can use the mark hiding feature in combination with CSS ::before.

Tip

You can also overwrite any of the default rules to adjust them to your needs.

Replace Rules

Match-and-replace rules have a function or regular expression that matches a string, and a function to replace the matched string with something else. They can be used in both line and inline grammar.

import { Editor } from '@slugcat-dev/mark-ed'

const editor = new Editor('my-editor', {
  markdown: {
    lineGrammar: {
      // Format lists like in the example above
      UnorderedList: {
        regex: /^([\t ]*)([-+*] )(.*)/,
        replace: (match, parser) => `${match[1]}<span class="md-list"><span class="md-mark">${match[2]}</span>${parser.parseInline(match[3])}</span>`
      }
    }
  }
})
The inline code rule uses a function for matching which is more complex.
// From grammar.ts

InlineCode: {
  match(str) {
    if (!str.startsWith('`'))
      return false

    let pos = 1

    while (pos < str.length && str[pos] === '`')
      pos++

    const size = pos
    let curSize = 0

    for (; pos < str.length; pos++) {
      if (str[pos] === '`') {
        curSize++

        if (curSize === size && str[pos + 1] !== '`') {
          const match = str.substring(0, pos + 1)
          const text = str.substring(size, pos - size + 1)
          const mark = str.substring(0, size)

          return [match, text, mark]
        }
      } else
        curSize = 0
    }

    return false
  },
  replace: match => `<code class="md-code"><span class="md-mark">${match[2]}</span>${escapeHTML(match[1])}<span class="md-mark">${match[2]}</span></code>`
}

Delimiter Rules

A delimiter rule can be used in inline grammar. In combination with the formatInline function, any text between a pair of the specified delimiter can be wrapped with a HTML tag.

import { Editor, formatInline } from '@slugcat-dev/mark-ed'

const editor = new Editor('my-editor', {
  inlineGrammar: {
    // Format x^2^ as x²
    Superscript: {
      delimiter: '^',
      length: 1,
      replace: formatInline('sup')
    },

    // Highlight ==text== or !!text!!
    Highlight: {
      delimiter: '=!',
      length: 2,
      replace: formatInline('span class="md-highlight"')
    }
  }
})
⚠️ **GitHub.com Fallback** ⚠️