From 0302bd43cbd8d22b73c56ef24951e87a0f710283 Mon Sep 17 00:00:00 2001 From: donteatfriedrice Date: Thu, 10 Apr 2025 04:48:08 +0000 Subject: [PATCH] fix(editor): should preserve indentation when pasting code with spaces into code block (#11587) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Close [BS-3087](https://linear.app/affine-design/issue/BS-3087/粘贴内容到-code-block-缩进会丢) --- .../blocks/paragraph/src/adapters/markdown.ts | 1 + .../root/src/clipboard/readonly-clipboard.ts | 16 +++--- .../src/adapters/markdown/markdown-inline.ts | 12 +++++ .../blocksuite/clipboard/clipboard.spec.ts | 50 +++++++++++++++++++ 4 files changed, 71 insertions(+), 8 deletions(-) diff --git a/blocksuite/affine/blocks/paragraph/src/adapters/markdown.ts b/blocksuite/affine/blocks/paragraph/src/adapters/markdown.ts index d01622d94..c7faae1a5 100644 --- a/blocksuite/affine/blocks/paragraph/src/adapters/markdown.ts +++ b/blocksuite/affine/blocks/paragraph/src/adapters/markdown.ts @@ -71,6 +71,7 @@ export const paragraphBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher = 'children' ) .closeNode(); + walkerContext.skipAllChildren(); break; } case 'heading': { diff --git a/blocksuite/affine/blocks/root/src/clipboard/readonly-clipboard.ts b/blocksuite/affine/blocks/root/src/clipboard/readonly-clipboard.ts index 3e8e24ef1..0f75d379f 100644 --- a/blocksuite/affine/blocks/root/src/clipboard/readonly-clipboard.ts +++ b/blocksuite/affine/blocks/root/src/clipboard/readonly-clipboard.ts @@ -34,12 +34,6 @@ const NotionClipboardConfig = ClipboardAdapterConfigExtension({ priority: 95, }); -const HtmlClipboardConfig = ClipboardAdapterConfigExtension({ - mimeType: 'text/html', - adapter: HtmlAdapter, - priority: 90, -}); - const imageClipboardConfigs = [ 'image/apng', 'image/avif', @@ -52,14 +46,20 @@ const imageClipboardConfigs = [ return ClipboardAdapterConfigExtension({ mimeType, adapter: ImageAdapter, - priority: 80, + priority: 85, }); }); const PlainTextClipboardConfig = ClipboardAdapterConfigExtension({ mimeType: 'text/plain', adapter: MixTextAdapter, - priority: 70, + priority: 80, +}); + +const HtmlClipboardConfig = ClipboardAdapterConfigExtension({ + mimeType: 'text/html', + adapter: HtmlAdapter, + priority: 75, }); const AttachmentClipboardConfig = ClipboardAdapterConfigExtension({ diff --git a/blocksuite/affine/inlines/preset/src/adapters/markdown/markdown-inline.ts b/blocksuite/affine/inlines/preset/src/adapters/markdown/markdown-inline.ts index 923aa5c41..6385f1a0c 100644 --- a/blocksuite/affine/inlines/preset/src/adapters/markdown/markdown-inline.ts +++ b/blocksuite/affine/inlines/preset/src/adapters/markdown/markdown-inline.ts @@ -79,6 +79,17 @@ export const markdownListToDeltaMatcher = MarkdownASTToDeltaExtension({ toDelta: () => [], }); +export const markdownHtmlToDeltaMatcher = MarkdownASTToDeltaExtension({ + name: 'html', + match: ast => ast.type === 'html', + toDelta: ast => { + if (!('value' in ast)) { + return []; + } + return [{ insert: ast.value }]; + }, +}); + export const MarkdownInlineToDeltaAdapterExtensions = [ markdownTextToDeltaMatcher, markdownInlineCodeToDeltaMatcher, @@ -89,4 +100,5 @@ export const MarkdownInlineToDeltaAdapterExtensions = [ markdownInlineMathToDeltaMatcher, markdownListToDeltaMatcher, markdownFootnoteReferenceToDeltaMatcher, + markdownHtmlToDeltaMatcher, ]; diff --git a/tests/affine-local/e2e/blocksuite/clipboard/clipboard.spec.ts b/tests/affine-local/e2e/blocksuite/clipboard/clipboard.spec.ts index 8df38a58f..416575b0a 100644 --- a/tests/affine-local/e2e/blocksuite/clipboard/clipboard.spec.ts +++ b/tests/affine-local/e2e/blocksuite/clipboard/clipboard.spec.ts @@ -323,4 +323,54 @@ test.describe('paste to code block', () => { await verifyCodeBlockContent(page, 0, 'hello test\ntest\ntest hello'); }); + + test('should preserve indentation when pasting code with spaces into code block', async ({ + page, + }) => { + await pressEnter(page); + await addCodeBlock(page); + + // Sample code with proper indentation for text/plain + const plainTextCode = [ + 'const fibonacci = (n: number): number => {', + ' if (n <= 1) return n;', + ' return fibonacci(n - 1) + fibonacci(n - 2);', + '}', + 'function generateSequence(length: number) {', + ' const sequence = [];', + ' for (let i = 0; i < length; i++) {', + ' sequence.push(fibonacci(i));', + ' }', + ' return sequence;', + '}', + ].join('\n'); + + const htmlCode = + '
const fibonacci = (n: number): number => {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
function generateSequence(length: number) {
const sequence = [];
for (let i = 0; i < length; i++) {
sequence.push(fibonacci(i));
}
return sequence;
}
'; + + await pasteContent(page, { + 'text/plain': plainTextCode, + 'text/html': htmlCode, + }); + await page.waitForTimeout(100); + + // Verify the pasted code maintains indentation + await verifyCodeBlockContent(page, 0, plainTextCode); + }); + + test('html tag should be treated as plain text when pasting', async ({ + page, + }) => { + await pressEnter(page); + await addCodeBlock(page); + + const textWithHtmlTags = + '
const fibonacci = (n: number): number => {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
function generateSequence(length: number) {
const sequence = [];
for (let i = 0; i < length; i++) {
sequence.push(fibonacci(i));
}
return sequence;
}
'; + + await pasteContent(page, { 'text/plain': textWithHtmlTags }); + await page.waitForTimeout(100); + + // Verify the pasted code maintains indentation + await verifyCodeBlockContent(page, 0, textWithHtmlTags); + }); });