VSCode extension - how to alter file's text

家住魔仙堡 提交于 2020-08-07 05:04:51

问题


I have an extension that grabs the open file's text and alters it. Once the text is altered, how do I put it back into the file that is displayed in VSCode?

// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {

  // Use the console to output diagnostic information (console.log) and errors (console.error)
  // This line of code will only be executed once when your extension is activated
  console.log('Congratulations, your extension "myExtension" is now active!');
  console.log(process.versions);

  // The command has been defined in the package.json file
  // Now provide the implementation of the command with  registerCommand
  // The commandId parameter must match the command field in package.json
  let disposable = vscode.commands.registerCommand('extension.myExtension', () => {
    // The code you place here will be executed every time your command is executed

    let activeEditor = vscode.window.activeTextEditor;
    if (!activeEditor) {
      return;
    }
    let text = activeEditor.document.getText();

    getAsyncApi(text).then((textToInsertIntoDoc) => {

      let finaldoc = insertTextIntoDoc(text, textToInsertIntoDoc);

      // not what I want - just used to see new text
      vscode.window.showInformationMessage(textToInsertIntoDoc);
    });

  });

  context.subscriptions.push(disposable);
}

回答1:


The API you can use here is TextEditor.edit, whose definition is

edit(callback: (editBuilder: TextEditorEdit) => void, options?: {   undoStopBefore: boolean; undoStopAfter: boolean; }): Thenable<boolean>;

It asks for a callback as the first parameter and in the callback, you can make edits to the document by visiting editBuilder.

I put a sample extension in https://github.com/Microsoft/vscode-extension-samples/tree/master/document-editing-sample which reverses the content in current selection, which is basically a simple use TextEditor.edit.




回答2:


This is a revision of the main function in Rebornix's extension sample (included with the set of Microsoft extension samples) that handles the selection issues you raised. It reverses the content of the selection(s) (leaving the selections) or if a selection is empty it will reverse the word under the cursor at that selection without leaving anything selected. It often makes sense to leave a selection, but you can add code to remove selection.

    let disposable = vscode.commands.registerCommand('extension.reverseWord', function () {
        // Get the active text editor
        const editor = vscode.window.activeTextEditor;

        if (editor) {
            const document = editor.document;
            editor.edit(editBuilder => {
                editor.selections.forEach(sel => {
                    const range = sel.isEmpty ? document.getWordRangeAtPosition(sel.start) || sel : sel;
                    let word = document.getText(range);
                    let reversed = word.split('').reverse().join('');
                    editBuilder.replace(range, reversed);
                })
            }) // apply the (accumulated) replacement(s) (if multiple cursors/selections)
        }
    });

Admittedly, while I could remove a single selection by setting .selection to a new empty selection that doesn't seem to work with .selections[i]. But you can make multiple changes without having selections in the first place.

What you don't want to do is make a selection through code just to alter text through code. Users make selections, you don't (unless the end purpose of the function is to make a selection).

I came to this question looking for a way to apply a textEdit[] array (which is normally returned by a provideDocumentRangeFormattingEdits callback function). If you build changes in the array you can apply them to your document in your own function:

        const { activeTextEditor } = vscode.window;

        if (activeTextEditor) {
            const { document } = activeTextEditor;
            if (document) {
                /*
                  build your textEdits similarly to the above with insert, delete, replace 
                  but not within an editBuilder arrow function
                const textEdits: vscode.TextEdit[] = [];
                textEdits.push(vscode.TextEdit.replace(...));
                textEdits.push(vscode.TextEdit.insert(...));
                */

                const workEdits = new vscode.WorkspaceEdit();
                workEdits.set(document.uri, textEdits); // give the edits
                vscode.workspace.applyEdit(workEdits); // apply the edits
            }
        }

So that's another way to apply edits to a document. Even though I got the editBuilder sample to work correctly without selecting text, I have had problems with selections in other cases. WorkspaceEdit doesn't select the changes.




回答3:


Due to the issues I commented about in the above answer, I ended up writing a quick function that does multi-cursor friendly insert, and if the selection was empty, then it does not leave the inserted text selected afterwards (i.e. it has the same intuitive behavior as if you had pressed CTRL + V, or typed text on the keyboard, etc.)

Invoking it is simple:

// x is the cursor index, it can be safely ignored if you don't need it.
InsertText(x => 'Hello World'); 

Implementation:

function InsertText(getText: (i:number) => string, i: number = 0, wasEmpty: boolean = false) {

    let activeEditor = vscode.window.activeTextEditor;
    if (!activeEditor) { return; }

    let sels = activeEditor.selections;

    if (i > 0 && wasEmpty)
    {
        sels[i - 1] = new vscode.Selection(sels[i - 1].end, sels[i - 1].end);
        activeEditor.selections = sels; // required or the selection updates will be ignored! 😱
    }

    if (i < 0 || i >= sels.length) { return; }

    let isEmpty = sels[i].isEmpty;
    activeEditor.edit(edit => edit.replace(sels[i], getText(i))).then(x => {

        InsertText(getText, i + 1, isEmpty);
    });
}



回答4:


Here is the code snippet that will solve your issue :

activeEditor.edit((selectedText) => {
    selectedText.replace(activeEditor.selection, newText);
})


来源:https://stackoverflow.com/questions/53585737/vscode-extension-how-to-alter-files-text

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!