refactor(editor): extensionalize html adapter (#9299)

This commit is contained in:
Saul-Mirone
2024-12-25 04:04:51 +00:00
parent a15009ce84
commit 50ff3655e5
16 changed files with 183 additions and 144 deletions
@@ -0,0 +1,31 @@
import type { ExtensionType } from '@blocksuite/block-std';
import {
createIdentifier,
type ServiceIdentifier,
} from '@blocksuite/global/di';
import type { BlockAdapterMatcher } from '../types/adapter.js';
import type { HtmlAST } from '../types/hast.js';
import type { HtmlDeltaConverter } from './delta-converter.js';
export type BlockHtmlAdapterMatcher = BlockAdapterMatcher<
HtmlAST,
HtmlDeltaConverter
>;
export const BlockHtmlAdapterMatcherIdentifier =
createIdentifier<BlockHtmlAdapterMatcher>('BlockHtmlAdapterMatcher');
export function BlockHtmlAdapterExtension(
matcher: BlockHtmlAdapterMatcher
): ExtensionType & {
identifier: ServiceIdentifier<BlockHtmlAdapterMatcher>;
} {
const identifier = BlockHtmlAdapterMatcherIdentifier(matcher.flavour);
return {
setup: di => {
di.addImpl(identifier, () => matcher);
},
identifier,
};
}
@@ -0,0 +1,134 @@
import type { ExtensionType } from '@blocksuite/block-std';
import {
createIdentifier,
type ServiceIdentifier,
} from '@blocksuite/global/di';
import type { DeltaInsert } from '@blocksuite/inline';
import type { AffineTextAttributes } from '../../types/index.js';
import {
type ASTToDeltaMatcher,
DeltaASTConverter,
type DeltaASTConverterOptions,
type InlineDeltaMatcher,
} from '../types/adapter.js';
import type { HtmlAST, InlineHtmlAST } from '../types/hast.js';
import { TextUtils } from '../utils/text.js';
export type InlineDeltaToHtmlAdapterMatcher = InlineDeltaMatcher<InlineHtmlAST>;
export const InlineDeltaToHtmlAdapterMatcherIdentifier =
createIdentifier<InlineDeltaToHtmlAdapterMatcher>(
'InlineDeltaToHtmlAdapterMatcher'
);
export function InlineDeltaToHtmlAdapterExtension(
matcher: InlineDeltaToHtmlAdapterMatcher
): ExtensionType & {
identifier: ServiceIdentifier<InlineDeltaToHtmlAdapterMatcher>;
} {
const identifier = InlineDeltaToHtmlAdapterMatcherIdentifier(matcher.name);
return {
setup: di => {
di.addImpl(identifier, () => matcher);
},
identifier,
};
}
export type HtmlASTToDeltaMatcher = ASTToDeltaMatcher<HtmlAST>;
export const HtmlASTToDeltaMatcherIdentifier =
createIdentifier<HtmlASTToDeltaMatcher>('HtmlASTToDeltaMatcher');
export function HtmlASTToDeltaExtension(
matcher: HtmlASTToDeltaMatcher
): ExtensionType & {
identifier: ServiceIdentifier<HtmlASTToDeltaMatcher>;
} {
const identifier = HtmlASTToDeltaMatcherIdentifier(matcher.name);
return {
setup: di => {
di.addImpl(identifier, () => matcher);
},
identifier,
};
}
export class HtmlDeltaConverter extends DeltaASTConverter<
AffineTextAttributes,
HtmlAST
> {
constructor(
readonly configs: Map<string, string>,
readonly inlineDeltaMatchers: InlineDeltaToHtmlAdapterMatcher[],
readonly htmlASTToDeltaMatchers: HtmlASTToDeltaMatcher[]
) {
super();
}
private _applyTextFormatting(
delta: DeltaInsert<AffineTextAttributes>
): InlineHtmlAST {
let hast: InlineHtmlAST = {
type: 'text',
value: delta.insert,
};
const context: {
configs: Map<string, string>;
current: InlineHtmlAST;
} = {
configs: this.configs,
current: hast,
};
for (const matcher of this.inlineDeltaMatchers) {
if (matcher.match(delta)) {
hast = matcher.toAST(delta, context);
context.current = hast;
}
}
return hast;
}
private _spreadAstToDelta(
ast: HtmlAST,
options: DeltaASTConverterOptions = Object.create(null)
): DeltaInsert<AffineTextAttributes>[] {
const context = {
configs: this.configs,
options,
toDelta: (ast: HtmlAST, options?: DeltaASTConverterOptions) =>
this._spreadAstToDelta(ast, options),
};
for (const matcher of this.htmlASTToDeltaMatchers) {
if (matcher.match(ast)) {
return matcher.toDelta(ast, context);
}
}
return 'children' in ast
? ast.children.flatMap(child => this._spreadAstToDelta(child, options))
: [];
}
astToDelta(
ast: HtmlAST,
options: DeltaASTConverterOptions = Object.create(null)
): DeltaInsert<AffineTextAttributes>[] {
return this._spreadAstToDelta(ast, options).reduce((acc, cur) => {
return TextUtils.mergeDeltas(acc, cur);
}, [] as DeltaInsert<AffineTextAttributes>[]);
}
deltaToAST(
deltas: DeltaInsert<AffineTextAttributes>[],
depth = 0
): InlineHtmlAST[] {
if (depth > 0) {
deltas.unshift({ insert: ' '.repeat(4).repeat(depth) });
}
return deltas.map(delta => this._applyTextFormatting(delta));
}
}
@@ -0,0 +1,3 @@
export * from './block-adapter.js';
export * from './delta-converter.js';
export * from './type.js';
@@ -0,0 +1 @@
export type Html = string;