EmacsでSwiftのコードスタイル、コンパイルエラーをチェック

Emacsのコード関連のチェックは、flymakeやflycheckがありますが
flymake使ってチェックを進めます。

コードスタイルについては、Swiftlintを利用して
コンパイルエラーは、Swiftコマンドでチェックを行います。

![swift-flymake.gif](https://qiita-image-store.s3.amazonaws.com/0/37724/afc15561-971a-04a5-a3a3-db795b5d3dc3.gif)

# 設定方法

init.elで入力毎にflymakeコマンドが走るように設定します。
swift-project-pathにはswiftのプロジェクトへのPathを設定し
swift-objc-header-pathにはBridging-Headerを設定します。
Bridging-Headerを利用してない場合は空文字を入れます。

```lisp:init.el
;; flymake
(defvar swift-project-path "/path/to/swift-project")
(defvar swift-objc-header-path "/path/to/Bridging-Header")
(defvar swfit-flymake-command "~/.emacs.d/bin/swift-flymake.sh")
(defun flymake-swift-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)))
         (objc-header-target (concat swift-objc-header-path "/*-Bridging-Header.h"))
         (objc-header-file-list (eshell-extended-glob objc-header-target)))
    (if (listp objc-header-file-list)
        (let ((objc-header-file (expand-file-name (car objc-header-file-list))))
          (list swfit-flymake-command (list swift-project-path local-file objc-header-file)))
      (list swfit-flymake-command (list swift-project-path local-file)))))
(add-to-list 'flymake-allowed-file-name-masks '("\\.swift\\'" flymake-swift-init))

;; flymakeにswiftチェックパターンはないので追加
(add-to-list 'flymake-err-line-patterns
             '("^.+swift\:\\([0-9]+\\)\:\\([0-9]+\\)?\: \\(error\\|warning\\)\: \\(.+\\)$" nil 1 2 4))
```

flymakeで実行するコマンドでは、コードスタイル、コンパイルエラーのチェックを一緒に行います。

```bash:swift-flymake.sh
#! /bin/bash

if [ "$3" ]; then
  ~/.emacs.d/bin/swift-code-checker.py --root-path "$1" --targetfile "$2" --objcheader "$3"
else
  ~/.emacs.d/bin/swift-code-checker.py --root-path "$1" --targetfile "$2"
fi

cd "$1" && swiftlint | grep "$2"
```

コンパイルエラーのチェックコマンド

```py:swift-code-checker.py
#! /usr/bin/env python
# coding: utf-8

from commands import getoutput
from optparse import OptionParser

# パラメータ設定
usage = "usage: %prog [options]"
parser = OptionParser(usage, version="ver:%s" % "%prog 1.0")
parser.add_option('-b', '--root-path', dest='root_path')
parser.add_option('-t', '--targetfile', dest='target_file')
parser.add_option('-o', '--objcheader', dest='objc_header')
opts, args = parser.parse_args()

swift_option_list = []

swift_option_list.append('/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift')
swift_option_list.append('-frontend -parse')
swift_option_list.append('-sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk')
swift_option_list.append('-F libPods.a')
swift_option_list.append('-target i386-apple-ios8.1')
if opts.objc_header:
    swift_option_list.append('-import-objc-header')
    swift_option_list.append(opts.objc_header)

# code-checkの対象ファイル
swift_option_list.append(opts.target_file)

# Swiftプロジェクトにobjectiv-cのヘッダーがあればimportする
regex = r"find {} -name '*.h' | sed -e 's/^\(.*\/\).*/\1/' | uniq | sed -e 's/^\.//'"
result = getoutput(regex.format(opts.root_path))
include_list = [u'-I {}'.format(x.replace(' ', r'\ ')) for x in result.split('\n')]
include_str = ' '.join(include_list)
swift_option_list.append(include_str)

# コマンド実行
command_result = getoutput(' '.join(swift_option_list))
print command_result
```