diff --git a/blocksuite/affine/blocks/block-code/src/code-block-service.ts b/blocksuite/affine/blocks/block-code/src/code-block-service.ts index 03cb10438..c2cd66bc0 100644 --- a/blocksuite/affine/blocks/block-code/src/code-block-service.ts +++ b/blocksuite/affine/blocks/block-code/src/code-block-service.ts @@ -1,10 +1,8 @@ -import { CodeBlockSchema, ColorScheme } from '@blocksuite/affine-model'; -import { textKeymap } from '@blocksuite/affine-rich-text'; +import { ColorScheme } from '@blocksuite/affine-model'; import { ThemeProvider } from '@blocksuite/affine-shared/services'; -import { BlockService } from '@blocksuite/block-std'; +import { LifeCycleWatcher } from '@blocksuite/block-std'; import { type Signal, signal } from '@preact/signals-core'; import { - bundledLanguagesInfo, createHighlighterCore, createOnigurumaEngine, type HighlighterCore, @@ -18,8 +16,8 @@ import { CODE_BLOCK_DEFAULT_LIGHT_THEME, } from './highlight/const.js'; -export class CodeBlockService extends BlockService { - static override readonly flavour = CodeBlockSchema.model.flavour; +export class CodeBlockHighlighter extends LifeCycleWatcher { + static override key = 'code-block-highlighter'; private _darkThemeKey: string | undefined; @@ -27,13 +25,6 @@ export class CodeBlockService extends BlockService { highlighter$: Signal = signal(null); - get langs() { - return ( - this.std.getOptional(CodeBlockConfigExtension.identifier)?.langs ?? - bundledLanguagesInfo - ); - } - get themeKey() { const theme = this.std.get(ThemeProvider).theme$.value; return theme === ColorScheme.Dark @@ -41,35 +32,31 @@ export class CodeBlockService extends BlockService { : this._lightThemeKey; } + private readonly _loadTheme = async ( + highlighter: HighlighterCore + ): Promise => { + const config = this.std.getOptional(CodeBlockConfigExtension.identifier); + const darkTheme = config?.theme?.dark ?? CODE_BLOCK_DEFAULT_DARK_THEME; + const lightTheme = config?.theme?.light ?? CODE_BLOCK_DEFAULT_LIGHT_THEME; + this._darkThemeKey = (await normalizeGetter(darkTheme)).name; + this._lightThemeKey = (await normalizeGetter(lightTheme)).name; + await highlighter.loadTheme(darkTheme, lightTheme); + this.highlighter$.value = highlighter; + }; + override mounted(): void { super.mounted(); - this.bindHotKey(textKeymap(this.std)); - createHighlighterCore({ engine: createOnigurumaEngine(() => getWasm), }) - .then(async highlighter => { - const config = this.std.getOptional( - CodeBlockConfigExtension.identifier - ); - const darkTheme = config?.theme?.dark ?? CODE_BLOCK_DEFAULT_DARK_THEME; - const lightTheme = - config?.theme?.light ?? CODE_BLOCK_DEFAULT_LIGHT_THEME; - - this._darkThemeKey = (await normalizeGetter(darkTheme)).name; - this._lightThemeKey = (await normalizeGetter(lightTheme)).name; - - await highlighter.loadTheme(darkTheme, lightTheme); - - this.highlighter$.value = highlighter; - - this.disposables.add(() => { - highlighter.dispose(); - }); - }) + .then(this._loadTheme) .catch(console.error); } + + override unmounted(): void { + this.highlighter$.value?.dispose(); + } } /** diff --git a/blocksuite/affine/blocks/block-code/src/code-block-spec.ts b/blocksuite/affine/blocks/block-code/src/code-block-spec.ts index b3cb3bf88..f38d82e15 100644 --- a/blocksuite/affine/blocks/block-code/src/code-block-spec.ts +++ b/blocksuite/affine/blocks/block-code/src/code-block-spec.ts @@ -12,7 +12,8 @@ import { CodeBlockInlineManagerExtension, CodeBlockUnitSpecExtension, } from './code-block-inline.js'; -import { CodeBlockService } from './code-block-service.js'; +import { CodeBlockHighlighter } from './code-block-service.js'; +import { CodeKeymapExtension } from './code-keymap.js'; import { AFFINE_CODE_TOOLBAR_WIDGET } from './code-toolbar/index.js'; import { codeSlashMenuConfig } from './configs/slash-menu.js'; @@ -24,11 +25,12 @@ export const codeToolbarWidget = WidgetViewExtension( export const CodeBlockSpec: ExtensionType[] = [ FlavourExtension('affine:code'), - CodeBlockService, + CodeBlockHighlighter, BlockViewExtension('affine:code', literal`affine-code`), codeToolbarWidget, CodeBlockInlineManagerExtension, CodeBlockUnitSpecExtension, CodeBlockAdapterExtensions, SlashMenuConfigExtension('affine:code', codeSlashMenuConfig), + CodeKeymapExtension, ].flat(); diff --git a/blocksuite/affine/blocks/block-code/src/code-block.ts b/blocksuite/affine/blocks/block-code/src/code-block.ts index 693d46a49..15c530e71 100644 --- a/blocksuite/affine/blocks/block-code/src/code-block.ts +++ b/blocksuite/affine/blocks/block-code/src/code-block.ts @@ -26,18 +26,15 @@ import { computed, effect, type Signal, signal } from '@preact/signals-core'; import { html, nothing, type TemplateResult } from 'lit'; import { query } from 'lit/decorators.js'; import { classMap } from 'lit/directives/class-map.js'; -import type { ThemedToken } from 'shiki'; +import { bundledLanguagesInfo, type ThemedToken } from 'shiki'; import { CodeClipboardController } from './clipboard/index.js'; import { CodeBlockConfigExtension } from './code-block-config.js'; import { CodeBlockInlineManagerExtension } from './code-block-inline.js'; -import type { CodeBlockService } from './code-block-service.js'; +import { CodeBlockHighlighter } from './code-block-service.js'; import { codeBlockStyles } from './styles.js'; -export class CodeBlockComponent extends CaptionedBlockComponent< - CodeBlockModel, - CodeBlockService -> { +export class CodeBlockComponent extends CaptionedBlockComponent { static override styles = codeBlockStyles; private _inlineRangeProvider: InlineRangeProvider | null = null; @@ -52,7 +49,7 @@ export class CodeBlockComponent extends CaptionedBlockComponent< return 'Plain Text'; } - const matchedInfo = this.service.langs.find(info => info.id === lang); + const matchedInfo = this.langs.find(info => info.id === lang); return matchedInfo ? matchedInfo.name : 'Plain Text'; }); @@ -75,6 +72,17 @@ export class CodeBlockComponent extends CaptionedBlockComponent< return this.doc.readonly; } + get langs() { + return ( + this.std.getOptional(CodeBlockConfigExtension.identifier)?.langs ?? + bundledLanguagesInfo + ); + } + + get highlighter() { + return this.std.get(CodeBlockHighlighter); + } + override get topContenteditableElement() { if (this.std.get(DocModeProvider).getEditorMode() === 'edgeless') { return this.closest(NOTE_SELECTOR); @@ -89,7 +97,7 @@ export class CodeBlockComponent extends CaptionedBlockComponent< return; } - const matchedInfo = this.service.langs.find( + const matchedInfo = this.langs.find( info => info.id === modelLang || info.name === modelLang || @@ -101,8 +109,8 @@ export class CodeBlockComponent extends CaptionedBlockComponent< const langImport = matchedInfo.import; const lang = matchedInfo.id; - const highlighter = this.service.highlighter$.value; - const theme = this.service.themeKey; + const highlighter = this.highlighter.highlighter$.value; + const theme = this.highlighter.themeKey; if (!theme || !highlighter) { this.highlightTokens$.value = []; return; diff --git a/blocksuite/affine/blocks/block-code/src/code-keymap.ts b/blocksuite/affine/blocks/block-code/src/code-keymap.ts new file mode 100644 index 000000000..a7f3d3fd5 --- /dev/null +++ b/blocksuite/affine/blocks/block-code/src/code-keymap.ts @@ -0,0 +1,7 @@ +import { CodeBlockSchema } from '@blocksuite/affine-model'; +import { textKeymap } from '@blocksuite/affine-rich-text'; +import { KeymapExtension } from '@blocksuite/block-std'; + +export const CodeKeymapExtension = KeymapExtension(textKeymap, { + flavour: CodeBlockSchema.model.flavour, +}); diff --git a/blocksuite/affine/blocks/block-code/src/code-toolbar/components/lang-button.ts b/blocksuite/affine/blocks/block-code/src/code-toolbar/components/lang-button.ts index 89b0a6adf..0bc07d3b6 100644 --- a/blocksuite/affine/blocks/block-code/src/code-toolbar/components/lang-button.ts +++ b/blocksuite/affine/blocks/block-code/src/code-toolbar/components/lang-button.ts @@ -104,13 +104,11 @@ export class LanguageListButton extends WithDisposable( if (langList) { this._sortedBundledLanguages = JSON.parse(langList); } else { - this._sortedBundledLanguages = this.blockComponent.service.langs.map( - lang => ({ - label: lang.name, - name: lang.id, - aliases: lang.aliases, - }) - ); + this._sortedBundledLanguages = this.blockComponent.langs.map(lang => ({ + label: lang.name, + name: lang.id, + aliases: lang.aliases, + })); } this.disposables.add(() => {