Custom Markdown Rules - slugcat-dev/mark-ed GitHub Wiki
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
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
}
}
})
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.
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>`
}
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"')
}
}
})