Skip to content

Commit 4fa01ca

Browse files
committed
Render mermaid
1 parent 2dd2717 commit 4fa01ca

7 files changed

Lines changed: 408 additions & 3 deletions

File tree

package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"css-loader": "^3.1.0",
4747
"glob": "^7.1.4",
4848
"ignore-loader": "^0.1.2",
49+
"jquery": "^3.4.1",
4950
"mermaid": "^8.2.3",
5051
"mini-css-extract-plugin": "^0.8.0",
5152
"mocha": "^6.1.4",
@@ -67,6 +68,7 @@
6768
"markdown-it-mathjax": "^2.0.0",
6869
"markdown-it-sub": "^1.0.0",
6970
"markdown-it-sup": "^1.0.0",
70-
"markdown-it-table-of-contents": "^0.4.4"
71+
"markdown-it-table-of-contents": "^0.4.4",
72+
"string": "git+ssh://git@github.com/hackmdio/string.js.git#a68176b3d"
7173
}
7274
}

src/extension.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import * as vscode from 'vscode';
44

55
import * as markdownitContainer from 'markdown-it-container';
6+
import * as S from 'string';
67

78
function render( tokens, idx, options, env, self): string {
89
tokens[idx].attrJoin('role', 'alert');
@@ -11,6 +12,83 @@ function render( tokens, idx, options, env, self): string {
1112
return self.renderToken(...arguments);
1213
}
1314

15+
function parseFenceCodeParams (lang) {
16+
const attrMatch = lang.match(/{(.*)}/)
17+
const params = {}
18+
if (attrMatch && attrMatch.length >= 2) {
19+
const attrs = attrMatch[1]
20+
const paraMatch = attrs.match(/([#.](\S+?)\s)|((\S+?)\s*=\s*("(.+?)"|'(.+?)'|\[[^\]]*\]|\{[}]*\}|(\S+)))/g)
21+
paraMatch && paraMatch.forEach(param => {
22+
param = param.trim()
23+
if (param[0] === '#') {
24+
params['id'] = param.slice(1)
25+
} else if (param[0] === '.') {
26+
if (params['class']) params['class'] = []
27+
params['class'] = params['class'].concat(param.slice(1))
28+
} else {
29+
const offset = param.indexOf('=')
30+
const id = param.substring(0, offset).trim().toLowerCase()
31+
let val = param.substring(offset + 1).trim()
32+
const valStart = val[0]
33+
const valEnd = val[val.length - 1]
34+
if (['"', "'"].indexOf(valStart) !== -1 && ['"', "'"].indexOf(valEnd) !== -1 && valStart === valEnd) {
35+
val = val.substring(1, val.length - 1)
36+
}
37+
if (id === 'class') {
38+
if (params['class']) params['class'] = []
39+
params['class'] = params['class'].concat(val)
40+
} else {
41+
params[id] = val
42+
}
43+
}
44+
})
45+
}
46+
return params
47+
}
48+
49+
function highlightRender (code, lang) {
50+
if (!lang || /no(-?)highlight|plain|text/.test(lang)) { return }
51+
// support adding extra attributes for fence code block
52+
// ex: ```graphviz {engine="neato"}
53+
const params = parseFenceCodeParams(lang) as any;
54+
lang = lang.split(/\s+/g)[0]
55+
code = S(code).escapeHTML().s
56+
if (lang === 'sequence') {
57+
return `<span class="sequence-diagram raw">${code}</span>`
58+
} else if (lang === 'flow') {
59+
return `<span class="flow-chart raw">${code}</span>`
60+
} else if (lang === 'graphviz') {
61+
// support to specify layout engine of graphviz
62+
let dataAttrs = ''
63+
if (params.hasOwnProperty('engine')) {
64+
dataAttrs = ' data-engine="' + params.engine + '"'
65+
}
66+
return `<span class="graphviz raw"${dataAttrs}>${code}</span>`
67+
} else if (lang === 'mermaid') {
68+
return `<span class="mermaid raw">${code}</span>`
69+
} else if (lang === 'abc') {
70+
return `<span class="abc raw">${code}</span>`
71+
}
72+
const result = {
73+
value: code
74+
}
75+
const showlinenumbers = /=$|=\d+$|=\+$/.test(lang)
76+
if (showlinenumbers) {
77+
let startnumber = 1
78+
const matches = lang.match(/=(\d+)$/)
79+
if (matches) { startnumber = parseInt(matches[1]) }
80+
const lines = result.value.split('\n')
81+
const linenumbers = []
82+
for (let i = 0; i < lines.length - 1; i++) {
83+
linenumbers[i] = `<span data-linenumber='${startnumber + i}'></span>`
84+
}
85+
const continuelinenumber = /=\+$/.test(lang)
86+
const linegutter = `<div class='gutter linenumber${continuelinenumber ? ' continue' : ''}'>${linenumbers.join('\n')}</div>`
87+
result.value = `<div class='wrapper'>${linegutter}<div class='code'>${result.value}</div></div>`
88+
}
89+
return result.value
90+
}
91+
1492
// this method is called when your extension is activated
1593
// your extension is activated the very first time the command is executed
1694
export function activate(context: vscode.ExtensionContext) {
@@ -41,6 +119,10 @@ export function activate(context: vscode.ExtensionContext) {
41119
md.use(markdownitContainer, 'warning', { render });
42120
md.use(markdownitContainer, 'danger', { render });
43121

122+
md.options.linkify = true;
123+
md.options.typographer = true;
124+
md.options.highlight = highlightRender;
125+
44126
return md;
45127
}
46128
};

0 commit comments

Comments
 (0)