↩️

【VSCode拡張機能開発】vscode.WorkspaceEditが便利

2024/03/26に公開

各カーソルで選択した文字を大文字にする簡単な拡張機能を作ってみます。

vscode.WorkspaceEdit を使った書き方

extension.ts
import * as vscode from "vscode";

const formatSelections = (editor: vscode.TextEditor) => {
  const workspaceEdit = new vscode.WorkspaceEdit();

  editor.selections.forEach((sel: vscode.Selection) => {
    if (sel.isEmpty) {
      return;
    }
    const selText = editor.document.getText(sel);
    workspaceEdit.replace(editor.document.uri, sel, selText.toUpperCase());
  });

  vscode.workspace.applyEdit(workspaceEdit).then(() => {
    console.log("置換完了!")
  });
};

export function activate(context: vscode.ExtensionContext) {
  context.subscriptions.push(
    vscode.commands.registerTextEditorCommand("my-extension.execute", (editor: vscode.TextEditor) => {
      formatSelections(editor);
    })
  );
}

export function deactivate() {}

vscode.WorkspaceEdit のインスタンスに replace メソッドで置換位置と置換文字列を登録していって、vscode.workspace.applyEdit で登録した操作を一括で反映させるイメージです。

参考: vscode.TextEdit を使った書き方

この書き方もできますが、後述するように vscode.WorkspaceEdit を使ったほうが何かと便利です。

extension.ts
import * as vscode from "vscode";

const formatSelections = (editor: vscode.TextEditor) => {
  editor.edit((editBuilder) => {
    editor.selections.forEach((sel) => {
      if (sel.isEmpty) {
        return;
      }
      const text = editor.document.getText(sel);
      const newText = text.toUpperCase();
      if (text != newText) { // ★
        editBuilder.replace(sel, newText);
      }
    });
  })then(() => {
    console.log("置換完了!")
  });;
};

// export function activate... 以降は変更ナシ

うれしいポイント1:ネストが減る

言わずもがなですね。

うれしいポイント2:Undo操作への影響を意識しなくてよくなる

こちらが本題。
拡張機能を実行して選択文字列が変化しなかったとき、エディタ状での扱いが以下のように異なります:

  • vscode.TextEdit:「同じ文字が挿入された」という操作1回としてカウントされる
  • vscode.WorkspaceEdit:文字列に変更がなければスキップされる(操作としてカウントされない

具体例で考えてみましょう。

①操作Aを行う
↓
②既に大文字になっている文字列に対して拡張機能を実行する(見た目は変化しない)
↓
③Ctrl+Zを押す

vscode.TextEdit を使った場合、②で見た目上は変化しなくても、「同じ文字が挿入された」という操作が行われたという扱いになります。
そのため、③のキー押しによって巻き戻されるのは②のステップまでとなり、①の状態まで戻すにはCtrl+Zを2回行わなくてはいけません。

普段の操作感からすると一瞬戸惑うことになります。
この違和感をなくすため、先述のコードでは条件分岐を入れています( の部分)。

対して vscode.WorkspaceEdit を使うと、この場合は②がスキップされるため、③ですぐに①の状態にまで戻れます。

処理結果とUndoへの影響を意識しなくてよくなるのは大きなメリットです。


AIによるサジェストでこの書き方を知りました。VSCode APIの奥深さを垣間見た気がします。

Discussion