Skip to content

Commit 72b4268

Browse files
authored
Bugfix/issue 3159 (#4009)
Closes #3159
1 parent 80fb6d8 commit 72b4268

File tree

4 files changed

+73
-22
lines changed

4 files changed

+73
-22
lines changed

news/changelog-1.3.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
- remove scaffolding div from conditional content in final output ([#3847](https://github.com/quarto-dev/quarto-cli/issues/3847)).
112112
- ensure proof titles are appended to paragraph nodes ([#3772](https://github.com/quarto-dev/quarto-cli/issues/3772)).
113113
- Support parsing markdown in table captions in LaTeX and HTML tables ([#2573](https://github.com/quarto-dev/quarto-cli/issues/2573)).
114+
- Improve parsing of include shortcodes ([#3159](https://github.com/quarto-dev/quarto-cli/issues/3159)).
114115

115116
## Pandoc filter changes
116117

src/core/lib/parse-shortcode.ts

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -30,31 +30,75 @@ export interface Shortcode {
3030
params: string[];
3131
}
3232

33-
export function parseShortcode(shortCodeCapture: string): Shortcode {
34-
const [name, ...args] = shortCodeCapture.trim().split(" ");
35-
const namedParams: Record<string, string> = {};
36-
const params: string[] = [];
37-
const rawParams = args.map((v) => {
38-
const p = v.indexOf("=");
39-
let name: string | undefined = undefined;
40-
let value: string;
41-
if (p === -1) {
42-
value = v;
43-
params.push(value);
33+
// shortcode capture BNF:
34+
// shortcode = shortcode-name shortcode-params?
35+
// shortcode-name = [a-zA-Z0-9_]+
36+
// shortcode-params = shortcode-param*
37+
// shortcode-param = shortcode-param-value | shortcode-param-name "=" shortcode-param-value
38+
// shortcode-param-name = [a-zA-Z0-9_-]+
39+
// shortcode-param-value = [^"'\s]+ | '"' [^"]* '"' | "'" [^']* "'"
40+
function parseShortcodeCapture(capture: string): Shortcode | undefined {
41+
// match shortcode name
42+
const nameMatch = capture.match(/^[a-zA-Z0-9_]+/);
43+
if (!nameMatch) {
44+
return;
45+
}
46+
const params: Shortcode["params"] = [];
47+
const namedParams: Shortcode["namedParams"] = {};
48+
const rawParams: Shortcode["rawParams"] = [];
49+
50+
const name = nameMatch[0];
51+
let paramStr = capture.slice(name.length).trim();
52+
// match params
53+
const paramName = "([a-zA-Z0-9_-]+)";
54+
const paramValue1 = "([^\"'\\s]+)";
55+
const paramValue2 = `"([^"]*)"`;
56+
const paramValue3 = `'([^\']*)'`;
57+
58+
const paramValue = `(?:${paramValue1})|(?:${paramValue2})|(?:${paramValue3})`;
59+
const paramNameAndValue =
60+
`(?:${paramName}\\s*=\\s*${paramValue1})|(?:${paramName}\\s*=\\s*${paramValue2})|(?:${paramName}\\s*=\\s*${paramValue3})`;
61+
62+
const paramRe = new RegExp(`(?:${paramValue}|${paramNameAndValue})`);
63+
64+
while (paramStr.length) {
65+
const paramMatch = paramStr.match(paramRe);
66+
if (!paramMatch) {
67+
throw new Error("invalid shortcode: " + capture);
68+
}
69+
70+
const captures = paramMatch.slice(1).filter((x) => x !== undefined);
71+
if (captures.length === 1) {
72+
params.push(captures[0]);
73+
rawParams.push({
74+
value: captures[0],
75+
});
76+
// value only
77+
} else if (captures.length === 2) {
78+
namedParams[captures[0]] = captures[1];
79+
rawParams.push({
80+
name: captures[0],
81+
value: captures[1],
82+
});
4483
} else {
45-
name = v.slice(0, p);
46-
value = v.slice(p + 1);
47-
namedParams[name] = value;
84+
throw new Error(
85+
"Internal Error, could not determine correct shortcode capture for " +
86+
capture,
87+
);
4888
}
49-
return { name, value };
50-
});
5189

52-
return {
53-
name,
54-
rawParams,
55-
namedParams,
56-
params,
57-
};
90+
paramStr = paramStr.slice(paramMatch[0].length).trim();
91+
}
92+
return { name, params, namedParams, rawParams };
93+
}
94+
95+
// TODO this should be handled by a pandoc parser.
96+
export function parseShortcode(shortCodeCapture: string): Shortcode {
97+
const result = parseShortcodeCapture(shortCodeCapture);
98+
if (!result) {
99+
throw new Error("invalid shortcode: " + shortCodeCapture);
100+
}
101+
return result;
58102
}
59103

60104
export function getShortcodeUnnamedParams(shortcode: Shortcode): string[] {
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
title: 3159
3+
---
4+
5+
{{< include "file with spaces.md" >}}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This is some content

0 commit comments

Comments
 (0)