Code Runnerを使いこなす

# Code Runnerを使いこなす

## Abstract

VScodeの拡張機能で、コードのお手軽実行といえば[Code Runner](https://github.com/formulahendry/vscode-code-runner)ではないでしょうか。
本記事ではCode Runnerで様々な言語を即座に実行できる環境を作り、その応用を考えます。

## Code Runner とは

コードを手軽に実行するための拡張機能です。対応している言語は[公式](https://github.com/formulahendry/vscode-code-runner)によれば

> C, C++, Java, JavaScript, PHP, Python, Perl, Perl 6, Ruby, Go, Lua, Groovy, PowerShell, BAT/CMD, BASH/SH, F# Script, F# (.NET Core), C# Script, C# (.NET Core), VBScript, TypeScript, CoffeeScript, Scala, Swift, Julia, Crystal, OCaml Script, R, AppleScript, Elixir, Visual Basic .NET, Clojure, Haxe, Objective-C, Rust, Racket, Scheme, AutoHotkey, AutoIt, Kotlin, Dart, Free Pascal, Haskell, Nim, D, Lisp, Kit, V, SCSS, Sass, CUDA, Less, Fortran

であり、非常に多くの言語に対応しています。
もう少し詳しくに言えば、 **開いている言語に応じてコマンドを(既定のターミナルなどで)実行する拡張機能** です。
また、選択範囲だけを対象にして実行する機能も持ちます。なにそれ凄い。

## 環境

- Windows 10
- ターミナルはコマンドプロンプト(その他のターミナルでも可)
- VScode 1.51.1
- Python 3.8.6 (サンプルで使用しています)

なお、この記事ではパスは全て通している前提です。その為各言語の環境構築などには触れません。
また例には基本的にCとPythonを用います。全く使用できない言語は恐らくないです(後述)。

## インストール

普通に拡張機能なので、ctrl+shift+Xで拡張機能マーケットを開いて検索窓に「code runner」や「formulahendry.code-runner」と入力して入手できます。

## C言語の例

さっそく例を見てみます。適当なフォルダを開いて、hello.cを作りました。

```C
#include<stdio.h>

int main(){
    printf("Hello World !");
    return 0;
}
```

これをエディタで開いた状態で右クリックからRun Codeを選択しましょう。(Ctrl+alt+NでもOK)
以下のような表示がでればOkです。

![code-runner-example.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483769/4d0df578-628f-78fc-c5db-f34e08153f60.jpeg)


## カスタマイズしよう

個人的には必須なのが以下のrunInTerminalです。
デフォルトでは、scanf関数など標準入力が必要な場面では使えません。どうやら通常は読み取り専用のもので実行されるようです。
![read-only.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483769/a05d1c8a-2623-58a6-9e09-9bcf7ddf161d.png)


というわけでsetting.jsonに以下を追記します。

```setting.json
{
     "code-runner.runInTerminal": true
}
```

これにより、既定のターミナルで動作させることができます。
なお既定のターミナルの設定は

```jsonc:setting.json
{
    "terminal.integrated.defaultProfile.windows": "なんちゃら",
    // linux, macなどはwindowsのところが違う？
    // なんちゃらは自分で設定したprofile名
}
```

で指定します。profileについては別記事で。→[VSCodeのターミナルの新しい設定まとめ](https://qiita.com/take_me/items/47f192a126d75a9264a9)
仮想環境に入って実行する場合は、profileに引数で渡して事前にactivateする必要があります。こちらは応用のセクションの方で。

### コマンドの変更

デフォルトのものだけでは、例えば出力先を変えたいときなどに対応できません。
そこでまずは"code-runner.executorMap"を設定します。
デフォルト設定は以下になっています。

```jsonc:setting.json
{
    "code-runner.executorMap": {
		"javascript": "node",
		"java": "cd $dir && javac $fileName && java $fileNameWithoutExt",
		"c": "cd $dir && gcc $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt",
		"cpp": "cd $dir && g++ $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt",
		"objective-c": "cd $dir && gcc -framework Cocoa $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt",
		"php": "php",
		"python": "python -u",
		"perl": "perl",
		"perl6": "perl6",
		"ruby": "ruby",
		"go": "go run",
		"lua": "lua",
		"groovy": "groovy",
		"powershell": "powershell -ExecutionPolicy ByPass -File",
		"bat": "cmd /c",
		"shellscript": "bash",
		"fsharp": "fsi",
		"csharp": "scriptcs",
		"vbscript": "cscript //Nologo",
		"typescript": "ts-node",
		"coffeescript": "coffee",
		"scala": "scala",
		"swift": "swift",
		"julia": "julia",
		"crystal": "crystal",
		"ocaml": "ocaml",
		"r": "Rscript",
		"applescript": "osascript",
		"clojure": "lein exec",
		"haxe": "haxe --cwd $dirWithoutTrailingSlash --run $fileNameWithoutExt",
		"rust": "cd $dir && rustc $fileName && $dir$fileNameWithoutExt",
		"racket": "racket",
		"scheme": "csi -script",
		"ahk": "autohotkey",
		"autoit": "autoit3",
		"dart": "dart",
		"pascal": "cd $dir && fpc $fileName && $dir$fileNameWithoutExt",
		"d": "cd $dir && dmd $fileName && $dir$fileNameWithoutExt",
		"haskell": "runhaskell",
		"nim": "nim compile --verbosity:0 --hints:off --run",
		"lisp": "sbcl --script",
		"kit": "kitc --run",
		"v": "v run",
		"sass": "sass --style expanded",
		"scss": "scss --style expanded",
		"less": "cd $dir && lessc $fileName $fileNameWithoutExt.css",
		"FortranFreeForm": "cd $dir && gfortran $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt",
		"fortran-modern": "cd $dir && gfortran $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt",
		"fortran_fixed-form": "cd $dir && gfortran $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt",
		"fortran": "cd $dir && gfortran $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt"
	},
}
```

多いですね！
勿論自分の使用する言語だけ取り出して変更すればよいです。その為にまずは特殊変数を使いこなしましょう！

#### 使用できる特殊変数

例として、VScodeでrootフォルダを開いていて、rootフォルダはC:\hoge\fuga\にあるとします。

```
C:\
└ hoge\
    └ fuga\
        └ root\
            └ src\
                └ main.c
```

例は、上記のディレクトリ構造の際に、main.cを開いてRun Codeした場合の値を示しています。
(つまりエディタでmain.cを開いてる状態で、ctrl+alt+N or 右クリックメニューからRun Codeした想定)

|特殊変数|展開される値(説明)|例|
|-----|----|---|
|\$workspaceRoot | VScodeが開いているフォルダの絶対パス| C:\hoge\fuga\root\ |
|\$dir | 実行対象のファイルが存在するディレクトリ|"root\src\\"|
|\$dirWithoutTrailingSlash | 実行対象のファイルが存在するディレクトリ(末尾にスラッシュなし)|"root\src"|
|\$fullFileName | 実行対象のファイルのフルパス|"C:\hoge\fuga\root\src\main.c"|
|\$fileName | 実行対象のファイルの名前(拡張子含む+ディレクトリ情報などは含まない)|main.c|
|\$fileNameWithoutExt | 実行対象のファイルの名前(拡張子含まない+ディレクトリ情報などは含まない)|main|
|\$driveLetter | 実行対象のファイルがあるドライブ(windows限定)|C:|
|\$pythonPath | Pythonのパス。拡張機能のPython: Select Interpreter で決めた値。|C:\Users\hoge\AppData\Local\Programs\Python\Python38\python.exe|

pythonだけ\$pythonPathが用意されているの、なんかいいですね(Python大好きマン)。

### 設定例

ここでは、実際の設定例を挙げていきます。

main.cをコンパイルしてbinフォルダにexeを置きたい時

```
root\
|- bin\
|	└ main.exe
|
└ src\
	└ main.c
```

ターミナルは開いたフォルダがルートパスですから、他のディレクトリに行かないようにすれば

```jsonc:setting.json
{
    "code-runner.executorMap": {
		"c": "gcc $fullFileName -o bin/$fileNameWithoutExt && bin/$fileNameWithoutExt.exe",
	},
}
```

上記の設定で実現できます。毎回rootディレクトリに移動するようにするならcd $workspaceRootを追加すればよいでしょう。

```setting.json
{
    "code-runner.executorMap": {
		"c": "cd $workspaceRoot && gcc $fullFileName -o bin/$fileNameWithoutExt && bin/$fileNameWithoutExt.exe",
	},
}
```

(例えば、src/foo/main.cのコンパイル先をbin/foo/main.exeにするみたいな、もっと複雑なことは多分Code Runnerでは無理、、、bat等を組み合わせれば可能なのかな？？)

また、Pythonへのパスが特殊変数に用意されているので

```jsonc:setting.json
{
    "code-runner.executorMap": {
		"python" : "$pythonPath -u $fullFileName",
	},
}
```

とすれば、Pythonの拡張機能で選択したバージョンで実行可能です。仮想環境とかでも、pythonPathをpythonの拡張機能で選択しておけば勝手に対応してくれるので便利ですね。

### 私の言語が対応していませんが？

案ずることなかれ。
Code Runnerは拡張子によって、また正規表現的なものでコマンドを指定できます！！

#### ファイルの拡張子から実行コマンドを指定する

例えば、[gnuplot](http://www.gnuplot.info/)で使用するスクリプトである.pltファイルは、対応していませんが、以下のようにしてコマンドを設定することで対応させることができます。

```jsonc:setting.json
{
	"code-runner.executorMapByFileExtension": {
		".plt" : "gnuplot $fullFileName"
	},
}
```

その他の言語も、拡張子でコマンドを指定できるため実質的に全ての言語に対応できそうですね！！！

#### 正規表現的なものから実行コマンドを指定する

code-runner.executorMapByGlobから[Glob](https://en.wikipedia.org/wiki/Glob_(programming))の書式(正規表現と同じ？よくわかりません)で指定できます。ファイル名がその正規表現に一致するときに実行する、という具合です。なお後ほど触れますが、この設定は他の設定よりも優先されるようなので、グローバルで設定するのは控えた方がいいです。

さて、デフォルトは以下です。

```jsonc:setting.json
{
	"code-runner.executorMapByGlob": {
		"pom.xml": "cd $dir && mvn clean package"
	},
}
```

ここに、ファイル名とコマンド追加します。
例として、以下のようなディレクトリ構造で、Pythonのunittestの実行を簡略化してみましょう。

```
root\
|- calc\
|	|- __init__.py
|	└ add.py
|
└  test\
	|- __init__.py
	└ test_add.py
```

```add.py
def add2(a, b):
    return a + b
```

```test_add.py
import unittest
from calc import add

class TestAdd(unittest.TestCase):
    def test_add(self):
        a = 2
        b = 8
        expected = 10
        actual = add.add2(a, b)
        self.assertEqual(expected, actual)

if __name__ == '__main__':
    unittest.main()
```

上記のようなディレクトリ構造でtest_add.pyのテストを実行するためには

```bat
C:\root>python -m unittest test.test_add
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK

```

のように実行します。rootディレクトリで実行することに注意してください。
参考：[一般的なテストディレクトリ構造でunittestを実行する](https://qastack.jp/programming/1896918/running-unittest-with-typical-test-directory-structure)

一般にtest用のpythonファイルはtest_hoge.pyの名前にするので、code-runnerの設定を以下のようにしてみました。

```jsonc:setting.json
{
	"code-runner.executorMapByGlob": {
		"pom.xml": "cd $dir && mvn clean package",
		"test_*.py": "python -m unittest test.$fileNameWithoutExt"
	},
}
```

これにより、test_hoge.pyファイルに対してRun Codeすると

```bat
C:\root>python -m unittest test.test_hoge
```

と実行されます。簡略化成功ですね。
注意事項でも述べますが、test_hoge.pyを通常通り実行したい場合も上記のコマンドになってしまいますので、ターミナルで手打ち or Pythonの拡張機能による実行になります。なのでGlobによる指定は慎重に行うべきです。ワークスペース設定でだけ指定するなど、範囲を限定する方がよいのかなと思います。(大事なことなので2回言いました。)

## 小ネタ集

拡張子でコマンドを指定できるので、様々なことに応用できそうです。
以下では思いついて試した結果、できたこととできなかったことを列挙します。


### (できる).CE/.LB3ファイルをアプリで開く

.CE3/.LB3ファイルは[水魚堂の回路図エディタ](https://www.suigyodo.com/online/schsoft.htm)で使用される拡張子です。VScodeの拡張機能には、これらの拡張子に対応した拡張機能はなさげですから、専用のアプリで開きます。

```jsonc:setting.json
    "code-runner.executorMapByFileExtension": {
		".CE3" : "C:/hoge/bs3vp160504rtl/bsch3v.exe $fullFileName",
		".LB3" : "C:/hoge/bs3vp160504rtl/LCoV.exe $fullFileName",
	},
```

これにより、エクスプローラーから対象の.CE3などを選んで、Run Codeでアプリを起動してすぐに回路図編集できます。

### (できない)画像ファイルに対してコマンドを設定する

windowsでは

```bat
start image/path/name.png
```

で画像ファイルを拡張子と結びついたアプリで開くことができます。
なのでVScodeのエクスプローラーから画像ファイルに対して右クリック→Run Codeで行けそうだと思いましたが、残念ながらダメでした。
VScodeが画像ファイルと認識している拡張子に対してはCode Runnerは無力なようです。

なお.epsファイルはコマンド設定できました！

```jsonc:setting.json
"code-runner.executorMapByFileExtension": {
	".eps": "rungs $fullFileName",
},
```

注：自分はtexliveなのでGhostScriptはrungsで起動するらしいです。
以下[Ghostscript/使い方 - TeX Wiki](https://texwiki.texjp.org/?Ghostscript%2F%E4%BD%BF%E3%81%84%E6%96%B9)より引用。

> Microsoft Windows なら
> TeX Live, W32TeX に付属の rungs の場合は
> rungs
> 64-bit 版の Ghostscript の場合は
> gswin64c
> 32-bit 版の Ghostscript の場合は
> gswin32c
> です。
>
> UNIX 互換 OS なら
> $ gs
> です。


## 自分の設定

需要があるのかは知りませんが。。

```jsonc:setting.json
{
    "code-runner.runInTerminal": true, // 既定のターミナルで起動する
	// 拡張子と言語名を対応させる
	"code-runner.languageIdToFileExtensionMap": {
		"verilog": ".v"
	},
	// 拡張子から実行するコマンドを決める
	"code-runner.executorMapByFileExtension": {
		".CE3": "\"C:/Program Files/Bsch3v/bsch3v.exe\" $fullFileName",
		".LB3": "\"C:/Program Files/Bsch3v/LCoV.exe\" $fullFileName",
		".kv": "python -m kivy.tools.kviewer $fullFileName",
		".plt": "gnuplot $fullFileName",
		".eps": "rungs $fullFileName",
		".md": "marp --html -s $workspaceRoot"
	},
	"code-runner.executorMapByGlob": {
		"pom.xml": "cd $dir && mvn clean package",
		"test_*.py": "$pythonPath -m unittest tests.$fileNameWithoutExt"
	},
	// 言語に対して実行するコマンドを設定する
	"code-runner.executorMap": {
		"C": "cd $dir && gcc $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt",
		"java": "cd $dir && java $fileName",
		"verilog": "cd $dir && iverilog $fileName && vvp a.out",
		"python": "$pythonPath -u $fullFileName"
	},
}
```

その他ワークスペースでは適宜変更しています。
WSL上では、コンパイルしたexeファイルに実行権限がなかったりしたので、

```jsonc:setting.json
"code-runner.executorMap": {
		"c": "gcc $fullFileName -o bin/$fileNameWithoutExt && chmod u+x bin/$fileNameWithoutExt.exe && bin/$fileNameWithoutExt.exe"
	},
```

として、実行権限を付与してから実行するなどの工夫を施しました。

## 注意事項

### パスについて

バックスラッシュはエスケープしてください。\\\\のように使いましょう。

また、パス中に空白を含む場合は\" \"で囲みましょう。

```jsonc:setting.json
{
    "code-runner.executorMap": {
        // パス中にProgram Files (x86)と空白を含むので、囲む。
        "html": "\"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe\""
    }
}
```

### 実行されるコマンドの優先度

Globによる設定は拡張子の設定よりも優先します。
恐らく、Run Codeするときには、まず初めにGlobで設定したものに当てはまるかを確認して、当てはまるパターンがない場合に拡張子での設定を確認するからだと思います。
例えば、以下のような設定でtest.pltをRun Codeすると

```jsonc:setting.json
	"code-runner.executorMapByFileExtension": {
		".plt": "cd $dir"
	},
	"code-runner.executorMapByGlob": {
		"*.plt": "gnuplot $fullFileName"
	},
}
```

```bat
C:\root>cd "root\"
```

のようになるということです。

### 1.56からの不具合(解決済み)

(2021/06/17追記) Code Runnerが新たな設定に対応したと思われます。ただしgit bushではまだバグが残っているという報告も見られました。自分の環境(windows, コマンドプロンプト)では**解決済みと思ってよさそうです。以下は消すのもアレなので残しているだけですので、悪しからず。**

(2021/05/11追記)VScodeがversion 1.56にアップデートされましたが、これの影響で少し厄介なことが起きています。リポジトリにも報告があり、[issue](https://github.com/formulahendry/vscode-code-runner/issues/783)に再現方法も載っています。

バグの再現法ですが、まず推奨されるターミナルの設定にします。これは[Profile improvements](https://code.visualstudio.com/updates/v1_56#_terminal)に説明があります。
> Going forward, these settings are the recommended way to switch out the default terminal configuration and the terminal.integrated.shell and terminal.integrated.shellArgs settings have been deprecated.

それに従い、今までの設定を削除して新たなターミナルの設定を以下のようにしたとします。

```jsonc:setting.json
{
	"terminal.integrated.defaultProfile.windows": "Command Prompt",
	"terminal.integrated.profiles.windows": {
		"PowerShell": {
			"source": "PowerShell",
			"icon": "terminal-powershell"
		},
		"Command Prompt": {
			"path": [
				"${env:windir}\\Sysnative\\cmd.exe",
				"${env:windir}\\System32\\cmd.exe"
			],
			"args": [
				"/k",
				"chcp",
				"65001",
				"&",
				"doskey",
				"/macrofile=%USERPROFILE%\\macros.txt"
			],
			"icon": "terminal-cmd"
		},
		"Git Bash": {
			"source": "Git Bash"
		}
	},
}
```

この設定の下で、Code Runnerのコマンドに`&&`を含むとき、うまく動作しないという問題です。
なおこのターミナルの設定については[VSCodeのターミナルの新しい設定まとめ](https://qiita.com/take_me/items/47f192a126d75a9264a9)にて解説しています。
~~さて、この問題はひとまずは以下のように、deprecatedとされる設定を残しておくことで予定通りに動作します。~~
git bush以外ではバグは解決したと思われます。(2021/06/17追記)

```jsonc:setting.json
{
    "terminal.integrated.shell.windows": "C:\\WINDOWS\\system32\\cmd.exe",
}
```

~~Code-Runner側が対応するまでは消去せず、残しておくことをお勧めします。~~