@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,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;
|