Skip to content

Commit 3793378

Browse files
authored
Merge pull request #2394 from daiyam/fence-attrs
add attributes to fence blocks
2 parents 1dcc51e + c5bcfe6 commit 3793378

File tree

9 files changed

+242
-108
lines changed

9 files changed

+242
-108
lines changed

browser/components/MarkdownPreview.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -737,7 +737,6 @@ export default class MarkdownPreview extends React.Component {
737737
el.addEventListener('click', this.linkClickHandler)
738738
})
739739
} catch (e) {
740-
console.error(e)
741740
el.className = 'flowchart-error'
742741
el.innerHTML = 'Flowchart parse error: ' + e.message
743742
}
@@ -758,7 +757,6 @@ export default class MarkdownPreview extends React.Component {
758757
el.addEventListener('click', this.linkClickHandler)
759758
})
760759
} catch (e) {
761-
console.error(e)
762760
el.className = 'sequence-error'
763761
el.innerHTML = 'Sequence diagram parse error: ' + e.message
764762
}
@@ -771,12 +769,18 @@ export default class MarkdownPreview extends React.Component {
771769
try {
772770
const chartConfig = JSON.parse(el.innerHTML)
773771
el.innerHTML = ''
774-
var canvas = document.createElement('canvas')
772+
773+
const canvas = document.createElement('canvas')
775774
el.appendChild(canvas)
776-
/* eslint-disable no-new */
777-
new Chart(canvas, chartConfig)
775+
776+
const height = el.attributes.getNamedItem('data-height')
777+
if (height && height.value !== 'undefined') {
778+
el.style.height = height.value + 'vh'
779+
canvas.height = height.value + 'vh'
780+
}
781+
782+
const chart = new Chart(canvas, chartConfig)
778783
} catch (e) {
779-
console.error(e)
780784
el.className = 'chart-error'
781785
el.innerHTML = 'chartjs diagram parse error: ' + e.message
782786
}

browser/components/markdown.styl

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -209,41 +209,39 @@ code
209209
text-decoration none
210210
margin-right 2px
211211
pre
212-
padding 0.5em !important
212+
padding 0.5rem !important
213213
border solid 1px #D1D1D1
214214
border-radius 5px
215215
overflow-x auto
216-
margin 0 0 1em
216+
margin 0 0 1rem
217217
display flex
218218
line-height 1.4em
219-
&.flowchart, &.sequence, &.chart
220-
display flex
221-
justify-content center
222-
background-color white
223-
&.CodeMirror
224-
height initial
225-
flex-wrap wrap
226-
&>code
227-
flex 1
228-
overflow-x auto
229219
code
230220
background-color inherit
231221
margin 0
232222
padding 0
233223
border none
234224
border-radius 0
225+
&.CodeMirror
226+
height initial
227+
flex-wrap wrap
228+
&>code
229+
flex 1
230+
overflow-x auto
231+
&.mermaid svg
232+
max-width 100% !important
235233
&>span.filename
236-
width 100%
237-
border-radius: 5px 0px 0px 0px
238-
margin -8px 100% 8px -8px
239-
padding 0px 6px
234+
margin -0.5rem 100% 0.5rem -0.5rem
235+
padding 0.125rem 0.375rem
240236
background-color #777;
241237
color white
238+
&:empty
239+
display none
242240
&>span.lineNumber
243241
display none
244242
font-size 1em
245-
padding 0.5em 0
246-
margin -0.5em 0.5em -0.5em -0.5em
243+
padding 0.5rem 0
244+
margin -0.5rem 0.5rem -0.5rem -0.5rem
247245
border-right 1px solid
248246
text-align right
249247
border-top-left-radius 4px
@@ -375,6 +373,19 @@ for name, val in admonition_types
375373
color: val[color]
376374
content: val[icon]
377375

376+
pre.fence
377+
flex-wrap wrap
378+
379+
.chart, .flowchart, .mermaid, .sequence
380+
display flex
381+
justify-content center
382+
background-color white
383+
max-width 100%
384+
flex-grow 1
385+
386+
canvas, svg
387+
max-width 100% !important
388+
378389
themeDarkBackground = darken(#21252B, 10%)
379390
themeDarkText = #f9f9f9
380391
themeDarkBorder = lighten(themeDarkBackground, 20%)

browser/components/render/MermaidRender.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,30 @@ function getRandomInt (min, max) {
1111
}
1212

1313
function getId () {
14-
var pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
15-
var id = 'm-'
16-
for (var i = 0; i < 7; i++) {
14+
const pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
15+
let id = 'm-'
16+
for (let i = 0; i < 7; i++) {
1717
id += pool[getRandomInt(0, 16)]
1818
}
1919
return id
2020
}
2121

2222
function render (element, content, theme) {
2323
try {
24+
const height = element.attributes.getNamedItem('data-height')
25+
if (height && height.value !== 'undefined') {
26+
element.style.height = height.value + 'vh'
27+
}
2428
let isDarkTheme = theme === 'dark' || theme === 'solarized-dark' || theme === 'monokai' || theme === 'dracula'
2529
mermaidAPI.initialize({
2630
theme: isDarkTheme ? 'dark' : 'default',
27-
themeCSS: isDarkTheme ? darkThemeStyling : ''
31+
themeCSS: isDarkTheme ? darkThemeStyling : '',
32+
useMaxWidth: false
2833
})
2934
mermaidAPI.render(getId(), content, (svgGraph) => {
3035
element.innerHTML = svgGraph
3136
})
3237
} catch (e) {
33-
console.error(e)
3438
element.className = 'mermaid-error'
3539
element.innerHTML = 'mermaid diagram parse error: ' + e.message
3640
}

browser/lib/markdown-it-fence.js

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
'use strict'
2+
3+
module.exports = function (md, renderers, defaultRenderer) {
4+
function fence (state, startLine, endLine) {
5+
let pos = state.bMarks[startLine] + state.tShift[startLine]
6+
let max = state.eMarks[startLine]
7+
8+
if (state.sCount[startLine] - state.blkIndent >= 4 || pos + 3 > max) {
9+
return false
10+
}
11+
12+
const marker = state.src.charCodeAt(pos)
13+
if (!(marker === 96 || marker === 126)) {
14+
return false
15+
}
16+
17+
let mem = pos
18+
pos = state.skipChars(pos, marker)
19+
20+
let len = pos - mem
21+
if (len < 3) {
22+
return false
23+
}
24+
25+
const markup = state.src.slice(mem, pos)
26+
const params = state.src.slice(pos, max)
27+
28+
let nextLine = startLine
29+
let haveEndMarker = false
30+
31+
while (true) {
32+
nextLine++
33+
if (nextLine >= endLine) {
34+
break
35+
}
36+
37+
pos = mem = state.bMarks[nextLine] + state.tShift[nextLine]
38+
max = state.eMarks[nextLine]
39+
40+
if (pos < max && state.sCount[nextLine] < state.blkIndent) {
41+
break
42+
}
43+
if (state.src.charCodeAt(pos) !== marker || state.sCount[nextLine] - state.blkIndent >= 4) {
44+
continue
45+
}
46+
47+
pos = state.skipChars(pos, marker)
48+
49+
if (pos - mem < len) {
50+
continue
51+
}
52+
53+
pos = state.skipSpaces(pos)
54+
55+
if (pos >= max) {
56+
haveEndMarker = true
57+
break
58+
}
59+
}
60+
61+
len = state.sCount[startLine]
62+
state.line = nextLine + (haveEndMarker ? 1 : 0)
63+
64+
const parameters = {}
65+
let langType = ''
66+
let fileName = ''
67+
let firstLineNumber = 1
68+
69+
let match = /^(\w[-\w]*)?(?:\(((?:\s*\w[-\w]*(?:=(?:'(?:.*?[^\\])?'|"(?:.*?[^\\])?"|(?:[^'"][^\s]*)))?)*)\))?(?::([^:]*)(?::(\d+))?)?\s*$/.exec(params)
70+
if (match) {
71+
if (match[1]) {
72+
langType = match[1]
73+
}
74+
if (match[3]) {
75+
fileName = match[3]
76+
}
77+
if (match[4]) {
78+
firstLineNumber = parseInt(match[4], 10)
79+
}
80+
81+
if (match[2]) {
82+
const params = match[2]
83+
const regex = /(\w[-\w]*)(?:=(?:'(.*?[^\\])?'|"(.*?[^\\])?"|([^'"][^\s]*)))?/g
84+
85+
let name, value
86+
while ((match = regex.exec(params))) {
87+
name = match[1]
88+
value = match[2] || match[3] || match[4] || null
89+
90+
const height = /^(\d+)h$/.exec(name)
91+
if (height && !value) {
92+
parameters.height = height[1]
93+
} else {
94+
parameters[name] = value
95+
}
96+
}
97+
}
98+
}
99+
100+
let token
101+
if (renderers[langType]) {
102+
token = state.push(`${langType}_fence`, 'div', 0)
103+
} else {
104+
token = state.push('_fence', 'code', 0)
105+
}
106+
107+
token.langType = langType
108+
token.fileName = fileName
109+
token.firstLineNumber = firstLineNumber
110+
token.parameters = parameters
111+
112+
token.content = state.getLines(startLine + 1, nextLine, len, true)
113+
token.markup = markup
114+
token.map = [startLine, state.line]
115+
116+
return true
117+
}
118+
119+
md.block.ruler.before('fence', '_fence', fence, {
120+
alt: ['paragraph', 'reference', 'blockquote', 'list']
121+
})
122+
123+
for (const name in renderers) {
124+
md.renderer.rules[`${name}_fence`] = (tokens, index) => renderers[name](tokens[index])
125+
}
126+
127+
if (defaultRenderer) {
128+
md.renderer.rules['_fence'] = (tokens, index) => defaultRenderer(tokens[index])
129+
}
130+
}

browser/lib/markdown-it-sanitize-html.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ module.exports = function sanitizePlugin (md, options) {
1515
options
1616
)
1717
}
18-
if (state.tokens[tokenIdx].type === 'fence') {
18+
if (state.tokens[tokenIdx].type === '_fence') {
1919
// escapeHtmlCharacters has better performance
2020
state.tokens[tokenIdx].content = escapeHtmlCharacters(
2121
state.tokens[tokenIdx].content,

browser/lib/markdown.js

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -28,32 +28,6 @@ class Markdown {
2828
html: true,
2929
xhtmlOut: true,
3030
breaks: config.preview.breaks,
31-
highlight: function (str, lang) {
32-
const delimiter = ':'
33-
const langInfo = lang.split(delimiter)
34-
const langType = langInfo[0]
35-
const fileName = langInfo[1] || ''
36-
const firstLineNumber = parseInt(langInfo[2], 10)
37-
38-
if (langType === 'flowchart') {
39-
return `<pre class="flowchart">${str}</pre>`
40-
}
41-
if (langType === 'sequence') {
42-
return `<pre class="sequence">${str}</pre>`
43-
}
44-
if (langType === 'chart') {
45-
return `<pre class="chart">${str}</pre>`
46-
}
47-
if (langType === 'mermaid') {
48-
return `<pre class="mermaid">${str}</pre>`
49-
}
50-
return '<pre class="code CodeMirror">' +
51-
'<span class="filename">' + fileName + '</span>' +
52-
createGutter(str, firstLineNumber) +
53-
'<code class="' + langType + '">' +
54-
str +
55-
'</code></pre>'
56-
},
5731
sanitize: 'STRICT'
5832
}
5933

@@ -157,6 +131,39 @@ class Markdown {
157131
this.md.use(require('markdown-it-admonition'), {types: ['note', 'hint', 'attention', 'caution', 'danger', 'error']})
158132
this.md.use(require('./markdown-it-frontmatter'))
159133

134+
this.md.use(require('./markdown-it-fence'), {
135+
chart: token => {
136+
return `<pre class="fence" data-line="${token.map[0]}">
137+
<span class="filename">${token.fileName}</span>
138+
<div class="chart" data-height="${token.parameters.height}">${token.content}</div>
139+
</pre>`
140+
},
141+
flowchart: token => {
142+
return `<pre class="fence" data-line="${token.map[0]}">
143+
<span class="filename">${token.fileName}</span>
144+
<div class="flowchart" data-height="${token.parameters.height}">${token.content}</div>
145+
</pre>`
146+
},
147+
mermaid: token => {
148+
return `<pre class="fence" data-line="${token.map[0]}">
149+
<span class="filename">${token.fileName}</span>
150+
<div class="mermaid" data-height="${token.parameters.height}">${token.content}</div>
151+
</pre>`
152+
},
153+
sequence: token => {
154+
return `<pre class="fence" data-line="${token.map[0]}">
155+
<span class="filename">${token.fileName}</span>
156+
<div class="sequence" data-height="${token.parameters.height}">${token.content}</div>
157+
</pre>`
158+
}
159+
}, token => {
160+
return `<pre class="code CodeMirror" data-line="${token.map[0]}">
161+
<span class="filename">${token.fileName}</span>
162+
${createGutter(token.content, token.firstLineNumber)}
163+
<code class="${token.langType}">${token.content}</code>
164+
</pre>`
165+
})
166+
160167
const deflate = require('markdown-it-plantuml/lib/deflate')
161168
this.md.use(require('markdown-it-plantuml'), '', {
162169
generateSource: function (umlCode) {

tests/lib/snapshots/markdown-test.js.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,12 @@ Generated by [AVA](https://ava.li).
4747

4848
> Snapshot 1
4949
50-
`<pre class="code CodeMirror"><span class="filename">filename.js</span><span class="lineNumber CodeMirror-gutters"><span class="CodeMirror-linenumber">2</span></span><code class="js">var project = 'boostnote';␊
51-
</code></pre>␊
52-
`
50+
`<pre class="code CodeMirror" data-line="1">␊
51+
<span class="filename">filename.js</span>␊
52+
<span class="lineNumber CodeMirror-gutters"><span class="CodeMirror-linenumber">2</span></span>␊
53+
<code class="js">var project = 'boostnote';␊
54+
</code>␊
55+
</pre>`
5356

5457
## Markdown.render() should renders markdown correctly
5558

5 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)