summaryrefslogtreecommitdiff
path: root/packages/frontend/src/utility/code-highlighter.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/frontend/src/utility/code-highlighter.ts')
-rw-r--r--packages/frontend/src/utility/code-highlighter.ts99
1 files changed, 99 insertions, 0 deletions
diff --git a/packages/frontend/src/utility/code-highlighter.ts b/packages/frontend/src/utility/code-highlighter.ts
new file mode 100644
index 0000000000..4f2aff9d4c
--- /dev/null
+++ b/packages/frontend/src/utility/code-highlighter.ts
@@ -0,0 +1,99 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { createHighlighterCore } from 'shiki/core';
+import { createOnigurumaEngine } from 'shiki/engine/oniguruma';
+import darkPlus from 'shiki/themes/dark-plus.mjs';
+import { bundledThemesInfo } from 'shiki/themes';
+import { bundledLanguagesInfo } from 'shiki/langs';
+import lightTheme from '@@/themes/_light.json5';
+import darkTheme from '@@/themes/_dark.json5';
+import defaultLightTheme from '@@/themes/l-light.json5';
+import defaultDarkTheme from '@@/themes/d-green-lime.json5';
+import { unique } from './array.js';
+import { deepClone } from './clone.js';
+import { deepMerge } from './merge.js';
+import type { HighlighterCore, LanguageRegistration, ThemeRegistration, ThemeRegistrationRaw } from 'shiki/core';
+import { prefer } from '@/preferences.js';
+
+let _highlighter: HighlighterCore | null = null;
+
+export async function getTheme(mode: 'light' | 'dark', getName: true): Promise<string>;
+export async function getTheme(mode: 'light' | 'dark', getName?: false): Promise<ThemeRegistration | ThemeRegistrationRaw>;
+export async function getTheme(mode: 'light' | 'dark', getName = false): Promise<ThemeRegistration | ThemeRegistrationRaw | string | null> {
+ const theme = deepClone(mode === 'light' ? prefer.s.lightTheme ?? defaultLightTheme : prefer.s.darkTheme ?? defaultDarkTheme);
+
+ if (theme.base) {
+ const base = [lightTheme, darkTheme].find(x => x.id === theme.base);
+ if (base && base.codeHighlighter) theme.codeHighlighter = Object.assign({}, base.codeHighlighter, theme.codeHighlighter);
+ }
+
+ if (theme.codeHighlighter) {
+ let _res: ThemeRegistration = {};
+ if (theme.codeHighlighter.base === '_none_') {
+ _res = deepClone(theme.codeHighlighter.overrides);
+ } else {
+ const base = await bundledThemesInfo.find(t => t.id === theme.codeHighlighter!.base)?.import() ?? darkPlus;
+ _res = deepMerge(theme.codeHighlighter.overrides ?? {}, 'default' in base ? base.default : base);
+ }
+ if (_res.name == null) {
+ _res.name = theme.id;
+ }
+ _res.type = mode;
+
+ if (getName) {
+ return _res.name;
+ }
+ return _res;
+ }
+
+ if (getName) {
+ return 'dark-plus';
+ }
+ return darkPlus;
+}
+
+export async function getHighlighter(): Promise<HighlighterCore> {
+ if (!_highlighter) {
+ return await initHighlighter();
+ }
+ return _highlighter;
+}
+
+async function initHighlighter() {
+ // テーマの重複を消す
+ const themes = unique([
+ darkPlus,
+ ...(await Promise.all([getTheme('light'), getTheme('dark')])),
+ ]);
+
+ const jsLangInfo = bundledLanguagesInfo.find(t => t.id === 'javascript');
+ const highlighter = await createHighlighterCore({
+ engine: createOnigurumaEngine(() => import('shiki/onig.wasm?init')),
+ themes,
+ langs: [
+ ...(jsLangInfo ? [async () => await jsLangInfo.import()] : []),
+ async () => (await import('aiscript-vscode/aiscript/syntaxes/aiscript.tmLanguage.json')).default as unknown as LanguageRegistration,
+ ],
+ });
+
+ // TODO
+ //watch('lightTheme', async () => {
+ // const newTheme = await getTheme('light');
+ // if (newTheme.name && !highlighter.getLoadedThemes().includes(newTheme.name)) {
+ // highlighter.loadTheme(newTheme);
+ // }
+ //});
+ //watch('darkTheme', async () => {
+ // const newTheme = await getTheme('dark');
+ // if (newTheme.name && !highlighter.getLoadedThemes().includes(newTheme.name)) {
+ // highlighter.loadTheme(newTheme);
+ // }
+ //});
+
+ _highlighter = highlighter;
+
+ return highlighter;
+}