VSCode + Python で複数行の Run Selection がしたい

概要

macOS (10.15.4) + VSCode (1.44.2) + Python extension (2020.4.76186) という環境下で以下のコードを実行することを考える。

for i in range(2):
    print('hoge')
    print('fuga')

アクティブなターミナルで iPython を起動&エディタで上記のコードを選択した状態で、上記を python.execSelectionInTerminal を用いて実行すると

In [1]: for i in range(2): 
   ...:         print('hoge') 
   ...:             print('fuga')                                                   
  File "<ipython-input-2-fea876064538>", line 3
    print('fuga')
    ^
IndentationError: unexpected indent

と、3 行目に謎のインデントが挿入され IndentationError が発生してしまう。アクティブなターミナルで (iPython ではなく) Python を起動している場合、あるいは同様の作業を Windows で行っている場合だと生じない不具合ということもあり、どうしてこんな挙動になるのかさっぱりわからない。

これは以前から指摘されていた問題らしく こちら などで熱い議論が交わされている。当該 issue は最終的に何らかが merge されて close されているものの、少なくとも手元の環境では直っていないので方法を考えることにした。

解決策

上記のように python.execSelectionInTerminal で実行するのではなく、コードをエディタからコピー → ターミナルにフォーカス → ターミナルにペースト という手順を踏めばどうやらインデントの問題は発生しないようである。いちいちこれを実行するのはあまりにも面倒なので、これを Shift + Return で一発でできるようにしたい。

今回は拡張機能 geddski.macros をインストールすることによりこの動作を実現する。これは複数の Command をまとめて一つの Command を定義できるようにするものであり、今回の場合は settings.json に以下を追記すれば上記の動作を copySelectionAndPasteToActiveTerminal という Command にまとめることができる。

[
    "macros": {
        "copySelectionAndPasteToActiveTerminal": [
            "editor.action.clipboardCopyAction",
            "workbench.action.terminal.focus",
            "workbench.action.terminal.paste",
        ]
    }
]

あとは Keyboard Shortcuts の設定で copySelectionAndPasteToActiveTerminalShift + Return を割り振ればいいだけである。クリップボードが汚れてしまう形になるので微妙だが、とりあえずこれでちゃんと動作するようにはなる。

In [3]: for i in range(2): 
   ...:     print('hoge') 
   ...:     print('fuga') 
   ...:                                                                             
hoge
fuga
hoge
fuga

やったね!