@quarto/jupyterlab-quarto 0.1.44 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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,210 @@
|
|
|
1
|
+
import Token from 'markdown-it/lib/token';
|
|
2
|
+
import { addClass, readAttrValue } from './utils/markdownit';
|
|
3
|
+
import { kTokDivClose, kTokDivOpen } from './divs';
|
|
4
|
+
const kTokCalloutOpen = 'quarto_callout_open';
|
|
5
|
+
const kTokCalloutClose = 'quarto_callout_close';
|
|
6
|
+
const kTokCalloutTitleOpen = 'quarto_callout_title_open';
|
|
7
|
+
const kTokCalloutTitleClose = 'quarto_callout_title_close';
|
|
8
|
+
const kTokCalloutContentOpen = 'quarto_callout_content_open';
|
|
9
|
+
const kTokCalloutContentClose = 'quarto_callout_content_close';
|
|
10
|
+
const kCalloutPrefix = 'callout-';
|
|
11
|
+
const kCalloutRuleName = 'quarto-callouts';
|
|
12
|
+
export const calloutPlugin = (md) => {
|
|
13
|
+
// Handle pandoc-style divs
|
|
14
|
+
md.core.ruler.push(kCalloutRuleName, state => {
|
|
15
|
+
const noteStartCallout = (callout, depth) => {
|
|
16
|
+
if (calloutDepth === -1) {
|
|
17
|
+
calloutDepth = depth;
|
|
18
|
+
}
|
|
19
|
+
state.env['quarto-active-callout'] = callout;
|
|
20
|
+
};
|
|
21
|
+
const noteCloseCallout = () => {
|
|
22
|
+
calloutDepth = -1;
|
|
23
|
+
state.env['quarto-active-callout'] = undefined;
|
|
24
|
+
};
|
|
25
|
+
const activeCallout = () => {
|
|
26
|
+
return state.env['quarto-active-callout'];
|
|
27
|
+
};
|
|
28
|
+
const isCloseCallout = (depth) => {
|
|
29
|
+
return calloutDepth === depth;
|
|
30
|
+
};
|
|
31
|
+
const titleOpenTok = (title) => {
|
|
32
|
+
const token = new Token(kTokCalloutTitleOpen, '', 1);
|
|
33
|
+
token.tag = 'div';
|
|
34
|
+
token.attrs = [['class', 'callout-header']];
|
|
35
|
+
if (title) {
|
|
36
|
+
token.attrs.push(['title', title]);
|
|
37
|
+
}
|
|
38
|
+
return token;
|
|
39
|
+
};
|
|
40
|
+
const titleCloseTok = () => {
|
|
41
|
+
const token = new Token(kTokCalloutTitleClose, '', -1);
|
|
42
|
+
token.tag = 'div';
|
|
43
|
+
return token;
|
|
44
|
+
};
|
|
45
|
+
const contentOpenTok = () => {
|
|
46
|
+
const token = new Token(kTokCalloutContentOpen, '', 1);
|
|
47
|
+
token.tag = 'div';
|
|
48
|
+
token.attrs = [['class', 'callout-body-container callout-body']];
|
|
49
|
+
return token;
|
|
50
|
+
};
|
|
51
|
+
const contentCloseTok = () => {
|
|
52
|
+
const token = new Token(kTokCalloutContentClose, '', -1);
|
|
53
|
+
token.tag = 'div';
|
|
54
|
+
return token;
|
|
55
|
+
};
|
|
56
|
+
const outTokens = [];
|
|
57
|
+
let calloutDepth = -1;
|
|
58
|
+
let divDepth = 0;
|
|
59
|
+
// just started callout - process title
|
|
60
|
+
// finished processing title - process content
|
|
61
|
+
let calloutState = 'scanning';
|
|
62
|
+
for (const token of state.tokens) {
|
|
63
|
+
switch (calloutState) {
|
|
64
|
+
case 'add-title':
|
|
65
|
+
if (token.type === 'heading_open') {
|
|
66
|
+
outTokens.push(titleOpenTok());
|
|
67
|
+
calloutState = 'capturing-title';
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
const callout = activeCallout();
|
|
71
|
+
outTokens.push(titleOpenTok(callout.title));
|
|
72
|
+
outTokens.push(titleCloseTok());
|
|
73
|
+
calloutState = 'add-body';
|
|
74
|
+
}
|
|
75
|
+
break;
|
|
76
|
+
case 'capturing-title':
|
|
77
|
+
if (token.type === 'heading_close') {
|
|
78
|
+
outTokens.push(titleCloseTok());
|
|
79
|
+
calloutState = 'add-body';
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
outTokens.push(token);
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
case 'add-body':
|
|
86
|
+
outTokens.push(contentOpenTok());
|
|
87
|
+
outTokens.push(token);
|
|
88
|
+
calloutState = 'capturing-body';
|
|
89
|
+
break;
|
|
90
|
+
case 'scanning':
|
|
91
|
+
default:
|
|
92
|
+
if (token.type === kTokDivOpen) {
|
|
93
|
+
divDepth++;
|
|
94
|
+
const callout = parseCallout(token.attrs);
|
|
95
|
+
if (callout) {
|
|
96
|
+
noteStartCallout(callout, divDepth);
|
|
97
|
+
calloutState = 'add-title';
|
|
98
|
+
const openCallout = new Token(kTokCalloutOpen, '', 1);
|
|
99
|
+
openCallout.attrs = openCallout.attrs || [];
|
|
100
|
+
openCallout.meta = callout;
|
|
101
|
+
outTokens.push(openCallout);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
outTokens.push(token);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else if (token.type === kTokDivClose) {
|
|
108
|
+
if (isCloseCallout(divDepth)) {
|
|
109
|
+
outTokens.push(contentCloseTok());
|
|
110
|
+
outTokens.push(new Token(kTokCalloutClose, '', -1));
|
|
111
|
+
noteCloseCallout();
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
outTokens.push(token);
|
|
115
|
+
}
|
|
116
|
+
divDepth--;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
outTokens.push(token);
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
state.tokens = outTokens;
|
|
125
|
+
return false;
|
|
126
|
+
});
|
|
127
|
+
md.renderer.rules[kTokCalloutOpen] = renderStartCallout;
|
|
128
|
+
md.renderer.rules[kTokCalloutClose] = renderEndCallout;
|
|
129
|
+
md.renderer.rules[kTokCalloutTitleOpen] = renderStartCalloutTitle;
|
|
130
|
+
md.renderer.rules[kTokCalloutTitleClose] = renderEndCalloutTitle;
|
|
131
|
+
};
|
|
132
|
+
// Render pandoc-style divs
|
|
133
|
+
function renderStartCallout(tokens, idx, _options, _env, self) {
|
|
134
|
+
const token = tokens[idx];
|
|
135
|
+
const callout = token.meta;
|
|
136
|
+
// Add classes decorating as callout
|
|
137
|
+
token.attrs = addClass(`callout ${callout.clz}`, token.attrs);
|
|
138
|
+
// Add class that reflects the style
|
|
139
|
+
token.attrs = addClass(appearanceClass(callout.appearance), token.attrs);
|
|
140
|
+
return `<div ${self.renderAttrs(token)}>`;
|
|
141
|
+
}
|
|
142
|
+
// Render pandoc-style divs
|
|
143
|
+
function renderEndCallout() {
|
|
144
|
+
return '</div>';
|
|
145
|
+
}
|
|
146
|
+
function renderStartCalloutTitle(tokens, idx) {
|
|
147
|
+
const token = tokens[idx];
|
|
148
|
+
const title = readAttrValue('title', token.attrs) || '';
|
|
149
|
+
const startContent = `
|
|
150
|
+
<div class="callout-header">
|
|
151
|
+
<div class="callout-icon-container">
|
|
152
|
+
<i class="callout-icon"></i>
|
|
153
|
+
</div>
|
|
154
|
+
<div class="callout-title-container">${title}
|
|
155
|
+
`;
|
|
156
|
+
return startContent;
|
|
157
|
+
}
|
|
158
|
+
function renderEndCalloutTitle() {
|
|
159
|
+
return '</div>\n</div>';
|
|
160
|
+
}
|
|
161
|
+
const calloutAppearance = (val) => {
|
|
162
|
+
if (val) {
|
|
163
|
+
switch (val) {
|
|
164
|
+
case 'minimal':
|
|
165
|
+
return 'minimal';
|
|
166
|
+
case 'simple':
|
|
167
|
+
return 'simple';
|
|
168
|
+
case 'default':
|
|
169
|
+
default:
|
|
170
|
+
return 'default';
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
return 'default';
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
const parseCallout = (attrs) => {
|
|
178
|
+
if (attrs === null) {
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
const classAttr = attrs.find(attr => {
|
|
182
|
+
return attr[0] === 'class';
|
|
183
|
+
});
|
|
184
|
+
if (!classAttr) {
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
const classes = classAttr[1].split(' ');
|
|
188
|
+
const calloutClass = classes.find(clz => {
|
|
189
|
+
return clz.startsWith('callout-');
|
|
190
|
+
});
|
|
191
|
+
if (calloutClass) {
|
|
192
|
+
const type = calloutClass.replace(kCalloutPrefix, '');
|
|
193
|
+
const title = readAttrValue('title', attrs) ||
|
|
194
|
+
type.slice(0, 1).toUpperCase() + type.slice(1);
|
|
195
|
+
const appearance = calloutAppearance(readAttrValue('appearance', attrs));
|
|
196
|
+
return {
|
|
197
|
+
type: type || 'note',
|
|
198
|
+
clz: calloutClass,
|
|
199
|
+
title,
|
|
200
|
+
appearance
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
const appearanceClass = (appearance) => {
|
|
208
|
+
const style = appearance || 'default';
|
|
209
|
+
return `callout-style-${style}`;
|
|
210
|
+
};
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* citation.ts
|
|
3
|
+
*
|
|
4
|
+
* Copyright (C) 2020-2023 Posit Software, PBC
|
|
5
|
+
*
|
|
6
|
+
*/
|
|
7
|
+
const kTokCite = 'quarto_cite';
|
|
8
|
+
export const citationPlugin = (md) => {
|
|
9
|
+
// Very simple plugin example that surrounds @text with `code`
|
|
10
|
+
md.core.ruler.push('quarto-citation', state => {
|
|
11
|
+
const tokens = state.tokens;
|
|
12
|
+
for (const token of tokens) {
|
|
13
|
+
if (token.type === 'inline' && token.children) {
|
|
14
|
+
// Rebuild the child list
|
|
15
|
+
const children = [];
|
|
16
|
+
for (let i = 0; i < token.children.length; i++) {
|
|
17
|
+
const child = token.children[i];
|
|
18
|
+
if (child.type === 'text') {
|
|
19
|
+
const content = child.content;
|
|
20
|
+
const textToken = (text) => {
|
|
21
|
+
const newToken = new state.Token('text', '', 0);
|
|
22
|
+
newToken.content = text.join('');
|
|
23
|
+
return newToken;
|
|
24
|
+
};
|
|
25
|
+
let text = [];
|
|
26
|
+
const flushText = () => {
|
|
27
|
+
if (text.length) {
|
|
28
|
+
children.push(textToken(text));
|
|
29
|
+
text = [];
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
let cite = [];
|
|
33
|
+
const flushCite = () => {
|
|
34
|
+
var _a;
|
|
35
|
+
if (cite.length) {
|
|
36
|
+
// Determine the cite style
|
|
37
|
+
let style = cite[0] === '-' ? 'suppress-author' : 'in-text';
|
|
38
|
+
if (bracketCount > 0) {
|
|
39
|
+
style = 'normal';
|
|
40
|
+
}
|
|
41
|
+
// The classes
|
|
42
|
+
const clz = ['cite', style];
|
|
43
|
+
// If the cite ends in punctuation, trim that off and make that text
|
|
44
|
+
const puncText = [];
|
|
45
|
+
// Trim off ending punctuation
|
|
46
|
+
if ([
|
|
47
|
+
':',
|
|
48
|
+
'.',
|
|
49
|
+
'#',
|
|
50
|
+
'$',
|
|
51
|
+
'%',
|
|
52
|
+
'&',
|
|
53
|
+
'-',
|
|
54
|
+
'+',
|
|
55
|
+
'?',
|
|
56
|
+
'<',
|
|
57
|
+
'>',
|
|
58
|
+
'~',
|
|
59
|
+
'/',
|
|
60
|
+
'!'
|
|
61
|
+
].includes(cite[cite.length - 1])) {
|
|
62
|
+
puncText.push(cite[cite.length - 1]);
|
|
63
|
+
cite = cite.slice(0, -1);
|
|
64
|
+
}
|
|
65
|
+
// Make a cite token
|
|
66
|
+
const newToken = new state.Token(kTokCite, '', 0);
|
|
67
|
+
newToken.content = cite.join('');
|
|
68
|
+
newToken.attrs = newToken.attrs || [];
|
|
69
|
+
(_a = newToken.attrs) === null || _a === void 0 ? void 0 : _a.push(['class', clz.join(' ')]);
|
|
70
|
+
children.push(newToken);
|
|
71
|
+
cite = [];
|
|
72
|
+
if (puncText.length > 0) {
|
|
73
|
+
children.push(textToken(puncText));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
let capture = 'text';
|
|
78
|
+
let bracketCount = 0;
|
|
79
|
+
for (let j = 0; j < content.length; j++) {
|
|
80
|
+
const char = content.charAt(j);
|
|
81
|
+
if (char === '@') {
|
|
82
|
+
if ((text.length === 1 && text[0] === '-') ||
|
|
83
|
+
(text.length > 1 &&
|
|
84
|
+
text[text.length - 1] === '-' &&
|
|
85
|
+
text[text.length - 2] === '[')) {
|
|
86
|
+
cite.push('-');
|
|
87
|
+
cite.push(char);
|
|
88
|
+
text.pop();
|
|
89
|
+
flushText();
|
|
90
|
+
capture = 'cite';
|
|
91
|
+
}
|
|
92
|
+
else if (text[text.length - 1] === ' ') {
|
|
93
|
+
flushText();
|
|
94
|
+
cite.push(char);
|
|
95
|
+
capture = 'cite';
|
|
96
|
+
}
|
|
97
|
+
else if (text[text.length - 1] === '-' &&
|
|
98
|
+
text[text.length - 2] === ' ') {
|
|
99
|
+
text = text.slice(0, -1);
|
|
100
|
+
flushText();
|
|
101
|
+
cite.push('-');
|
|
102
|
+
cite.push(char);
|
|
103
|
+
capture = 'cite';
|
|
104
|
+
}
|
|
105
|
+
else if (text[text.length - 1] === '[' &&
|
|
106
|
+
text[text.length - 2] === ' ') {
|
|
107
|
+
flushText();
|
|
108
|
+
cite.push(char);
|
|
109
|
+
capture = 'cite';
|
|
110
|
+
}
|
|
111
|
+
else if (text.length === 0) {
|
|
112
|
+
cite.push(char);
|
|
113
|
+
capture = 'cite';
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
if (capture === 'cite') {
|
|
117
|
+
cite.push(char);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
text.push(char);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else if (char === ' ') {
|
|
125
|
+
capture = 'text';
|
|
126
|
+
flushCite();
|
|
127
|
+
text.push(char);
|
|
128
|
+
}
|
|
129
|
+
else if (char === '[') {
|
|
130
|
+
bracketCount++;
|
|
131
|
+
text.push(char);
|
|
132
|
+
}
|
|
133
|
+
else if (char === ']') {
|
|
134
|
+
bracketCount--;
|
|
135
|
+
capture = 'text';
|
|
136
|
+
flushCite();
|
|
137
|
+
text.push(char);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
if (capture === 'cite') {
|
|
141
|
+
cite.push(char);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
text.push(char);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
flushCite();
|
|
149
|
+
flushText();
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
children.push(child);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
token.children = children.length > 0 ? children : null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
md.renderer.rules[kTokCite] = renderCite;
|
|
160
|
+
};
|
|
161
|
+
// Render pandoc-style divs
|
|
162
|
+
function renderCite(tokens, idx, _options, _env, self) {
|
|
163
|
+
const token = tokens[idx];
|
|
164
|
+
const citeContent = `<code ${self.renderAttrs(token)}>${token.content}</code>`;
|
|
165
|
+
return citeContent;
|
|
166
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import Token from 'markdown-it/lib/token';
|
|
2
|
+
import { attributeDecorator, decorator } from './utils/html';
|
|
3
|
+
import { kTokDivOpen } from './divs';
|
|
4
|
+
import { kTokFigureOpen } from './figures';
|
|
5
|
+
import { kTokHeadingOpen, kTokTableOpen } from './utils/tok';
|
|
6
|
+
import { kTokMathBlock } from './math';
|
|
7
|
+
const kTokDecorator = 'quarto_decorator';
|
|
8
|
+
const kQuartoDecoratorOptions = 'quarto-decorator-options';
|
|
9
|
+
export const decoratorPlugin = (md) => {
|
|
10
|
+
md.core.ruler.push('quarto-decorator', state => {
|
|
11
|
+
const outTokens = [];
|
|
12
|
+
for (const token of state.tokens) {
|
|
13
|
+
if (token.type === 'fence' && !token.attrs && token.info) {
|
|
14
|
+
outTokens.push(decoratorTokForToken(token));
|
|
15
|
+
}
|
|
16
|
+
else if (token.type === kTokHeadingOpen && token.attrs) {
|
|
17
|
+
outTokens.push(decoratorTokForToken(token));
|
|
18
|
+
}
|
|
19
|
+
else if (token.type === kTokDivOpen && token.attrs) {
|
|
20
|
+
outTokens.push(decoratorTokForToken(token));
|
|
21
|
+
}
|
|
22
|
+
else if (token.type === kTokFigureOpen && token.attrs) {
|
|
23
|
+
outTokens.push(decoratorTokForToken(token, { hide: { attributes: true } }));
|
|
24
|
+
}
|
|
25
|
+
else if (token.type === kTokTableOpen && token.attrs) {
|
|
26
|
+
outTokens.push(decoratorTokForToken(token));
|
|
27
|
+
}
|
|
28
|
+
else if (token.type === kTokMathBlock && token.attrs) {
|
|
29
|
+
outTokens.push(decoratorTokForToken(token));
|
|
30
|
+
}
|
|
31
|
+
outTokens.push(token);
|
|
32
|
+
}
|
|
33
|
+
state.tokens = outTokens;
|
|
34
|
+
});
|
|
35
|
+
md.renderer.rules[kTokDecorator] = renderDecorator;
|
|
36
|
+
};
|
|
37
|
+
function decoratorTokForToken(token, options) {
|
|
38
|
+
const decoratorTok = new Token(kTokDecorator, 'div', 1);
|
|
39
|
+
decoratorTok.attrs = token.attrs;
|
|
40
|
+
decoratorTok.info = token.info;
|
|
41
|
+
if (options) {
|
|
42
|
+
decoratorTok.meta = decoratorTok.meta || {};
|
|
43
|
+
decoratorTok.meta[kQuartoDecoratorOptions] = options;
|
|
44
|
+
}
|
|
45
|
+
return decoratorTok;
|
|
46
|
+
}
|
|
47
|
+
// Render pandoc-style divs
|
|
48
|
+
function renderDecorator(tokens, idx) {
|
|
49
|
+
var _a;
|
|
50
|
+
const token = tokens[idx];
|
|
51
|
+
const decoratorOptions = (_a = token.meta) === null || _a === void 0 ? void 0 : _a[kQuartoDecoratorOptions];
|
|
52
|
+
if (token.info) {
|
|
53
|
+
return decorator([token.info]);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
return attributeDecorator(token, decoratorOptions);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { addClass } from './utils/markdownit';
|
|
2
|
+
export const kDivRuleName = 'pandocDiv';
|
|
3
|
+
export const kTokDivOpen = 'pandoc_div_open';
|
|
4
|
+
export const kTokDivClose = 'pandoc_div_close';
|
|
5
|
+
export const divPlugin = (md) => {
|
|
6
|
+
// Render pandoc-style divs
|
|
7
|
+
function renderStartDiv(tokens, idx, _options, _env, self) {
|
|
8
|
+
// Add a class to designate that this is a quarto dev
|
|
9
|
+
const token = tokens[idx];
|
|
10
|
+
token.attrs = addClass('quarto-div', token.attrs);
|
|
11
|
+
return `<div ${self.renderAttrs(token)}>`;
|
|
12
|
+
}
|
|
13
|
+
// Render pandoc-style divs
|
|
14
|
+
function renderEndDiv() {
|
|
15
|
+
return '</div>';
|
|
16
|
+
}
|
|
17
|
+
// TODO Implement a better test during validation run
|
|
18
|
+
// Handle pandoc-style divs
|
|
19
|
+
md.block.ruler.before('fence', kDivRuleName, (state, start, _end, silent) => {
|
|
20
|
+
// This is a validation run, can ignore
|
|
21
|
+
if (silent) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
// Get the line for parsing
|
|
25
|
+
const lineStart = state.bMarks[start] + state.tShift[start];
|
|
26
|
+
const lineEnd = state.eMarks[start];
|
|
27
|
+
const line = state.src.slice(lineStart, lineEnd);
|
|
28
|
+
// The current state of the divs (e.g. is there an open)
|
|
29
|
+
// div. Data structure holds key that is the number of colons
|
|
30
|
+
const divState = state.env.quartoOpenDivs || {};
|
|
31
|
+
const incrementDivCount = (fence) => {
|
|
32
|
+
var _a;
|
|
33
|
+
state.env.quartoDivLevel = ((_a = state.env.quartoDivLevel) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
34
|
+
state.env.quartoOpenDivs = state.env.quartoOpenDivs || {};
|
|
35
|
+
const current = state.env.quartoOpenDivs[fence] || 0;
|
|
36
|
+
state.env.quartoOpenDivs[fence] = Math.max(0, current + 1);
|
|
37
|
+
};
|
|
38
|
+
const decrementDivCount = (fence) => {
|
|
39
|
+
state.env.quartoDivLevel--;
|
|
40
|
+
state.env.quartoOpenDivs = state.env.quartoOpenDivs || {};
|
|
41
|
+
const current = state.env.quartoOpenDivs[fence] || 0;
|
|
42
|
+
state.env.quartoOpenDivs[fence] = Math.max(0, current - 1);
|
|
43
|
+
};
|
|
44
|
+
// Three or more colons followed by a an optional brace with attributes
|
|
45
|
+
const divBraceRegex = /^(:::+)\s*(?:(\{[\s\S]+?\}))?$/;
|
|
46
|
+
// Three or more colons followed by a string with no braces
|
|
47
|
+
const divNoBraceRegex = /^(:::+)\s*(?:([^{}\s]+?))?$/;
|
|
48
|
+
const matchers = [divBraceRegex, divNoBraceRegex];
|
|
49
|
+
let match;
|
|
50
|
+
for (const matcher of matchers) {
|
|
51
|
+
match = matcher.exec(line);
|
|
52
|
+
if (match) {
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (match) {
|
|
57
|
+
// There is a div here, is one already open?
|
|
58
|
+
const divFence = match[1];
|
|
59
|
+
const attr = match[2];
|
|
60
|
+
// Is this open?
|
|
61
|
+
let isOpenDiv = false;
|
|
62
|
+
const openCount = divState[divFence];
|
|
63
|
+
if (!openCount || openCount === 0) {
|
|
64
|
+
// There isn't an existing open div at this level (number of colons)
|
|
65
|
+
isOpenDiv = true;
|
|
66
|
+
}
|
|
67
|
+
else if (attr) {
|
|
68
|
+
// If it has attributes it is always open
|
|
69
|
+
isOpenDiv = true;
|
|
70
|
+
}
|
|
71
|
+
if (isOpenDiv) {
|
|
72
|
+
// Add to the open count (or set it to 1)
|
|
73
|
+
incrementDivCount(divFence);
|
|
74
|
+
// Make an open token
|
|
75
|
+
const token = state.push(kTokDivOpen, 'div', 1);
|
|
76
|
+
token.markup = line;
|
|
77
|
+
// Allow this to be parsed for attributes by markdown-it-attr
|
|
78
|
+
if (attr && attr.startsWith('{')) {
|
|
79
|
+
token.info = attr;
|
|
80
|
+
}
|
|
81
|
+
else if (attr) {
|
|
82
|
+
token.info = `{.${attr}}`;
|
|
83
|
+
}
|
|
84
|
+
token.block = true;
|
|
85
|
+
token.meta = {
|
|
86
|
+
line: state.line,
|
|
87
|
+
level: state.env.quartoDivLevel
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
// Subtract from the open count (min zero)
|
|
92
|
+
const level = state.env.quartoDivLevel;
|
|
93
|
+
decrementDivCount(divFence);
|
|
94
|
+
// Make a close token
|
|
95
|
+
const token = state.push(kTokDivClose, 'div', -1);
|
|
96
|
+
token.markup = line;
|
|
97
|
+
token.meta = {
|
|
98
|
+
line: state.line,
|
|
99
|
+
level
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
state.line = start + 1;
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}, { alt: [] });
|
|
109
|
+
md.renderer.rules[kTokDivOpen] = renderStartDiv;
|
|
110
|
+
md.renderer.rules[kTokDivClose] = renderEndDiv;
|
|
111
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* figure-divs.ts
|
|
3
|
+
*
|
|
4
|
+
* Copyright (C) 2020-2023 Posit Software, PBC
|
|
5
|
+
*
|
|
6
|
+
*/
|
|
7
|
+
import { readAttrValue } from './utils/markdownit';
|
|
8
|
+
import { kTokInline, kTokParaClose, kTokParaOpen } from './utils/tok';
|
|
9
|
+
import { kTokDivClose, kTokDivOpen } from './divs';
|
|
10
|
+
import { kTokFigCaptionClose, kTokFigCaptionOpen, mutateToFigureTok } from './figures';
|
|
11
|
+
const kFigureDivRuleName = 'quarto-figure-divs';
|
|
12
|
+
const kFigurePrefix = 'fig-';
|
|
13
|
+
export const figureDivsPlugin = (md) => {
|
|
14
|
+
// Handle pandoc-style divs
|
|
15
|
+
md.core.ruler.push(kFigureDivRuleName, state => {
|
|
16
|
+
const isFigureDiv = [];
|
|
17
|
+
for (let i = 0; i < state.tokens.length; i++) {
|
|
18
|
+
const token = state.tokens[i];
|
|
19
|
+
if (token.type === kTokDivOpen) {
|
|
20
|
+
const id = readAttrValue('id', token.attrs);
|
|
21
|
+
if (id === null || id === void 0 ? void 0 : id.startsWith(kFigurePrefix)) {
|
|
22
|
+
isFigureDiv.push(true);
|
|
23
|
+
mutateToFigureTok(token, 'open');
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
// Note the div, but not a figure div
|
|
27
|
+
isFigureDiv.push(false);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
else if (token.type === kTokDivClose) {
|
|
31
|
+
const isFigDiv = isFigureDiv.pop();
|
|
32
|
+
if (isFigDiv) {
|
|
33
|
+
// If the preview token is paragraph, use that as the caption
|
|
34
|
+
if (i - 3 >= 0) {
|
|
35
|
+
const maybeParaStart = state.tokens[i - 3];
|
|
36
|
+
const maybeInline = state.tokens[i - 2];
|
|
37
|
+
const maybeParaEnd = state.tokens[i - 1];
|
|
38
|
+
if (maybeParaStart.type === kTokParaOpen &&
|
|
39
|
+
maybeParaEnd.type === kTokParaClose &&
|
|
40
|
+
maybeInline.type === kTokInline) {
|
|
41
|
+
mutateToFigCaption(state.tokens[i - 3], 'open');
|
|
42
|
+
mutateToFigCaption(state.tokens[i - 1], 'close');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
mutateToFigureTok(token, 'close');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
const mutateToFigCaption = (token, type) => {
|
|
52
|
+
token.tag = 'figcaption';
|
|
53
|
+
token.type = type === 'open' ? kTokFigCaptionClose : kTokFigCaptionOpen;
|
|
54
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import MarkdownIt from 'markdown-it';
|
|
2
|
+
import Token from 'markdown-it/lib/token';
|
|
3
|
+
export interface FigureOptions {
|
|
4
|
+
dataType?: boolean;
|
|
5
|
+
link?: boolean;
|
|
6
|
+
figcaption?: boolean;
|
|
7
|
+
copyAttrs?: boolean;
|
|
8
|
+
tabindex?: boolean;
|
|
9
|
+
lazyLoading?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare const kTokFigureOpen = "figure_open";
|
|
12
|
+
export declare const kTokFigureClose = "figure_close";
|
|
13
|
+
export declare const kTokFigCaptionOpen = "figcaption_open";
|
|
14
|
+
export declare const kTokFigCaptionClose = "figcaption_close";
|
|
15
|
+
export declare const mutateToFigureTok: (token: Token, type: 'open' | 'close') => void;
|
|
16
|
+
export declare function figuresPlugin(md: MarkdownIt, options: FigureOptions): void;
|