ui: shiki: improve load speed

This commit is contained in:
undefined committed 2024-07-26 15:19:35 +08:00
1 parent 89f01e5b6e
commit 8966b9c532
2 files changed
+95 -57

No files matched your search

@@ -89,6 +89,7 @@ code
border-radius: 0px !important
background: none !important
box-shadow: 0 2px #0003
text-decoration: none
padding: 0 .5em
color: #bbb
@@ -2,11 +2,14 @@ import {
transformerMetaHighlight, transformerNotationDiff, transformerNotationHighlight,
transformerNotationWordHighlight, transformerRenderWhitespace,
} from '@shikijs/transformers';
import type { ElementContent } from 'hast';
import $ from 'jquery';
import Notification from 'vj/components/notification/index';
import { getFeatures, load as loadModule } from 'vj/lazyload';
import { i18n } from 'vj/utils';
import { addLanguage, bundledLanguages, createHighlighter } from './bundle';
import {
addLanguage, bundledLanguages, bundledLanguagesInfo, createHighlighter,
} from './bundle';
$(document).on('click', '[data-clipboard-text]', function (ev) {
const content = $(this).attr('data-clipboard-text');
@@ -19,7 +22,7 @@ $(document).on('click', '[data-clipboard-text]', function (ev) {
ev.preventDefault();
});
const transformers = [
const baseTransformers = [
transformerNotationDiff(),
transformerNotationHighlight(),
transformerNotationWordHighlight(),
@@ -27,20 +30,101 @@ const transformers = [
];
if (UserContext.showInvisibleChar) {
transformers.push(transformerRenderWhitespace());
baseTransformers.push(transformerRenderWhitespace());
}
async function highlightBlocks($dom) {
for (const code of $dom.find('pre code').get()) {
const nodes = $dom.find('pre code').get();
if (!nodes.length) return;
const shikiPlain = await createHighlighter({ langs: ['text'], themes: ['github-dark', 'light-plus'] });
const requestedLanguages = new Set<string>([]);
const tasks = [];
for (const code of nodes) {
const $code = $(code);
const $pre = $code.parent();
if ($pre.hasClass('syntax-hl')) continue;
const language = ($(code).attr('class') || '').trim();
// try to map the language name
const m = language.match(/language-([a-z0-9]+)(?=\.line-numbers)?(?=\|([\d,-]+))?/);
const m = language.match(/language-([a-zA-Z0-9]+)(?=\.line-numbers)?(?=\|([\d,-]+))?/);
if (!m) continue;
if (!bundledLanguages[m[1]]) {
const features = await getFeatures(`shiki-${m[1]}`);
const name = m[1].toLowerCase();
const toolbarContents: ElementContent[] = [
{
tagName: 'a',
type: 'element',
properties: {
href: 'javascript:;',
class: 'copy-to-clipboard',
'data-clipboard-text': $code.text(),
},
children: [
{ type: 'text', value: 'Copy' },
],
},
];
if (!name.startsWith('input') && !name.startsWith('output')) {
toolbarContents.unshift({
tagName: 'span',
type: 'element',
properties: {
style: 'padding: 0 10px 0 10px',
},
children: [
{ type: 'text', value: bundledLanguagesInfo[m[1]]?.name || m[1] },
],
});
}
const toolbar: ElementContent = {
tagName: 'div',
type: 'element',
properties: {
class: 'toolbar',
},
children: [
{
tagName: 'div',
type: 'element',
properties: {
class: 'toolbar-item',
},
children: toolbarContents,
},
],
};
const transformers = baseTransformers.concat({
pre(node) {
this.addClassToHast(node, 'syntax-hl');
if (language.includes('line-numbers')) {
this.addClassToHast(node, 'line-numbers');
}
node.children.push(toolbar);
},
});
const $plain = $(shikiPlain.codeToHtml($code.text(), {
lang: 'text',
theme: 'light-plus',
meta: {
__raw: m[2] ? `{${m[2]}}` : null,
},
transformers,
}));
$pre.replaceWith($plain);
requestedLanguages.add(name);
tasks.push((shiki) => {
if (!bundledLanguages[name]) return;
$plain.replaceWith($(shiki.codeToHtml($code.text(), {
lang: name,
theme: 'light-plus',
meta: {
__raw: m[2] ? `{${m[2]}}` : null,
},
transformers,
})));
});
}
for (const name of requestedLanguages) {
if (!bundledLanguages[name]) {
const features = await getFeatures(`shiki-${name}`);
for (const item of features) {
let apply = typeof item === 'function'
? item
@@ -51,57 +135,10 @@ async function highlightBlocks($dom) {
if (typeof apply === 'function') await apply(addLanguage);
}
}
const highlightLang = bundledLanguages[m[1]] ? m[1] : 'none';
const shiki = await createHighlighter({
langs: [highlightLang],
themes: ['github-dark', 'light-plus'],
});
$pre.replaceWith($(shiki.codeToHtml($code.text(), {
lang: highlightLang,
theme: 'light-plus',
meta: {
__raw: m[2] ? `{${m[2]}}` : null,
},
transformers: transformers.concat({
pre(node) {
this.addClassToHast(node, 'syntax-hl');
if (language.includes('line-numbers')) {
this.addClassToHast(node, 'line-numbers');
}
node.children.push({
tagName: 'div',
type: 'element',
properties: {
class: 'toolbar',
},
children: [
{
tagName: 'div',
type: 'element',
properties: {
class: 'toolbar-item',
},
children: [
{
tagName: 'a',
type: 'element',
properties: {
href: 'javascript:;',
class: 'copy-to-clipboard',
'data-clipboard-text': $code.text(),
},
children: [
{ type: 'text', value: 'Copy' },
],
},
],
},
],
});
},
}),
})));
if (!bundledLanguages[name]) requestedLanguages.delete(name);
}
const shiki = await createHighlighter({ langs: Array.from(requestedLanguages), themes: ['github-dark', 'light-plus'] });
for (const task of tasks) task(shiki);
}
async function highlight(text: string, grammar, language) {