VSCodeで「任意行までの一括選択・コピー」を実現するTypeScriptマクロ開発【サクラエディタ移行の集大成】

Development
スポンサーリンク
スポンサーリンク

はじめに:テキスト編集における「範囲選択の不便さ」

VSCodeで長いログファイルやドキュメントを編集していて、「この行から特定の文字列まで一気にコピーしたい」と思ったことはありませんか?

通常のVSCodeでは、以下のような手順が必要です:

  1. 開始位置でクリック
  2. スクロールして目的の行を探す
  3. Shiftを押しながら終了位置をクリック
  4. Ctrl+Cでコピー

数百行離れた箇所を選択する場合、この作業は非常に煩雑です。

私はサクラエディタから移行してきた際、この「任意行までの一括選択・コピー」機能がなくて困っていました。以前サクラエディタ用に作成したJavaScriptマクロ(任意文字を含む行まで一括範囲選択コピー)の機能をVSCodeでも実現したかったのです。

そこで、TypeScriptでVSCode拡張機能を自作し、キーボードショートカット一発で範囲選択・コピーできる機能を実装しました。

本記事では、実際に開発した4つの機能とそのTypeScriptコードを紹介します。同じような悩みを持つ方の参考になれば幸いです。

実装した4つの機能

今回実装したのは以下の4つの機能です:

1. Ctrl+Alt+C: 現在行から次の空行までコピー

現在のカーソル位置から次の空行(改行のみの行)までを一括でコピーします。ログファイルやMarkdownのセクション単位でのコピーに便利です。

2. Ctrl+Alt+S: 現在行から次の空行まで選択

コピーせずに選択だけを行います。選択後に別の操作(削除、移動など)を行いたい場合に使用します。

3. Ctrl+Shift+Alt+C: ダイアログで指定した行までコピー

これが最も強力な機能です。 ダイアログが開き、以下の指定方法で終了位置を決定できます:

  • 文字列を入力: その文字列を含む最初の行まで
  • EOF: ファイル末尾まで
  • デフォルト値: (三点リーダー)

4. Ctrl+Shift+Alt+S: ダイアログで指定した行まで選択

上記3と同様ですが、コピーは行わず選択のみです。

具体的な使用場面とメリット

場面1:ログファイルの特定区間を抽出

大量のログファイルから、STARTからENDまでのセクションだけを抽出したい場合:

2026-02-08 10:00:00 INFO: Application started
2026-02-08 10:00:01 DEBUG: Loading configuration
START
2026-02-08 10:00:05 ERROR: Connection timeout
2026-02-08 10:00:06 WARN: Retrying connection
... (数百行のログ)
END
2026-02-08 11:00:00 INFO: Process completed

操作手順:

  1. STARTの行にカーソルを置く
  2. Ctrl+Shift+Alt+Cを押す
  3. ダイアログにENDと入力してEnter
  4. → 一瞬でSTARTからENDまでがコピーされる

従来の方法だと、スクロールしながらShiftクリックで選択する必要があり、数百行離れていると非常に面倒でした。

場面2:Markdownドキュメントのセクション単位でコピー

Markdownファイルで特定のセクションだけをコピーしたい場合:

## はじめに

本記事では...

## インストール手順

以下の手順で...
... (長い説明)

## 設定方法

設定ファイルに...

見出しの行でCtrl+Alt+Cを押せば、次の空行(セクションの区切り)までが自動コピーされます。

場面3:コード内の関数やクラスを抽出

JavaScriptやPythonのコードで、特定の関数だけを別ファイルにコピーしたい場合:

export async function processData() {
    const editor = vscode.window.activeTextEditor;
    // ... 処理内容 (50行)
}

関数の先頭行でCtrl+Alt+Cを押せば、次の空行(関数の終わり)まで一括コピーできます。

メリットまとめ

  • スクロール不要: 目的の文字列を入力するだけ
  • 正確: クリック位置のミスがない
  • 高速: キーボードだけで完結
  • 汎用性: 任意の文字列を指定可能

実装コード

実際のTypeScriptコードを紹介します。copyToEmptyLine.tsファイルに以下の内容を実装しました。

