Emacs で C# を書こう

# はじめに

OmniSharp はエディタ上で intellisence に近い機能を実現するための OSS ソフトウェアです。
今回は emacs から OmniSharp を使うことについて紹介します。

紹介のためのスクリーンキャスト

[![](http://img.youtube.com/vi/YssUy7Ba8BM/0.jpg)](https://www.youtube.com/watch?v=YssUy7Ba8BM)

短い動画ですが、

1. `omnisharp-add-dot-and-auto-complete` で補完
2. `omnisharp-run-code-action-refactoring` で `using System.Linq` を追加
3. `omnisharp-rename` で名前変更 （15行目のnumは影響を受けない！）
4. `omnisharp-auto-complete-overrides` で `ToString` をオーバーライド

を行なっています。この他にも

* 定義ジャンプ
* ソリューション内のファイルを開く
* ビルド
* 単体テスト実行

などなど便利コマンドがたくさん用意されているので今すぐ導入しよう！
VisualStudio を emacs キーバインドにして使う必要なんかなかったんや！

# 環境構築
以下は Debian Wheezy, emacs24.3 24.4 で試した記録です。

#### Mono
まず実行環境とコンパイラをインストールします。
http://www.mono-project.com/docs/getting-started/install/linux/

普通に README 通りにインストールします。

```bash
$ apt-key adv --keyserver pgp.mit.edu --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
$ echo "deb http://download.mono-project.com/repo/debian wheezy main" > /etc/apt/sources.list.d/mono-xamarin.list
$ apt-get update
$ apt-get install mono-complete

```

#### NUnit
テストライブラリ。
http://www.nunit.org/index.php?p=download
今回は 2.6.3 をダウンロードしました。適当なディレクトリに入れます。

#### OmniSharp Server
今回の主役 OmniSharp Server。
https://github.com/OmniSharp/omnisharp-server#osx--linux

``` bash
$ git clone https://github.com/nosami/OmniSharpServer.git
$ cd OmniSharpServer
$ git submodule update --init --recursive
```

ここで `OmniSharpServer/OmniSharp/config.json` を編集して、nunit-console.exe の場所を指定します。

``` javascript
"TestCommands": {
  "All": "nunit-console.exe -nologo {{AssemblyPath}}",
  "Fixture": "nunit-console.exe -nologo {{AssemblyPath}} -run={{TypeName}}",
  "Single": "nunit-console.exe -nologo {{AssemblyPath}} -run={{TypeName}}.{{MethodName}}"
},
```

この部分をnunitのインストール先にあわせて書き換えます。

``` javascript
"TestCommands": {
  "All": "mono --runtime=v4.0 --debug /path/to/nunit/bin/nunit-console.exe -nologo {{AssemblyPath}}",
  "Fixture": "mono --runtime=v4.0 --debug /path/to/nunit/bin/nunit-console.exe -nologo {{AssemblyPath}} -run={{TypeName}}",
  "Single": "mono --runtime=v4.0 --debug /path/to/nunit/bin/nunit-console.exe -nologo {{AssemblyPath}} -run={{TypeName}}.{{MethodName}}"
},
```

あとは README.md 通り `xbuild` でビルドします。

#### omnisharp-mode
Emacsからomnisharp-serverにアクセスするための拡張をインストールし、omnisharp-server-executable-path に実行ファイルのパスを指定します。

```lisp
(add-hook 'csharp-mode-hook 'omnisharp-mode)
(setq omnisharp-server-executable-path "/path/to/OmniSharp.exe")
```

補完候補を出すために auto-complete-mode など入れておくと良いです。
flycheck と相性が悪いので、 flycheck をいったん切るか omnisharp.el を編集してください（下の方に詳しく書きました）。

#### NuGet
おなじみパッケージマネージャ。
http://headsigned.com/article/running-nuget-command-line-on-linux を参考に、
NuGet.exe と Microsoft.Build.dll をダウンロード。

#### grunt-init-scharpsolution
OmniSharp 動かすのにソリューション(.sln) が必須っぽいです。
Grunt で自動生成してくれるやつがあるのでこれを使いました。

grunt-init-csharpsolution
https://github.com/nosami/grunt-init-csharpsolution

# 実際に使ってみる

まずソリューションを作って、依存をインストール。

```bash
$ grunt-init csharpsolution
$ mono /path/to/nuget/NuGet.exe restore
```

適当なMain.csを書いて、
```M-x omnisharp-add-to-solution-current-file``` を叩いて Main.cs をソリューションに追加します。

.csprojファイルを編集して、OutputTypeをLibraryからExeに変更します。
.csproj の7行目あたり

``` xml
    <OutputType>Exe</OutputType>
```

最後に ` M-x omnisharp-build-in-emacs ` でビルドしてみましょう！

# キーバインドなど
各自で最強の init.el を作ってください。

私のはこんな感じです。

``` lisp
;; auto-complete.el
(require 'auto-complete-config)
(add-to-list 'ac-dictionary-directories "~/.emacs.d/elisp/ac-dict")
(ac-config-default)

;; flycheck
(add-hook 'after-init-hook #'global-flycheck-mode)

;; C# mode
(require 'csharp-mode)
(add-hook 'csharp-mode-hook
          '(lambda()
             (setq c-basic-offset 4)
             (c-set-offset 'substatement-open 0)
             (c-set-offset 'case-label '+)
             (c-set-offset 'arglist-intro '+)
             (c-set-offset 'arglist-close 0)))
(add-hook 'csharp-mode-hook 'omnisharp-mode)
(add-hook 'csharp-mode-hook 'turn-on-eldoc-mode)

(require 'omnisharp)
(setq omnisharp-server-executable-path
      "/opt/OmniSharpServer/OmniSharp/bin/Debug/OmniSharp.exe")

(define-key omnisharp-mode-map "\C-c\C-s" 'omnisharp-start-omnisharp-server)
(define-key omnisharp-mode-map "\M-/" 'omnisharp-auto-complete)
(define-key omnisharp-mode-map "." 'omnisharp-add-dot-and-auto-complete)
(define-key omnisharp-mode-map "\C-c\C-c" 'omnisharp-build-in-emacs)
(define-key omnisharp-mode-map "\C-c\C-N" 'omnisharp-navigate-to-solution-member)
(define-key omnisharp-mode-map "\C-c\C-n" 'omnisharp-navigate-to-current-file-member)
(define-key omnisharp-mode-map "\C-c\C-f" 'omnisharp-navigate-to-solution-file)
(define-key omnisharp-mode-map "\C-c\C-g" 'omnisharp-go-to-definition)
(define-key omnisharp-mode-map "\C-c\C-r" 'omnisharp-rename)
(define-key omnisharp-mode-map "\C-c\C-v" 'omnisharp-run-code-action-refactoring)
(define-key omnisharp-mode-map "\C-c\C-o" 'omnisharp-auto-complete-overrides)
(define-key omnisharp-mode-map "\C-c\C-u" 'omnisharp-helm-find-symbols)
(define-key omnisharp-mode-map "\C-c\C-t\C-s" (lambda() (interactive) (omnisharp-unit-test "single")))
(define-key omnisharp-mode-map "\C-c\C-t\C-r" (lambda() (interactive) (omnisharp-unit-test "fixture")))
(define-key omnisharp-mode-map "\C-c\C-t\C-e" (lambda() (interactive) (omnisharp-unit-test "all")))

```


# つまづいたとことか

#### NUnitの出力のエラー行が `<filename unknown>:0` になる
`mono --runtime=v4.0 --debug /path/to/nunit-console.exe YourProject/bin/Debug/YourProject.Tests.dll`
のように、ランタイムのバージョンを正しく指定する必要がありました。

#### emacs内でビルドできない
OmniSharp-Server が起動しているか確認
``` M-x omnisharp-start-omnisharp-server ``` を叩いて.slnファイルを指定して起動します。

#### flycheck と相性が悪い
https://github.com/OmniSharp/omnisharp-emacs/issues/129
ここで言及されてるように、

``` diff
-  (--each '('csharp-omnisharp-curl
-            'csharp-omnisharp-curl-code-issues
-            'csharp-omnisharp-curl-semantic-errors)
+  (--each '(csharp-omnisharp-curl
+            csharp-omnisharp-curl-code-issues
+            csharp-omnisharp-curl-semantic-errors)
```

 直接 omnisharp.el を編集すれば解決しました。

# まとめ
OmniSharp の導入について紹介しました。
emacs に限らず、使い慣れたエディタで賢い補完ができるので、魅力的な選択肢だと思います。
開発も活発なようですし、今後にも期待したいですね！