2011.12.17
■[Haskell][emacs] Haskell 用の emacs カスタマイズ例
Haskell Advent Calendar 2011 への参加記事です。ふだん emacs でコードを書いているプログラマが Haskell を使ってみようと思ったときに、10 分でそれなりの環境構築するための、便利な手順書となることを目指して書きました。~/.emacs や ~/.emacs.d/init.el などの設定ファイルは、dot.emacs と表記しています。
Haskell の環境構築
Haskell 自体の環境構築の説明はこの記事の範囲外ですが、前提ですので、信頼できそうな説明へのリンクを載せておきます。
- トラビスさんの『Haskell Platform インストール Linux編』
- shelarcy さんの『Haskell Platform インストール Mac編』
- tanakh さんの『Haskell Platform インストール Windows編』
ただし、私は Ubuntu ユーザですが、トラビスさんとは異なり、ソースからではなく sudo apt-get install haskell-platform で済ませています。それから、以降の内容が Windows 環境でもうまくいくのか、よくわかっていません。
haskell-mode のインストール
まずは haskell-mode をインストールしましょう。2011/12 の時点で、最新版は 2.8.0 です。
- http://projects.haskell.org/haskellmode-emacs/
- http://www.haskell.org/haskellwiki/Haskell_mode_for_Emacs
解凍してできた haskell-mode-2.8.0 というディレクトリを ~/.emacs.d/elisp/haskell-mode-2.8.0 に配置し、dot.emacs に次のように記載します。
(add-to-list 'load-path "~/.emacs.d/elisp/haskell-mode-2.8.0") (require 'haskell-mode) (require 'haskell-cabal)
拡張子とメジャーモードの関連付け
dot.emacs に次の内容を追記して下さい。
(add-to-list 'auto-mode-alist '("\\.hs$" . haskell-mode)) (add-to-list 'auto-mode-alist '("\\.lhs$" . literate-haskell-mode)) (add-to-list 'auto-mode-alist '("\\.cabal\\'" . haskell-cabal-mode))
拡張子が ".hs" のファイルは haskell-mode で開きます。拡張子が ".lhs" のファイルは literate-haskell-mode で開きます。literate-haskell-mode は、先ほどインストールした、haskell-mode.el の中で定義されています。拡張子が ".lhs" のファイルは Literate Haskell のソースコードです。 Literate Haskell は、まるで自然言語の文章が主、ソースコードが従であるかのような見かけをした、Haskell のプログラムです。コードの行頭には ">" が付いています。私はたとえば、書籍「プログラミング Hasklell」を教材にした「スタート Haskell」という講習の課題でこのフォーマットを見かけました。literate-haskell-mode で開くことでコード部がシンタックスハイライトされます。後述する flymake も効いています(20行目の左端)。
拡張子が ".cabal" のファイルは、Haskell のパッケージ管理システム用の設定ファイルです。haskell-cabal-mode で開くようにしました。 このメジャーモードは、先ほどインストールした、haskell-cabal.el の中で定義されています(~/.emacs.d/elisp/haskell-mode-2.8.0 の下にあります)。たんなる色づけです。
Haskell 製のコマンドと haskell-mode の関連付け
(add-to-list 'interpreter-mode-alist '("runghc" . haskell-mode)) ;#!/usr/bin/env runghc 用 (add-to-list 'interpreter-mode-alist '("runhaskell" . haskell-mode)) ;#!/usr/bin/env runhaskell 用
この 2 行の設定で、1 行目にシェバンがあり、拡張子のないコマンドのファイルも、Haskell モードで開くことができます。
#!/usr/bin/env runhaskell main = putStrLn "Hello, World!"
cabal でインストールしたコンソールアプリのコードを開いたときの小さなストレスがなくなります。
ghc-mod の導入
次に ghc-mod をインストールします。
cabal update cabal install cabal-install cabal install ghc-mod
dot.emacs に次のように書きます。
;; ghc-mod ;; cabal でインストールしたライブラリのコマンドが格納されている bin ディレクトリへのパスを exec-path に追加する (add-to-list 'exec-path (concat (getenv "HOME") "/.cabal/bin")) ;; ghc-flymake.el などがあるディレクトリ ghc-mod を ~/.emacs.d 以下で管理することにした (add-to-list 'load-path "~/.emacs.d/elisp/ghc-mod") (autoload 'ghc-init "ghc" nil t) (add-hook 'haskell-mode-hook (lambda () (ghc-init)))
ghc-mod で何ができるかは、公式ページに記載されていますので、そちらを参照して下さい。
ghc-mod の機能の一つに、ghc-browse-document() という、インストール済みのパッケージのマニュアルをブラウザで開くコマンドがあります。C-M-d に割り当てられています。これの anything 版を作成しました。ここではそれを紹介します。次のコードを dot.emacs に貼りつけて下さい。anything.el 自体の説明や導入方法の解説は省略します。
(require 'anything) (require 'anything-config) (require 'anything-match-plugin) (defvar anything-c-source-ghc-mod '((name . "ghc-browse-document") (init . anything-c-source-ghc-mod) (candidates-in-buffer) (candidate-number-limit . 9999999) (action ("Open" . anything-c-source-ghc-mod-action)))) (defun anything-c-source-ghc-mod () (unless (executable-find "ghc-mod") (error "ghc-mod を利用できません。ターミナルで which したり、*scratch* で exec-path を確認したりしましょう")) (let ((buffer (anything-candidate-buffer 'global))) (with-current-buffer buffer (call-process "ghc-mod" nil t t "list")))) (defun anything-c-source-ghc-mod-action (candidate) (interactive "P") (let* ((pkg (ghc-resolve-package-name candidate))) (anything-aif (and pkg candidate) (ghc-display-document pkg it nil) (message "No document found")))) (defun anything-ghc-browse-document () (interactive) (anything anything-c-source-ghc-mod)) ;; M-x anything-ghc-browse-document() に対応するキーの割り当て ;; ghc-mod の設定のあとに書いた方がよいかもしれません (add-hook 'haskell-mode-hook (lambda() (define-key haskell-mode-map (kbd "C-M-d") 'anything-ghc-browse-document)))
これで、M-x anything-ghc-browse-document を実行すると、anything のやり方でモジュールを絞り込めます。上の設定では、C-M-d に割り当てています。たとえば、"da ar" で検索すると次のようになります。
上の候補画面で RET を押下すると、ブラウザにマニュアルを表示します。
補足ですが、dot.emacs に (setq browse-url-browser-function 'browse-url-firefox) と書き、 Firefox で閲覧するよう emacs に設定しているとします。その場合、端末から firefox を起動できる必要があります。Mac だとどうやるのでしょうか。私は次のような ~/bin/firefox を作成しています。Mac には詳しくないので、もっと良い方法があるのかもしれません。
#!/bin/sh open -a firefox.app ${@+"$@"}
それからもう一つ脱線すると、私は anything の候補画面では候補をたくさん表示したいので、dot.emacs に以下のように書いて、上下分割ではなく、左右分割にしています。
(defun anything-default-display-buffer (buf) (if anything-samewindow (switch-to-buffer buf) (progn (delete-other-windows) (split-window (selected-window) nil t) (pop-to-buffer buf))))
flymake でシンタックスチェック
haskell-mode.el には、hlint を利用した flymake 用のしくみがあります。ghc-mod に同梱されている ghc-flymake.el では、ghc -fno-code (デフォルト)または hlint を用いた flymake 用のしくみがあります。正直なところ、両者の違いをあまりわかっていないのですが、私は ghc-mod で ghc -fno-code を使った flymake を利用しています。そのためには、haskell-mode.el での flymake の設定を無効にする必要があります。先ほどの ghc-mod の設定を次のように (flymake-mode) を含む内容に変更すれば、うまくゆきます(この方法は ghc.el の冒頭にも載っているのですが、この記事のコメント欄で教わりました)。
(autoload 'ghc-init "ghc" nil t) (add-hook 'haskell-mode-hook (lambda () (ghc-init) (flymake-mode)))
auto-complete での補完
auto-complete.el で、ghc-mod の M-C-i での補完機能の一部を利用した候補作成、絞り込み、挿入が利用できます。私は Version: 1.3 の auto-complete に、GitHub にある最新の auto-complete から ac-source-ghc-mod などを生成しているところを持ってきて、次のように設定しています。
;; https://github.com/m2ym/auto-complete (ac-define-source ghc-mod '((depends ghc) (candidates . (ghc-select-completion-symbol)) (symbol . "s") (cache))) (defun my-ac-haskell-mode () (setq ac-sources '(ac-source-words-in-same-mode-buffers ac-source-dictionary ac-source-ghc-mod))) (add-hook 'haskell-mode-hook 'my-ac-haskell-mode) (defun my-haskell-ac-init () (when (member (file-name-extension buffer-file-name) '("hs" "lhs")) (auto-complete-mode t) (setq ac-sources '(ac-source-words-in-same-mode-buffers ac-source-dictionary ac-source-ghc-mod)))) (add-hook 'find-file-hook 'my-haskell-ac-init)
ちなみに auto-complete コマンドは key-chord.el を導入し、jk 同時押しに割り当てています。
(eval-after-load "key-chord" '(progn (key-chord-define-global "jk" 'auto-complete)))
右端に s と付いているのが ghc-mod から取得した補完候補です。
定義元への移動
いわゆるタグジャンプのためのライブラリには、hasktags、hothasktags、gasbag があります。hasktags で作成したタグファイルを anything から利用する、anything-hasktags.el を書きました。anything-exuberant-ctags.el を雛形にしています。
- https://github.com/wakaran/anything-hasktags
- https://raw.github.com/wakaran/anything-hasktags/master/anything-hasktags.el (ソースコード)
設定例は以下になります。
(require 'anything) (require 'anything-hasktags) (add-hook 'haskell-mode-hook (lambda() (define-key haskell-mode-map (kbd "C-c j") 'anything-hasktags-select)))
hasketags 自体は、cabal install hasktags でインストールできます。anything-hasktags.el では、etags ではなく ctags 用のタグファイルを使います。私は .bashrc に次のような alias を書いています。Mac だと xargs のオプションが使えないかもしれません。各自で工夫して下さい。
alias hasktags-r="find . -type f -name \*.\*hs -print0 | xargs -0 hasktags -c"
以下は動作サンプルです。候補を移動するたびに定義箇所をプレビューできます。haskctags の生成するタグファイルの質がいまいちだとしても、このように一覧表示して絞り込むスタイルにすれば、有益なツールになります。
リンク集
- Haskell mode for Emacs - HaskellWiki
- Happy Haskell programming on Emacs
- kazu-yamamoto/ghc-mod - GitHub
- polypus74/HSnippets - GitHub
- Auto Complete Mode - GNU Emacsのための最も賢い自動補完機能
- Haskellのエディタ支援ツールあれこれ - maoeのブログ
- nominolo/scion - GitHub
- luqui/hothasktags - GitHub
- Announce: hothasktags -The Haskell-Cafe April 2010 Archive by thread
- gasbag: ctags and etags from haskell code
- The Haskell-Beginners Mailing List - Discussion of primarily beginner-level topics related to Haskell ()