import * as vscode from 'vscode';

/**
 * 現在行から次の空行までを選択してコピー
 */
export async function copyToEmptyLine() {
    const editor = vscode.window.activeTextEditor;
    if (!editor) {
        vscode.window.showWarningMessage('アクティブなエディタがありません');
        return;
    }

    const document = editor.document;
    const currentLine = editor.selection.active.line;
    
    // 次の空行を検索
    let endLine = findNextEmptyLine(document, currentLine);
    
    // 範囲選択
    const startPos = new vscode.Position(currentLine, 0);
    const endPos = new vscode.Position(endLine, document.lineAt(endLine).text.length);
    const range = new vscode.Range(startPos, endPos);
    
    // 選択
    editor.selection = new vscode.Selection(range.start, range.end);
    
    // コピー
    await vscode.commands.executeCommand('editor.action.clipboardCopyAction');
    
    // 次の行に移動
    if (endLine + 1 < document.lineCount) {
        const nextLinePos = new vscode.Position(endLine + 1, 0);
        editor.selection = new vscode.Selection(nextLinePos, nextLinePos);
    }
}

/**
 * 現在行から次の空行までを選択(コピーなし)
 */
export async function selectToEmptyLine() {
    const editor = vscode.window.activeTextEditor;
    if (!editor) {
        vscode.window.showWarningMessage('アクティブなエディタがありません');
        return;
    }

    const document = editor.document;
    const currentLine = editor.selection.active.line;
    
    // 次の空行を検索
    let endLine = findNextEmptyLine(document, currentLine);
    
    // 範囲選択
    const startPos = new vscode.Position(currentLine, 0);
    const endPos = new vscode.Position(endLine, document.lineAt(endLine).text.length);
    const range = new vscode.Range(startPos, endPos);
    
    // 選択のみ
    editor.selection = new vscode.Selection(range.start, range.end);
}

/**
 * 現在行から指定行までをコピー
 */
export async function copyToSpecifiedLine() {
    const editor = vscode.window.activeTextEditor;
    if (!editor) {
        vscode.window.showWarningMessage('アクティブなエディタがありません');
        return;
    }

    const document = editor.document;
    const currentLine = editor.selection.active.line;
    
    // 対象行をダイアログで入力
    const endLine = await promptForEndLine(document, currentLine);
    if (endLine === null) {
        return; // キャンセル
    }
    
    // 範囲選択
    const startPos = new vscode.Position(currentLine, 0);
    const endPos = new vscode.Position(endLine, document.lineAt(endLine).text.length);
    const range = new vscode.Range(startPos, endPos);
    
    // 選択
    editor.selection = new vscode.Selection(range.start, range.end);
    
    // コピー
    await vscode.commands.executeCommand('editor.action.clipboardCopyAction');
    
    // 次の行に移動
    if (endLine + 1 < document.lineCount) {
        const nextLinePos = new vscode.Position(endLine + 1, 0);
        editor.selection = new vscode.Selection(nextLinePos, nextLinePos);
    }
}

/**
 * 現在行から指定行までを選択(コピーなし)
 */
export async function selectToSpecifiedLine() {
    const editor = vscode.window.activeTextEditor;
    if (!editor) {
        vscode.window.showWarningMessage('アクティブなエディタがありません');
        return;
    }

    const document = editor.document;
    const currentLine = editor.selection.active.line;
    
    // 対象行をダイアログで入力
    const endLine = await promptForEndLine(document, currentLine);
    if (endLine === null) {
        return; // キャンセル
    }
    
    // 範囲選択
    const startPos = new vscode.Position(currentLine, 0);
    const endPos = new vscode.Position(endLine, document.lineAt(endLine).text.length);
    const range = new vscode.Range(startPos, endPos);
    
    // 選択のみ
    editor.selection = new vscode.Selection(range.start, range.end);
}

/**
 * 次の空行を検索
 * @param document ドキュメント
 * @param startLine 開始行
 * @returns 空行の行番号(見つからない場合は最終行)
 */
