How to decide whether text or HTML is pasted? (VS Code paste support)

I’m trying to add support for copying from VS code and pasting in ProseMirror. VS code adds a special vscode-editor-data section in the clipboard that specifies things like language (e.g. typescript.) Example, copying test from VS code, results in this from Clipboard Inspector (evercoder.github.io)

I tried looking at handlePaste, transformPastedText, transformPastedHTML, however all of those are called after ProseMirror determines whether to process the clipboard contents as text or HTML in the internal-only parseFromClipboard function. I’d really like to be able to direct the clipboard parsing in case of VS Code to a) get the text and b) insert it either as plain text or <code lang=SomeLanguageFromVsCode> slice.

Perhaps making parseFromClipboard an extensibility point would provide that control. Is this the right approach or am I missing something important?

2 Likes

handlePaste has access to the event, and could thus look at this data itself and re-parse as appropriate. Does that not work in this case?

1 Like

Yes this is definitely possible. I’ve created a plugin to support code blocks pasted from VS Code right now. Maybe that helps.

Thanks both for the help! handlePaste works, however I didn’t like that some processing occurs before it in doPaste that is later discarded. For this specific case, handlePaste will work. There’s a hypothetical case (that I was assuming was my case as well, until now) where one might want to have logic to choose “text/plain” and run it through the default pipeline of text parsers, etc. in ProseMirror - for example, I was trying to paste [[date]] from VS code in my custom editor that has a parser to create a “date node”. I’m no longer trying to do that, just mentioning to illustrate the case I had in mind before I decided to “just paste as code”.

re: tiptap plugin: awesome job @philippkuehn!

I will use @philippkuehn’s plugin since I’m using tiptap. Here’s some code I was just testing for “raw ProseMirror” users. After a bit of digging I found handleDOMEvents which gets called before all the internal doPaste code.

handleDOMEvents: {
    paste(view: EditorView<any>, event: ClipboardEvent): boolean {
        const vsCodeData: string | undefined = event.clipboardData?.getData("vscode-editor-data");
        if (typeof vsCodeData !== "string" || vsCodeData === "") { // if not VS code
            return false; // default processing
        }

        const text = event.clipboardData?.getData("text/plain");
        if (!text) {
            return false;
        }

        // VS code processing:
        // TODO: in the future, make this get the language mode from metadata.mode, etc.
        // see ClipboardStoredMetadata from https://github.com/microsoft/vscode/blob/main/src/vs/editor/browser/controller/textAreaInput.ts#L51
        // and paste from VS code into https://evercoder.github.io/clipboard-inspector/ to check the format
        const $context = view.state.selection.$from;
        const isInCode = $context.parent.type.name === "codeBlock";
        const schema = view.state.schema;
        const textNode = schema.text(text.replace(/\r\n?/g, "\n"));
        const resultSlice: Slice<any> = new Slice(Fragment.from(isInCode ? textNode : schema.nodes.codeBlock.create(null, textNode, null)), 0, 0);
        const tr = view.state.tr.replaceSelection(resultSlice);
        view.dispatch(tr.scrollIntoView().setMeta("paste", true).setMeta("uiEvent", "paste"));
        event.preventDefault();
        return true;
    },
},

Thanks again for the prompt responses!

1 Like