Mercurial > hg > Members > kokubo > emacs
comparison .emacs.d/haskell-mode/haskell-mode.el @ 0:2764b4f45f9f
1st commit
author | Shohei KOKUBO <e105744@ie.u-ryukyu.ac.jp> |
---|---|
date | Mon, 21 Apr 2014 04:30:59 +0900 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:2764b4f45f9f |
---|---|
1 ;;; haskell-mode.el --- A Haskell editing mode -*- coding: utf-8 -*- | |
2 | |
3 ;; Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc | |
4 ;; Copyright (C) 1992, 1997-1998 Simon Marlow, Graeme E Moss, and Tommy Thorn | |
5 | |
6 ;; Author: 1992 Simon Marlow | |
7 ;; 1997-1998 Graeme E Moss <gem@cs.york.ac.uk> and | |
8 ;; Tommy Thorn <thorn@irisa.fr>, | |
9 ;; 2001-2002 Reuben Thomas (>=v1.4) | |
10 ;; 2003 Dave Love <fx@gnu.org> | |
11 ;; Keywords: faces files Haskell | |
12 ;; URL: https://github.com/haskell/haskell-mode | |
13 | |
14 ;; This file is not part of GNU Emacs. | |
15 | |
16 ;; This file is free software; you can redistribute it and/or modify | |
17 ;; it under the terms of the GNU General Public License as published by | |
18 ;; the Free Software Foundation; either version 3, or (at your option) | |
19 ;; any later version. | |
20 | |
21 ;; This file is distributed in the hope that it will be useful, | |
22 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
23 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
24 ;; GNU General Public License for more details. | |
25 | |
26 ;; You should have received a copy of the GNU General Public License | |
27 ;; along with this program. If not, see <http://www.gnu.org/licenses/>. | |
28 | |
29 ;;; Commentary: | |
30 | |
31 ;; A major mode for editing Haskell (the functional programming | |
32 ;; language, see URL `http://www.haskell.org') in Emacs. | |
33 ;; | |
34 ;; Some of its major features include: | |
35 ;; | |
36 ;; - syntax highlighting (font lock), | |
37 ;; | |
38 ;; - automatic indentation, | |
39 ;; | |
40 ;; - on-the-fly documentation, | |
41 ;; | |
42 ;; - interaction with inferior GHCi/Hugs instance, | |
43 ;; | |
44 ;; - scans declarations and places them in a menu. | |
45 ;; | |
46 ;; See URL `https://github.com/haskell/haskell-mode' and/or | |
47 ;; Info node `(haskell-mode)Introduction' for more information. | |
48 ;; | |
49 ;; Use `M-x haskell-mode-view-news` (after Haskell Mode is installed) | |
50 ;; to show information on recent changes in Haskell Mode. | |
51 | |
52 ;;; Change Log: | |
53 | |
54 ;; This mode is based on an editing mode by Simon Marlow 11/1/92 | |
55 ;; and heavily modified by Graeme E Moss and Tommy Thorn 7/11/98. | |
56 ;; | |
57 ;; Version 1.5: | |
58 ;; Added autoload for haskell-indentation | |
59 ;; | |
60 ;; Version 1.43: | |
61 ;; Various tweaks to doc strings and customization support from | |
62 ;; Ville Skyttä <scop@xemacs.org>. | |
63 ;; | |
64 ;; Version 1.42: | |
65 ;; Added autoload for GHCi inferior mode (thanks to Scott | |
66 ;; Williams for the bug report and fix). | |
67 ;; | |
68 ;; Version 1.41: | |
69 ;; Improved packaging, and made a couple more variables | |
70 ;; interactively settable. | |
71 ;; | |
72 ;; Version 1.4: | |
73 ;; Added GHCi mode from Chris Webb, and tidied up a little. | |
74 ;; | |
75 ;; Version 1.3: | |
76 ;; The literate or non-literate style of a buffer is now indicated | |
77 ;; by just the variable haskell-literate: nil, `bird', or `tex'. | |
78 ;; For literate buffers with ambiguous style, the value of | |
79 ;; haskell-literate-default is used. | |
80 ;; | |
81 ;; Version 1.2: | |
82 ;; Separated off font locking, declaration scanning and simple | |
83 ;; indentation, and made them separate modules. Modules can be | |
84 ;; added easily now. Support for modules haskell-doc, | |
85 ;; haskell-indent, and haskell-hugs. Literate and non-literate | |
86 ;; modes integrated into one mode, and literate buffer indicated by | |
87 ;; value of haskell-literate(-bird-style). | |
88 ;; | |
89 ;; Version 1.1: | |
90 ;; Added support for declaration scanning under XEmacs via | |
91 ;; func-menu. Moved operators to level two fontification. | |
92 ;; | |
93 ;; Version 1.0: | |
94 ;; Added a nice indention support from Heribert Schuetz | |
95 ;; <Heribert.Schuetz@informatik.uni-muenchen.de>: | |
96 ;; | |
97 ;; I have just hacked an Emacs Lisp function which you might prefer | |
98 ;; to `indent-relative' in haskell-mode.el. See below. It is not | |
99 ;; really Haskell-specific because it does not take into account | |
100 ;; keywords like `do', `of', and `let' (where the layout rule | |
101 ;; applies), but I already find it useful. | |
102 ;; | |
103 ;; Cleaned up the imenu support. Added support for literate scripts. | |
104 ;; | |
105 ;; Version 0.103 [HWL]: | |
106 ;; From Hans Wolfgang Loidl <hwloidl@dcs.gla.ac.uk>: | |
107 ;; | |
108 ;; I (HWL) added imenu support by copying the appropriate functions | |
109 ;; from hugs-mode. A menu-bar item "Declarations" is now added in | |
110 ;; haskell mode. The new code, however, needs some clean-up. | |
111 ;; | |
112 ;; Version 0.102: | |
113 ;; | |
114 ;; Moved C-c C-c key binding to comment-region. Leave M-g M-g to do | |
115 ;; the work. comment-start-skip is changed to comply with comment-start. | |
116 ;; | |
117 ;; Version 0.101: | |
118 ;; | |
119 ;; Altered indent-line-function to indent-relative. | |
120 ;; | |
121 ;; Version 0.100: | |
122 ;; | |
123 ;; First official release. | |
124 | |
125 ;;; Code: | |
126 | |
127 (require 'dabbrev) | |
128 (require 'compile) | |
129 (require 'flymake) | |
130 (require 'outline) | |
131 (require 'haskell-align-imports) | |
132 (require 'haskell-sort-imports) | |
133 (require 'haskell-string) | |
134 (with-no-warnings (require 'cl)) | |
135 | |
136 ;; FIXME: code-smell: too many forward decls for haskell-session are required here | |
137 (defvar haskell-session) | |
138 (declare-function haskell-process-do-try-info "haskell-process" (sym)) | |
139 (declare-function haskell-process-generate-tags "haskell-process" (&optional and-then-find-this-tag)) | |
140 (declare-function haskell-session "haskell-session" ()) | |
141 (declare-function haskell-session-all-modules "haskell-session" (&optional DONTCREATE)) | |
142 (declare-function haskell-session-cabal-dir "haskell-session" (session)) | |
143 (declare-function haskell-session-maybe "haskell-session" ()) | |
144 (declare-function haskell-session-tags-filename "haskell-session" (session)) | |
145 (declare-function haskell-session-current-dir "haskell-session" (session)) | |
146 | |
147 ;; All functions/variables start with `(literate-)haskell-'. | |
148 | |
149 ;; Version of mode. | |
150 (defconst haskell-version "@VERSION@" | |
151 "The release version of `haskell-mode'.") | |
152 | |
153 (defconst haskell-git-version "@GIT_VERSION@" | |
154 "The Git version of `haskell-mode'.") | |
155 | |
156 (defvar haskell-mode-pkg-base-dir (file-name-directory load-file-name) | |
157 "Package base directory of installed `haskell-mode'. | |
158 Used for locating additional package data files.") | |
159 | |
160 ;;;###autoload | |
161 (defun haskell-version (&optional here) | |
162 "Show the `haskell-mode` version in the echo area. | |
163 With prefix argument HERE, insert it at point. | |
164 When FULL is non-nil, use a verbose version string. | |
165 When MESSAGE is non-nil, display a message with the version." | |
166 (interactive "P") | |
167 (let* ((haskell-mode-dir (ignore-errors | |
168 (file-name-directory (or (locate-library "haskell-mode") "")))) | |
169 (_version (format "haskell-mode version %s (%s @ %s)" | |
170 haskell-version | |
171 haskell-git-version | |
172 haskell-mode-dir))) | |
173 (if here | |
174 (insert _version) | |
175 (message "%s" _version)))) | |
176 | |
177 ;;;###autoload | |
178 (defun haskell-mode-view-news () | |
179 "Display information on recent changes to haskell-mode." | |
180 (interactive) | |
181 (with-current-buffer (find-file-read-only (expand-file-name "NEWS" haskell-mode-pkg-base-dir)) | |
182 (goto-char (point-min)) | |
183 (hide-sublevels 1) | |
184 (outline-next-visible-heading 1) | |
185 (show-subtree))) | |
186 | |
187 (defgroup haskell nil | |
188 "Major mode for editing Haskell programs." | |
189 :link '(custom-manual "(haskell-mode)") | |
190 :group 'languages | |
191 :prefix "haskell-") | |
192 | |
193 ;;;###autoload | |
194 (defun haskell-customize () | |
195 "Browse the haskell customize sub-tree. | |
196 This calls 'customize-browse' with haskell as argument and makes | |
197 sure all haskell customize definitions have been loaded." | |
198 (interactive) | |
199 ;; make sure all modules with (defcustom ...)s are loaded | |
200 (mapc 'require | |
201 '(haskell-checkers haskell-compile haskell-doc haskell-font-lock haskell-indentation haskell-indent haskell-interactive-mode haskell-menu haskell-process haskell-yas inf-haskell)) | |
202 (customize-browse 'haskell)) | |
203 | |
204 ;; Are we looking at a literate script? | |
205 (defvar haskell-literate nil | |
206 "*If not nil, the current buffer contains a literate Haskell script. | |
207 Possible values are: `bird' and `tex', for Bird-style and LaTeX-style | |
208 literate scripts respectively. Set by `haskell-mode' and | |
209 `literate-haskell-mode'. For an ambiguous literate buffer -- i.e. does | |
210 not contain either \"\\begin{code}\" or \"\\end{code}\" on a line on | |
211 its own, nor does it contain \">\" at the start of a line -- the value | |
212 of `haskell-literate-default' is used.") | |
213 (make-variable-buffer-local 'haskell-literate) | |
214 (put 'haskell-literate 'safe-local-variable 'symbolp) | |
215 ;; Default literate style for ambiguous literate buffers. | |
216 (defcustom haskell-literate-default 'bird | |
217 "Default value for `haskell-literate'. | |
218 Used if the style of a literate buffer is ambiguous. This variable should | |
219 be set to the preferred literate style." | |
220 :group 'haskell | |
221 :type '(choice (const bird) (const tex) (const nil))) | |
222 | |
223 ;;;###autoload | |
224 (defvar haskell-mode-map | |
225 (let ((map (make-sparse-keymap))) | |
226 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
227 ;; For inferior haskell mode, use the below bindings | |
228 ;; (define-key map [?\M-C-x] 'inferior-haskell-send-defun) | |
229 ;; (define-key map [?\C-x ?\C-e] 'inferior-haskell-send-last-sexp) | |
230 ;; (define-key map [?\C-c ?\C-r] 'inferior-haskell-send-region) | |
231 (define-key map [?\C-x ?\C-d] 'inferior-haskell-send-decl) | |
232 (define-key map [?\C-c ?\C-z] 'switch-to-haskell) | |
233 (define-key map [?\C-c ?\C-l] 'inferior-haskell-load-file) | |
234 ;; I think it makes sense to bind inferior-haskell-load-and-run to C-c | |
235 ;; C-r, but since it used to be bound to `reload' until June 2007, I'm | |
236 ;; going to leave it out for now. | |
237 ;; (define-key map [?\C-c ?\C-r] 'inferior-haskell-load-and-run) | |
238 (define-key map [?\C-c ?\C-b] 'switch-to-haskell) | |
239 ;; (define-key map [?\C-c ?\C-s] 'inferior-haskell-start-process) | |
240 ;; That's what M-; is for. | |
241 ;; (define-key map "\C-c\C-c" 'comment-region) | |
242 (define-key map (kbd "C-c C-t") 'inferior-haskell-type) | |
243 (define-key map (kbd "C-c C-i") 'inferior-haskell-info) | |
244 (define-key map (kbd "C-c M-.") 'inferior-haskell-find-definition) | |
245 (define-key map (kbd "C-c C-d") 'inferior-haskell-find-haddock) | |
246 (define-key map [?\C-c ?\C-v] 'haskell-check) | |
247 | |
248 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
249 ;; Editing-specific commands | |
250 (define-key map (kbd "C-c C-.") 'haskell-mode-format-imports) | |
251 (define-key map [remap delete-indentation] 'haskell-delete-indentation) | |
252 | |
253 map) | |
254 "Keymap used in Haskell mode.") | |
255 | |
256 (easy-menu-define haskell-mode-menu haskell-mode-map | |
257 "Menu for the Haskell major mode." | |
258 ;; Suggestions from Pupeno <pupeno@pupeno.com>: | |
259 ;; - choose the underlying interpreter | |
260 ;; - look up docs | |
261 `("Haskell" | |
262 ["Indent line" indent-according-to-mode] | |
263 ["Indent region" indent-region mark-active] | |
264 ["(Un)Comment region" comment-region mark-active] | |
265 "---" | |
266 ["Start interpreter" switch-to-haskell] | |
267 ["Load file" inferior-haskell-load-file] | |
268 "---" | |
269 ["Load tidy core" ghc-core-create-core] | |
270 "---" | |
271 ,(if (default-boundp 'eldoc-documentation-function) | |
272 ["Doc mode" eldoc-mode | |
273 :style toggle :selected (bound-and-true-p eldoc-mode)] | |
274 ["Doc mode" haskell-doc-mode | |
275 :style toggle :selected (and (boundp 'haskell-doc-mode) haskell-doc-mode)]) | |
276 ["Customize" (customize-group 'haskell)] | |
277 )) | |
278 | |
279 ;; Syntax table. | |
280 (defvar haskell-mode-syntax-table | |
281 (let ((table (make-syntax-table))) | |
282 (modify-syntax-entry ?\ " " table) | |
283 (modify-syntax-entry ?\t " " table) | |
284 (modify-syntax-entry ?\" "\"" table) | |
285 (modify-syntax-entry ?\' "\'" table) | |
286 (modify-syntax-entry ?_ "w" table) | |
287 (modify-syntax-entry ?\( "()" table) | |
288 (modify-syntax-entry ?\) ")(" table) | |
289 (modify-syntax-entry ?\[ "(]" table) | |
290 (modify-syntax-entry ?\] ")[" table) | |
291 | |
292 (cond ((featurep 'xemacs) | |
293 ;; I don't know whether this is equivalent to the below | |
294 ;; (modulo nesting). -- fx | |
295 (modify-syntax-entry ?{ "(}5" table) | |
296 (modify-syntax-entry ?} "){8" table) | |
297 (modify-syntax-entry ?- "_ 1267" table)) | |
298 (t | |
299 ;; In Emacs 21, the `n' indicates that they nest. | |
300 ;; The `b' annotation is actually ignored because it's only | |
301 ;; meaningful on the second char of a comment-starter, so | |
302 ;; on Emacs 20 and before we get wrong results. --Stef | |
303 (modify-syntax-entry ?\{ "(}1nb" table) | |
304 (modify-syntax-entry ?\} "){4nb" table) | |
305 (modify-syntax-entry ?- "_ 123" table))) | |
306 (modify-syntax-entry ?\n ">" table) | |
307 | |
308 (let (i lim) | |
309 (map-char-table | |
310 (lambda (k v) | |
311 (when (equal v '(1)) | |
312 ;; The current Emacs 22 codebase can pass either a char | |
313 ;; or a char range. | |
314 (if (consp k) | |
315 (setq i (car k) | |
316 lim (cdr k)) | |
317 (setq i k | |
318 lim k)) | |
319 (while (<= i lim) | |
320 (when (> i 127) | |
321 (modify-syntax-entry i "_" table)) | |
322 (setq i (1+ i))))) | |
323 (standard-syntax-table))) | |
324 | |
325 (modify-syntax-entry ?\` "$`" table) | |
326 (modify-syntax-entry ?\\ "\\" table) | |
327 (mapc (lambda (x) | |
328 (modify-syntax-entry x "_" table)) | |
329 ;; Some of these are actually OK by default. | |
330 "!#$%&*+./:<=>?@^|~") | |
331 (unless (featurep 'mule) | |
332 ;; Non-ASCII syntax should be OK, at least in Emacs. | |
333 (mapc (lambda (x) | |
334 (modify-syntax-entry x "_" table)) | |
335 (concat "¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿" | |
336 "×÷")) | |
337 (mapc (lambda (x) | |
338 (modify-syntax-entry x "w" table)) | |
339 (concat "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ" | |
340 "ØÙÚÛÜÝÞß" | |
341 "àáâãäåæçèéêëìíîïðñòóôõö" | |
342 "øùúûüýþÿ"))) | |
343 table) | |
344 "Syntax table used in Haskell mode.") | |
345 | |
346 (defun haskell-ident-at-point () | |
347 "Return the identifier under point, or nil if none found. | |
348 May return a qualified name." | |
349 (let ((reg (haskell-ident-pos-at-point))) | |
350 (when reg | |
351 (buffer-substring-no-properties (car reg) (cdr reg))))) | |
352 | |
353 (defun haskell-ident-pos-at-point () | |
354 "Return the span of the identifier under point, or nil if none found. | |
355 May return a qualified name." | |
356 (save-excursion | |
357 ;; Skip whitespace if we're on it. That way, if we're at "map ", we'll | |
358 ;; see the word "map". | |
359 (if (and (not (eobp)) | |
360 (eq ? (char-syntax (char-after)))) | |
361 (skip-chars-backward " \t")) | |
362 | |
363 (let ((case-fold-search nil)) | |
364 (multiple-value-bind (start end) | |
365 (if (looking-at "\\s_") | |
366 (list (progn (skip-syntax-backward "_") (point)) | |
367 (progn (skip-syntax-forward "_") (point))) | |
368 (list | |
369 (progn (skip-syntax-backward "w'") | |
370 (skip-syntax-forward "'") (point)) | |
371 (progn (skip-syntax-forward "w'") (point)))) | |
372 ;; If we're looking at a module ID that qualifies further IDs, add | |
373 ;; those IDs. | |
374 (goto-char start) | |
375 (while (and (looking-at "[[:upper:]]") (eq (char-after end) ?.) | |
376 ;; It's a module ID that qualifies further IDs. | |
377 (goto-char (1+ end)) | |
378 (save-excursion | |
379 (when (not (zerop (skip-syntax-forward | |
380 (if (looking-at "\\s_") "_" "w'")))) | |
381 (setq end (point)))))) | |
382 ;; If we're looking at an ID that's itself qualified by previous | |
383 ;; module IDs, add those too. | |
384 (goto-char start) | |
385 (if (eq (char-after) ?.) (forward-char 1)) ;Special case for "." | |
386 (while (and (eq (char-before) ?.) | |
387 (progn (forward-char -1) | |
388 (not (zerop (skip-syntax-backward "w'")))) | |
389 (skip-syntax-forward "'") | |
390 (looking-at "[[:upper:]]")) | |
391 (setq start (point))) | |
392 ;; This is it. | |
393 (cons start end))))) | |
394 | |
395 (defun haskell-delete-indentation (&optional arg) | |
396 "Like `delete-indentation' but ignoring Bird-style \">\"." | |
397 (interactive "*P") | |
398 (let ((fill-prefix (or fill-prefix (if (eq haskell-literate 'bird) ">")))) | |
399 (delete-indentation arg))) | |
400 | |
401 ;; Various mode variables. | |
402 | |
403 (defcustom haskell-mode-hook nil | |
404 "Hook run after entering `haskell-mode'. | |
405 | |
406 Some of the supported modules that can be activated via this hook: | |
407 | |
408 `haskell-decl-scan', Graeme E Moss | |
409 Scans top-level declarations, and places them in a menu. | |
410 | |
411 `haskell-doc', Hans-Wolfgang Loidl | |
412 Echoes types of functions or syntax of keywords when the cursor is idle. | |
413 | |
414 `haskell-indentation', Kristof Bastiaensen | |
415 Intelligent semi-automatic indentation Mk2 | |
416 | |
417 `haskell-indent', Guy Lapalme | |
418 Intelligent semi-automatic indentation. | |
419 | |
420 `haskell-simple-indent', Graeme E Moss and Heribert Schuetz | |
421 Simple indentation. | |
422 | |
423 Module X is activated using the command `turn-on-X'. For example, | |
424 `haskell-doc' is activated using `turn-on-haskell-doc'. | |
425 For more information on a specific module, see the help for its `X-mode' | |
426 function. Some modules can be deactivated using `turn-off-X'. | |
427 | |
428 See Info node `(haskell-mode)haskell-mode-hook' for more details. | |
429 | |
430 Warning: do not enable more than one of the three indentation | |
431 modes. See Info node `(haskell-mode)indentation' for more | |
432 details." | |
433 :type 'hook | |
434 :group 'haskell | |
435 :link '(info-link "(haskell-mode)haskell-mode-hook") | |
436 :link '(function-link haskell-mode) | |
437 :options `(capitalized-words-mode | |
438 imenu-add-menubar-index | |
439 turn-on-eldoc-mode | |
440 turn-on-haskell-decl-scan | |
441 turn-on-haskell-doc | |
442 turn-on-haskell-indent | |
443 turn-on-haskell-indentation | |
444 turn-on-haskell-simple-indent | |
445 turn-on-haskell-unicode-input-method)) | |
446 | |
447 (defvar eldoc-print-current-symbol-info-function) | |
448 | |
449 ;; For compatibility with Emacs < 24, derive conditionally | |
450 (defalias 'haskell-parent-mode | |
451 (if (fboundp 'prog-mode) 'prog-mode 'fundamental-mode)) | |
452 | |
453 ;; The main mode functions | |
454 ;;;###autoload | |
455 (define-derived-mode haskell-mode haskell-parent-mode "Haskell" | |
456 "Major mode for editing Haskell programs. | |
457 | |
458 See also Info node `(haskell-mode)Getting Started' for more | |
459 information about this mode. | |
460 | |
461 \\<haskell-mode-map> | |
462 Literate scripts are supported via `literate-haskell-mode'. | |
463 The variable `haskell-literate' indicates the style of the script in the | |
464 current buffer. See the documentation on this variable for more details. | |
465 | |
466 Use `haskell-version' to find out what version of Haskell mode you are | |
467 currently using. | |
468 | |
469 Additional Haskell mode modules can be hooked in via `haskell-mode-hook'; | |
470 see documentation for that variable for more details." | |
471 :group 'haskell | |
472 (set (make-local-variable 'paragraph-start) (concat "^$\\|" page-delimiter)) | |
473 (set (make-local-variable 'paragraph-separate) paragraph-start) | |
474 (set (make-local-variable 'fill-paragraph-function) 'haskell-fill-paragraph) | |
475 ;; (set (make-local-variable 'adaptive-fill-function) 'haskell-adaptive-fill) | |
476 (set (make-local-variable 'adaptive-fill-mode) nil) | |
477 (set (make-local-variable 'comment-start) "-- ") | |
478 (set (make-local-variable 'comment-padding) 0) | |
479 (set (make-local-variable 'comment-start-skip) "[-{]-[ \t]*") | |
480 (set (make-local-variable 'comment-end) "") | |
481 (set (make-local-variable 'comment-end-skip) "[ \t]*\\(-}\\|\\s>\\)") | |
482 (set (make-local-variable 'parse-sexp-ignore-comments) nil) | |
483 (set (make-local-variable 'indent-line-function) 'haskell-mode-suggest-indent-choice) | |
484 ;; Set things up for eldoc-mode. | |
485 (set (make-local-variable 'eldoc-documentation-function) | |
486 'haskell-doc-current-info) | |
487 ;; Set things up for imenu. | |
488 (set (make-local-variable 'imenu-create-index-function) | |
489 'haskell-ds-create-imenu-index) | |
490 ;; Set things up for font-lock. | |
491 (set (make-local-variable 'font-lock-defaults) | |
492 '(haskell-font-lock-choose-keywords | |
493 nil nil ((?\' . "w") (?_ . "w")) nil | |
494 (font-lock-syntactic-keywords | |
495 . haskell-font-lock-choose-syntactic-keywords) | |
496 (font-lock-syntactic-face-function | |
497 . haskell-syntactic-face-function) | |
498 ;; Get help from font-lock-syntactic-keywords. | |
499 (parse-sexp-lookup-properties . t))) | |
500 ;; Haskell's layout rules mean that TABs have to be handled with extra care. | |
501 ;; The safer option is to avoid TABs. The second best is to make sure | |
502 ;; TABs stops are 8 chars apart, as mandated by the Haskell Report. --Stef | |
503 (set (make-local-variable 'indent-tabs-mode) nil) | |
504 (set (make-local-variable 'tab-width) 8) | |
505 ;; dynamic abbrev support: recognize Haskell identifiers | |
506 ;; Haskell is case-sensitive language | |
507 (set (make-local-variable 'dabbrev-case-fold-search) nil) | |
508 (set (make-local-variable 'dabbrev-case-distinction) nil) | |
509 (set (make-local-variable 'dabbrev-case-replace) nil) | |
510 (set (make-local-variable 'dabbrev-abbrev-char-regexp) "\\sw\\|[.]") | |
511 (setq haskell-literate nil) | |
512 (add-hook 'before-save-hook 'haskell-mode-before-save-handler nil t) | |
513 (add-hook 'after-save-hook 'haskell-mode-after-save-handler nil t) | |
514 ) | |
515 | |
516 (defun haskell-fill-paragraph (justify) | |
517 (save-excursion | |
518 ;; Fill paragraph should only work in comments. | |
519 ;; The -- comments are handled properly by default | |
520 ;; The {- -} comments need some extra love. | |
521 (let* ((syntax-values (syntax-ppss)) | |
522 (comment-num (nth 4 syntax-values))) | |
523 (cond | |
524 ((eq t comment-num) | |
525 ;; standard fill works wonders inside a non-nested comment | |
526 (fill-comment-paragraph justify)) | |
527 | |
528 ((integerp comment-num) | |
529 ;; we are in a nested comment. lets narrow to comment content | |
530 ;; and use plain paragraph fill for that | |
531 (let* ((comment-start-point (nth 8 syntax-values)) | |
532 (comment-end-point | |
533 (save-excursion | |
534 (re-search-forward "-}" (point-max) t comment-num) | |
535 (point))) | |
536 (fill-paragraph-handle-comment nil)) | |
537 (save-restriction | |
538 (narrow-to-region (+ 2 comment-start-point) (- comment-end-point 2)) | |
539 (fill-paragraph justify)))) | |
540 ((eolp) | |
541 ;; do nothing outside of a comment | |
542 t) | |
543 (t | |
544 ;; go to end of line and try again | |
545 (end-of-line) | |
546 (haskell-fill-paragraph justify)))))) | |
547 | |
548 | |
549 ;; (defun haskell-adaptive-fill () | |
550 ;; ;; We want to use "-- " as the prefix of "-- |", etc. | |
551 ;; (let* ((line-end (save-excursion (end-of-line) (point))) | |
552 ;; (line-start (point))) | |
553 ;; (save-excursion | |
554 ;; (unless (in-comment) | |
555 ;; ;; Try to find the start of a comment. We only fill comments. | |
556 ;; (search-forward-regexp comment-start-skip line-end t)) | |
557 ;; (when (in-comment) | |
558 ;; (let ();(prefix-start (point))) | |
559 ;; (skip-syntax-forward "^w") | |
560 ;; (make-string (- (point) line-start) ?\s)))))) | |
561 | |
562 | |
563 | |
564 ;;;###autoload | |
565 (define-derived-mode literate-haskell-mode haskell-mode "LitHaskell" | |
566 "As `haskell-mode' but for literate scripts." | |
567 (setq haskell-literate | |
568 (save-excursion | |
569 (goto-char (point-min)) | |
570 (cond | |
571 ((re-search-forward "^\\\\\\(begin\\|end\\){code}$" nil t) 'tex) | |
572 ((re-search-forward "^>" nil t) 'bird) | |
573 (t haskell-literate-default)))) | |
574 (if (eq haskell-literate 'bird) | |
575 ;; fill-comment-paragraph isn't much use there, and even gets confused | |
576 ;; by the syntax-table text-properties we add to mark the first char | |
577 ;; of each line as a comment-starter. | |
578 (set (make-local-variable 'fill-paragraph-handle-comment) nil)) | |
579 (set (make-local-variable 'mode-line-process) | |
580 '("/" (:eval (symbol-name haskell-literate))))) | |
581 | |
582 ;;;###autoload(add-to-list 'auto-mode-alist '("\\.\\(?:[gh]s\\|hi\\)\\'" . haskell-mode)) | |
583 ;;;###autoload(add-to-list 'auto-mode-alist '("\\.l[gh]s\\'" . literate-haskell-mode)) | |
584 ;;;###autoload(add-to-list 'interpreter-mode-alist '("runghc" . haskell-mode)) | |
585 ;;;###autoload(add-to-list 'interpreter-mode-alist '("runhaskell" . haskell-mode)) | |
586 | |
587 (defcustom haskell-hoogle-command | |
588 (if (executable-find "hoogle") "hoogle") | |
589 "Name of the command to use to query Hoogle. | |
590 If nil, use the Hoogle web-site." | |
591 :group 'haskell | |
592 :type '(choice (const :tag "Use Web-site" nil) | |
593 string)) | |
594 | |
595 ;;;###autoload | |
596 (defun haskell-hoogle (query) | |
597 "Do a Hoogle search for QUERY." | |
598 (interactive | |
599 (let ((def (haskell-ident-at-point))) | |
600 (if (and def (symbolp def)) (setq def (symbol-name def))) | |
601 (list (read-string (if def | |
602 (format "Hoogle query (default %s): " def) | |
603 "Hoogle query: ") | |
604 nil nil def)))) | |
605 (if (null haskell-hoogle-command) | |
606 (browse-url (format "http://haskell.org/hoogle/?q=%s" query)) | |
607 (lexical-let ((temp-buffer (help-buffer))) | |
608 (with-output-to-temp-buffer temp-buffer | |
609 (with-current-buffer standard-output | |
610 (let ((hoogle-process | |
611 (start-process "hoogle" (current-buffer) haskell-hoogle-command query)) | |
612 (scroll-to-top | |
613 (lambda (process event) | |
614 (set-window-start (get-buffer-window temp-buffer t) 1)))) | |
615 (set-process-sentinel hoogle-process scroll-to-top))))))) | |
616 | |
617 ;;;###autoload | |
618 (defalias 'hoogle 'haskell-hoogle) | |
619 | |
620 ;;;###autoload | |
621 (defun haskell-hayoo (query) | |
622 "Do a Hayoo search for QUERY." | |
623 (interactive | |
624 (let ((def (haskell-ident-at-point))) | |
625 (if (and def (symbolp def)) (setq def (symbol-name def))) | |
626 (list (read-string (if def | |
627 (format "Hayoo query (default %s): " def) | |
628 "Hayoo query: ") | |
629 nil nil def)))) | |
630 (browse-url (format "http://holumbus.fh-wedel.de/hayoo/hayoo.html?query=%s" query))) | |
631 | |
632 ;;;###autoload | |
633 (defalias 'hayoo 'haskell-hayoo) | |
634 | |
635 (defcustom haskell-check-command "hlint" | |
636 "*Command used to check a Haskell file." | |
637 :group 'haskell | |
638 :type '(choice (const "hlint") | |
639 (const "ghc -fno-code") | |
640 (string :tag "Other command"))) | |
641 | |
642 (defcustom haskell-completing-read-function 'ido-completing-read | |
643 "Default function to use for completion." | |
644 :group 'haskell | |
645 :type '(choice | |
646 (function-item :tag "ido" :value ido-completing-read) | |
647 (function-item :tag "helm" :value helm--completing-read-default) | |
648 (function-item :tag "completing-read" :value completing-read) | |
649 (function :tag "Custom function"))) | |
650 | |
651 (defcustom haskell-stylish-on-save nil | |
652 "Whether to run stylish-haskell on the buffer before saving." | |
653 :group 'haskell | |
654 :type 'boolean) | |
655 | |
656 (defcustom haskell-tags-on-save nil | |
657 "Generate tags via hasktags after saving." | |
658 :group 'haskell | |
659 :type 'boolean) | |
660 | |
661 (defvar haskell-saved-check-command nil | |
662 "Internal use.") | |
663 | |
664 (defcustom haskell-indent-spaces 2 | |
665 "Number of spaces to indent inwards.") | |
666 | |
667 ;; Like Python. Should be abstracted, sigh. | |
668 (defun haskell-check (command) | |
669 "Check a Haskell file (default current buffer's file). | |
670 Runs COMMAND, a shell command, as if by `compile'. | |
671 See `haskell-check-command' for the default." | |
672 (interactive | |
673 (list (read-string "Checker command: " | |
674 (or haskell-saved-check-command | |
675 (concat haskell-check-command " " | |
676 (let ((name (buffer-file-name))) | |
677 (if name | |
678 (file-name-nondirectory name)))))))) | |
679 (setq haskell-saved-check-command command) | |
680 (save-some-buffers (not compilation-ask-about-save) nil) | |
681 (compilation-start command)) | |
682 | |
683 (defun haskell-flymake-init () | |
684 "Flymake init function for Haskell. | |
685 To be added to `flymake-init-create-temp-buffer-copy'." | |
686 (let ((checker-elts (and haskell-saved-check-command | |
687 (split-string haskell-saved-check-command)))) | |
688 (list (car checker-elts) | |
689 (append (cdr checker-elts) | |
690 (list (flymake-init-create-temp-buffer-copy | |
691 'flymake-create-temp-inplace)))))) | |
692 | |
693 (add-to-list 'flymake-allowed-file-name-masks '("\\.l?hs\\'" haskell-flymake-init)) | |
694 | |
695 (defun haskell-mode-suggest-indent-choice () | |
696 "Ran when the user tries to indent in the buffer but no indentation mode has been selected. | |
697 Brings up the documentation for haskell-mode-hook." | |
698 (describe-variable 'haskell-mode-hook)) | |
699 | |
700 (defun haskell-mode-format-imports () | |
701 "Format the imports by aligning and sorting them." | |
702 (interactive) | |
703 (let ((col (current-column))) | |
704 (haskell-sort-imports) | |
705 (haskell-align-imports) | |
706 (goto-char (+ (line-beginning-position) | |
707 col)))) | |
708 | |
709 (defun haskell-mode-message-line (str) | |
710 "Message only one line, multiple lines just disturbs the programmer." | |
711 (let ((lines (split-string str "\n" t))) | |
712 (when (and (car lines) (stringp (car lines))) | |
713 (message "%s" | |
714 (concat (car lines) | |
715 (if (and (cdr lines) (stringp (cadr lines))) | |
716 (format " [ %s .. ]" (haskell-string-take (haskell-trim (cadr lines)) 10)) | |
717 "")))))) | |
718 | |
719 (defun haskell-mode-contextual-space () | |
720 "Contextually do clever stuff when hitting space." | |
721 (interactive) | |
722 (if (not (haskell-session-maybe)) | |
723 (self-insert-command 1) | |
724 (cond ((save-excursion (forward-word -1) | |
725 (looking-at "^import$")) | |
726 (insert " ") | |
727 (let ((module (funcall haskell-completing-read-function "Module: " (haskell-session-all-modules)))) | |
728 (insert module) | |
729 (haskell-mode-format-imports))) | |
730 ((not (string= "" (save-excursion (forward-char -1) (haskell-ident-at-point)))) | |
731 (let ((ident (save-excursion (forward-char -1) (haskell-ident-at-point)))) | |
732 (insert " ") | |
733 (haskell-process-do-try-info ident))) | |
734 (t (insert " "))))) | |
735 | |
736 (defun haskell-mode-before-save-handler () | |
737 "Function that will be called before buffer's saving." | |
738 ) | |
739 | |
740 (defun haskell-mode-after-save-handler () | |
741 "Function that will be called after buffer's saving." | |
742 (when haskell-tags-on-save | |
743 (ignore-errors (when (and (boundp 'haskell-session) haskell-session) | |
744 (haskell-process-generate-tags)))) | |
745 (when haskell-stylish-on-save | |
746 (ignore-errors (haskell-mode-stylish-buffer))) | |
747 (let ((before-save-hook '()) | |
748 (after-save-hook '())) | |
749 (basic-save-buffer)) | |
750 ) | |
751 | |
752 (defun haskell-mode-buffer-apply-command (cmd) | |
753 "Execute shell command CMD with current buffer as input and | |
754 replace the whole buffer with the output. If CMD fails the buffer | |
755 remains unchanged." | |
756 (set-buffer-modified-p t) | |
757 (let* ((chomp (lambda (str) | |
758 (while (string-match "\\`\n+\\|^\\s-+\\|\\s-+$\\|\n+\\'" str) | |
759 (setq str (replace-match "" t t str))) | |
760 str)) | |
761 (errout (lambda (fmt &rest args) | |
762 (let* ((warning-fill-prefix " ")) | |
763 (display-warning cmd (apply 'format fmt args) :warning)))) | |
764 (filename (buffer-file-name (current-buffer))) | |
765 (cmd-prefix (replace-regexp-in-string " .*" "" cmd)) | |
766 (tmp-file (make-temp-file cmd-prefix)) | |
767 (err-file (make-temp-file cmd-prefix)) | |
768 (default-directory (if (and (boundp 'haskell-session) | |
769 haskell-session) | |
770 (haskell-session-cabal-dir haskell-session) | |
771 default-directory)) | |
772 (errcode (with-temp-file tmp-file | |
773 (call-process cmd filename | |
774 (list (current-buffer) err-file) nil))) | |
775 (stderr-output | |
776 (with-temp-buffer | |
777 (insert-file-contents err-file) | |
778 (funcall chomp (buffer-substring-no-properties (point-min) (point-max))))) | |
779 (stdout-output | |
780 (with-temp-buffer | |
781 (insert-file-contents tmp-file) | |
782 (buffer-substring-no-properties (point-min) (point-max))))) | |
783 (if (string= "" stderr-output) | |
784 (if (string= "" stdout-output) | |
785 (funcall errout | |
786 "Error: %s produced no output, leaving buffer alone" cmd) | |
787 (save-restriction | |
788 (widen) | |
789 ;; command successful, insert file with replacement to preserve | |
790 ;; markers. | |
791 (insert-file-contents tmp-file nil nil nil t))) | |
792 ;; non-null stderr, command must have failed | |
793 (funcall errout "%s failed: %s" cmd stderr-output) | |
794 ) | |
795 (delete-file tmp-file) | |
796 (delete-file err-file) | |
797 )) | |
798 | |
799 (defun haskell-mode-stylish-buffer () | |
800 "Apply stylish-haskell to the current buffer." | |
801 (interactive) | |
802 (let ((column (current-column)) | |
803 (line (line-number-at-pos))) | |
804 (haskell-mode-buffer-apply-command "stylish-haskell") | |
805 (goto-char (point-min)) | |
806 (forward-line (1- line)) | |
807 (goto-char (+ column (point))))) | |
808 | |
809 (defun haskell-mode-tag-find (&optional next-p) | |
810 "The tag find function, specific for the particular session." | |
811 (interactive "P") | |
812 (cond | |
813 ((eq 'font-lock-string-face | |
814 (get-text-property (point) 'face)) | |
815 (let* ((string (save-excursion | |
816 (buffer-substring-no-properties | |
817 (1+ (search-backward-regexp "\"" (line-beginning-position) nil 1)) | |
818 (1- (progn (forward-char 1) | |
819 (search-forward-regexp "\"" (line-end-position) nil 1)))))) | |
820 (fp (expand-file-name string | |
821 (haskell-session-cabal-dir (haskell-session))))) | |
822 (find-file | |
823 (read-file-name | |
824 "" | |
825 fp | |
826 fp)))) | |
827 (t (let ((tags-file-name (haskell-session-tags-filename (haskell-session))) | |
828 (tags-revert-without-query t) | |
829 (ident (haskell-ident-at-point))) | |
830 (when (not (string= "" (haskell-trim ident))) | |
831 (cond ((file-exists-p tags-file-name) | |
832 (find-tag ident next-p)) | |
833 (t (haskell-process-generate-tags ident)))))))) | |
834 | |
835 ;; From Bryan O'Sullivan's blog: | |
836 ;; http://www.serpentine.com/blog/2007/10/09/using-emacs-to-insert-scc-annotations-in-haskell-code/ | |
837 (defun haskell-mode-insert-scc-at-point () | |
838 "Insert an SCC annotation at point." | |
839 (interactive) | |
840 (if (or (looking-at "\\b\\|[ \t]\\|$") (and (not (bolp)) | |
841 (save-excursion | |
842 (forward-char -1) | |
843 (looking-at "\\b\\|[ \t]")))) | |
844 (let ((space-at-point (looking-at "[ \t]"))) | |
845 (unless (and (not (bolp)) (save-excursion | |
846 (forward-char -1) | |
847 (looking-at "[ \t]"))) | |
848 (insert " ")) | |
849 (insert "{-# SCC \"\" #-}") | |
850 (unless space-at-point | |
851 (insert " ")) | |
852 (forward-char (if space-at-point -5 -6))) | |
853 (error "Not over an area of whitespace"))) | |
854 | |
855 ;; Also Bryan O'Sullivan's. | |
856 (defun haskell-mode-kill-scc-at-point () | |
857 "Kill the SCC annotation at point." | |
858 (interactive) | |
859 (save-excursion | |
860 (let ((old-point (point)) | |
861 (scc "\\({-#[ \t]*SCC \"[^\"]*\"[ \t]*#-}\\)[ \t]*")) | |
862 (while (not (or (looking-at scc) (bolp))) | |
863 (forward-char -1)) | |
864 (if (and (looking-at scc) | |
865 (<= (match-beginning 1) old-point) | |
866 (> (match-end 1) old-point)) | |
867 (kill-region (match-beginning 0) (match-end 0)) | |
868 (error "No SCC at point"))))) | |
869 | |
870 (defun haskell-rgrep (&optional prompt) | |
871 "Grep the effective project for the symbol at point. Very | |
872 useful for codebase navigation. Prompts for an arbitrary regexp | |
873 given a prefix arg." | |
874 (interactive "P") | |
875 (let ((sym (if prompt | |
876 (read-from-minibuffer "Look for: ") | |
877 (haskell-ident-at-point)))) | |
878 (rgrep sym | |
879 "*.hs" ;; TODO: common Haskell extensions. | |
880 (haskell-session-current-dir (haskell-session))))) | |
881 | |
882 (defun haskell-fontify-as-mode (text mode) | |
883 "Fontify TEXT as MODE, returning the fontified text." | |
884 (with-temp-buffer | |
885 (funcall mode) | |
886 (insert text) | |
887 (font-lock-fontify-buffer) | |
888 (buffer-substring (point-min) (point-max)))) | |
889 | |
890 (defun haskell-guess-module-name () | |
891 "Guess the current module name of the buffer." | |
892 (interactive) | |
893 (let ((components (loop for part | |
894 in (reverse (split-string (buffer-file-name) "/")) | |
895 while (let ((case-fold-search nil)) | |
896 (string-match "^[A-Z]+" part)) | |
897 collect (replace-regexp-in-string "\\.l?hs$" "" part)))) | |
898 (mapconcat 'identity (reverse components) "."))) | |
899 | |
900 | |
901 ;; Provide ourselves: | |
902 | |
903 (provide 'haskell-mode) | |
904 | |
905 ;; Local Variables: | |
906 ;; byte-compile-warnings: (not cl-functions) | |
907 ;; End: | |
908 | |
909 ;;; haskell-mode.el ends here |