function findNextEmptyLine(document: vscode.TextDocument, startLine: number): number {
    for (let i = startLine + 1; i < document.lineCount; i++) {
        const line = document.lineAt(i);
        
        // 完全な空行(改行のみ)をチェック
        if (line.isEmptyOrWhitespace) {
            return i;
        }
    }
    
    // 空行が見つからない場合は最終行
    return document.lineCount - 1;
}

/**
 * 終了行の入力プロンプト
 * @param document ドキュメント
 * @param startLine 開始行(0-based)
 * @returns 終了行番号(0-based)、またはnull(キャンセル時)
 */
async function promptForEndLine(document: vscode.TextDocument, startLine: number): Promise {
    const lastLine = document.lineCount - 1;
    
    const input = await vscode.window.showInputBox({
        prompt: '終了位置を指定してください',
        placeHolder: '「…」で次の「…」まで / 「EOF」でファイル末尾まで / キャンセルで中止',
        value: '…'
    });
    
    if (!input) {
        return null; // キャンセル
    }
    
    // EOFの場合
    if (input.toUpperCase() === 'EOF') {
        return lastLine;
    }
    
    // 文字列検索
    for (let i = startLine + 1; i <= lastLine; i++) {
        const lineText = document.lineAt(i).text;
        if (lineText.includes(input)) {
            return i;
        }
    }
    
    // 見つからない場合は最終行
    vscode.window.showInformationMessage(`「${input}」が見つからないため、ファイル末尾まで選択します`);
    return lastLine;
}

コードのポイント解説

1. VSCode APIの活用

const editor = vscode.window.activeTextEditor;
const document = editor.document;

VSCodeのエディタとドキュメントにアクセスすることで、現在開いているファイルの内容や選択範囲を操作できます。

2. 空行検出のロジック

if (line.isEmptyOrWhitespace) {
    return i;
}

isEmptyOrWhitespaceプロパティを使うことで、改行のみまたは空白のみの行を正確に判定できます。

3. ダイアログでの入力受付

const input = await vscode.window.showInputBox({
    prompt: '終了位置を指定してください',
    placeHolder: '「…」で次の「…」まで / 「EOF」でファイル末尾まで / キャンセルで中止',
    value: '…'
});

showInputBoxを使うことで、ユーザーに文字列を入力してもらえます。デフォルト値としてを設定しています。

4. 文字列検索

for (let i = startLine + 1; i <= lastLine; i++) {
    const lineText = document.lineAt(i).text;
    if (lineText.includes(input)) {
        return i;
    }
}

シンプルなincludesメソッドで、指定文字列を含む行を検索します。正規表現ではなくリテラル検索なので、特殊文字のエスケープは不要です。

セットアップ方法

この機能を使うには、VSCode拡張機能として開発する必要があります。詳細な手順は以下の関連記事を参照してください:

  • VSCodeのTypeScriptマクロ開発ガイド
VSCode TypeScriptマクロ開発環境の完全ガイド【セットアップから運用まで】
はじめにVSCodeでカスタムマクロを作成したいけど、どうやって開発環境を構築すればいいか分からない。そんな悩みを持つ方に向けて、TypeScriptでVSCode拡張機能を開発する環境の構築から、実際にマクロを作成して使えるようにするまで...
  • サクラエディタからVSCodeへのマクロ移行方法
サクラエディタからVSCodeへマクロ移行!快適開発環境の構築記録
はじめに長年愛用してきたサクラエディタのマクロ機能。便利なJavaScript/VBSマクロを多数作成して日常業務で活用してきましたが、最近のAWS開発やブログ執筆でVSCodeを使う機会が増えてきました。「VSCodeでもサクラエディタの...

