~ubuntu-branches/ubuntu/trusty/haskell-mode/trusty-proposed

« back to all changes in this revision

Viewing changes to haskell-decl-scan.el

  • Committer: Package Import Robot
  • Author(s): Barak A. Pearlmutter
  • Date: 2013-12-27 17:38:40 UTC
  • mfrom: (1.3.5)
  • Revision ID: package-import@ubuntu.com-20131227173840-5qjuscd4uj7ag382
Tags: 13.10-2
* build dependency on texinfo for /usr/bin/makeinfo (closes: #730955)
* no haskell-ghci.el breaking agda-mode (<< 2.3.2) (closes: #731326)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
;;; haskell-decl-scan.el --- Declaration scanning module for Haskell Mode
2
2
 
3
3
;; Copyright (C) 2004, 2005, 2007, 2009  Free Software Foundation, Inc.
4
 
;; Copyright (C) 1997-1998 Graeme E Moss
 
4
;; Copyright (C) 1997-1998  Graeme E Moss
5
5
 
6
6
;; Author: 1997-1998 Graeme E Moss <gem@cs.york.ac.uk>
7
7
;; Maintainer: Stefan Monnier <monnier@gnu.org>
32
32
;;
33
33
;;
34
34
;; Installation:
35
 
;; 
 
35
;;
36
36
;; To turn declaration scanning on for all Haskell buffers under the
37
37
;; Haskell mode of Moss&Thorn, add this to .emacs:
38
38
;;
43
43
;;
44
44
;; Customisation:
45
45
;;
46
 
;; None available so far.
 
46
;; M-x customize-group haskell-decl-scan
47
47
;;
48
48
;;
49
49
;; History:
54
54
;; example of the problem or suggestion.  Note that this library
55
55
;; requires a reasonably recent version of Emacs.
56
56
;;
57
 
;; Uses `imenu' under Emacs, and `func-menu' under XEmacs.
 
57
;; Uses `imenu' under Emacs.
58
58
;;
59
59
;; Version 1.2:
60
60
;;   Added support for LaTeX-style literate scripts.
89
89
;;   that it does.  The ability to turn off scanning would also be
90
90
;;   useful.  (Note that re-running (literate-)haskell-mode seems to
91
91
;;   cause no problems.)
92
 
;;
93
 
;; . Inconsistency: we define the start of a declaration in `imenu' as
94
 
;;   the start of the line the declaration starts on, but in
95
 
;;   `func-menu' as the start of the name that the declaration is
96
 
;;   given (eg. "class Eq a => Ord a ..." starts at "class" in `imenu'
97
 
;;   but at "Ord" in `func-menu').  This avoids rescanning of the
98
 
;;   buffer by the goto functions of `func-menu' but allows `imenu' to
99
 
;;   have the better definition of the start of the declaration (IMO).
100
 
;;
101
 
;; . `func-menu' cannot cope well with spaces in declaration names.
102
 
;;   This is unavoidable in "instance Eq Int" (changing the spaces to
103
 
;;   underscores would cause rescans of the buffer).  Note though that
104
 
;;   `fume-prompt-function-goto' (usually bound to "C-c g") does cope
105
 
;;   with spaces okay.
106
 
;;
107
 
;; . Would like to extend the goto functions given by `func-menu'
108
 
;;   under XEmacs to Emacs.  Would have to implement these
109
 
;;   ourselves as `imenu' does not provide them.
110
 
;;
111
 
;; . `func-menu' uses its own syntax table when grabbing a declaration
112
 
;;   name to lookup (why doesn't it use the syntax table of the
113
 
;;   buffer?) so some declaration names will not be grabbed correctly,
114
 
;;   eg. "fib'" will be grabbed as "fib" since "'" is not a word or
115
 
;;   symbol constituent under the syntax table `func-menu' uses.
116
92
 
117
93
;; All functions/variables start with
118
94
;; `(turn-(on/off)-)haskell-decl-scan' or `haskell-ds-'.
127
103
(require 'haskell-mode)
128
104
(require 'syntax)
129
105
(with-no-warnings (require 'cl))
130
 
 
131
 
;;;###autoload
132
 
;; As `cl' defines macros that `imenu' uses, we must require them at
133
 
;; compile time.
134
 
(eval-when-compile
135
 
  (condition-case nil
136
 
      (require 'imenu)
137
 
    (error nil))
138
 
  ;; It makes a big difference if we don't copy the syntax table here,
139
 
  ;; as Emacs 21 does, but Emacs 22 doesn't.
140
 
  (unless (eq (syntax-table)
141
 
              (with-syntax-table (syntax-table) (syntax-table)))
142
 
    (defmacro with-syntax-table (table &rest body)
143
 
      "Evaluate BODY with syntax table of current buffer set to a copy of TABLE.
144
 
The syntax table of the current buffer is saved, BODY is evaluated, and the
145
 
saved table is restored, even in case of an abnormal exit.
146
 
Value is what BODY returns."
147
 
      (let ((old-table (make-symbol "table"))
148
 
            (old-buffer (make-symbol "buffer")))
149
 
        `(let ((,old-table (syntax-table))
150
 
               (,old-buffer (current-buffer)))
151
 
           (unwind-protect
152
 
               (progn
153
 
                 (set-syntax-table ,table)
154
 
                 ,@body)
155
 
             (save-current-buffer
156
 
               (set-buffer ,old-buffer)
157
 
               (set-syntax-table ,old-table))))))))
 
106
(require 'imenu)
 
107
 
 
108
(defgroup haskell-decl-scan nil
 
109
  "Haskell declaration scanning (`imenu' support)."
 
110
  :link '(custom-manual "(haskell-mode)haskell-decl-scan-mode")
 
111
  :group 'haskell
 
112
  :prefix "haskell-decl-scan-")
 
113
 
 
114
(defcustom haskell-decl-scan-bindings-as-variables nil
 
115
  "Whether to put top-level value bindings into a \"Variables\" category."
 
116
  :group 'haskell-decl-scan
 
117
  :type 'boolean)
 
118
 
 
119
(defcustom haskell-decl-scan-add-to-menubar t
 
120
  "Whether to add a \"Declarations\" menu entry to menu bar."
 
121
  :group 'haskell-decl-scan
 
122
  :type 'boolean)
158
123
 
159
124
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
160
125
;; General declaration scanning functions.
161
126
 
162
 
(defalias 'haskell-ds-match-string
163
 
  (if (fboundp 'match-string-no-properties)
164
 
      'match-string-no-properties
165
 
    (lambda (num)
166
 
      "As `match-string' except that the string is stripped of properties."
167
 
      (format "%s" (match-string num)))))
168
 
 
169
127
(defvar haskell-ds-start-keywords-re
170
128
  (concat "\\(\\<"
171
 
          "class\\|data\\|i\\(mport\\|n\\(fix\\(\\|[lr]\\)\\|stance\\)\\)\\|"
172
 
          "module\\|primitive\\|type\\|newtype"
173
 
          "\\)\\>")
 
129
          "class\\|data\\|i\\(mport\\|n\\(fix\\(\\|[lr]\\)\\|stance\\)\\)\\|"
 
130
          "module\\|primitive\\|type\\|newtype"
 
131
          "\\)\\>")
174
132
  "Keywords that may start a declaration.")
175
133
 
176
134
(defvar haskell-ds-syntax-table
197
155
      (if (looking-at haskell-ds-start-keywords-re)
198
156
          nil
199
157
        (or ;; Parenthesized symbolic variable.
200
 
         (and (looking-at "(\\(\\s_+\\))") (haskell-ds-match-string 1))
 
158
         (and (looking-at "(\\(\\s_+\\))") (match-string-no-properties 1))
201
159
         ;; General case.
202
160
         (if (looking-at
203
161
              (if (eq ?\( (char-after))
208
166
                    ;; possible speeds things up.
209
167
                    "\\(\\'\\)?\\s-*\\(\\s_+\\|`\\(\\sw+\\)`\\)")
210
168
                "\\(\\sw+\\)?\\s-*\\(\\s_+\\|`\\(\\sw+\\)`\\)"))
211
 
             (let ((match2 (haskell-ds-match-string 2)))
 
169
             (let ((match2 (match-string-no-properties 2)))
212
170
               ;; Weed out `::', `∷',`=' and `|' from potential infix
213
171
               ;; symbolic variable.
214
172
               (if (member match2 '("::" "∷" "=" "|"))
215
173
                   ;; Variable identifier.
216
 
                   (haskell-ds-match-string 1)
 
174
                   (match-string-no-properties 1)
217
175
                 (if (eq (aref match2 0) ?\`)
218
176
                     ;; Infix variable identifier.
219
 
                     (haskell-ds-match-string 3)
 
177
                     (match-string-no-properties 3)
220
178
                   ;; Infix symbolic variable.
221
179
                   match2))))
222
180
         ;; Variable identifier.
223
 
         (and (looking-at "\\sw+") (haskell-ds-match-string 0)))))))
 
181
         (and (looking-at "\\sw+") (match-string-no-properties 0)))))))
224
182
 
225
183
(defun haskell-ds-move-to-start-regexp (inc regexp)
226
184
  "Move to beginning of line that succeeds/precedes (INC = 1/-1)
229
187
  ;; no effect on efficiency.  It is probably not called enough to do
230
188
  ;; so.
231
189
  (while (and (= (forward-line inc) 0)
232
 
              (or (not (looking-at regexp))
233
 
                  (eq (get-text-property (point) 'face)
234
 
                      'font-lock-comment-face)))))
 
190
              (or (not (looking-at regexp))
 
191
                  (eq (get-text-property (point) 'face)
 
192
                      'font-lock-comment-face)))))
235
193
 
236
194
(defun haskell-ds-move-to-start-regexp-skipping-comments (inc regexp)
237
195
  "Like haskell-ds-move-to-start-regexp, but uses syntax-ppss to
239
197
  (let (p)
240
198
    (loop
241
199
     do (setq p (point))
242
 
        (haskell-ds-move-to-start-regexp inc regexp)
 
200
     (haskell-ds-move-to-start-regexp inc regexp)
243
201
     while (and (nth 4 (syntax-ppss))
244
202
                (/= p (point))))))
245
203
 
271
229
  ;; argument, this function will treat such value bindings as
272
230
  ;; separate from the declarations surrounding it.
273
231
  (let ( ;; The variable typed or bound in the current series of
274
 
        ;; declarations.
275
 
        name
276
 
        ;; The variable typed or bound in the new declaration.
277
 
        newname
278
 
        ;; Hack to solve hard problem for Bird-style literate scripts
279
 
        ;; that start with a declaration.  We are in the abyss if
280
 
        ;; point is before start of this declaration.
281
 
        abyss
282
 
        (line-prefix (if bird-literate literate-haskell-ds-line-prefix ""))
283
 
        ;; The regexp to match for the start of a declaration.
284
 
        (start-decl-re (if bird-literate
285
 
                           literate-haskell-ds-start-decl-re
286
 
                         haskell-ds-start-decl-re))
287
 
        (increment (if direction 1 -1))
288
 
        (bound (if direction (point-max) (point-min))))
 
232
        ;; declarations.
 
233
        name
 
234
        ;; The variable typed or bound in the new declaration.
 
235
        newname
 
236
        ;; Hack to solve hard problem for Bird-style literate scripts
 
237
        ;; that start with a declaration.  We are in the abyss if
 
238
        ;; point is before start of this declaration.
 
239
        abyss
 
240
        (line-prefix (if bird-literate literate-haskell-ds-line-prefix ""))
 
241
        ;; The regexp to match for the start of a declaration.
 
242
        (start-decl-re (if bird-literate
 
243
                           literate-haskell-ds-start-decl-re
 
244
                         haskell-ds-start-decl-re))
 
245
        (increment (if direction 1 -1))
 
246
        (bound (if direction (point-max) (point-min))))
289
247
    ;; Change syntax table.
290
248
    (with-syntax-table haskell-ds-syntax-table
291
249
      ;; move to beginning of line that starts the "current
405
363
of the declaration.  The name is a string, the positions are buffer
406
364
positions and the type is one of the symbols \"variable\", \"datatype\",
407
365
\"class\", \"import\" and \"instance\"."
408
 
  (let (;; The name, type and name-position of the declaration to
409
 
        ;; return.
410
 
        name
411
 
        type
412
 
        name-pos
413
 
        ;; Buffer positions marking the start and end of the space
414
 
        ;; containing a declaration.
415
 
        start
416
 
        end)
 
366
  (let ( ;; The name, type and name-position of the declaration to
 
367
        ;; return.
 
368
        name
 
369
        type
 
370
        name-pos
 
371
        ;; Buffer positions marking the start and end of the space
 
372
        ;; containing a declaration.
 
373
        start
 
374
        end)
417
375
    ;; Change to declaration scanning syntax.
418
376
    (with-syntax-table haskell-ds-syntax-table
419
 
    ;; Stop when we are at the end of the buffer or when a valid
420
 
    ;; declaration is grabbed.
421
 
    (while (not (or (eobp) name))
422
 
      ;; Move forward to next declaration at or after point.
423
 
      (haskell-ds-move-to-decl t bird-literate t)
424
 
      ;; Start and end of search space is currently just the starting
425
 
      ;; line of the declaration.
426
 
      (setq start (point)
427
 
            end   (line-end-position))
428
 
      (cond
429
 
       ;; If the start of the top-level declaration does not begin
430
 
       ;; with a starting keyword, then (if legal) must be a type
431
 
       ;; signature or value binding, and the variable concerned is
432
 
       ;; grabbed.
433
 
       ((not (looking-at haskell-ds-start-keywords-re))
434
 
        (setq name (haskell-ds-get-variable ""))
435
 
        (if name
436
 
            (progn
437
 
              (setq type 'variable)
438
 
              (re-search-forward (regexp-quote name) end t)
439
 
              (setq name-pos (match-beginning 0)))))
440
 
       ;; User-defined datatype declaration.
441
 
       ((re-search-forward "\\=\\(data\\|newtype\\|type\\)\\>" end t)
442
 
        (re-search-forward "=>" end t)
443
 
        (if (looking-at "[ \t]*\\(\\sw+\\)")
444
 
            (progn
445
 
              (setq name (haskell-ds-match-string 1))
446
 
              (setq name-pos (match-beginning 1))
447
 
              (setq type 'datatype))))
448
 
       ;; Class declaration.
449
 
       ((re-search-forward "\\=class\\>" end t)
450
 
        (re-search-forward "=>" end t)
451
 
        (if (looking-at "[ \t]*\\(\\sw+\\)")
452
 
            (progn
453
 
              (setq name (haskell-ds-match-string 1))
454
 
              (setq name-pos (match-beginning 1))
455
 
              (setq type 'class))))
456
 
       ;; Import declaration.
457
 
       ((looking-at "import[ \t]+\\(qualified[ \t]+\\)?\\(\\(?:\\sw\\|.\\)+\\)")
458
 
        (setq name (haskell-ds-match-string 2))
459
 
        (setq name-pos (match-beginning 2))
460
 
        (setq type 'import))
461
 
       ;; Instance declaration.
462
 
       ((re-search-forward "\\=instance[ \t]+" end t)
463
 
        (re-search-forward "=>[ \t]+" end t)
464
 
        ;; The instance "title" starts just after the `instance' (and
465
 
        ;; any context) and finishes just before the _first_ `where'
466
 
        ;; if one exists.  This solution is ugly, but I can't find a
467
 
        ;; nicer one---a simple regexp will pick up the last `where',
468
 
        ;; which may be rare but nevertheless...
469
 
        (setq name-pos (point))
470
 
        (setq name (format "%s"
471
 
                           (buffer-substring
472
 
                            (point)
473
 
                            (progn
474
 
                              ;; Look for a `where'.
475
 
                              (if (re-search-forward "\\<where\\>" end t)
476
 
                                  ;; Move back to just before the `where'.
477
 
                                  (progn
478
 
                                    (re-search-backward "\\s-where")
479
 
                                    (point))
480
 
                                ;; No `where' so move to last non-whitespace
481
 
                                ;; before `end'.
482
 
                                (progn
483
 
                                  (goto-char end)
484
 
                                  (skip-chars-backward " \t")
485
 
                                  (point)))))))
486
 
        ;; If we did not manage to extract a name, cancel this
487
 
        ;; declaration (eg. when line ends in "=> ").
488
 
        (if (string-match "^[ \t]*$" name) (setq name nil))
489
 
        (setq type 'instance)))
490
 
      ;; Move past start of current declaration.
491
 
      (goto-char end))
492
 
    ;; If we have a valid declaration then return it, otherwise return
493
 
    ;; nil.
494
 
    (if name
495
 
        (cons (cons name (cons (copy-marker start t) (copy-marker name-pos t)))
496
 
              type)
497
 
      nil))))
 
377
      ;; Stop when we are at the end of the buffer or when a valid
 
378
      ;; declaration is grabbed.
 
379
      (while (not (or (eobp) name))
 
380
        ;; Move forward to next declaration at or after point.
 
381
        (haskell-ds-move-to-decl t bird-literate t)
 
382
        ;; Start and end of search space is currently just the starting
 
383
        ;; line of the declaration.
 
384
        (setq start (point)
 
385
              end   (line-end-position))
 
386
        (cond
 
387
         ;; If the start of the top-level declaration does not begin
 
388
         ;; with a starting keyword, then (if legal) must be a type
 
389
         ;; signature or value binding, and the variable concerned is
 
390
         ;; grabbed.
 
391
         ((not (looking-at haskell-ds-start-keywords-re))
 
392
          (setq name (haskell-ds-get-variable ""))
 
393
          (if name
 
394
              (progn
 
395
                (setq type 'variable)
 
396
                (re-search-forward (regexp-quote name) end t)
 
397
                (setq name-pos (match-beginning 0)))))
 
398
         ;; User-defined datatype declaration.
 
399
         ((re-search-forward "\\=\\(data\\|newtype\\|type\\)\\>" end t)
 
400
          (re-search-forward "=>" end t)
 
401
          (if (looking-at "[ \t]*\\(\\sw+\\)")
 
402
              (progn
 
403
                (setq name (match-string-no-properties 1))
 
404
                (setq name-pos (match-beginning 1))
 
405
                (setq type 'datatype))))
 
406
         ;; Class declaration.
 
407
         ((re-search-forward "\\=class\\>" end t)
 
408
          (re-search-forward "=>" end t)
 
409
          (if (looking-at "[ \t]*\\(\\sw+\\)")
 
410
              (progn
 
411
                (setq name (match-string-no-properties 1))
 
412
                (setq name-pos (match-beginning 1))
 
413
                (setq type 'class))))
 
414
         ;; Import declaration.
 
415
         ((looking-at "import[ \t]+\\(?:safe[\t ]+\\)?\\(?:qualified[ \t]+\\)?\\(?:\"[^\"]*\"[\t ]+\\)?\\(\\(?:\\sw\\|.\\)+\\)")
 
416
          (setq name (match-string-no-properties 1))
 
417
          (setq name-pos (match-beginning 1))
 
418
          (setq type 'import))
 
419
         ;; Instance declaration.
 
420
         ((re-search-forward "\\=instance[ \t]+" end t)
 
421
          (re-search-forward "=>[ \t]+" end t)
 
422
          ;; The instance "title" starts just after the `instance' (and
 
423
          ;; any context) and finishes just before the _first_ `where'
 
424
          ;; if one exists.  This solution is ugly, but I can't find a
 
425
          ;; nicer one---a simple regexp will pick up the last `where',
 
426
          ;; which may be rare but nevertheless...
 
427
          (setq name-pos (point))
 
428
          (setq name (buffer-substring-no-properties
 
429
                      (point)
 
430
                      (progn
 
431
                        ;; Look for a `where'.
 
432
                        (if (re-search-forward "\\<where\\>" end t)
 
433
                            ;; Move back to just before the `where'.
 
434
                            (progn
 
435
                              (re-search-backward "\\s-where")
 
436
                              (point))
 
437
                          ;; No `where' so move to last non-whitespace
 
438
                          ;; before `end'.
 
439
                          (progn
 
440
                            (goto-char end)
 
441
                            (skip-chars-backward " \t")
 
442
                            (point))))))
 
443
          ;; If we did not manage to extract a name, cancel this
 
444
          ;; declaration (eg. when line ends in "=> ").
 
445
          (if (string-match "^[ \t]*$" name) (setq name nil))
 
446
          (setq type 'instance)))
 
447
        ;; Move past start of current declaration.
 
448
        (goto-char end))
 
449
      ;; If we have a valid declaration then return it, otherwise return
 
450
      ;; nil.
 
451
      (if name
 
452
          (cons (cons name (cons (copy-marker start t) (copy-marker name-pos t)))
 
453
                type)
 
454
        nil))))
498
455
 
499
456
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
500
457
;; Declaration scanning via `imenu'.
507
464
  ;; Each list has elements of the form `(INDEX-NAME . INDEX-POSITION)'.
508
465
  ;; These lists are nested using `(INDEX-TITLE . INDEX-ALIST)'.
509
466
  (let* ((bird-literate (haskell-ds-bird-p))
510
 
         (index-alist '())
511
 
         (index-class-alist '())   ;; Classes
512
 
         (index-var-alist '())     ;; Variables
513
 
         (index-imp-alist '())     ;; Imports
514
 
         (index-inst-alist '())    ;; Instances
515
 
         (index-type-alist '())    ;; Datatypes
516
 
         ;; Variables for showing progress.
517
 
         (bufname (buffer-name))
518
 
         (divisor-of-progress (max 1 (/ (buffer-size) 100)))
519
 
         ;; The result we wish to return.
520
 
         result)
 
467
         (index-alist '())
 
468
         (index-class-alist '()) ;; Classes
 
469
         (index-var-alist '())   ;; Variables
 
470
         (index-imp-alist '())   ;; Imports
 
471
         (index-inst-alist '())  ;; Instances
 
472
         (index-type-alist '())  ;; Datatypes
 
473
         ;; Variables for showing progress.
 
474
         (bufname (buffer-name))
 
475
         (divisor-of-progress (max 1 (/ (buffer-size) 100)))
 
476
         ;; The result we wish to return.
 
477
         result)
521
478
    (goto-char (point-min))
522
479
    ;; Loop forwards from the beginning of the buffer through the
523
480
    ;; starts of the top-level declarations.
524
481
    (while (< (point) (point-max))
525
482
      (message "Scanning declarations in %s... (%3d%%)" bufname
526
 
               (/ (- (point) (point-min)) divisor-of-progress))
 
483
               (/ (- (point) (point-min)) divisor-of-progress))
527
484
      ;; Grab the next declaration.
528
485
      (setq result (haskell-ds-generic-find-next-decl bird-literate))
529
486
      (if result
530
 
          ;; If valid, extract the components of the result.
531
 
          (let* ((name-posns (car result))
532
 
                 (name (car name-posns))
533
 
                 (posns (cdr name-posns))
534
 
                 (start-pos (car posns))
535
 
                 (type (cdr result))
536
 
                 ;; Place `(name . start-pos)' in the correct alist.
537
 
                 (sym (cdr (assq type
 
487
          ;; If valid, extract the components of the result.
 
488
          (let* ((name-posns (car result))
 
489
                 (name (car name-posns))
 
490
                 (posns (cdr name-posns))
 
491
                 (start-pos (car posns))
 
492
                 (type (cdr result))
 
493
                 ;; Place `(name . start-pos)' in the correct alist.
 
494
                 (sym (cdr (assq type
538
495
                                 '((variable . index-var-alist)
539
496
                                   (datatype . index-type-alist)
540
497
                                   (class . index-class-alist)
541
498
                                   (import . index-imp-alist)
542
499
                                   (instance . index-inst-alist))))))
543
 
            (set sym (cons (cons name start-pos) (symbol-value sym))))))
 
500
            (set sym (cons (cons name start-pos) (symbol-value sym))))))
544
501
    ;; Now sort all the lists, label them, and place them in one list.
545
502
    (message "Sorting declarations in %s..." bufname)
546
 
    (and index-type-alist
547
 
         (push (cons "Datatypes"
548
 
                     (sort index-type-alist 'haskell-ds-imenu-label-cmp))
549
 
               index-alist))
550
 
    (and index-inst-alist
551
 
         (push (cons "Instances"
552
 
                     (sort index-inst-alist 'haskell-ds-imenu-label-cmp))
553
 
               index-alist))
554
 
    (and index-imp-alist
555
 
         (push (cons "Imports"
556
 
                     (sort index-imp-alist 'haskell-ds-imenu-label-cmp))
557
 
               index-alist))
558
 
    (and index-var-alist
559
 
         (push (cons "Variables"
560
 
                     (sort index-var-alist 'haskell-ds-imenu-label-cmp))
561
 
               index-alist))
562
 
    (and index-class-alist
563
 
         (push (cons "Classes"
564
 
                     (sort index-class-alist 'haskell-ds-imenu-label-cmp))
565
 
               index-alist))
 
503
    (when index-type-alist
 
504
      (push (cons "Datatypes"
 
505
                  (sort index-type-alist 'haskell-ds-imenu-label-cmp))
 
506
            index-alist))
 
507
    (when index-inst-alist
 
508
      (push (cons "Instances"
 
509
                  (sort index-inst-alist 'haskell-ds-imenu-label-cmp))
 
510
            index-alist))
 
511
    (when index-imp-alist
 
512
      (push (cons "Imports"
 
513
                  (sort index-imp-alist 'haskell-ds-imenu-label-cmp))
 
514
            index-alist))
 
515
    (when index-class-alist
 
516
      (push (cons "Classes"
 
517
                  (sort index-class-alist 'haskell-ds-imenu-label-cmp))
 
518
            index-alist))
 
519
    (when index-var-alist
 
520
      (if haskell-decl-scan-bindings-as-variables
 
521
          (push (cons "Variables"
 
522
                      (sort index-var-alist 'haskell-ds-imenu-label-cmp))
 
523
                index-alist)
 
524
        (setq index-alist (append index-alist
 
525
                                  (sort index-var-alist 'haskell-ds-imenu-label-cmp)))))
566
526
    (message "Sorting declarations in %s...done" bufname)
567
527
    ;; Return the alist.
568
528
    index-alist))
574
534
(defun haskell-ds-imenu ()
575
535
  "Install `imenu' for Haskell scripts."
576
536
  (setq imenu-create-index-function 'haskell-ds-create-imenu-index)
577
 
  (if (fboundp 'imenu-add-to-menubar)
578
 
      (imenu-add-to-menubar "Declarations")))
579
 
 
580
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
581
 
;; Declaration scanning via `func-menu'.
582
 
 
583
 
(defun haskell-ds-func-menu-next (buffer)
584
 
  "Non-literate Haskell version of `haskell-ds-generic-func-menu-next'."
585
 
  (haskell-ds-generic-func-menu-next (haskell-ds-bird-p) buffer))
586
 
 
587
 
(defun haskell-ds-generic-func-menu-next (bird-literate buffer)
588
 
  "Return `(name . pos)' of next declaration."
589
 
  (set-buffer buffer)
590
 
  (let ((result (haskell-ds-generic-find-next-decl bird-literate)))
591
 
    (if result
592
 
        (let* ((name-posns (car result))
593
 
               (name (car name-posns))
594
 
               (posns (cdr name-posns))
595
 
               (name-pos (cdr posns))
596
 
               ;;(type (cdr result))
597
 
               )
598
 
          (cons ;(concat
599
 
                 ;; func-menu has problems with spaces, and adding a
600
 
                 ;; qualifying keyword will not allow the "goto fn"
601
 
                 ;; functions to work properly.  Sigh.
602
 
                 ;; (cond
603
 
                 ;;  ((eq type 'variable) "")
604
 
                 ;;  ((eq type 'datatype) "datatype ")
605
 
                 ;;  ((eq type 'class) "class ")
606
 
                 ;;  ((eq type 'import) "import ")
607
 
                 ;;  ((eq type 'instance) "instance "))
608
 
                 name;)
609
 
                name-pos))
610
 
      nil)))
611
 
 
612
 
(defvar haskell-ds-func-menu-regexp
613
 
  (concat "^" haskell-ds-start-decl-re)
614
 
  "Regexp to match the start of a possible declaration.")
615
 
 
616
 
(defvar literate-haskell-ds-func-menu-regexp
617
 
  (concat "^" literate-haskell-ds-start-decl-re)
618
 
  "As `haskell-ds-func-menu-regexp' but for Bird-style literate scripts.")
619
 
 
620
 
(declare-function fume-add-menubar-entry "ext:func-menu")
621
 
(defvar fume-menubar-menu-name)
622
 
(defvar fume-function-name-regexp-alist)
623
 
(defvar fume-find-function-name-method-alist)
624
 
 
625
 
(defun haskell-ds-func-menu ()
626
 
  "Use `func-menu' to establish declaration scanning for Haskell scripts."
627
 
  (require 'func-menu)
628
 
  (set (make-local-variable 'fume-menubar-menu-name) "Declarations")
629
 
  (set (make-local-variable 'fume-function-name-regexp-alist)
630
 
       (if (haskell-ds-bird-p)
631
 
           '((haskell-mode . literate-haskell-ds-func-menu-regexp))
632
 
         '((haskell-mode . haskell-ds-func-menu-regexp))))
633
 
  (set (make-local-variable 'fume-find-function-name-method-alist)
634
 
       '((haskell-mode . haskell-ds-func-menu-next)))
635
 
  (fume-add-menubar-entry)
636
 
  (local-set-key "\C-cl" 'fume-list-functions)
637
 
  (local-set-key "\C-cg" 'fume-prompt-function-goto)
638
 
  (local-set-key [(meta button1)] 'fume-mouse-function-goto))
 
537
  (when haskell-decl-scan-add-to-menubar
 
538
    (imenu-add-to-menubar "Declarations")))
639
539
 
640
540
;; The main functions to turn on declaration scanning.
641
541
;;;###autoload
642
542
(defun turn-on-haskell-decl-scan ()
643
 
  (interactive)
644
543
  "Unconditionally activate `haskell-decl-scan-mode'."
645
 
  (haskell-decl-scan-mode 1))
646
 
 
647
 
(defvar haskell-decl-scan-mode nil)
648
 
(make-variable-buffer-local 'haskell-decl-scan-mode)
 
544
  (interactive)
 
545
  (haskell-decl-scan-mode))
649
546
 
650
547
;;;###autoload
651
 
(defun haskell-decl-scan-mode (&optional arg)
652
 
  "Minor mode for declaration scanning for Haskell mode.
653
 
Top-level declarations are scanned and listed in the menu item \"Declarations\".
654
 
Selecting an item from this menu will take point to the start of the
655
 
declaration.
656
 
 
657
 
\\[haskell-ds-forward-decl] and \\[haskell-ds-backward-decl] move forward and backward to the start of a declaration.
658
 
 
659
 
Under XEmacs, the following keys are also defined:
660
 
 
661
 
\\[fume-list-functions] lists the declarations of the current buffer,
662
 
\\[fume-prompt-function-goto] prompts for a declaration to move to, and
663
 
\\[fume-mouse-function-goto] moves to the declaration whose name is at point.
664
 
 
665
 
This may link with `haskell-doc' (only for Emacs currently).
 
548
(define-minor-mode haskell-decl-scan-mode
 
549
  "Toggle Haskell declaration scanning minor mode on or off.
 
550
With a prefix argument ARG, enable minor mode if ARG is
 
551
positive, and disable it otherwise.  If called from Lisp, enable
 
552
the mode if ARG is omitted or nil, and toggle it if ARG is `toggle'.
 
553
 
 
554
See also info node `(haskell-mode)haskell-decl-scan-mode' for
 
555
more details about this minor mode.
 
556
 
 
557
Top-level declarations are scanned and listed in the menu item
 
558
\"Declarations\" (if enabled via option
 
559
`haskell-decl-scan-add-to-menubar').  Selecting an item from this
 
560
menu will take point to the start of the declaration.
 
561
 
 
562
\\[beginning-of-defun] and \\[end-of-defun] move forward and backward to the start of a declaration.
 
563
 
 
564
This may link with `haskell-doc-mode'.
666
565
 
667
566
For non-literate and LaTeX-style literate scripts, we assume the
668
567
common convention that top-level declarations start at the first
674
573
declaration.  Therefore, using Haskell font locking with comments
675
574
coloured in `font-lock-comment-face' improves declaration scanning.
676
575
 
677
 
To turn on declaration scanning for all Haskell buffers, add this to
678
 
.emacs:
679
 
 
680
 
  (add-hook 'haskell-mode-hook 'turn-on-haskell-decl-scan)
681
 
 
682
 
To turn declaration scanning on for the current buffer, call
683
 
`turn-on-haskell-decl-scan'.
684
 
 
685
576
Literate Haskell scripts are supported: If the value of
686
 
`haskell-literate' (automatically set by the Haskell mode of
687
 
Moss&Thorn) is `bird', a Bird-style literate script is assumed.  If it
688
 
is nil or `tex', a non-literate or LaTeX-style literate script is
 
577
`haskell-literate' (set automatically by `literate-haskell-mode')
 
578
is `bird', a Bird-style literate script is assumed.  If it is nil
 
579
or `tex', a non-literate or LaTeX-style literate script is
689
580
assumed, respectively.
690
581
 
691
 
Invokes `haskell-decl-scan-mode-hook'."
692
 
  (interactive)
693
 
  (if (boundp 'beginning-of-defun-function)
694
 
      (if haskell-decl-scan-mode
695
 
          (progn
696
 
            (set (make-local-variable 'beginning-of-defun-function)
697
 
                 'haskell-ds-backward-decl)
698
 
            (set (make-local-variable 'end-of-defun-function)
699
 
                 'haskell-ds-forward-decl))
700
 
        (kill-local-variable 'beginning-of-defun-function)
701
 
        (kill-local-variable 'end-of-defun-function))
702
 
    (local-set-key "\M-\C-e"
703
 
                   (if haskell-decl-scan-mode 'haskell-ds-forward-decl))
704
 
    (local-set-key "\M-\C-a"
705
 
                   (if haskell-decl-scan-mode 'haskell-ds-backward-decl)))
706
 
  (if haskell-decl-scan-mode
707
 
      (if (fboundp 'imenu)
708
 
          (haskell-ds-imenu)
709
 
        (haskell-ds-func-menu))
710
 
    ;; How can we cleanly remove that menus?
711
 
    (local-set-key [menu-bar index] nil))
712
 
  (run-hooks 'haskell-decl-scan-mode-hook))
 
582
Invokes `haskell-decl-scan-mode-hook' on activation."
 
583
  :group 'haskell-decl-scan
 
584
 
 
585
  (kill-local-variable 'beginning-of-defun-function)
 
586
  (kill-local-variable 'end-of-defun-function)
 
587
  (kill-local-variable 'imenu-create-index-function)
 
588
  (unless haskell-decl-scan-mode
 
589
    ;; How can we cleanly remove the "Declarations" menu?
 
590
    (when haskell-decl-scan-add-to-menubar
 
591
      (local-set-key [menu-bar index] nil)))
 
592
 
 
593
  (when haskell-decl-scan-mode
 
594
    (set (make-local-variable 'beginning-of-defun-function)
 
595
         'haskell-ds-backward-decl)
 
596
    (set (make-local-variable 'end-of-defun-function)
 
597
         'haskell-ds-forward-decl)
 
598
    (haskell-ds-imenu)))
 
599
 
713
600
 
714
601
;; Provide ourselves:
715
602