Appearance
Inline Rules
An inline rule matches text inside a block with a regular expression, adds a CSS class to the match, and optionally gives it a ⌘-click (Ctrl-click) action and a list in the detail panel.
Inline rules are decorations. They never change the document, the Markdown, or what is saved — #123 stays #123 in the file.
json
"contributes": {
"inlineRules": [
{
"id": "issue",
"pattern": "(?<![\\w/&#])#(\\d+)\\b",
"class": "md-ext-issue",
"title": "Issue #$1 (⌘-click to open)",
"action": { "openUrl": "https://github.com/typora/typora-issues/issues/$1" }
},
{
"id": "tag",
"pattern": "(?<=^|\\s)#([\\p{L}_][\\p{L}\\p{N}_/-]*)",
"class": "md-ext-tag",
"title": "Tag #$1 (⌘-click to search)",
"action": { "command": "search.global", "args": { "query": "#$1" } },
"index": { "title": "Tags", "icon": "tags", "sfSymbol": "tag", "groupBy": "#$1" }
}
]
}Fields
| Field | Required | Description |
|---|---|---|
id | yes | Local id of the rule. |
pattern | yes | JavaScript regular expression source. It always runs with the u flag, so \p{L} and lookbehind work. Remember to escape \ in JSON. |
flags | Extra flags: any of i, m, s. | |
exclude | Inline kinds to skip. Default: ["code", "link", "url", "math", "html"]. | |
class | CSS class added to the match. Every match also gets md-ext-rule. | |
title | Tooltip. $1…$9 are capture groups. | |
action | What ⌘-click (Ctrl-click on Windows and Linux) does. See Actions. | |
index | Adds a detail view listing every match. See Index views. |
exclude values
| Value | Skips |
|---|---|
code | Inline code |
link | Links, reference links, wiki links and embeds, <a> tags |
url | Bare URLs and autolinks |
math | Inline math |
html | Inline HTML, tags, comments, <img>, entities |
image | Images |
footnote | Footnote references |
citation | Citations |
emoji | Emoji shortcodes |
Pass [] to match everywhere.
Actions
json
{ "openUrl": "https://example.com/issues/$1" }
{ "command": "search.global", "args": { "query": "#$1" } }openUrlopens anhttp/httpsURL in the browser.$ncapture groups are URL-encoded. The template must start withhttp://,https://or a setting reference{config.key}, which inserts the value of one of your configuration properties.commandruns a Typora command.search.globalopens search across the workspace withargs.query(capture groups are substituted into string arguments).
Index views
With index, the rule also contributes a detail-panel tab listing every match in the document:
| Field | Description |
|---|---|
title | Tab title, e.g. Tags. |
icon | Optional icon name. |
sfSymbol | Optional SF Symbol name for the macOS menus, as in detail views. |
groupBy | Group the list by a template of capture groups, e.g. "#$1" gives one group per tag. |
Clicking an item selects the match in the editor.
Styling
Every match is a span with the class md-ext-rule (which Typora styles) plus your own class. Custom styling will come with theme contributions; until then, pick a descriptive class name so a theme can target it later.
Semantics and limits
- Rules run over the Markdown source of each block, skipping the
excluderanges. A heading's leading#is followed by a space, so a tag pattern that requires a character after#doesn't match it. - Rules apply in declaration order, then extension load order. Matches can't overlap; the first one wins.
- At most 200 matches per rule per block are decorated.
- Rules are matched in the background, so a decoration can appear a moment after the text it marks. While you type inside or at the end of a match, the decoration grows with it until the new match arrives.
- A rule that takes longer than 20 ms on one block, or hangs (a pattern that backtracks catastrophically, such as
(a+)+$), is switched off for the session. Typora shows a notice and writes an error to the extension's log, which you can open from Settings ▸ Extensions ▸ View Log. Editing is never blocked: rules are matched in the background.
See the Issue Links & Tags sample.