|
| 1 | +;;; haskell-compile.el --- Haskell/GHC compilation sub-mode |
| 2 | + |
| 3 | +;; Copyright (C) 2013 Herbert Valerio Riedel |
| 4 | + |
| 5 | +;; Author: Herbert Valerio Riedel <[email protected]> |
| 6 | + |
| 7 | +;; This file is not part of GNU Emacs. |
| 8 | + |
| 9 | +;; This file is free software; you can redistribute it and/or modify |
| 10 | +;; it under the terms of the GNU General Public License as published by |
| 11 | +;; the Free Software Foundation; either version 3 of the License, or |
| 12 | +;; (at your option) any later version. |
| 13 | + |
| 14 | +;; This file is distributed in the hope that it will be useful, |
| 15 | +;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 17 | +;; GNU General Public License for more details. |
| 18 | + |
| 19 | +;; You should have received a copy of the GNU General Public License |
| 20 | +;; along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 21 | + |
| 22 | +;;; Commentary: |
| 23 | + |
| 24 | +;; Simple GHC-centric compilation sub-mode; see info node |
| 25 | +;; `(haskell-mode)compilation' for more information |
| 26 | + |
| 27 | +;;; Code: |
| 28 | + |
| 29 | +(require 'compile) |
| 30 | +(require 'haskell-cabal) |
| 31 | + |
| 32 | +(defgroup haskell-compile nil |
| 33 | + "Settings for Haskell compilation mode" |
| 34 | + :link '(custom-manual "(haskell-mode)compilation") |
| 35 | + :group 'haskell) |
| 36 | + |
| 37 | +(defcustom haskell-compile-cabal-build-command |
| 38 | + "cd %s; cabal build --ghc-option=-ferror-spans" |
| 39 | + "Default build command to use for `haskell-cabal-build' when a cabal file is detected. |
| 40 | +The `%s' placeholder is replaced by the cabal package top folder." |
| 41 | + :group 'haskell-compile |
| 42 | + :type 'string) |
| 43 | + |
| 44 | +(defcustom haskell-compile-cabal-build-alt-command |
| 45 | + "cd %s; cabal clean -s; cabal build --ghc-option=-ferror-spans" |
| 46 | + "Alternative build command to use when `haskell-cabal-build' is called with a negative prefix argument. |
| 47 | +The `%s' placeholder is replaced by the cabal package top folder." |
| 48 | + :group 'haskell-compile |
| 49 | + :type 'string) |
| 50 | + |
| 51 | +(defcustom haskell-compile-command |
| 52 | + "ghc -Wall -ferror-spans -fforce-recomp -c %s" |
| 53 | + "Default build command to use for `haskell-cabal-build' when no cabal file is detected. |
| 54 | +The `%s' placeholder is replaced by the current buffer's filename." |
| 55 | + :group 'haskell-compile |
| 56 | + :type 'string) |
| 57 | + |
| 58 | +(defcustom haskell-compile-ghc-filter-linker-messages |
| 59 | + t |
| 60 | + "Filter out unremarkable \"Loading package...\" linker messages during compilation." |
| 61 | + :group 'haskell-compile |
| 62 | + :type 'boolean) |
| 63 | + |
| 64 | +(defconst haskell-compilation-error-regexp-alist |
| 65 | + `((,(concat |
| 66 | + "^\\(?1:[^ \t\r\n]+?\\):" |
| 67 | + "\\(?:" |
| 68 | + "\\(?2:[0-9]+\\):\\(?4:[0-9]+\\)\\(?:-\\(?5:[0-9]+\\)\\)?" ;; "121:1" & "12:3-5" |
| 69 | + "\\|" |
| 70 | + "(\\(?2:[0-9]+\\),\\(?4:[0-9]+\\))-(\\(?3:[0-9]+\\),\\(?5:[0-9]+\\))" ;; "(289,5)-(291,36)" |
| 71 | + "\\)" |
| 72 | + ":\\(?6: Warning:\\)?") |
| 73 | + 1 (2 . 3) (4 . 5) (6 . nil)) ;; error/warning locus |
| 74 | + |
| 75 | + ;; multiple declarations |
| 76 | + ("^ \\(?:Declared at:\\| \\) \\(?1:[^ \t\r\n]+\\):\\(?2:[0-9]+\\):\\(?4:[0-9]+\\)$" |
| 77 | + 1 2 4 0) ;; info locus |
| 78 | + |
| 79 | + ;; this is the weakest pattern as it's subject to line wrapping et al. |
| 80 | + (" at \\(?1:[^ \t\r\n]+\\):\\(?2:[0-9]+\\):\\(?4:[0-9]+\\)\\(?:-\\(?5:[0-9]+\\)\\)?[)]?$" |
| 81 | + 1 2 (4 . 5) 0)) ;; info locus |
| 82 | + "Regexps used for matching GHC compile messages. |
| 83 | +See `compilation-error-regexp-alist' for semantics.") |
| 84 | + |
| 85 | +(defvar haskell-compilation-mode-map |
| 86 | + (let ((map (make-sparse-keymap))) |
| 87 | + (set-keymap-parent map compilation-mode-map)) |
| 88 | + "Keymap for `haskell-compilation-mode' buffers. |
| 89 | +This is a child of `compilation-mode-map'.") |
| 90 | + |
| 91 | +(defun haskell-compilation-filter-hook () |
| 92 | + "Local `compilation-filter-hook' for `haskell-compilation-mode'." |
| 93 | + |
| 94 | + (when haskell-compile-ghc-filter-linker-messages |
| 95 | + (delete-matching-lines "^Loading package [^ \t\r\n]+ [.]+ linking [.]+ done\\.$" |
| 96 | + (save-excursion (goto-char compilation-filter-start) |
| 97 | + (line-beginning-position)) |
| 98 | + (point)))) |
| 99 | + |
| 100 | +(define-compilation-mode haskell-compilation-mode "HsCompilation" |
| 101 | + "Haskell/GHC specific `compilation-mode' derivative. |
| 102 | +This mode provides support for GHC 7.[46]'s compile |
| 103 | +messages. Specifically, also the `-ferror-spans` source location |
| 104 | +format is supported, as well as info-locations within compile |
| 105 | +messages pointing to additional source locations. |
| 106 | +
|
| 107 | +See Info node `(haskell-mode)compilation' for more details." |
| 108 | + (set (make-local-variable 'compilation-error-regexp-alist) |
| 109 | + haskell-compilation-error-regexp-alist) |
| 110 | + |
| 111 | + (add-hook 'compilation-filter-hook |
| 112 | + 'haskell-compilation-filter-hook nil t) |
| 113 | +) |
| 114 | + |
| 115 | +;;;###autoload |
| 116 | +(defun haskell-compile (&optional edit-command) |
| 117 | + "Compile the Haskell program including the current buffer. |
| 118 | +Tries to locate the next cabal description in current or parent |
| 119 | +folders via `haskell-cabal-find-dir' and if found, invoke |
| 120 | +`haskell-compile-cabal-build-command' from the cabal package root |
| 121 | +folder. If no cabal package could be detected, |
| 122 | +`haskell-compile-command' is used instead. |
| 123 | +
|
| 124 | +If prefix argument EDIT-COMMAND is non-nil (and not a negative |
| 125 | +prefix `-'), `haskell-compile' prompts for custom compile |
| 126 | +command. |
| 127 | +
|
| 128 | +If EDIT-COMMAND contains the negative prefix argument `-', |
| 129 | +`haskell-compile' calls the alternative command defined in |
| 130 | +`haskell-compile-cabal-build-alt-command' if a cabal package was |
| 131 | +detected. |
| 132 | +
|
| 133 | +`haskell-compile' uses `haskell-compilation-mode' which is |
| 134 | +derived from `compilation-mode'. See Info |
| 135 | +node `(haskell-mode)compilation' for more details." |
| 136 | + (interactive "P") |
| 137 | + (save-some-buffers (not compilation-ask-about-save) |
| 138 | + compilation-save-buffers-predicate) |
| 139 | + (let* ((cabdir (haskell-cabal-find-dir)) |
| 140 | + (command1 (if (eq edit-command '-) |
| 141 | + haskell-compile-cabal-build-alt-command |
| 142 | + haskell-compile-cabal-build-command)) |
| 143 | + (srcname (buffer-file-name)) |
| 144 | + (command (if cabdir |
| 145 | + (format command1 cabdir) |
| 146 | + (if (and srcname (derived-mode-p 'haskell-mode)) |
| 147 | + (format haskell-compile-command srcname) |
| 148 | + command1)))) |
| 149 | + (when (and edit-command (not (eq edit-command '-))) |
| 150 | + (setq command (compilation-read-command command))) |
| 151 | + |
| 152 | + (compilation-start command 'haskell-compilation-mode))) |
| 153 | + |
| 154 | +(provide 'haskell-compile) |
| 155 | +;;; haskell-compile.el ends here |
0 commit comments