'VSCode API: Editor.edit editbuilder.replace fails without reason (possibly due to formatting?)

In my extension I want to edit the document on a few specific document edits.

My actual use case is a bit complicated so I have created a minimal example. The code below listens for any document edit. If the word "hello" exists in the edit (i.e. the user pasted some code that contains the word "hello") then we replace the change range with the pasted text but just make it upper case.

We also console.log if the edit was successful, and any potential reason the edit was rejected.

vscode.workspace.onDidChangeTextDocument(event => {
    for (const change of event.contentChanges) {
        if (change.text.includes("hello")) {
            activeEditor.edit(editBuilder => {
                editBuilder.replace(change.range, change.text.toUpperCase());
            }).then(
                value => console.log("SUCCESS: "+value),
                reason => console.log("FAIL REASON: "+reason),
            );
        }
    }
});

A working example would be selecting some text in a document and pasting in the text const hello = 5;. As expected, the extension replaces the text with CONST HELLO = 5; and logs SUCCESS: true.

But when I paste in some text that automatically get formatted I run into problems. If I were to paste in:

    const hello = 5;
    const lol = 10;
    const lmao = 20;

Including all the whitespaces/tabs, then vscode wants to "format" or correct my lines, i.e. remove the whitespace. So the resulting text will be:

const hello = 5;
const lol = 10;
const lmao = 20;

The extension tries to make it uppercase still but only prints SUCCESS: false. No reason is logged at all; the reject function is not executed.

Why does the edit not succeed? Should I await the other edits somehow or keep re-trying the edit until it succeeds? Am I logging the rejection incorrectly?



Solution 1:[1]

In case it helps, here is code I use - I found it better to have the editBuilder outside the loop. I think you can adapt it for your purposes:

    editor.edit( (editBuilder) => {

      // put your for (const change of event.contentChanges) {} here

      for (const match of matches) {
        resolvedReplace = variables.buildReplace(args, "replace", match, editor.selection, null, index);

        const matchStartPos = document.positionAt(match.index);
        const matchEndPos = document.positionAt(match.index + match[0].length);
        const matchRange = new vscode.Range(matchStartPos, matchEndPos);

        editBuilder.replace(matchRange, resolvedReplace);
      }
    }).then(success => {
        if (!success) {
            return;
        }
        if (success) {  ... do something here if you need to }
    });

Solution 2:[2]

One solution is just to "keep trying again". I do not like this solution, but it is a solution nevertheless, and it currently works for my use-case.

    async function makeReplaceEdit(range: vscode.Range, text: string, maxRetries = 10) {
        for (let i = 0; i <= maxRetries; i++) {
            const editor = vscode.window.activeTextEditor;
            if (!editor) return;

            const success = await editor.edit(editBuilder => {
                editBuilder.replace(
                    range,
                    text
                );
            }, { undoStopBefore: false, undoStopAfter: false });

            if (success) break;
        }
    };

    vscode.workspace.onDidChangeTextDocument((event) => {

        // See if any change contained "hello"        
        let foundHello = false;
        for (const change of event.contentChanges) {
            if (change.text.includes("hello")) {
                foundHello = true;
            }
        }

        if (foundHello) {
            console.log("inside1");
            const editor = vscode.window.activeTextEditor;
            if (!editor) return;
            makeReplaceEdit(editor.document.lineAt(0).range, "Change");
        }
    });

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2