左右2画面でファイルを連続表示＋同期スクロールするVSCode拡張機能を試しに作った

# はじめに
VSCodeを用いて開発を行うとき、1行を長くせずに適切な位置で改行するフォーマッターを用いることがあると思います。
その時に私は思いました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3943301/1fa8517b-94f7-175d-1f8f-e8730b205c13.png)

(ソースは今回作成した拡張機能のものです）

この右の余白もコード表示に使えたら便利なんじゃないかなあと

# 作るもの
左右にエディタを分割し、左側で現在表示している範囲の続きを右側に表示することで、画面の切り替えの手間なく多くの内容を同時に参照できるようにします。
本記事で示すコードは、左側エディタを「マスター」とし、左側スクロール停止後一定時間(200ms)経過した段階で右側エディタを自動的に同期表示する仕組みを実装しています。

# 作ってみた
VSCodeの拡張機能作成の勉強がてら作ってみました。

src/extension.ts
```typescript
import * as vscode from 'vscode';

const DEBOUNCE_MS = 200;

function clampLine(line: number, maxLine: number): number {
    return Math.max(0, Math.min(line, maxLine));
}

function getTopVisibleLine(editor: vscode.TextEditor): number | undefined {
    const vr = editor.visibleRanges[0];
    return vr ? vr.start.line : undefined;
}

function getVisibleLineCount(editor: vscode.TextEditor): number {
    const vr = editor.visibleRanges[0];
    if (!vr) return 0;
    return vr.end.line - vr.start.line + 1;
}

function syncFromLeftToRight(
    leftEditor: vscode.TextEditor,
    rightEditor: vscode.TextEditor
) {
    const topLeftLine = getTopVisibleLine(leftEditor);
    if (topLeftLine === undefined) return;

    const visibleCount = getVisibleLineCount(leftEditor);
    const docRight = rightEditor.document;
    let expectedRightTop = topLeftLine + visibleCount;
    expectedRightTop = clampLine(expectedRightTop, docRight.lineCount - 1);

    rightEditor.revealRange(
        new vscode.Range(expectedRightTop, 0, expectedRightTop, 0),
        vscode.TextEditorRevealType.InCenterIfOutsideViewport
    );
}

export function activate(context: vscode.ExtensionContext) {
    const disposable = vscode.commands.registerCommand(
        'extension.openSyncedSplitView',
        async () => {
            const activeEditor = vscode.window.activeTextEditor;
            if (!activeEditor) {
                vscode.window.showErrorMessage("No active editor");
                return;
            }

            await vscode.commands.executeCommand(
                'workbench.action.splitEditorRight'
            );
            const editors = vscode.window.visibleTextEditors;
            const leftEditor = editors[0];
            const rightEditor = editors[1];

            if (!leftEditor || !rightEditor) {
                vscode.window.showErrorMessage(
                    "Could not open two editors."
                );
                return;
            }

            let debounceTimer: ReturnType<typeof setTimeout> | undefined;

            const handleVisibleRangeChange = (
                event: vscode.TextEditorVisibleRangesChangeEvent
            ) => {
                if (event.textEditor !== leftEditor) {
                    return;
                }

                if (debounceTimer) {
                    clearTimeout(debounceTimer);
                }

                debounceTimer = setTimeout(() => {
                    debounceTimer = undefined;
                    syncFromLeftToRight(leftEditor, rightEditor);
                }, DEBOUNCE_MS);
            };

            context.subscriptions.push(
                vscode.window.onDidChangeTextEditorVisibleRanges(
                    handleVisibleRangeChange
                )
            );
        }
    );

    context.subscriptions.push(disposable);
}

export function deactivate() {}

```

# 使ってみた
こんな感じで左エディタをスクロールさせると、右側も同期してスクロールされます。
![Animation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3943301/7572caf5-e2a6-f768-c634-d925e6bc2080.gif)

# 実装のポイント
## 左右分割の実行
コマンド `openSyncedSplitView` 実行時に `workbench.action.splitEditorRight` を呼び出し、アクティブなエディタを左右に分割します。

## 左エディタをトリガーとした同期
`onDidChangeTextEditorVisibleRanges` イベントハンドラを登録し、左エディタでスクロールイベントが発生したらデバウンス（200ms待機）後に右側を同期します。

## 可視行数を利用した続き位置の計算
左エディタで表示中の行数を取得し、その分だけ下の行から右側を表示することで「左の続き」を右側で自然に閲覧できるようにしています。

# おわりに
拡張機能を自分で簡易的にでも作ることでVSCodeを更に使いやすくすることができると感じました。ほかにも使いやすいアイデアを見つけたらいろいろ試しに作ってみたいと思います。