mirror of
https://github.com/discourse/discourse.git
synced 2025-09-06 10:50:21 +08:00
DEV: adds initial support for custom blocks using code fencing (#15743)
Allows to write custom code blocks: ``` ```mermaid height=200,foo=bar test ``` ``` Which will then get converted to: ``` <pre data-code-wrap="mermaid" data-code-height="200" data-code-foo="bar"> <code class="lang-nohighlight"> test </code> </pre> ```
This commit is contained in:
parent
c38114f0c6
commit
b3ecf00c98
8 changed files with 173 additions and 32 deletions
|
@ -1,35 +1,78 @@
|
|||
// we need a custom renderer for code blocks cause we have a slightly non compliant
|
||||
// format with special handling for text and so on
|
||||
|
||||
const TEXT_CODE_CLASSES = ["text", "pre", "plain"];
|
||||
|
||||
function extractTokenInfo(info, md) {
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
|
||||
info = info.trim();
|
||||
|
||||
const matches = info.match(/(^\s*\S*)\s*(.*)/i);
|
||||
if (!matches) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ensure the token has only valid chars
|
||||
// c++, strucuted-text and p91, are all valid
|
||||
if (!/^[\w+-]*$/i.test(matches[1])) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ASCII_REGEX = /[^\x00-\x7F]/;
|
||||
const tag = md.utils.unescapeAll(matches[1].replace(ASCII_REGEX, ""));
|
||||
const extractedData = { tag, attributes: {} };
|
||||
|
||||
if (matches[2]?.length) {
|
||||
md.utils
|
||||
.unescapeAll(matches[2].replace(ASCII_REGEX, ""))
|
||||
.split(",")
|
||||
.forEach((potentialPair) => {
|
||||
const [key, value] = potentialPair.trim().split(/\s+/g)[0].split("=");
|
||||
|
||||
// invalid pairs would get caught here and not used, eg `foo=`
|
||||
if (key && value) {
|
||||
extractedData.attributes[key] = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return extractedData;
|
||||
}
|
||||
|
||||
function render(tokens, idx, options, env, slf, md) {
|
||||
let token = tokens[idx],
|
||||
info = token.info ? md.utils.unescapeAll(token.info) : "",
|
||||
langName = md.options.discourse.defaultCodeLang,
|
||||
className,
|
||||
escapedContent = md.utils.escapeHtml(token.content);
|
||||
const token = tokens[idx];
|
||||
const escapedContent = md.utils.escapeHtml(token.content);
|
||||
const tokenInfo = extractTokenInfo(token.info, md);
|
||||
const tag = tokenInfo?.tag || md.options.discourse.defaultCodeLang;
|
||||
const attributes = tokenInfo?.attributes || {};
|
||||
|
||||
if (info) {
|
||||
// strip off any additional languages
|
||||
info = info.trim().split(/\s+/g)[0];
|
||||
let className;
|
||||
|
||||
const acceptableCodeClasses =
|
||||
md.options.discourse.acceptableCodeClasses || [];
|
||||
|
||||
if (TEXT_CODE_CLASSES.indexOf(tag) > -1) {
|
||||
className = "lang-nohighlight";
|
||||
} else if (acceptableCodeClasses.indexOf(tag) > -1) {
|
||||
className = `lang-${tag}`;
|
||||
} else {
|
||||
className = "lang-nohighlight";
|
||||
attributes["wrap"] = tag;
|
||||
}
|
||||
|
||||
const acceptableCodeClasses = md.options.discourse.acceptableCodeClasses;
|
||||
if (
|
||||
acceptableCodeClasses &&
|
||||
info &&
|
||||
acceptableCodeClasses.indexOf(info) !== -1
|
||||
) {
|
||||
langName = info;
|
||||
}
|
||||
const dataAttributes = Object.keys(attributes)
|
||||
.map((key) => {
|
||||
const value = md.utils.escapeHtml(attributes[key]);
|
||||
key = md.utils.escapeHtml(key);
|
||||
return `data-code-${key}="${value}"`;
|
||||
})
|
||||
.join(" ");
|
||||
|
||||
className =
|
||||
TEXT_CODE_CLASSES.indexOf(info) !== -1
|
||||
? "lang-nohighlight"
|
||||
: "lang-" + langName;
|
||||
|
||||
return `<pre><code class="${className}">${escapedContent}</code></pre>\n`;
|
||||
return `<pre${dataAttributes ? ` ${dataAttributes}` : ""}><code${
|
||||
className ? ` class="${className}"` : ""
|
||||
}>${escapedContent}</code></pre>\n`;
|
||||
}
|
||||
|
||||
export function setup(helper) {
|
||||
|
@ -41,6 +84,8 @@ export function setup(helper) {
|
|||
.concat(["auto", "nohighlight"]);
|
||||
});
|
||||
|
||||
helper.allowList(["pre[data-code-*]"]);
|
||||
|
||||
helper.allowList({
|
||||
custom(tag, name, value) {
|
||||
if (tag === "code" && name === "class") {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue