🧷 VS Code拡張を常駐HTTPサーバとして使い、未保存(isDirty)状態を外部から取得する
🧭 はじめに
VS Codeで編集中のファイルを、外部スクリプト(PowerShell / Python など)から同期・上書きする場合、VS Code側で「未保存(dirty)」の状態にあるファイルを誤って上書きしてしまう事故が最も危険である。
本記事では、
- VS Codeは常に起動している前提
- VS Code拡張機能を常駐HTTPサーバとして動作させる
- 外部からファイルパスを渡して
isDirtyを問い合わせる
という構成で、「APIっぽく」未保存状態を取得する方法を解説する。
VS Code拡張APIには TextDocument.isDirty という公式プロパティがあり、これを使うのが未保存判定の唯一の正攻法。
🧩 なぜHTTPサーバ方式なのか
VS Code拡張には「外部プロセスに値を返すCLI API」は存在しない。 そのため、
code --commandのような方法では 戻り値(true/false)を受け取れない- URIハンドラも 同期的なレスポンスを返せない
という制約がある。
VS Codeのコマンド実行は「VS Code内部で完結」する設計で、外部CLIから関数の戻り値を直接受け取る用途は想定されていない。
そこで、
- VS Code拡張を ローカルHTTPサーバとして常駐
- PowerShell / Python から HTTPリクエストで問い合わせ
- JSONで結果を返す
という構成を取ることで、**実質的に「isDirty API」**を実現する。
🏗️ 全体構成
🎯 目的
外部スクリプト
↓ HTTP
VS Code 拡張(常駐)
↓
TextDocument.isDirty を判定
↓
JSON { isDirty: true/false } を返す
🧱 前提条件
- VS Code が起動している
- 対象ファイルは VS Code で一度は開かれている
- ローカル通信(127.0.0.1)を許可できる環境
同期処理の直前に1回HTTPで問い合わせるだけなので、既存のPowerShell/Pythonスクリプトに組み込みやすい。
🔌 HTTP API仕様(例)
📍 エンドポイント
GET http://127.0.0.1:17891/isDirty
📥 クエリパラメータ
| 名前 | 内容 |
|---|---|
| path | 判定したいファイルの絶対パス |
📤 レスポンス(JSON)
{
"path": "/path/to/file.txt",
"isDirty": true,
"found": true
}
found=falseの場合は「VS Codeで開かれていない」ことを意味する
未保存判定は「VS Codeで開かれているファイル」にしか適用できない。未オープンファイルは dirty 判定不能。
🧠 VS Code拡張側:実装の要点
🔑 1. HTTPサーバを起動する
- Node.js の
httpモジュールを使用 127.0.0.1にバインド(外部公開しない)- 拡張
activate()時に起動 deactivate()時に必ず close
`0.0.0.0` にバインドすると、LAN越しにファイル状態を覗かれるリスクがある。必ず 127.0.0.1 に限定する。
🔍 2. path から TextDocument を探す
vscode.workspace.textDocumentsを走査doc.uri.fsPath === pathで一致判定doc.isDirtyを返す
URI比較ではなく fsPath 比較にすることで、file:// の表記揺れを避けられる。
⏱️ 3. isDirty のタイミング問題
VS Codeでは、次のようなケースがある。
onDidChangeTextDocument直後- まだ
isDirty === falseの瞬間がある
そのため、
- HTTPリクエスト受信時に 即座に現在値を読む
- 状態更新イベントに依存しない
設計が安定する。
イベント駆動でキャッシュした isDirty を返すと、タイミングずれで誤判定しやすい。
🧪 VS Code拡張:最小実装イメージ(概念)
拡張がやっていること(要約)
- 起動時にHTTPサーバ開始
/isDirty?path=...を受信textDocumentsから該当ファイルを検索isDirtyを JSON で返却
(※ BookStack ではコード全文は省略。実装は数十行で済む)
🧷 PowerShell からの利用例
$path = "C:\repo\src\foo.ts"
$url = "http://127.0.0.1:17891/isDirty?path=$([uri]::EscapeDataString($path))"
$res = Invoke-RestMethod $url
if ($res.found -and $res.isDirty) {
Write-Error "VS Code側で未保存のため同期中断: $path"
exit 2
}
HTTPレスポンスが即時返るため、同期前チェックとして自然に組み込める。
🐍 Python からの利用例
import requests
import sys
from urllib.parse import quote
path = r"C:\repo\src\foo.ts"
url = f"http://127.0.0.1:17891/isDirty?path={quote(path)}"
res = requests.get(url, timeout=1).json()
if res.get("found") and res.get("isDirty"):
print(f"VS Code側で未保存のため同期中断: {path}", file=sys.stderr)
sys.exit(2)
🧯 想定されるエッジケース
🟡 VS Codeが起動していない
- HTTP接続エラーになる
- → 安全側に倒して同期中断がおすすめ
「VS Codeが起動していない=未保存が無い」と解釈するのは危険。
🟡 ファイルが開かれていない
-
found=falseが返る -
運用で判断:
- 「開かれていない=安全」とみなす
- あるいは「判定不能なので中断」
🟡 複数ワークスペース
textDocumentsは全ワークスペース横断- fsPath 一致で問題なし
🧠 設計まとめ
- 未保存(dirty)はOS/Gitからは絶対に取れない
- VS Code拡張 + HTTP が最も自然なAPI形態
- 引数でファイルパスを渡し、即
isDirtyを返せる - 同期処理の安全装置として非常に強力
「VS Codeを編集中に、外部ツールが勝手に上書きする」という事故を、確実に防止できる構成。