簡易手順

  1. VSCode拡張機能プロジェクトを作成
  2. 上記TypeScriptコードをsrc/macros/copyToEmptyLine.tsに配置
  3. package.jsonにコマンドとキーバインディングを登録
{
  "contributes": {
    "commands": [
      {
        "command": "myMacros.copyToEmptyLine",
        "title": "Copy to Empty Line"
      },
      {
        "command": "myMacros.selectToEmptyLine",
        "title": "Select to Empty Line"
      },
      {
        "command": "myMacros.copyToSpecifiedLine",
        "title": "Copy to Specified Line"
      },
      {
        "command": "myMacros.selectToSpecifiedLine",
        "title": "Select to Specified Line"
      }
    ],
    "keybindings": [
      {
        "command": "myMacros.copyToEmptyLine",
        "key": "ctrl+alt+c",
        "when": "editorTextFocus"
      },
      {
        "command": "myMacros.selectToEmptyLine",
        "key": "ctrl+alt+s",
        "when": "editorTextFocus"
      },
      {
        "command": "myMacros.copyToSpecifiedLine",
        "key": "ctrl+shift+alt+c",
        "when": "editorTextFocus"
      },
      {
        "command": "myMacros.selectToSpecifiedLine",
        "key": "ctrl+shift+alt+s",
        "when": "editorTextFocus"
      }
    ]
  }
}
  1. ビルドしてVSIXファイルをインストール
npm run compile
vsce package
code --install-extension my-macros-0.0.1.vsix

サクラエディタからの移行という視点

かつてサクラエディタで実装していた機能(任意文字を含む行まで一括範囲選択コピー)を、VSCodeでも実現できました。

サクラエディタのJavaScriptマクロでは以下のようなコードでした:

// サクラエディタ版(抜粋)
var userInput = Editor.InputBox("終了位置を指定してください", "…");
for (var y = start; y <= last; y++) {
    var s = getLineBySelect(y);
    if (s.indexOf(marker) >= 0) return y;
}

VSCodeでは以下のように書き直せます:

// VSCode版
const input = await vscode.window.showInputBox({
    value: '…'
});
for (let i = startLine + 1; i <= lastLine; i++) {
    const lineText = document.lineAt(i).text;
    if (lineText.includes(input)) {
        return i;
    }
}

TypeScriptの方が型安全で、VSCode APIも充実しているため、より堅牢な実装が可能です。

まとめ:キーボードだけで完結するテキスト編集

今回実装した4つの機能により、以下が実現できました:

  • Ctrl+Alt+C / S: 空行まで自動選択・コピー
  • Ctrl+Shift+Alt+C / S: 任意の文字列まで選択・コピー

これらの機能は、以下のような作業で特に威力を発揮します:

  1. ログファイル解析: 特定のセクションだけを抽出
  2. ドキュメント編集: Markdownのセクション単位でコピー
  3. コードレビュー: 関数やクラス単位で抽出
  4. データ整形: 区切り文字で範囲指定して一括処理

「需要はないかもしれない」と思っていましたが、一度使うと手放せない便利機能です。特に大量のテキストを扱う作業では、作業効率が劇的に向上します。

同じような悩みを持つ方は、ぜひ試してみてください。TypeScriptでVSCodeマクロを書く楽しさも味わえるはずです。

参考リンク

  • サクラエディタ版の元記事: [任意文字を含む行まで一括範囲選択コピー]
【サクラエディタ】任意文字を含む行まで一括範囲選択コピー【操作画像あり】
サクラエディタで複数行の範囲選択テキストを一括で超簡単にコピーするマクロをJavaScriptで作ってみました。わざわざマウスを使ってある程度まとまった行を選択する操作、めんどくさくないですか?頻繁にコピー作業する人にはきっと役に立つと思い...

関連記事

  • サクラエディタからVSCodeへのマクロ移行完全ガイド
サクラエディタからVSCodeへマクロ移行!快適開発環境の構築記録
はじめに長年愛用してきたサクラエディタのマクロ機能。便利なJavaScript/VBSマクロを多数作成して日常業務で活用してきましたが、最近のAWS開発やブログ執筆でVSCodeを使う機会が増えてきました。「VSCodeでもサクラエディタの...
  • VSCodeでTypeScriptマクロ開発を始めるための完全ガイド
VSCode TypeScriptマクロ開発環境の完全ガイド【セットアップから運用まで】
はじめにVSCodeでカスタムマクロを作成したいけど、どうやって開発環境を構築すればいいか分からない。そんな悩みを持つ方に向けて、TypeScriptでVSCode拡張機能を開発する環境の構築から、実際にマクロを作成して使えるようにするまで...

コメント