EmacsでUnity開発(c#)するための環境構築

2015/9/2〜〜〜〜〜〜〜〜〜〜〜
ここで紹介している以下の機能を入れるより
omnisharpを入れた方が幸せになれますので
そちらをお勧めします。
- C#用のメジャーモードを導入
- imenu
- 自動補完
- シンタックスチェック

omnisharpの導入手順は以下を参考しました
- [EmacsでUnity開発をする](http://blog.tkeo.info/blog/2014/12/10/unity-with-emacs/)
- [Emacs で C# を書こう](http://qiita.com/haripo/items/bbe2b91b83bf3c71dac8)
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜

EmacsでUnityの実践的な開発を行うにあたっては
以下の機能を入れると楽になります。

# 導入する機能
- C#用のメジャーモードを導入
- imenu
- 自動補完
- tagジャンプ
- コードスタイルチェック
- シンタックスチェック
- カジュアルな実行環境

## C#用のメジャーモードを導入
Emacsには、デフォルトでC#用のメジャーモードは用意されていません。
なので、Google Codeのcsharpmodeプロジェクトが出してるcsharp-modeを入れます
(もう開発はされてないようですが..)
https://code.google.com/p/csharpmode/downloads/list

## imenu
クラス、メソッド、アクセッサーの一覧を表示し、インクリントサーチで絞り込みジャンプできる機能です。
csharp-modeにimenuは用意されてますが、以下を修正しないと動かなかったです。

```lisp:csharp-mode.el
@@ -5561,8 +5561,8 @@ Key bindings:
         ;; `imenu-generic-expression'; imenu will do a "generic scan" for you.
         ;; csharp-mode uses the former method.
         ;;
-        (setq imenu-create-index-function 'csharp-imenu-create-index)
-        (imenu-add-menubar-index)))
+        (setq imenu-create-index-function 'csharp-imenu-create-index)))
+        ;; (imenu-add-menubar-index)))
```
※ 注意
- 2000行以上くらいのコードになると、一覧表示するまでに数秒かかります
- ジェネリックで定義してるメソッドとクラスは、imenuからは対象外になってます

## 自動補完
auto-complete-modeを使用すれば、開いて保持してるバッファの単語と
csharp-mode用の辞書は補完候補として表示できます。
Emacsでdllの中まで見て補完候補を探してくれる機能はまだ実現できてないです。

※ omnisharpというelispを使用するとdllを中まで見て補完候補を出してくれるようで
試したのですが出なかったです...。(できた方いらしたら教えてほしいです)

## tagジャンプ
tagジャンプは、gtagsを利用します。
以下のlispとGTAGSの作成スクリプトをcronで設定します

```lisp:my-csharp-mode.el
(require 'gtags)
(global-set-key (kbd "C-'") 'gtags-find-tag-other-window)  ; (別バッファで)関数の定義元(関数の実体)へジャンプ
(global-set-key (kbd "C-M-'") 'gtags-find-tag)             ; 変数等のジャンプ
```

```sh:update_gtags.sh
#!/bin/bash

PROJ1=$HOME/projects/proj1
PROJ2=$HOME/projects/proj2
for project_path in ${PROJ1} ${PROJ2}
do
    cd $project_path
    rm GPATH GRTAGS GTAGS
    find . -name "*.cs" -follow -print | /usr/local/bin/gtags --gtagslabel=plugin-example -f -
done
```

## コードスタイルチェック、シンタックスチェック
### コードスタイルチェックについて
C#のコードスタイルはstylecopが一般的らしいです。(pythonでいうpep8みたいなやつ)
flymakeを利用するので、mac環境のコマンドラインから結果を返してるもの探してて、
stylecop CLIっというのを見つけたのでこちらを導入手順を紹介します。

**導入手順**
準備として、MonoDevelopインストールしておく必要があります

1. 以下のサイトからバイナリをダウンロード
http://sourceforge.net/projects/stylecopcli/files/Binary/

2. MonoDevelopのアドオンマネージャーからsytlecopを追加
メニューのアドオンマネージャー→gallery →IDE extentions →sytlecop supportからインストール

3. flymakeに設定
これで StyleCopCLI.exe が動作できるようになるので
flymakeのチェックスクリプト、エラー文言の検出パターンを設定します

### シンタックスチェックについて
シンタックスチェックは以下を実行することで確認できます

```
/usr/bin/mcs -r:ライブラリpath $1 -target:Library
````

ただ、シンタックスが正しいかは判断できるけど、エラーかどうかまでは判断してくれないみたい
たとえば、以下のような判定が下る

```C#
System.Console.WriteLine("hoge");  // flymakeでは正常扱い
tem.Console.WriteLine("hoge");     // flymakeでは正常扱い
Sys tem.Console.WriteLine("hoge"); // flymakeでは異常扱い
```

### コードスタイルとシンタックスのチェックの設定
```bash:csharp-checker.sh
#! /bin/bash

# シンタックスチェック
/usr/bin/mcs -r:/Applications/Unity/Unity.app/Contents/Frameworks/Managed/UnityEngine.dll,/Applications/Unity/Unity.app/Contents/Frameworks/Managed/UnityEditor.dll,~/projects/proj1/Library/ScriptAssemblies/Assembly-CSharp.dll,~/projects/proj1/Library/ScriptAssemblies/Assembly-CSharp-Editor.dll $1 -target:library
dll_file=`echo $1 | sed -e "s/\.cs/.dll/"`
ls ${dll_file} && rm ${dll_file}

# コードスタイルチェック
/usr/bin/mono ~/.emacs.d/bin/StyleCopCLI.exe -cs $PWD/$1 -out /tmp/stylecop.log
# エラーとして扱いたくないものは、grepに追加していく
cat /tmp/stylecop.log | sed -e "s/\"//g" | sed -e "s/>/ /g" | grep -v "SA1001\|SA1202\|SA15013"
ls $PWD/StyleCop.Cache && rm $PWD/StyleCop.Cache
```

```lisp:my-csharp-mode.el
;; Flymake
(defun flymake-csharp-init ()
  (let* ((temp-file (flymake-init-create-temp-buffer-copy
                     'flymake-create-temp-inplace))
         (local-file (file-relative-name
                      temp-file
                      (file-name-directory buffer-file-name))))
    (list "~/.emacs.d/bin/csharp-checker" (list local-file))))
(add-to-list 'flymake-allowed-file-name-masks
             '("\\.cs$" flymake-csharp-init))
(add-to-list 'flymake-err-line-patterns
             '("cs\(\\([0-9]+\\),\\([0-9]+\\)\)\: \\(error\\|warning\\) \\(.+\\)$" nil 1 2 4))
(add-to-list 'flymake-err-line-patterns
             '("LineNumber=\\([0-9]+\\) .* RuleId=\\(.+\\)</Violation" nil 1 1 2))
```

## カジュアルな実行環境
quickrunを利用するとEmacs上からC#をインタラクディブっぽく実行できるようになります。
ちょっと関数とか書いて動作確認するときに便利です。

[quickrun.el](https://github.com/syohex/emacs-quickrun "quickrun.el")を入れて M-x quickrun を実行すると、以下のように動作確認できます。

![スクリーンショット 2014-03-21 18.12.05.png](https://qiita-image-store.s3.amazonaws.com/0/37724/3d108d8b-b57c-ed27-04dd-cb0109cdaccd.png "スクリーンショット 2014-03-21 18.12.05.png")


## 最終的なcsharp-mode設定は以下になります
```lisp:my-csharp-mode.el
(require 'csharp-mode)
(setq auto-mode-alist
   (append '(("\\.cs$" . csharp-mode)) auto-mode-alist))

;; C#モードフック
(add-hook 'csharp-mode-hook
          '(lambda()
             (setq comment-column 40)
             (setq c-basic-offset 4)
             ;; (font-lock-add-magic-number)
             ;; オフセットの調整
             (c-set-offset 'substatement-open 0)
             (c-set-offset 'case-label '+)
             (c-set-offset 'arglist-intro '+)
             (c-set-offset 'arglist-close 0)
             (hl-line-mode)
             (auto-complete-mode)
             (flymake-mode)
             )
          )

;; Gtags設定
(require 'gtags)
(global-set-key (kbd "C-'") 'gtags-find-tag-other-window)  ; (別バッファで)関数の定義元(関数の実体)へジャンプ
(global-set-key (kbd "C-M-'") 'gtags-find-tag)             ; 変数等のジャンプ

;; Flymake
(defun flymake-csharp-init ()
  (let* ((temp-file (flymake-init-create-temp-buffer-copy
                     'flymake-create-temp-inplace))
         (local-file (file-relative-name
                      temp-file
                      (file-name-directory buffer-file-name))))
    (list "~/.emacs.d/bin/csharp-checker" (list local-file))))
(add-to-list 'flymake-allowed-file-name-masks
             '("\\.cs$" flymake-csharp-init))
(add-to-list 'flymake-err-line-patterns
             '("cs\(\\([0-9]+\\),\\([0-9]+\\)\)\: \\(error\\|warning\\) \\(.+\\)$" nil 1 2 4))
(add-to-list 'flymake-err-line-patterns
             '("LineNumber=\\([0-9]+\\) .* RuleId=\\(.+\\)</Violation" nil 1 1 2))

```