Emacs で C# を書こう

  • 35
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

はじめに

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

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

短い動画ですが、

  1. omnisharp-add-dot-and-auto-complete で補完
  2. omnisharp-run-code-action-refactoringusing System.Linq を追加
  3. omnisharp-rename で名前変更 (15行目のnumは影響を受けない!)
  4. omnisharp-auto-complete-overridesToString をオーバーライド

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

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

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

環境構築

以下は Debian Wheezy, emacs24.3 24.4 で試した記録です。

Mono

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

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

$ 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

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

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

"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のインストール先にあわせて書き換えます。

"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 に実行ファイルのパスを指定します。

(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

実際に使ってみる

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

$ 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行目あたり

    <OutputType>Exe</OutputType>

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

キーバインドなど

各自で最強の init.el を作ってください。

私のはこんな感じです。

;; 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
ここで言及されてるように、

-  (--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 に限らず、使い慣れたエディタで賢い補完ができるので、魅力的な選択肢だと思います。
開発も活発なようですし、今後にも期待したいですね!

この投稿は C# Advent Calendar 20145日目の記事です。