@quarto/jupyterlab-quarto 0.1.44 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- package/LICENSE +1 -1
- package/README.md +48 -15
- package/lib/__tests__/jupyterlab_quarto.spec.d.ts +3 -0
- package/lib/__tests__/jupyterlab_quarto.spec.js +9 -0
- package/lib/ast/ast.d.ts +2 -0
- package/lib/ast/ast.js +40 -0
- package/lib/const.d.ts +4 -0
- package/lib/const.js +11 -0
- package/lib/hooks/codemirror.d.ts +5 -0
- package/lib/hooks/codemirror.js +77 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.js +61 -0
- package/lib/manager.d.ts +8 -0
- package/lib/manager.js +115 -0
- package/lib/plugins/callouts.d.ts +2 -0
- package/lib/plugins/callouts.js +210 -0
- package/lib/plugins/cites.d.ts +2 -0
- package/lib/plugins/cites.js +166 -0
- package/lib/plugins/decorator.d.ts +2 -0
- package/lib/plugins/decorator.js +58 -0
- package/lib/plugins/divs.d.ts +5 -0
- package/lib/plugins/divs.js +111 -0
- package/lib/plugins/figure-divs.d.ts +2 -0
- package/lib/plugins/figure-divs.js +54 -0
- package/lib/plugins/figures.d.ts +16 -0
- package/lib/plugins/figures.js +98 -0
- package/lib/plugins/gridtables/common/gridtables/GetCells.d.ts +7 -0
- package/lib/plugins/gridtables/common/gridtables/GetCells.js +43 -0
- package/lib/plugins/gridtables/common/gridtables/GetColumnWidths.d.ts +7 -0
- package/lib/plugins/gridtables/common/gridtables/GetColumnWidths.js +22 -0
- package/lib/plugins/gridtables/common/markdown-it/ColumnAlignments.d.ts +7 -0
- package/lib/plugins/gridtables/common/markdown-it/ColumnAlignments.js +12 -0
- package/lib/plugins/gridtables/common/markdown-it/EmitTable.d.ts +4 -0
- package/lib/plugins/gridtables/common/markdown-it/EmitTable.js +64 -0
- package/lib/plugins/gridtables/common/markdown-it/GetCharCodeAtStartOfLine.d.ts +8 -0
- package/lib/plugins/gridtables/common/markdown-it/GetCharCodeAtStartOfLine.js +17 -0
- package/lib/plugins/gridtables/common/markdown-it/GetLine.d.ts +2 -0
- package/lib/plugins/gridtables/common/markdown-it/GetLine.js +9 -0
- package/lib/plugins/gridtables/common/markdown-it/ParseTable.d.ts +3 -0
- package/lib/plugins/gridtables/common/markdown-it/ParseTable.js +152 -0
- package/lib/plugins/gridtables/common/markdown-it/ParseTableResult.d.ts +12 -0
- package/lib/plugins/gridtables/common/markdown-it/ParseTableResult.js +17 -0
- package/lib/plugins/gridtables/index.d.ts +1 -0
- package/lib/plugins/gridtables/index.js +10 -0
- package/lib/plugins/gridtables/interfaces/markdown-it/IState.d.ts +10 -0
- package/lib/plugins/gridtables/interfaces/markdown-it/IState.js +5 -0
- package/lib/plugins/gridtables/interfaces/markdown-it/IToken.d.ts +6 -0
- package/lib/plugins/gridtables/interfaces/markdown-it/IToken.js +5 -0
- package/lib/plugins/gridtables/interfaces/markdown-it/TRuleFunction.d.ts +3 -0
- package/lib/plugins/gridtables/interfaces/markdown-it/TRuleFunction.js +5 -0
- package/lib/plugins/gridtables/rules/gridtable.d.ts +3 -0
- package/lib/plugins/gridtables/rules/gridtable.js +25 -0
- package/lib/plugins/index.d.ts +15 -0
- package/lib/plugins/index.js +21 -0
- package/lib/plugins/math.d.ts +6 -0
- package/lib/plugins/math.js +179 -0
- package/lib/plugins/mermaid/index.d.ts +4 -0
- package/lib/plugins/mermaid/index.js +60 -0
- package/lib/plugins/shortcodes.d.ts +3 -0
- package/lib/plugins/shortcodes.js +32 -0
- package/lib/plugins/spans.d.ts +2 -0
- package/lib/plugins/spans.js +37 -0
- package/lib/plugins/table-captions.d.ts +2 -0
- package/lib/plugins/table-captions.js +72 -0
- package/lib/plugins/utils/html.d.ts +11 -0
- package/lib/plugins/utils/html.js +50 -0
- package/lib/plugins/utils/markdownit.d.ts +15 -0
- package/lib/plugins/utils/markdownit.js +46 -0
- package/lib/plugins/utils/tok.d.ts +7 -0
- package/lib/plugins/utils/tok.js +13 -0
- package/lib/plugins/yaml.d.ts +2 -0
- package/lib/plugins/yaml.js +330 -0
- package/lib/providers/attrs.d.ts +1 -0
- package/lib/providers/attrs.js +15 -0
- package/lib/providers/callouts.d.ts +1 -0
- package/lib/providers/callouts.js +15 -0
- package/lib/providers/cites.d.ts +1 -0
- package/lib/providers/cites.js +15 -0
- package/lib/providers/decorator.d.ts +1 -0
- package/lib/providers/decorator.js +15 -0
- package/lib/providers/deflist.d.ts +1 -0
- package/lib/providers/deflist.js +15 -0
- package/lib/providers/divs.d.ts +1 -0
- package/lib/providers/divs.js +27 -0
- package/lib/providers/figure-divs.d.ts +1 -0
- package/lib/providers/figure-divs.js +15 -0
- package/lib/providers/figures.d.ts +1 -0
- package/lib/providers/figures.js +15 -0
- package/lib/providers/footnotes.d.ts +1 -0
- package/lib/providers/footnotes.js +15 -0
- package/lib/providers/gridtables.d.ts +1 -0
- package/lib/providers/gridtables.js +15 -0
- package/lib/providers/math.d.ts +1 -0
- package/lib/providers/math.js +104 -0
- package/lib/providers/mermaid.d.ts +1 -0
- package/lib/providers/mermaid.js +17 -0
- package/lib/providers/provider.d.ts +3 -0
- package/lib/providers/provider.js +12 -0
- package/lib/providers/shortcodes.d.ts +1 -0
- package/lib/providers/shortcodes.js +15 -0
- package/lib/providers/spans.d.ts +1 -0
- package/lib/providers/spans.js +15 -0
- package/lib/providers/sub.d.ts +1 -0
- package/lib/providers/sub.js +15 -0
- package/lib/providers/sup.d.ts +1 -0
- package/lib/providers/sup.js +15 -0
- package/lib/providers/table-captions.d.ts +1 -0
- package/lib/providers/table-captions.js +15 -0
- package/lib/providers/tasklists.d.ts +1 -0
- package/lib/providers/tasklists.js +15 -0
- package/lib/providers/yaml.d.ts +1 -0
- package/lib/providers/yaml.js +15 -0
- package/lib/types.d.ts +43 -0
- package/lib/types.js +1 -0
- package/lib/widgets.d.ts +14 -0
- package/lib/widgets.js +57 -0
- package/package.json +105 -39
- package/style/base.css +34 -33
- package/style/index.css +1 -1
- package/schema/plugin.json +0 -8
- package/src/@types/markdown-it-deflist.d.ts +0 -10
- package/src/@types/markdown-it-footnote.d.ts +0 -10
- package/src/@types/markdown-it-gridtables.d.ts +0 -10
- package/src/@types/markdown-it-implicit-figures.d.ts +0 -10
- package/src/@types/markdown-it-sub.d.ts +0 -10
- package/src/@types/markdown-it-sup.d.ts +0 -10
- package/src/@types/markdown-it-task-lists.d.ts +0 -10
@@ -0,0 +1,179 @@
|
|
1
|
+
/*
|
2
|
+
* math.ts
|
3
|
+
*
|
4
|
+
* Copyright (C) 2020-2023 Posit Software, PBC
|
5
|
+
*
|
6
|
+
*/
|
7
|
+
export const kTokMathBlock = 'math_block';
|
8
|
+
export const kTokMathInline = 'math_inline';
|
9
|
+
function renderMath(content, convertOptions) {
|
10
|
+
if (convertOptions.display) {
|
11
|
+
return `<div class='quarto-display-math'>\\[${content}\\]</div>`;
|
12
|
+
}
|
13
|
+
else {
|
14
|
+
return `<span class='quarto-inline-math'>\\(${content}\\)</span>`;
|
15
|
+
}
|
16
|
+
}
|
17
|
+
// Test if potential opening or closing delimieter
|
18
|
+
// Assumes that there is a "$" at state.src[pos]
|
19
|
+
function isValidDelim(state, pos) {
|
20
|
+
const max = state.posMax;
|
21
|
+
let can_open = true;
|
22
|
+
let can_close = true;
|
23
|
+
const prevChar = pos > 0 ? state.src.charCodeAt(pos - 1) : -1, nextChar = pos + 1 <= max ? state.src.charCodeAt(pos + 1) : -1;
|
24
|
+
// Check non-whitespace conditions for opening and closing, and
|
25
|
+
// check that closing delimeter isn't followed by a number
|
26
|
+
if (prevChar === 0x20 /* " " */ ||
|
27
|
+
prevChar === 0x09 /* \t */ ||
|
28
|
+
(nextChar >= 0x30 /* "0" */ && nextChar <= 0x39) /* "9" */) {
|
29
|
+
can_close = false;
|
30
|
+
}
|
31
|
+
if (nextChar === 0x20 /* " " */ || nextChar === 0x09 /* \t */) {
|
32
|
+
can_open = false;
|
33
|
+
}
|
34
|
+
return {
|
35
|
+
can_open: can_open,
|
36
|
+
can_close: can_close
|
37
|
+
};
|
38
|
+
}
|
39
|
+
function math_inline(state, silent) {
|
40
|
+
if (state.src[state.pos] !== '$') {
|
41
|
+
return false;
|
42
|
+
}
|
43
|
+
let res = isValidDelim(state, state.pos);
|
44
|
+
if (!res.can_open) {
|
45
|
+
if (!silent) {
|
46
|
+
state.pending += '$';
|
47
|
+
}
|
48
|
+
state.pos += 1;
|
49
|
+
return true;
|
50
|
+
}
|
51
|
+
// First check for and bypass all properly escaped delimieters
|
52
|
+
// This loop will assume that the first leading backtick can not
|
53
|
+
// be the first character in state.src, which is known since
|
54
|
+
// we have found an opening delimieter already.
|
55
|
+
const start = state.pos + 1;
|
56
|
+
let match = start;
|
57
|
+
while ((match = state.src.indexOf('$', match)) !== -1) {
|
58
|
+
// Found potential $, look for escapes, pos will point to
|
59
|
+
// first non escape when complete
|
60
|
+
let pos = match - 1;
|
61
|
+
while (state.src[pos] === '\\') {
|
62
|
+
pos -= 1;
|
63
|
+
}
|
64
|
+
// Even number of escapes, potential closing delimiter found
|
65
|
+
if ((match - pos) % 2 === 1) {
|
66
|
+
break;
|
67
|
+
}
|
68
|
+
match += 1;
|
69
|
+
}
|
70
|
+
// No closing delimter found. Consume $ and continue.
|
71
|
+
if (match === -1) {
|
72
|
+
if (!silent) {
|
73
|
+
state.pending += '$';
|
74
|
+
}
|
75
|
+
state.pos = start;
|
76
|
+
return true;
|
77
|
+
}
|
78
|
+
// Check if we have empty content, ie: $$. Do not parse.
|
79
|
+
if (match - start === 0) {
|
80
|
+
if (!silent) {
|
81
|
+
state.pending += '$$';
|
82
|
+
}
|
83
|
+
state.pos = start + 1;
|
84
|
+
return true;
|
85
|
+
}
|
86
|
+
// Check for valid closing delimiter
|
87
|
+
res = isValidDelim(state, match);
|
88
|
+
if (!res.can_close) {
|
89
|
+
if (!silent) {
|
90
|
+
state.pending += '$';
|
91
|
+
}
|
92
|
+
state.pos = start;
|
93
|
+
return true;
|
94
|
+
}
|
95
|
+
if (!silent) {
|
96
|
+
const token = state.push('math_inline', 'math', 0);
|
97
|
+
token.markup = '$';
|
98
|
+
token.content = state.src.slice(start, match);
|
99
|
+
}
|
100
|
+
state.pos = match + 1;
|
101
|
+
return true;
|
102
|
+
}
|
103
|
+
function math_block(state, start, end, silent) {
|
104
|
+
let next, lastPos;
|
105
|
+
let found = false, pos = state.bMarks[start] + state.tShift[start], max = state.eMarks[start], lastLine = '';
|
106
|
+
if (pos + 2 > max) {
|
107
|
+
return false;
|
108
|
+
}
|
109
|
+
if (state.src.slice(pos, pos + 2) !== '$$') {
|
110
|
+
return false;
|
111
|
+
}
|
112
|
+
pos += 2;
|
113
|
+
let firstLine = state.src.slice(pos, max);
|
114
|
+
if (silent) {
|
115
|
+
return true;
|
116
|
+
}
|
117
|
+
if (firstLine.trim().slice(-2) === '$$') {
|
118
|
+
// Single line expression
|
119
|
+
firstLine = firstLine.trim().slice(0, -2);
|
120
|
+
found = true;
|
121
|
+
}
|
122
|
+
let attrStr = undefined;
|
123
|
+
for (next = start; !found;) {
|
124
|
+
next++;
|
125
|
+
if (next >= end) {
|
126
|
+
break;
|
127
|
+
}
|
128
|
+
pos = state.bMarks[next] + state.tShift[next];
|
129
|
+
max = state.eMarks[next];
|
130
|
+
if (pos < max && state.tShift[next] < state.blkIndent) {
|
131
|
+
// non-empty line with negative indent should stop the list:
|
132
|
+
break;
|
133
|
+
}
|
134
|
+
const line = state.src.slice(pos, max).trim();
|
135
|
+
const match = line.match(/^\$\$\s*(\{.*\})?\s*$/);
|
136
|
+
if (match) {
|
137
|
+
lastPos = state.src.slice(0, max).lastIndexOf('$$');
|
138
|
+
lastLine = state.src.slice(pos, lastPos);
|
139
|
+
attrStr = match[1];
|
140
|
+
found = true;
|
141
|
+
}
|
142
|
+
}
|
143
|
+
state.line = next + 1;
|
144
|
+
const token = state.push(kTokMathBlock, 'math', 0);
|
145
|
+
token.block = true;
|
146
|
+
if (attrStr) {
|
147
|
+
token.info = attrStr;
|
148
|
+
}
|
149
|
+
token.content =
|
150
|
+
(firstLine && firstLine.trim() ? firstLine + '\n' : '') +
|
151
|
+
state.getLines(start + 1, next, state.tShift[start], true) +
|
152
|
+
(lastLine && lastLine.trim() ? lastLine : '');
|
153
|
+
token.map = [start, state.line];
|
154
|
+
token.markup = '$$';
|
155
|
+
return true;
|
156
|
+
}
|
157
|
+
export function mathjaxPlugin(md, options) {
|
158
|
+
// Default options
|
159
|
+
options = options || {};
|
160
|
+
const enableInlines = options.enableInlines !== undefined ? options.enableInlines : true;
|
161
|
+
const convertOptions = {
|
162
|
+
display: false
|
163
|
+
};
|
164
|
+
// set MathJax as the renderer for markdown-it-simplemath
|
165
|
+
md.inline.ruler.after('escape', kTokMathInline, math_inline);
|
166
|
+
md.block.ruler.after('blockquote', kTokMathBlock, math_block, {
|
167
|
+
alt: ['paragraph', 'reference', 'blockquote', 'list']
|
168
|
+
});
|
169
|
+
if (enableInlines) {
|
170
|
+
md.renderer.rules.math_inline = function (tokens, idx) {
|
171
|
+
convertOptions.display = false;
|
172
|
+
return renderMath(tokens[idx].content, convertOptions);
|
173
|
+
};
|
174
|
+
}
|
175
|
+
md.renderer.rules.math_block = function (tokens, idx) {
|
176
|
+
convertOptions.display = true;
|
177
|
+
return renderMath(tokens[idx].content, convertOptions);
|
178
|
+
};
|
179
|
+
}
|
@@ -0,0 +1,60 @@
|
|
1
|
+
import Mermaid from 'mermaid';
|
2
|
+
export default function mermaidPlugin(md, options) {
|
3
|
+
const kLang = 'mermaid';
|
4
|
+
const kContainer = 'quarto-mermaid';
|
5
|
+
Mermaid.initialize({
|
6
|
+
securityLevel: 'loose',
|
7
|
+
theme: options.dark ? 'dark' : 'default',
|
8
|
+
...options
|
9
|
+
});
|
10
|
+
const defaultFenceRenderer = md.renderer.rules.fence;
|
11
|
+
// Render custom code types as SVGs, letting the fence parser do all the heavy lifting.
|
12
|
+
function mermaidFenceRenderer(tokens, idx, options, env, slf) {
|
13
|
+
const token = tokens[idx];
|
14
|
+
if (token.info === kLang ||
|
15
|
+
(token.attrs !== null &&
|
16
|
+
token.attrs.length === 1 &&
|
17
|
+
token.attrs[0][0] === kLang)) {
|
18
|
+
let imageHTML = '';
|
19
|
+
const imageAttrs = [];
|
20
|
+
// Create element to render into
|
21
|
+
const element = document.createElement('div');
|
22
|
+
document.body.appendChild(element);
|
23
|
+
// Render with Mermaid
|
24
|
+
try {
|
25
|
+
Mermaid.mermaidAPI.render(kContainer, token.content, (html) => {
|
26
|
+
// We need to forcibly extract the max-width/height attributes to set on img tag
|
27
|
+
const mermaidEl = document.getElementById(kContainer);
|
28
|
+
if (mermaidEl !== null) {
|
29
|
+
imageAttrs.push([
|
30
|
+
'style',
|
31
|
+
`max-width:${mermaidEl.style.maxWidth};max-height:${mermaidEl.style.maxHeight}`
|
32
|
+
]);
|
33
|
+
}
|
34
|
+
// Store HTML
|
35
|
+
imageHTML = html;
|
36
|
+
}, element);
|
37
|
+
}
|
38
|
+
catch (e) {
|
39
|
+
return `<pre>Failed to render mermaid diagram.${e}</pre>`;
|
40
|
+
}
|
41
|
+
finally {
|
42
|
+
element.remove();
|
43
|
+
}
|
44
|
+
// Store encoded image data
|
45
|
+
imageAttrs.push([
|
46
|
+
'src',
|
47
|
+
`data:image/svg+xml,${encodeURIComponent(imageHTML)}`
|
48
|
+
]);
|
49
|
+
return `<img ${slf.renderAttrs({ attrs: imageAttrs })}>`;
|
50
|
+
}
|
51
|
+
else {
|
52
|
+
if (defaultFenceRenderer !== undefined) {
|
53
|
+
return defaultFenceRenderer(tokens, idx, options, env, slf);
|
54
|
+
}
|
55
|
+
// Missing fence renderer!
|
56
|
+
return '';
|
57
|
+
}
|
58
|
+
}
|
59
|
+
md.renderer.rules.fence = mermaidFenceRenderer;
|
60
|
+
}
|
@@ -0,0 +1,32 @@
|
|
1
|
+
import { escapeHtml } from 'markdown-it/lib/common/utils';
|
2
|
+
export const kShortcode = 'shortcode';
|
3
|
+
export const shortcodePlugin = (md) => {
|
4
|
+
const shortcode = (state, silent) => {
|
5
|
+
// {{< shortcode >}}
|
6
|
+
if (state.src.slice(state.pos, state.pos + 3) !== '{{<') {
|
7
|
+
return false;
|
8
|
+
}
|
9
|
+
const shortcodeEndRegex = />}}/g;
|
10
|
+
// ignore if shortcode doesn't end
|
11
|
+
const end = state.src.slice(state.pos).search(shortcodeEndRegex);
|
12
|
+
if (end === -1) {
|
13
|
+
return false;
|
14
|
+
}
|
15
|
+
const shortcodeContent = state.src.slice(state.pos + 3, state.pos + end);
|
16
|
+
if (!silent) {
|
17
|
+
const token = state.push('shortcode', 'shortcode', 0);
|
18
|
+
token.markup = '';
|
19
|
+
token.content = shortcodeContent;
|
20
|
+
}
|
21
|
+
state.pos += end + 3;
|
22
|
+
return true;
|
23
|
+
};
|
24
|
+
md.inline.ruler.after('escape', kShortcode, shortcode);
|
25
|
+
const renderShortcode = (tokens, idx) => {
|
26
|
+
const token = tokens[idx];
|
27
|
+
const content = token.content;
|
28
|
+
// insert shortcode braces and escape content's html entities
|
29
|
+
return `<span class="shortcode">${escapeHtml(`{{<${content}>}}`)}</span>`;
|
30
|
+
};
|
31
|
+
md.renderer.rules[kShortcode] = renderShortcode;
|
32
|
+
};
|
@@ -0,0 +1,37 @@
|
|
1
|
+
/*
|
2
|
+
* span.ts
|
3
|
+
*
|
4
|
+
* Copyright (C) 2020-2023 Posit Software, PBC
|
5
|
+
*
|
6
|
+
*/
|
7
|
+
export function spansPlugin(md) {
|
8
|
+
function span(state) {
|
9
|
+
const max = state.posMax;
|
10
|
+
if (state.src.charCodeAt(state.pos) !== 0x5b) {
|
11
|
+
// opening [
|
12
|
+
return false;
|
13
|
+
}
|
14
|
+
const labelStart = state.pos + 1;
|
15
|
+
const labelEnd = state.md.helpers.parseLinkLabel(state, state.pos, true);
|
16
|
+
if (labelEnd < 0) {
|
17
|
+
// parser failed to find closing ]
|
18
|
+
return false;
|
19
|
+
}
|
20
|
+
const pos = labelEnd + 1;
|
21
|
+
if (pos < max && state.src.charCodeAt(pos) === 0x7b /* { */) {
|
22
|
+
// probably found span
|
23
|
+
state.pos = labelStart;
|
24
|
+
state.posMax = labelEnd;
|
25
|
+
state.push('span_open', 'span', 1);
|
26
|
+
state.md.inline.tokenize(state);
|
27
|
+
state.push('span_close', 'span', -1);
|
28
|
+
state.pos = pos;
|
29
|
+
state.posMax = max;
|
30
|
+
return true;
|
31
|
+
}
|
32
|
+
else {
|
33
|
+
return false;
|
34
|
+
}
|
35
|
+
}
|
36
|
+
md.inline.ruler.push('quarto-spans', span);
|
37
|
+
}
|
@@ -0,0 +1,72 @@
|
|
1
|
+
/*
|
2
|
+
* table-captions.ts
|
3
|
+
*
|
4
|
+
* Copyright (C) 2020-2023 Posit Software, PBC
|
5
|
+
*
|
6
|
+
*/
|
7
|
+
import { kTokInline, kTokParaClose, kTokParaOpen, kTokTableClose, kTokTableOpen, kTokText } from './utils/tok';
|
8
|
+
const kTableCaptionRule = 'quarto-table-captions';
|
9
|
+
export const tableCaptionPlugin = (md) => {
|
10
|
+
md.core.ruler.push(kTableCaptionRule, state => {
|
11
|
+
const tableIdxs = [];
|
12
|
+
const tablePoss = [];
|
13
|
+
// Identify tables that we'd like to process
|
14
|
+
// The tables must be the bottom first (we will process them bottom to
|
15
|
+
// top to ensure that the positions remain accurate as the tokens
|
16
|
+
// are mutated
|
17
|
+
for (let i = 0; i < state.tokens.length; i++) {
|
18
|
+
const token = state.tokens[i];
|
19
|
+
if (token.type === kTokTableOpen) {
|
20
|
+
tableIdxs.push(i);
|
21
|
+
}
|
22
|
+
else if (token.type === kTokTableClose) {
|
23
|
+
const start = tableIdxs.pop();
|
24
|
+
if (start) {
|
25
|
+
tablePoss.unshift({
|
26
|
+
start,
|
27
|
+
end: i
|
28
|
+
});
|
29
|
+
}
|
30
|
+
}
|
31
|
+
}
|
32
|
+
// Look just past the tables and if there is a paragraph that is
|
33
|
+
// a table caption, extract that and place it in the table
|
34
|
+
for (const tablePos of tablePoss) {
|
35
|
+
resolveTableCaption(state.tokens, tablePos.start, tablePos.end);
|
36
|
+
}
|
37
|
+
});
|
38
|
+
};
|
39
|
+
function resolveTableCaption(tokens, tblStartPos, tblEndPos) {
|
40
|
+
// Must have at least three tokens past the table end
|
41
|
+
if (tokens.length > tblEndPos + 3) {
|
42
|
+
if (tokens[tblEndPos + 1].type === kTokParaOpen &&
|
43
|
+
tokens[tblEndPos + 2].type === kTokInline &&
|
44
|
+
tokens[tblEndPos + 3].type === kTokParaClose) {
|
45
|
+
const maybeCaption = tokens[tblEndPos + 2];
|
46
|
+
const isText = maybeCaption.children !== null &&
|
47
|
+
maybeCaption.children.length > 0 &&
|
48
|
+
maybeCaption.children[0].type === kTokText;
|
49
|
+
const maybeCaptionText = isText && maybeCaption.children ? maybeCaption.children[0].content : '';
|
50
|
+
const match = maybeCaptionText.match(/^:\s([^{}]*)(?:\{.*\}){0,1}$/);
|
51
|
+
if (match && match[1]) {
|
52
|
+
// Carve out the existing tokens
|
53
|
+
const capTokens = tokens.splice(tblEndPos + 1, 3);
|
54
|
+
// We have the caption, remove the paragraph and return
|
55
|
+
// the caption
|
56
|
+
capTokens[0].type = 'table_caption';
|
57
|
+
capTokens[0].tag = 'caption';
|
58
|
+
// Forward any attributes from the caption up to the table
|
59
|
+
tokens[tblStartPos].attrs = capTokens[0].attrs;
|
60
|
+
capTokens[0].attrs = [];
|
61
|
+
// Trim the content
|
62
|
+
if (capTokens[1].children && capTokens[1].children.length > 0) {
|
63
|
+
capTokens[1].children[0].content = match[1];
|
64
|
+
}
|
65
|
+
// Close the caption
|
66
|
+
capTokens[2].type = 'table_caption';
|
67
|
+
capTokens[2].tag = 'caption';
|
68
|
+
tokens.splice(tblStartPos + 1, 0, ...capTokens);
|
69
|
+
}
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import Token from 'markdown-it/lib/token';
|
2
|
+
export interface DecoratorOptions {
|
3
|
+
customClass?: string;
|
4
|
+
hide: {
|
5
|
+
id?: boolean;
|
6
|
+
classes?: boolean;
|
7
|
+
attributes?: boolean;
|
8
|
+
};
|
9
|
+
}
|
10
|
+
export declare const decorator: (contents: string[], customClass?: string) => string;
|
11
|
+
export declare const attributeDecorator: (token: Token, options?: DecoratorOptions) => string;
|
@@ -0,0 +1,50 @@
|
|
1
|
+
/*
|
2
|
+
* html.ts
|
3
|
+
*
|
4
|
+
* Copyright (C) 2020-2023 Posit Software, PBC
|
5
|
+
*
|
6
|
+
*/
|
7
|
+
import { readAttrValue } from './markdownit';
|
8
|
+
export const decorator = (contents, customClass) => {
|
9
|
+
if (contents.length > 0) {
|
10
|
+
// Provide a decorator with the attributes
|
11
|
+
return `<div class="quarto-attribute-decorator${customClass ? ' ' + customClass : ''}">${contents.map(decoratorSpan).join('')}</div>`;
|
12
|
+
}
|
13
|
+
else {
|
14
|
+
// There is no decorator - no attributes
|
15
|
+
return '';
|
16
|
+
}
|
17
|
+
};
|
18
|
+
export const attributeDecorator = (token, options) => {
|
19
|
+
var _a;
|
20
|
+
// id
|
21
|
+
const id = readAttrValue('id', token.attrs);
|
22
|
+
// classes
|
23
|
+
const clz = readAttrValue('class', token.attrs);
|
24
|
+
// other attributes
|
25
|
+
const otherAttrs = (_a = token.attrs) === null || _a === void 0 ? void 0 : _a.filter(attr => {
|
26
|
+
return attr[0] !== 'id' && attr[0] !== 'class';
|
27
|
+
});
|
28
|
+
// Create a decorator for the div
|
29
|
+
const contents = [];
|
30
|
+
if (id && !(options === null || options === void 0 ? void 0 : options.hide.id)) {
|
31
|
+
contents.push(`#${id}`);
|
32
|
+
}
|
33
|
+
if (clz && !(options === null || options === void 0 ? void 0 : options.hide.classes)) {
|
34
|
+
const clzStr = clz
|
35
|
+
.split(' ')
|
36
|
+
.map(cls => `.${cls}`)
|
37
|
+
.join(' ');
|
38
|
+
contents.push(clzStr);
|
39
|
+
}
|
40
|
+
if (otherAttrs && otherAttrs.length > 0 && !(options === null || options === void 0 ? void 0 : options.hide.attributes)) {
|
41
|
+
const otherAttrStr = otherAttrs === null || otherAttrs === void 0 ? void 0 : otherAttrs.map(attr => {
|
42
|
+
return `${attr[0]}="${attr[1]}"`;
|
43
|
+
}).join(' ');
|
44
|
+
contents.push(otherAttrStr);
|
45
|
+
}
|
46
|
+
return decorator(contents, options === null || options === void 0 ? void 0 : options.customClass);
|
47
|
+
};
|
48
|
+
const decoratorSpan = (contents) => {
|
49
|
+
return `<span class="quarto-attribute-decorator-content">${contents}</span>`;
|
50
|
+
};
|
@@ -0,0 +1,15 @@
|
|
1
|
+
export declare const hasClass: (clz: string, attrs: null | [
|
2
|
+
string,
|
3
|
+
string
|
4
|
+
][]) => boolean | undefined;
|
5
|
+
export declare const readAttrValue: (name: string, attrs: null | [
|
6
|
+
string,
|
7
|
+
string
|
8
|
+
][]) => string | undefined;
|
9
|
+
export declare const addClass: (clz: string, attrs: null | [
|
10
|
+
string,
|
11
|
+
string
|
12
|
+
][]) => [
|
13
|
+
string,
|
14
|
+
string
|
15
|
+
][];
|
@@ -0,0 +1,46 @@
|
|
1
|
+
/*
|
2
|
+
* markdownit.ts
|
3
|
+
*
|
4
|
+
* Copyright (C) 2020-2023 Posit Software, PBC
|
5
|
+
*
|
6
|
+
*/
|
7
|
+
export const hasClass = (clz, attrs) => {
|
8
|
+
if (attrs === null) {
|
9
|
+
return false;
|
10
|
+
}
|
11
|
+
const classes = readAttrValue('class', attrs);
|
12
|
+
if (classes === null) {
|
13
|
+
return false;
|
14
|
+
}
|
15
|
+
else {
|
16
|
+
return classes === null || classes === void 0 ? void 0 : classes.split(' ').includes(clz);
|
17
|
+
}
|
18
|
+
};
|
19
|
+
export const readAttrValue = (name, attrs) => {
|
20
|
+
if (attrs === null) {
|
21
|
+
return undefined;
|
22
|
+
}
|
23
|
+
const attr = attrs.find(attr => {
|
24
|
+
return attr[0] === name;
|
25
|
+
});
|
26
|
+
return attr ? attr[1] : undefined;
|
27
|
+
};
|
28
|
+
export const addClass = (clz, attrs) => {
|
29
|
+
if (attrs === null) {
|
30
|
+
attrs = [];
|
31
|
+
attrs.push(['class', clz]);
|
32
|
+
return attrs;
|
33
|
+
}
|
34
|
+
else {
|
35
|
+
const clzIdx = attrs.findIndex(attr => attr[0] === 'class');
|
36
|
+
if (clzIdx >= 0) {
|
37
|
+
const currentClz = attrs[clzIdx];
|
38
|
+
attrs[clzIdx] = ['class', `${currentClz[1]} ${clz}`.trim()];
|
39
|
+
return attrs;
|
40
|
+
}
|
41
|
+
else {
|
42
|
+
attrs.push(['class', clz]);
|
43
|
+
return attrs;
|
44
|
+
}
|
45
|
+
}
|
46
|
+
};
|
@@ -0,0 +1,7 @@
|
|
1
|
+
export declare const kTokParaOpen = "paragraph_open";
|
2
|
+
export declare const kTokParaClose = "paragraph_close";
|
3
|
+
export declare const kTokInline = "inline";
|
4
|
+
export declare const kTokTableOpen = "table_open";
|
5
|
+
export declare const kTokTableClose = "table_close";
|
6
|
+
export declare const kTokText = "text";
|
7
|
+
export declare const kTokHeadingOpen = "heading_open";
|
@@ -0,0 +1,13 @@
|
|
1
|
+
/*
|
2
|
+
* tok.ts
|
3
|
+
*
|
4
|
+
* Copyright (C) 2020-2023 Posit Software, PBC
|
5
|
+
*
|
6
|
+
*/
|
7
|
+
export const kTokParaOpen = 'paragraph_open';
|
8
|
+
export const kTokParaClose = 'paragraph_close';
|
9
|
+
export const kTokInline = 'inline';
|
10
|
+
export const kTokTableOpen = 'table_open';
|
11
|
+
export const kTokTableClose = 'table_close';
|
12
|
+
export const kTokText = 'text';
|
13
|
+
export const kTokHeadingOpen = 'heading_open';
|