2023年政策修订增补工作正在进行中,欢迎参与!
  • Moegirl.ICU:萌娘百科流亡社群 581077156(QQ),欢迎对萌娘百科运营感到失望的编辑者加入
  • Moegirl.ICU:账号认领正在试运行,有意者请参照账号认领流程

User:机智的小鱼君/gadget/Shiki.js

萌娘百科,万物皆可萌的百科全书!转载请标注来源页面的网页链接,并声明引自萌娘百科。内容不可商用。
跳转到导航 跳转到搜索

注意:在保存之后,您可能需要清除浏览器缓存才能看到所作出的变更的影响。

  • Firefox/Safari:按住“Shift”的同时单击“刷新”,或按“Ctrl-F5”或“Ctrl-R”(Mac为“⌘-R”)
  • Google Chrome:按“Ctrl-Shift-R”(Mac为“⌘-Shift-R”)
  • Internet Explorer:按住“Ctrl”的同时单击“刷新”,或按“Ctrl-F5”
  • Opera:在“工具→首选项”中清除缓存
如果您已登录但该页面出现未登录状态,请尝试在地址栏的地址最后添加代码?_=1来访问最新页面。
添加代码后的本页地址如下:-{R|https://moegirl.icu/User:%E6%9C%BA%E6%99%BA%E7%9A%84%E5%B0%8F%E9%B1%BC%E5%90%9B/gadget/Shiki.js?_=1}-
/**
 * MediaWiki Gadget Shiki.js Code Highlighter
 * @author Dragon-Fish <[email protected]>
 * @license MIT
 */
"use strict";
(async () => {
    const targets = document.querySelectorAll(["pre.highlight", "pre.hljs", "pre.prettyprint", "pre.mw-code", "pre[lang]", "code[lang]", "pre[data-lang]", "code[data-lang]"]);

    if (!targets.length) {
        return console.info("[SHIKI]", "No targets found");
    }

    console.info("[SHIKI]", "Found targets", targets.length, targets);
    main(await import("https://esm.sh/[email protected]"), targets);

    async function main(shiki, targets) {
        await injectStyles();
        const hlBlocks = await Promise.all(Array.from(targets).map((el) => renderBlock(shiki, el)));
        await Promise.all(hlBlocks.filter((i) => !!i).map((el) => applyLineNumbers(el)));
    }

    async function injectStyles() {
        const sheet = new CSSStyleSheet();
        await sheet.replace(`
#mw-content-text pre code {
  all: unset;
}
`);
        document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
        return true;
    }

    function getLangFromContentModel() {
        const nsNumber = mw.config.get("wgNamespaceNumber");
        const pageName = mw.config.get("wgPageName");
        const contentModel = mw.config.get("wgPageContentModel", "").toLowerCase();
        if (pageName.endsWith(".js") || contentModel === "javascript") {
            return "javascript";
        } else if (pageName.endsWith(".css") || contentModel === "css") {
            return "css";
        } else if (
            // Lua
            (nsNumber === 828 || ["scribunto", "lua"].includes(contentModel)) &&
            !pageName.endsWith("/doc")
        ) {
            return "lua";
        }
    }

    /**
     *
     * @param {HTMLElement} el
     */
    function getLangFromElement(el) {
        const lang = el.getAttribute("lang") || el.dataset.lang || Array.from(el.classList).find((c) => c.startsWith("language-") || c.startsWith("lang-"));
        if (lang) {
            return lang.includes("-") ? lang.split("-")[1] : lang;
        }
        return "";
    }

    /**
     * @param {any} shiki
     * @param {HTMLElement} el
     * @returns {Promise<HTMLElement | null>}
     */
    function renderBlock(shiki, el) {
        if (el.classList.contains("shiki") || !!el.dataset.shiki) {
            return Promise.resolve(null);
        }
        const lang = getLangFromElement(el) || getLangFromContentModel();
        console.info("[SHIKI]", "Rendering", el, lang);
        if (!lang) {
            return Promise.resolve(null);
        }
        return shiki
            .codeToHtml(el.innerText.trimEnd(), { lang, theme: "one-dark-pro" })
            .then((html) => {
                el.style.display = "none";
                el.dataset.shiki = "true";
                const wrapper = document.createElement("div");
                wrapper.innerHTML = html;
                const pre = wrapper.querySelector("pre");
                el.insertAdjacentElement("afterend", pre);
                return pre;
            })
            .catch((e) => {
                console.error("[SHIKI] Render failed", el, e);
                return null;
            });
    }

    /**
     * @param {HTMLElement} pre
     */
    function applyLineNumbers(pre) {
        const lineFromRaw = pre.dataset.lineFrom || pre.dataset.from;
        const lineEmphaticRaw = pre.dataset.lineEmphatic || pre.dataset.emphatic || pre.dataset.linePin || pre.dataset.pin || "";

        const lineFrom = Math.max(parseInt(lineFromRaw, 10) || 1, 1);
        const emphaticLines = lineEmphaticRaw.split(",").map((n) => parseInt(n.trim(), 10));

        /** @type {{ no: number; emphatic: boolean }[]} */
        const linesStore = [];

        /** @type {HTMLSpanElement} */
        const code = pre.querySelector("code");
        code.querySelectorAll("span.line").forEach((line, i) => {
            const no = lineFrom + i;
            const emphatic = emphaticLines.includes(no);
            linesStore.push({ no, emphatic });
            line.dataset.line = no;
            if (emphatic) {
                line.classList.add("emphatic");
            }
        });

        const lineNumsCode = document.createElement("code");
        lineNumsCode.innerHTML = linesStore
            .map((conf) => {
                const line = document.createElement("span");
                line.classList.add("line", "line-number");
                line.dataset.line = conf.no;
                if (conf.emphatic) {
                    line.classList.add("emphatic");
                }
                line.innerText = conf.no.toString();
                return line.outerHTML;
            })
            .join("\n");

        pre.style.display = "flex";
        pre.style.flexDirection = "row-reverse";

        code.style.flex = "1 1 auto";
        code.style.whiteSpace = "pre";
        code.style.overflowX = "auto";

        lineNumsCode.classList.add("line-numbers");
        lineNumsCode.style.cssText = `text-align: center; padding-right: 0.5em; user-select: none; pointer-events: none;`;

        pre.insertAdjacentElement("beforeend", lineNumsCode);

        return pre;
    }
})();