fix: code block content insertion location error (#5737)

#### What type of PR is this?

/kind bug
/area editor
/milestone 2.15.x

#### What this PR does / why we need it:

在编写多行文本后,粘贴代码块时,会出现粘贴的内容插入错误,其与代码块分割开。

[例如 #5736 中的示例所示](https://github.com/halo-dev/halo/assets/44745967/4b1ef8dc-60bf-47fd-b64d-23b0d6537d9e)

在本 PR 中,在创建 CodeBlock 时,将 Text 转为 `TextNode` 后,一同传入作为 `CodeBlock` 的 content。
另外为了保证插入代码块之后,光标处于代码块中,将会从插入位置的 from 开始往文档顶部搜索。

#### How to test it?

测试示例中的场景下,代码块插入是否正常。

测试在代码块前后放入其他代码块或者其他块,插入代码块后光标是否在代码块中。

#### Which issue(s) this PR fixes:

Fixes #5736 

#### Does this PR introduce a user-facing change?
```release-note
修复默认编辑器中粘贴代码块会出现错行的问题
```
pull/5655/head
Takagi 2024-04-18 12:42:06 +08:00 committed by GitHub
parent 410a7557f9
commit fdc2453cc8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 73 additions and 1 deletions

View File

@ -6,7 +6,13 @@ import {
findParentNode,
VueNodeViewRenderer,
} from "@/tiptap/vue-3";
import { EditorState, TextSelection, type Transaction } from "@/tiptap/pm";
import {
EditorState,
Plugin,
PluginKey,
TextSelection,
type Transaction,
} from "@/tiptap/pm";
import CodeBlockLowlight from "@tiptap/extension-code-block-lowlight";
import type { CodeBlockLowlightOptions } from "@tiptap/extension-code-block-lowlight";
import CodeBlockViewRenderer from "./CodeBlockViewRenderer.vue";
@ -237,4 +243,70 @@ export default CodeBlockLowlight.extend<
},
};
},
addProseMirrorPlugins() {
return [
// Solve the paste problem. Because the upstream has not been
// able to deal with this problem for a long time, it is
// handled manually locally.
// see: https://github.com/ueberdosis/tiptap/pull/3606
new Plugin({
key: new PluginKey("codeBlockVSCodeHandlerFixPaste"),
props: {
handlePaste: (view, event) => {
if (!event.clipboardData) {
return false;
}
// dont create a new code block within code blocks
if (this.editor.isActive(this.type.name)) {
return false;
}
const text = event.clipboardData.getData("text/plain");
const vscode = event.clipboardData.getData("vscode-editor-data");
const vscodeData = vscode ? JSON.parse(vscode) : undefined;
const language = vscodeData?.mode;
if (!text || !language) {
return false;
}
const { tr, schema } = view.state;
// add text to code block
// strip carriage return chars from text pasted as code
// see: https://github.com/ProseMirror/prosemirror-view/commit/a50a6bcceb4ce52ac8fcc6162488d8875613aacd
const contentTextNode = schema.text(text.replace(/\r\n?/g, "\n"));
// create an empty code block
tr.replaceSelectionWith(
this.type.create({ language }, contentTextNode)
);
const { selection } = tr;
// Whether the current position is code block, if not, move forward to code block.
let codeBlockPos = Math.max(0, selection.from - 1);
while (
codeBlockPos > 0 &&
tr.doc.resolve(codeBlockPos).parent.type.name !== this.type.name
) {
codeBlockPos--;
}
// put cursor inside the newly created code block
tr.setSelection(TextSelection.near(tr.doc.resolve(codeBlockPos)));
// store meta information
// this is useful for other plugins that depends on the paste event
// like the paste rule plugin
tr.setMeta("paste", true);
view.dispatch(tr);
return true;
},
},
}),
...(this.parent?.() || []),
];
},
});