2022-05-26 15:18:53 +08:00
|
|
|
import {addScript} from "../util/addScript";
|
|
|
|
|
import {Constants} from "../../constants";
|
|
|
|
|
import {focusByOffset} from "../util/selection";
|
2022-10-12 09:23:25 +08:00
|
|
|
import {setCodeTheme} from "../../util/assets";
|
2022-05-26 15:18:53 +08:00
|
|
|
|
|
|
|
|
export const highlightRender = (element: Element, cdn = Constants.PROTYLE_CDN) => {
|
|
|
|
|
let codeElements: NodeListOf<Element>;
|
|
|
|
|
let isPreview = false;
|
|
|
|
|
if (element.classList.contains("code-block")) {
|
|
|
|
|
// 编辑器内代码块编辑渲染
|
2022-12-31 22:14:42 +08:00
|
|
|
codeElements = element.querySelectorAll("[spellcheck]");
|
2022-05-26 15:18:53 +08:00
|
|
|
} else {
|
|
|
|
|
if (element.classList.contains("item__readme")) {
|
|
|
|
|
// bazaar reademe
|
|
|
|
|
codeElements = element.querySelectorAll("pre code");
|
|
|
|
|
codeElements.forEach(item => {
|
|
|
|
|
item.parentElement.setAttribute("lineNumber", "false");
|
|
|
|
|
});
|
|
|
|
|
} else if (element.classList.contains("b3-typography")) {
|
|
|
|
|
// preview & export html markdown
|
|
|
|
|
codeElements = element.querySelectorAll(".code-block code");
|
|
|
|
|
isPreview = true;
|
|
|
|
|
} else {
|
2022-12-31 22:14:42 +08:00
|
|
|
codeElements = element.querySelectorAll(".code-block [spellcheck]");
|
2022-05-26 15:18:53 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (codeElements.length === 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setCodeTheme(cdn);
|
|
|
|
|
|
2022-11-24 11:03:46 +08:00
|
|
|
addScript(`${cdn}/js/highlight.js/highlight.min.js?v=11.7.0`, "protyleHljsScript").then(() => {
|
|
|
|
|
addScript(`${cdn}/js/highlight.js/third-languages.js?v=1.0.1`, "protyleHljsThirdScript").then(() => {
|
2022-05-26 15:18:53 +08:00
|
|
|
codeElements.forEach((block: HTMLElement) => {
|
2023-07-19 13:55:42 +08:00
|
|
|
const iconElements = block.parentElement.querySelectorAll(".protyle-icon");
|
2023-07-19 19:19:40 +08:00
|
|
|
if (iconElements.length === 2) {
|
|
|
|
|
iconElements[0].setAttribute("aria-label", window.siyuan.languages.copy);
|
|
|
|
|
iconElements[1].setAttribute("aria-label", window.siyuan.languages.more);
|
|
|
|
|
}
|
2022-05-26 15:18:53 +08:00
|
|
|
if (block.getAttribute("data-render") === "true") {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const wbrElement = block.querySelector("wbr");
|
|
|
|
|
let startIndex = 0;
|
|
|
|
|
if (wbrElement) {
|
|
|
|
|
let previousSibling = wbrElement.previousSibling;
|
|
|
|
|
while (previousSibling) {
|
|
|
|
|
startIndex += previousSibling.textContent.length;
|
|
|
|
|
while (!previousSibling.previousSibling && previousSibling.parentElement.tagName !== "DIV") {
|
|
|
|
|
// 高亮 span 中输入
|
|
|
|
|
previousSibling = previousSibling.parentElement;
|
|
|
|
|
}
|
|
|
|
|
previousSibling = previousSibling.previousSibling;
|
|
|
|
|
}
|
|
|
|
|
wbrElement.remove();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let language;
|
|
|
|
|
if (isPreview) {
|
|
|
|
|
language = block.parentElement.getAttribute("data-language"); // preview
|
|
|
|
|
} else if (block.previousElementSibling) {
|
|
|
|
|
language = block.previousElementSibling.firstElementChild.textContent;
|
|
|
|
|
} else {
|
|
|
|
|
// bazaar readme
|
|
|
|
|
language = block.className.replace("language-", "");
|
|
|
|
|
}
|
2023-09-28 22:38:49 +08:00
|
|
|
if (!window.hljs.getLanguage(language)) {
|
2022-05-26 15:18:53 +08:00
|
|
|
language = "plaintext";
|
|
|
|
|
}
|
2022-06-24 15:26:10 +08:00
|
|
|
block.classList.add("hljs");
|
2022-05-26 15:18:53 +08:00
|
|
|
block.setAttribute("data-render", "true");
|
|
|
|
|
const autoEnter = block.parentElement.getAttribute("linewrap");
|
|
|
|
|
const ligatures = block.parentElement.getAttribute("ligatures");
|
|
|
|
|
const lineNumber = block.parentElement.getAttribute("linenumber");
|
|
|
|
|
if (autoEnter === "true" || (autoEnter !== "false" && window.siyuan.config.editor.codeLineWrap)) {
|
|
|
|
|
block.style.setProperty("white-space", "pre-wrap");
|
2024-05-15 17:31:40 +08:00
|
|
|
block.style.setProperty("word-break", "break-word");
|
2022-05-26 15:18:53 +08:00
|
|
|
} else {
|
2023-05-16 20:23:33 +08:00
|
|
|
// https://ld246.com/article/1684031600711 该属性会导致有 tab 后光标跳至末尾,目前无解
|
|
|
|
|
block.style.setProperty("white-space", "pre");
|
|
|
|
|
block.style.setProperty("word-break", "initial");
|
2022-05-26 15:18:53 +08:00
|
|
|
}
|
|
|
|
|
if (ligatures === "true" || (ligatures !== "false" && window.siyuan.config.editor.codeLigatures)) {
|
|
|
|
|
block.style.fontVariantLigatures = "normal";
|
|
|
|
|
} else {
|
|
|
|
|
block.style.fontVariantLigatures = "none";
|
|
|
|
|
}
|
2024-08-15 11:03:16 +08:00
|
|
|
const codeText = block.textContent;
|
2022-05-26 15:18:53 +08:00
|
|
|
if (!isPreview && (lineNumber === "true" || (lineNumber !== "false" && window.siyuan.config.editor.codeSyntaxHighlightLineNum))) {
|
|
|
|
|
// 需要先添加 class 以防止抖动 https://ld246.com/article/1648116585443
|
2024-08-15 11:21:05 +08:00
|
|
|
block.firstElementChild.classList.add("protyle-linenumber__rows")
|
2023-09-08 15:55:59 +08:00
|
|
|
lineNumberRender(block);
|
2022-05-26 15:18:53 +08:00
|
|
|
}
|
2024-08-15 11:03:16 +08:00
|
|
|
|
|
|
|
|
(block.childElementCount === 2 ? block.lastElementChild : block).innerHTML = window.hljs.highlight(
|
|
|
|
|
codeText + (codeText.endsWith("\n") ? "" : "\n"), // https://github.com/siyuan-note/siyuan/issues/4609
|
2022-11-22 21:39:32 +08:00
|
|
|
{
|
|
|
|
|
language,
|
|
|
|
|
ignoreIllegals: true
|
|
|
|
|
}).value;
|
2022-05-26 15:18:53 +08:00
|
|
|
if (wbrElement && getSelection().rangeCount > 0) {
|
|
|
|
|
focusByOffset(block, startIndex, startIndex);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const lineNumberRender = (block: HTMLElement) => {
|
2024-04-16 09:01:56 +08:00
|
|
|
const lineNumber = block.parentElement.getAttribute("lineNumber");
|
2024-04-13 00:11:10 +08:00
|
|
|
if (lineNumber === "false") {
|
2022-05-26 15:18:53 +08:00
|
|
|
return;
|
2024-04-13 00:08:52 +08:00
|
|
|
}
|
2024-04-13 00:11:10 +08:00
|
|
|
if (!window.siyuan.config.editor.codeSyntaxHighlightLineNum && lineNumber !== "true") {
|
2024-04-13 00:08:52 +08:00
|
|
|
return;
|
2022-05-26 15:18:53 +08:00
|
|
|
}
|
2023-09-26 12:43:13 +08:00
|
|
|
// clientHeight 总是取的整数
|
|
|
|
|
block.parentElement.style.lineHeight = `${((parseInt(block.parentElement.style.fontSize) || window.siyuan.config.editor.fontSize) * 1.625 * 0.85).toFixed(0)}px`;
|
2022-05-26 15:18:53 +08:00
|
|
|
const lineNumberTemp = document.createElement("div");
|
2024-08-15 11:21:05 +08:00
|
|
|
lineNumberTemp.className = "hljs";
|
2023-10-24 21:35:48 +08:00
|
|
|
lineNumberTemp.setAttribute("style", `box-sizing: border-box;width: calc(100% - 3.6em);position: absolute;padding-top:0 !important;padding-bottom:0 !important;min-height:auto !important;white-space:${block.style.whiteSpace};word-break:${block.style.wordBreak};font-variant-ligatures:${block.style.fontVariantLigatures};`);
|
2022-05-26 15:18:53 +08:00
|
|
|
lineNumberTemp.setAttribute("contenteditable", "true");
|
|
|
|
|
block.insertAdjacentElement("afterend", lineNumberTemp);
|
|
|
|
|
|
|
|
|
|
let lineNumberHTML = "";
|
2024-08-15 11:03:16 +08:00
|
|
|
const lineList = block.lastElementChild.textContent.split(/\r\n|\r|\n|\u2028|\u2029/g);
|
2022-05-26 15:18:53 +08:00
|
|
|
if (lineList[lineList.length - 1] === "" && lineList.length > 1) {
|
|
|
|
|
lineList.pop();
|
|
|
|
|
}
|
2024-05-15 17:31:40 +08:00
|
|
|
const isWrap = block.style.wordBreak === "break-word";
|
2022-05-26 15:18:53 +08:00
|
|
|
lineList.map((line) => {
|
|
|
|
|
let lineHeight = "";
|
|
|
|
|
if (isWrap) {
|
2024-08-15 11:03:16 +08:00
|
|
|
lineNumberTemp.textContent = line || "<br>";
|
2023-09-08 15:55:59 +08:00
|
|
|
// 不能使用 lineNumberTemp.getBoundingClientRect().height.toFixed(1) 否则
|
|
|
|
|
// windows 需等待字体下载完成再计算,否则导致不换行,高度计算错误
|
|
|
|
|
// https://github.com/siyuan-note/siyuan/issues/9029
|
|
|
|
|
// https://github.com/siyuan-note/siyuan/issues/9140
|
|
|
|
|
lineHeight = ` style="height:${lineNumberTemp.clientHeight}px;"`;
|
2022-05-26 15:18:53 +08:00
|
|
|
}
|
|
|
|
|
lineNumberHTML += `<span${lineHeight}></span>`;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
lineNumberTemp.remove();
|
2024-08-15 11:03:16 +08:00
|
|
|
block.firstElementChild.innerHTML = lineNumberHTML;
|
2022-05-26 15:18:53 +08:00
|
|
|
};
|