VS Code の Remote WSL 拡張があれば WSL 内のファイル・ディレクトリを Windows 側の VS Code の GUI で開くことができて大変便利です。また、VS Code インストールディレクトリの下記にあるシェルスクリプトを WSL 側で実行するとコマンドラインから VS Code でファイルを開くこともできます。
C:\Users\oreore\AppData\Local\Programs\Microsoft VS Code\bin\code
エイリアスなどで code だけで実行できるようにしておくと便利でしょう。
# ディレクトリを開く code . # ファイルを開く code file.md # ファイルを開いて、閉じられるのを待つ code -w file.md
code -w を core.editor に設定しておけば git のコミットメッセージなども VS Code で書けるようになります。
git config --global core.editor 'code -w'
ただ、1点問題があるとすれば・・コマンドを叩いてから実際に VS Code でファイルが開かれるまで、微妙に遅いんです。一呼吸待たされます。たまになら良いのですが Git でコミットするたびのことなので、この待ち時間は少し気になります。何とかしたいです。
なぜこんなに遅いのか
下記で実行して出てくるログを眺めるに、
VSCODE_WSL_DEBUG_INFO=true code README.md
遅いのは下記の部分です。
'/c/Users/oreore/AppData/Local/Programs/Microsoft VS Code/Code.exe' 'C:/Users/oreore/AppData/Local/Programs/Microsoft VS Code/XXX/resources/app/out/cli.js' --locate-extension ms-vscode-remote.remote-wsl
これは Windows 側の Code.exe が起動されています。Code.exe の実体はおそらく Electron の巨大バイナリのため、その起動が体感上の遅さにつながっているように見えます。
ただ、やっていることはファイル /tmp/remote-wsl-loc.txt に Windows 側の Remote-WSL 拡張のパスを書き出しているだけのようです。例えば次のような内容です。
cat /tmp/remote-wsl-loc.txt # c:\Users\oreore\.vscode\extensions\ms-vscode-remote.remote-wsl-0.104.3
Remote-WSL 拡張のパスを別の方法で得る
Remote-WSL 拡張のパスを得るためだけに巨大な exe が実行されているのが遅い原因だと分かったので、そこを適当な方法で置き換えてみます。
例えば、ディレクトリは固定で良さそうなので sort --version-sort でソートして最新版のパスを得てみたり、
ls -d /c/Users/oreore/.vscode/extensions/ms-vscode-remote.remote-wsl-* | sort --version-sort -r | head -1 # /c/Users/oreore/.vscode/extensions/ms-vscode-remote.remote-wsl-0.104.3
もしくは WSL 側で実行中の vscode-server のプロセスの環境変数から得たり。
pid="$(pgrep -n -f '.vscode/extensions/ms-vscode-remote.remote-wsl-.*/scripts/wslServer.sh')" cat /proc/$pid/environ | grep -z '^VSCODE_WSL_EXT_LOCATION=' | cut -z -d= -f2- | tr -d '\0' # /c/Users/oreore/.vscode/extensions/ms-vscode-remote.remote-wsl-0.104.3
Code.exe を実行して Remote-WSL 拡張のパスを得ている部分を、これらの方法に置き換えれば若干ですが早くなります。
統合ターミナルだともっと早い
ただ、それでもやっぱりまだ遅いです。strace してみたところ結局もう一度 Code.exe が実行されていました、どうやら Windows 側で Code.exe が新たに開始され、そして Windows 側で IPC 的な方法で起動済の Code.exe とやり取りしているようです。
一方、VS Code の統合ターミナル内で code を叩いてみると爆速です。統合ターミナルでは環境変数 PATH が追加され、code コマンドは次のような WSL 側の vscode-server の code コマンドが実行されるようになっていました。
which code
# ~/.vscode-server/bin/0958016b2af9f09bb4257e0df4a95e2f90590f9f/bin/remote-cli/code
ただ、これを VS Code の外のシェルで実行してもダメでした。
~/.vscode-server/bin/0958016b2af9f09bb4257e0df4a95e2f90590f9f/bin/remote-cli/code
# Command is only available in WSL or inside a Visual Studio Code terminal.
どうやら統合ターミナル内では VSCODE_IPC_HOOK_CLI という環境変数に vscode-server とやり取りするための Unix ドメインソケットのパスが入っているようで、これが必要なようです。
同じものを VS Code の外のシェルで実行するときにも設定しておけば、Code.exe が実行されることもなく爆速でファイルが開けそうです。
VSCODE_IPC_HOOK_CLI 自体は紆余曲折の末、次の方法で取るようにしてみました。
cmd="$(ls ~/.vscode-server/bin/*/node -t | head -1)" pattern="$(pidof "$cmd" | sed -e 's/ /,|,pid=/g' -e 's/^/,pid=/' -e 's/$/,/')" VSCODE_IPC_HOOK_CLI="$(ss -lxp | grep -F "${XDG_RUNTIME_DIR%/}/vscode-ipc" | grep -E "($pattern)" | head -1 | awk '{print $5}')" VSCODE_BIN="$(ls ~/.vscode-server/bin/*/bin/remote-cli/code -t | head -1)" export VSCODE_IPC_HOOK_CLI exec "$VSCODE_BIN" "$@"
これで Code.exe が実行されなくなるので、ファイルが爆速で開くようになります。
VS Code がフォアグラウンドにならない
まだ問題がありました。この方法だと VS Code のウィンドウがフォアグラウンドにならないのです。例えばターミナルで git commit したときに、ターミナルの裏にいる VS Code で COMMIT_EDITMSG が開かれるだけなので、自分でウィンドウを切り替えないとコミットメッセージが書けません。
これはだいぶ不便です。何とかしたいです。要するに前述の方法でファイルを開いた後に VS Code のウィンドウをアクティブ化できればいいのです。それぐらいなら PowerShell で簡単にできます。
cmd="$(ls ~/.vscode-server/bin/*/node -t | head -1)" pattern="$(pidof "$cmd" | sed -e 's/ /,|,pid=/g' -e 's/^/,pid=/' -e 's/$/,/')" VSCODE_IPC_HOOK_CLI="$(ss -lxp | grep -F "${XDG_RUNTIME_DIR%/}/vscode-ipc" | grep -E "($pattern)" | head -1 | awk '{print $5}')" VSCODE_BIN="$(ls ~/.vscode-server/bin/*/bin/remote-cli/code -t | head -1)" export VSCODE_IPC_HOOK_CLI powershell.exe -NoProfile -command '$null = (New-Object -ComObject Wscript.Shell).AppActivate("Visual Studio Code")' exec "$VSCODE_BIN" "$@"
完璧です。ファイルを爆速で開きつつ、VS Code のウィンドウもフォアグラウンドに切り替わります。
tmux との相性が悪い
大抵の人にはここまでで十分かもしれません。とはいえ、WSL で tmux を使っている人には都合が悪い問題がまだありました。 前述の方法、普通に使っている分には問題ないのですが、次のような条件で VS Code のフォアグラウンド化が失敗することがあります。
- ターミナルから tmux を開始する
- ターミナル自身を終了する
- ターミナルを再実行して tmux のセッションにアタッチする
この後、同じ方法で VS Code をフォアグラウンド化しようとしても、タスクバーで明滅はするもののウィンドウ自体は切り替えられません。
ここからは完全に観測結果に基づく推測になるのですが・・A の時点で開始した tmux が、この時点ではターミナルと同じフォアグラウンドプロセスだったのが、B の時点でバックグラウンドプロセスに落ちて、C ではバックグラウンドプロセスである tmux にアタッチした状態になっているので、その時点ではシェル自体がバックグラウンドプロセス扱いになり、他のウィンドウのフォアグラウンド化ができなくなってしまうためだと思います(この辺り Windows と WSL が絡み合って複雑なので詳しいことは全くわからない)。
https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-setforegroundwindow
- 呼び出し元のプロセスがフォアグラウンド プロセスです。
- 呼び出し元のプロセスは、フォアグラウンド プロセスによって開始されました。
これは PowerShell の Start-Process を挟むことで回避できます。
cmd="$(ls ~/.vscode-server/bin/*/node -t | head -1)" pattern="$(pidof "$cmd" | sed -e 's/ /,|,pid=/g' -e 's/^/,pid=/' -e 's/$/,/')" VSCODE_IPC_HOOK_CLI="$(ss -lxp | grep -F "${XDG_RUNTIME_DIR%/}/vscode-ipc" | grep -E "($pattern)" | head -1 | awk '{print $5}')" VSCODE_BIN="$(ls ~/.vscode-server/bin/*/bin/remote-cli/code -t | head -1)" export VSCODE_IPC_HOOK_CLI powershell.exe -NoProfile -Command "Start-Process powershell.exe -ArgumentList '-NoProfile -Command [void](New-Object -ComObject Wscript.Shell).AppActivate(''Visual Studio Code'')' -WindowStyle Hidden" exec "$VSCODE_BIN" "$@"
Start-Process は内部的に ShellExecuteEx が呼ばれており、これは Windows Shell を通じてプロセスが起動されるため、フォアグラウンドプロセスとして実行させることができるのだろうと思います(たぶん)。
さいごに
ここまでくれば EDITOR 環境変数も設定しちゃっていいでしょう。vim や nano を置き換える感覚で、VS Code が使えるようになります。
export EDITOR='code -w'
実際のところ素の方法だけでは Git の core.editor を code -w にするのは辛いものがあると思います。
なお、この記事で説明した方法は VS Code の(というか Remote-WSL 拡張の)実装に強く依存しているため、今後のアップデートで動かなくなる可能性はあると思います(今のところ3年ぐらいは問題なく使えている)。