about summary refs log tree commit diff
path: root/configs/shared/emacs/.emacs.d/elpa/haskell-mode-20180601.143/haskell-decl-scan.el
diff options
context:
space:
mode:
Diffstat (limited to 'configs/shared/emacs/.emacs.d/elpa/haskell-mode-20180601.143/haskell-decl-scan.el')
-rw-r--r--configs/shared/emacs/.emacs.d/elpa/haskell-mode-20180601.143/haskell-decl-scan.el687
1 files changed, 687 insertions, 0 deletions
diff --git a/configs/shared/emacs/.emacs.d/elpa/haskell-mode-20180601.143/haskell-decl-scan.el b/configs/shared/emacs/.emacs.d/elpa/haskell-mode-20180601.143/haskell-decl-scan.el
new file mode 100644
index 0000000000..75da0a9d6b
--- /dev/null
+++ b/configs/shared/emacs/.emacs.d/elpa/haskell-mode-20180601.143/haskell-decl-scan.el
@@ -0,0 +1,687 @@
+;;; haskell-decl-scan.el --- Declaration scanning module for Haskell Mode -*- lexical-binding: t -*-
+
+;; Copyright (C) 2004, 2005, 2007, 2009  Free Software Foundation, Inc.
+;; Copyright (C) 1997-1998  Graeme E Moss
+;; Copyright (C) 2016  Chris Gregory
+
+;; Author: 1997-1998 Graeme E Moss <gem@cs.york.ac.uk>
+;; Maintainer: Stefan Monnier <monnier@gnu.org>
+;; Keywords: declarations menu files Haskell
+;; URL: http://cvs.haskell.org/cgi-bin/cvsweb.cgi/fptools/CONTRIB/haskell-modes/emacs/haskell-decl-scan.el?rev=HEAD
+
+;; This file is not part of GNU Emacs.
+
+;; This file is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+
+;; This file is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Purpose:
+;;
+;; Top-level declarations are scanned and placed in a menu.  Supports
+;; full Latin1 Haskell 1.4 as well as literate scripts.
+;;
+;;
+;; Installation:
+;;
+;; To turn declaration scanning on for all Haskell buffers under the
+;; Haskell mode of Moss&Thorn, add this to .emacs:
+;;
+;;    (add-hook 'haskell-mode-hook 'haskell-decl-scan-mode)
+;;
+;; Otherwise, call `haskell-decl-scan-mode'.
+;;
+;;
+;; Customisation:
+;;
+;; M-x customize-group haskell-decl-scan
+;;
+;;
+;; History:
+;;
+;; If you have any problems or suggestions, after consulting the list
+;; below, email gem@cs.york.ac.uk quoting the version of the library
+;; you are using, the version of Emacs you are using, and a small
+;; example of the problem or suggestion.  Note that this library
+;; requires a reasonably recent version of Emacs.
+;;
+;; Uses `imenu' under Emacs.
+;;
+;; Version 1.2:
+;;   Added support for LaTeX-style literate scripts.
+;;
+;; Version 1.1:
+;;   Use own syntax table.  Fixed bug for very small buffers.  Use
+;;   markers instead of pointers (markers move with the text).
+;;
+;; Version 1.0:
+;;   Brought over from Haskell mode v1.1.
+;;
+;;
+;; Present Limitations/Future Work (contributions are most welcome!):
+;;
+;; . Declarations requiring information extending beyond starting line
+;;   don't get scanned properly, eg.
+;;   > class Eq a =>
+;;   >       Test a
+;;
+;; . Comments placed in the midst of the first few lexemes of a
+;;   declaration will cause havoc, eg.
+;;   > infixWithComments :: Int -> Int -> Int
+;;   > x {-nastyComment-} `infixWithComments` y = x + y
+;;   but are not worth worrying about.
+;;
+;; . Would be nice to scan other top-level declarations such as
+;;   methods of a class, datatype field labels...  any more?
+;;
+;; . Support for GreenCard?
+;;
+;; . Re-running (literate-)haskell-imenu should not cause the problems
+;;   that it does.  The ability to turn off scanning would also be
+;;   useful.  (Note that re-running (literate-)haskell-mode seems to
+;;   cause no problems.)
+
+;; All functions/variables start with
+;; `(turn-(on/off)-)haskell-decl-scan' or `haskell-ds-'.
+
+;; The imenu support is based on code taken from `hugs-mode',
+;; thanks go to Chris Van Humbeeck.
+
+;; Version.
+
+;;; Code:
+
+(require 'cl-lib)
+(require 'haskell-mode)
+(require 'syntax)
+(require 'imenu)
+
+;;;###autoload
+(defgroup haskell-decl-scan nil
+  "Haskell declaration scanning (`imenu' support)."
+  :link '(custom-manual "(haskell-mode)haskell-decl-scan-mode")
+  :group 'haskell
+  :prefix "haskell-decl-scan-")
+
+(defcustom haskell-decl-scan-bindings-as-variables nil
+  "Whether to put top-level value bindings into a \"Variables\" category."
+  :group 'haskell-decl-scan
+  :type 'boolean)
+
+(defcustom haskell-decl-scan-add-to-menubar t
+  "Whether to add a \"Declarations\" menu entry to menu bar."
+  :group 'haskell-decl-scan
+  :type 'boolean)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; General declaration scanning functions.
+
+(defvar haskell-ds-start-keywords-re
+  (concat "\\(\\<"
+          "class\\|data\\|i\\(mport\\|n\\(fix\\(\\|[lr]\\)\\|stance\\)\\)\\|"
+          "module\\|primitive\\|type\\|newtype"
+          "\\)\\>")
+  "Keywords that may start a declaration.")
+
+(defvar haskell-ds-syntax-table
+  (let ((table (copy-syntax-table haskell-mode-syntax-table)))
+    (modify-syntax-entry ?\' "w" table)
+    (modify-syntax-entry ?_  "w" table)
+    (modify-syntax-entry ?\\ "_" table)
+    table)
+  "Syntax table used for Haskell declaration scanning.")
+
+
+(defun haskell-ds-get-variable (prefix)
+  "Return variable involved in value binding or type signature.
+Assumes point is looking at the regexp PREFIX followed by the
+start of a declaration (perhaps in the middle of a series of
+declarations concerning a single variable).  Otherwise return nil.
+Point is not changed."
+  ;; I think I can now handle all declarations bar those with comments
+  ;; nested before the second lexeme.
+  (save-excursion
+    (with-syntax-table haskell-ds-syntax-table
+      (if (looking-at prefix) (goto-char (match-end 0)))
+      ;; Keyword.
+      (if (looking-at haskell-ds-start-keywords-re)
+          nil
+        (or ;; Parenthesized symbolic variable.
+         (and (looking-at "(\\(\\s_+\\))") (match-string-no-properties 1))
+         ;; General case.
+         (if (looking-at
+              (if (eq ?\( (char-after))
+                  ;; Skip paranthesised expression.
+                  (progn
+                    (forward-sexp)
+                    ;; Repeating this code and avoiding moving point if
+                    ;; possible speeds things up.
+                    "\\(\\'\\)?\\s-*\\(\\s_+\\|`\\(\\sw+\\)`\\)")
+                "\\(\\sw+\\)?\\s-*\\(\\s_+\\|`\\(\\sw+\\)`\\)"))
+             (let ((match2 (match-string-no-properties 2)))
+               ;; Weed out `::', `∷',`=' and `|' from potential infix
+               ;; symbolic variable.
+               (if (member match2 '("::" "∷" "=" "|"))
+                   ;; Variable identifier.
+                   (match-string-no-properties 1)
+                 (if (eq (aref match2 0) ?\`)
+                     ;; Infix variable identifier.
+                     (match-string-no-properties 3)
+                   ;; Infix symbolic variable.
+                   match2))))
+         ;; Variable identifier.
+         (and (looking-at "\\sw+") (match-string-no-properties 0)))))))
+
+(defun haskell-ds-move-to-start-regexp (inc regexp)
+  "Move to beginning of line that succeeds/precedes (INC = 1/-1)
+current line that starts with REGEXP and is not in `font-lock-comment-face'."
+  ;; Making this defsubst instead of defun appears to have little or
+  ;; no effect on efficiency.  It is probably not called enough to do
+  ;; so.
+  (while (and (= (forward-line inc) 0)
+              (or (not (looking-at regexp))
+                  (eq (get-text-property (point) 'face)
+                      'font-lock-comment-face)))))
+
+(defun haskell-ds-move-to-start-regexp-skipping-comments (inc regexp)
+  "Like haskell-ds-move-to-start-regexp, but uses syntax-ppss to
+  skip comments"
+  (let (p)
+    (cl-loop
+     do (setq p (point))
+     (haskell-ds-move-to-start-regexp inc regexp)
+     while (and (nth 4 (syntax-ppss))
+                (/= p (point))))))
+
+(defvar literate-haskell-ds-line-prefix "> ?"
+  "Regexp matching start of a line of Bird-style literate code.
+Current value is \"> \" as we assume top-level declarations start
+at column 3.  Must not contain the special \"^\" regexp as we may
+not use the regexp at the start of a regexp string.  Note this is
+only for `imenu' support.")
+
+(defvar haskell-ds-start-decl-re "\\(\\sw\\|(\\)"
+  "The regexp that starts a Haskell declaration.")
+
+(defvar literate-haskell-ds-start-decl-re
+  (concat literate-haskell-ds-line-prefix haskell-ds-start-decl-re)
+  "The regexp that starts a Bird-style literate Haskell declaration.")
+
+(defun haskell-ds-whitespace-p (char)
+  "Test if CHAR is a whitespace character."
+  ;; the nil is a bob/eob test
+  (member char '(nil ?\t ?\n ?\ )))
+
+(defun haskell-ds-move-to-decl (direction bird-literate fix)
+  "General function for moving to the start of a declaration,
+either forwards or backwards from point, with normal or with Bird-style
+literate scripts.  If DIRECTION is t, then forward, else backward.  If
+BIRD-LITERATE is t, then treat as Bird-style literate scripts, else
+normal scripts.  Returns point if point is left at the start of a
+declaration, and nil otherwise, ie. because point is at the beginning
+or end of the buffer and no declaration starts there.  If FIX is t,
+then point does not move if already at the start of a declaration."
+  ;; As `haskell-ds-get-variable' cannot separate an infix variable
+  ;; identifier out of a value binding with non-alphanumeric first
+  ;; argument, this function will treat such value bindings as
+  ;; separate from the declarations surrounding it.
+  (let ( ;; The variable typed or bound in the current series of
+        ;; declarations.
+        name
+        ;; The variable typed or bound in the new declaration.
+        newname
+        ;; Hack to solve hard problem for Bird-style literate scripts
+        ;; that start with a declaration.  We are in the abyss if
+        ;; point is before start of this declaration.
+        abyss
+        (line-prefix (if bird-literate literate-haskell-ds-line-prefix ""))
+        ;; The regexp to match for the start of a declaration.
+        (start-decl-re (if bird-literate
+                           literate-haskell-ds-start-decl-re
+                         haskell-ds-start-decl-re))
+        (increment (if direction 1 -1))
+        (bound (if direction (point-max) (point-min))))
+    ;; Change syntax table.
+    (with-syntax-table haskell-ds-syntax-table
+      ;; move to beginning of line that starts the "current
+      ;; declaration" (dependent on DIRECTION and FIX), and then get
+      ;; the variable typed or bound by this declaration, if any.
+      (let ( ;; Where point was at call of function.
+            (here (point))
+            ;; Where the declaration on this line (if any) starts.
+            (start (progn
+                     (beginning-of-line)
+                     ;; Checking the face to ensure a declaration starts
+                     ;; here seems to be the only addition to make this
+                     ;; module support LaTeX-style literate scripts.
+                     (if (and (looking-at start-decl-re)
+                              (not (elt (syntax-ppss) 4)))
+                         (match-beginning 1)))))
+        (if (and start
+                 ;; This complicated boolean determines whether we
+                 ;; should include the declaration that starts on the
+                 ;; current line as the "current declaration" or not.
+                 (or (and (or (and direction (not fix))
+                              (and (not direction) fix))
+                          (>= here start))
+                     (and (or (and direction fix)
+                              (and (not direction) (not fix)))
+                          (> here start))))
+            ;; If so, we are already at start of the current line, so
+            ;; do nothing.
+            ()
+          ;; If point was before start of a declaration on the first
+          ;; line of the buffer (possible for Bird-style literate
+          ;; scripts) then we are in the abyss.
+          (if (and start (bobp))
+              (setq abyss t)
+            ;; Otherwise we move to the start of the first declaration
+            ;; on a line preceding the current one, skipping comments
+            (haskell-ds-move-to-start-regexp-skipping-comments -1 start-decl-re))))
+      ;; If we are in the abyss, position and return as appropriate.
+      (if abyss
+          (if (not direction)
+              nil
+            (re-search-forward (concat "\\=" line-prefix) nil t)
+            (point))
+        ;; Get the variable typed or bound by this declaration, if any.
+        (setq name (haskell-ds-get-variable line-prefix))
+        (if (not name)
+            ;; If no such variable, stop at the start of this
+            ;; declaration if moving backward, or move to the next
+            ;; declaration if moving forward.
+            (if direction
+                (haskell-ds-move-to-start-regexp-skipping-comments 1 start-decl-re))
+          ;; If there is a variable, find the first
+          ;; succeeding/preceding declaration that does not type or
+          ;; bind it.  Check for reaching start/end of buffer and
+          ;; comments.
+          (haskell-ds-move-to-start-regexp-skipping-comments increment start-decl-re)
+          (while (and (/= (point) bound)
+                      (and (setq newname (haskell-ds-get-variable line-prefix))
+                           (string= name newname)))
+            (setq name newname)
+            (haskell-ds-move-to-start-regexp-skipping-comments increment start-decl-re))
+          ;; If we are going backward, and have either reached a new
+          ;; declaration or the beginning of a buffer that does not
+          ;; start with a declaration, move forward to start of next
+          ;; declaration (which must exist).  Otherwise, we are done.
+          (if (and (not direction)
+                   (or (and (looking-at start-decl-re)
+                            (not (string= name
+                                          ;; Note we must not use
+                                          ;; newname here as this may
+                                          ;; not have been set if we
+                                          ;; have reached the beginning
+                                          ;; of the buffer.
+                                          (haskell-ds-get-variable
+                                           line-prefix))))
+                       (and (not (looking-at start-decl-re))
+                            (bobp))))
+              (haskell-ds-move-to-start-regexp-skipping-comments 1 start-decl-re)))
+        ;; Store whether we are at the start of a declaration or not.
+        ;; Used to calculate final result.
+        (let ((at-start-decl (looking-at start-decl-re)))
+          ;; If we are at the beginning of a line, move over
+          ;; line-prefix, if present at point.
+          (if (bolp)
+              (re-search-forward (concat "\\=" line-prefix) (point-max) t))
+          ;; Return point if at the start of a declaration and nil
+          ;; otherwise.
+          (if at-start-decl (point) nil))))))
+
+(defun haskell-ds-bird-p ()
+  (and (boundp 'haskell-literate) (eq haskell-literate 'bird)))
+
+(defun haskell-ds-backward-decl ()
+  "Move backward to the first character that starts a top-level declaration.
+A series of declarations concerning one variable is treated as one
+declaration by this function.  So, if point is within a top-level
+declaration then move it to the start of that declaration.  If point
+is already at the start of a top-level declaration, then move it to
+the start of the preceding declaration.  Returns point if point is
+left at the start of a declaration, and nil otherwise, because
+point is at the beginning of the buffer and no declaration starts
+there."
+  (interactive)
+  (haskell-ds-move-to-decl nil (haskell-ds-bird-p) nil))
+
+(defun haskell-ds-comment-p
+    (&optional
+     pt)
+  "Test if the cursor is on whitespace or a comment.
+
+`PT' defaults to `(point)'"
+  ;; ensure result is `t' or `nil' instead of just truthy
+  (if (or
+       ;; is cursor on whitespace
+       (haskell-ds-whitespace-p (following-char))
+       ;; http://emacs.stackexchange.com/questions/14269/how-to-detect-if-the-point-is-within-a-comment-area
+       ;; is cursor at begging, inside, or end of comment
+       (let ((fontfaces (get-text-property (or pt
+                                               (point)) 'face)))
+         (when (not (listp fontfaces))
+           (setf fontfaces (list fontfaces)))
+         (delq nil (mapcar
+                    #'(lambda (f)
+                        (member f '(font-lock-comment-face
+                                    font-lock-doc-face
+                                    font-lock-comment-delimiter-face)))
+                    fontfaces))))
+      t
+    nil))
+
+(defun haskell-ds-line-commented-p ()
+  "Tests if all characters from `point' to `end-of-line' pass
+`haskell-ds-comment-p'"
+  (let ((r t))
+    (while (and r (not (eolp)))
+      (if (not (haskell-ds-comment-p))
+          (setq r nil))
+      (forward-char))
+    r))
+
+(defun haskell-ds-forward-decl ()
+  "Move forward to the first character that starts a top-level
+declaration.  As `haskell-ds-backward-decl' but forward."
+  (interactive)
+  (let ((p (point)) b e empty was-at-bob)
+    ;; Go back to beginning of defun, then go to beginning of next
+    (haskell-ds-move-to-decl nil (haskell-ds-bird-p) nil)
+    (setq b (point))
+    (haskell-ds-move-to-decl t (haskell-ds-bird-p) nil)
+    (setq e (point))
+    ;; tests if line is empty
+    (setq empty (and (<= (point) p)
+                     (not (eolp))))
+    (setq was-at-bob (and (= (point-min) b)
+                          (= b p)
+                          (< p e)))
+    ;; this conditional allows for when empty lines at end, first
+    ;; `C-M-e' will go to end of defun, next will go to end of file.
+    (when (or was-at-bob
+              empty)
+      (if (or (and was-at-bob
+                   (= ?\n
+                      (save-excursion
+                        (goto-char (point-min))
+                        (following-char))))
+              empty)
+          (haskell-ds-move-to-decl t (haskell-ds-bird-p) nil))
+      ;; Then go back to end of current
+      (forward-line -1)
+      (while (and (haskell-ds-line-commented-p)
+                  ;; prevent infinite loop
+                  (not (bobp)))
+        (forward-line -1))
+      (forward-line 1)))
+  (point))
+
+(defun haskell-ds-generic-find-next-decl (bird-literate)
+  "Find the name, position and type of the declaration at or after point.
+Return ((NAME . (START-POSITION . NAME-POSITION)) . TYPE)
+if one exists and nil otherwise.  The start-position is at the start
+of the declaration, and the name-position is at the start of the name
+of the declaration.  The name is a string, the positions are buffer
+positions and the type is one of the symbols \"variable\", \"datatype\",
+\"class\", \"import\" and \"instance\"."
+  (let ( ;; The name, type and name-position of the declaration to
+        ;; return.
+        name
+        type
+        name-pos
+        ;; Buffer positions marking the start and end of the space
+        ;; containing a declaration.
+        start
+        end)
+    ;; Change to declaration scanning syntax.
+    (with-syntax-table haskell-ds-syntax-table
+      ;; Stop when we are at the end of the buffer or when a valid
+      ;; declaration is grabbed.
+      (while (not (or (eobp) name))
+        ;; Move forward to next declaration at or after point.
+        (haskell-ds-move-to-decl t bird-literate t)
+        ;; Start and end of search space is currently just the starting
+        ;; line of the declaration.
+        (setq start (point)
+              end   (line-end-position))
+        (cond
+         ;; If the start of the top-level declaration does not begin
+         ;; with a starting keyword, then (if legal) must be a type
+         ;; signature or value binding, and the variable concerned is
+         ;; grabbed.
+         ((not (looking-at haskell-ds-start-keywords-re))
+          (setq name (haskell-ds-get-variable ""))
+          (if name
+              (progn
+                (setq type 'variable)
+                (re-search-forward (regexp-quote name) end t)
+                (setq name-pos (match-beginning 0)))))
+         ;; User-defined datatype declaration.
+         ((re-search-forward "\\=\\(data\\|newtype\\|type\\)\\>" end t)
+          (re-search-forward "=>" end t)
+          (if (looking-at "[ \t]*\\(\\sw+\\)")
+              (progn
+                (setq name (match-string-no-properties 1))
+                (setq name-pos (match-beginning 1))
+                (setq type 'datatype))))
+         ;; Class declaration.
+         ((re-search-forward "\\=class\\>" end t)
+          (re-search-forward "=>" end t)
+          (if (looking-at "[ \t]*\\(\\sw+\\)")
+              (progn
+                (setq name (match-string-no-properties 1))
+                (setq name-pos (match-beginning 1))
+                (setq type 'class))))
+         ;; Import declaration.
+         ((looking-at "import[ \t]+\\(?:safe[\t ]+\\)?\\(?:qualified[ \t]+\\)?\\(?:\"[^\"]*\"[\t ]+\\)?\\(\\(?:\\sw\\|.\\)+\\)")
+          (setq name (match-string-no-properties 1))
+          (setq name-pos (match-beginning 1))
+          (setq type 'import))
+         ;; Instance declaration.
+         ((re-search-forward "\\=instance[ \t]+" end t)
+          (re-search-forward "=>[ \t]+" end t)
+          ;; The instance "title" starts just after the `instance' (and
+          ;; any context) and finishes just before the _first_ `where'
+          ;; if one exists.  This solution is ugly, but I can't find a
+          ;; nicer one---a simple regexp will pick up the last `where',
+          ;; which may be rare but nevertheless...
+          (setq name-pos (point))
+          (setq name (buffer-substring-no-properties
+                      (point)
+                      (progn
+                        ;; Look for a `where'.
+                        (if (re-search-forward "\\<where\\>" end t)
+                            ;; Move back to just before the `where'.
+                            (progn
+                              (re-search-backward "\\s-where")
+                              (point))
+                          ;; No `where' so move to last non-whitespace
+                          ;; before `end'.
+                          (progn
+                            (goto-char end)
+                            (skip-chars-backward " \t")
+                            (point))))))
+          ;; If we did not manage to extract a name, cancel this
+          ;; declaration (eg. when line ends in "=> ").
+          (if (string-match "^[ \t]*$" name) (setq name nil))
+          (setq type 'instance)))
+        ;; Move past start of current declaration.
+        (goto-char end))
+      ;; If we have a valid declaration then return it, otherwise return
+      ;; nil.
+      (if name
+          (cons (cons name (cons (copy-marker start t) (copy-marker name-pos t)))
+                type)
+        nil))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Declaration scanning via `imenu'.
+
+;;;###autoload
+(defun haskell-ds-create-imenu-index ()
+  "Function for finding `imenu' declarations in Haskell mode.
+Finds all declarations (classes, variables, imports, instances and
+datatypes) in a Haskell file for the `imenu' package."
+  ;; Each list has elements of the form `(INDEX-NAME . INDEX-POSITION)'.
+  ;; These lists are nested using `(INDEX-TITLE . INDEX-ALIST)'.
+  (let* ((bird-literate (haskell-ds-bird-p))
+         (index-alist '())
+         (index-class-alist '()) ;; Classes
+         (index-var-alist '())   ;; Variables
+         (index-imp-alist '())   ;; Imports
+         (index-inst-alist '())  ;; Instances
+         (index-type-alist '())  ;; Datatypes
+         ;; Variables for showing progress.
+         (bufname (buffer-name))
+         (divisor-of-progress (max 1 (/ (buffer-size) 100)))
+         ;; The result we wish to return.
+         result)
+    (goto-char (point-min))
+    ;; Loop forwards from the beginning of the buffer through the
+    ;; starts of the top-level declarations.
+    (while (< (point) (point-max))
+      (message "Scanning declarations in %s... (%3d%%)" bufname
+               (/ (- (point) (point-min)) divisor-of-progress))
+      ;; Grab the next declaration.
+      (setq result (haskell-ds-generic-find-next-decl bird-literate))
+      (if result
+          ;; If valid, extract the components of the result.
+          (let* ((name-posns (car result))
+                 (name (car name-posns))
+                 (posns (cdr name-posns))
+                 (start-pos (car posns))
+                 (type (cdr result)))
+                 ;; Place `(name . start-pos)' in the correct alist.
+                 (cl-case type
+                   (variable
+                    (setq index-var-alist
+                          (cl-acons name start-pos index-var-alist)))
+                   (datatype
+                    (setq index-type-alist
+                          (cl-acons name start-pos index-type-alist)))
+                   (class
+                    (setq index-class-alist
+                          (cl-acons name start-pos index-class-alist)))
+                   (import
+                    (setq index-imp-alist
+                          (cl-acons name start-pos index-imp-alist)))
+                   (instance
+                    (setq index-inst-alist
+                          (cl-acons name start-pos index-inst-alist)))))))
+    ;; Now sort all the lists, label them, and place them in one list.
+    (message "Sorting declarations in %s..." bufname)
+    (when index-type-alist
+      (push (cons "Datatypes"
+                  (sort index-type-alist 'haskell-ds-imenu-label-cmp))
+            index-alist))
+    (when index-inst-alist
+      (push (cons "Instances"
+                  (sort index-inst-alist 'haskell-ds-imenu-label-cmp))
+            index-alist))
+    (when index-imp-alist
+      (push (cons "Imports"
+                  (sort index-imp-alist 'haskell-ds-imenu-label-cmp))
+            index-alist))
+    (when index-class-alist
+      (push (cons "Classes"
+                  (sort index-class-alist 'haskell-ds-imenu-label-cmp))
+            index-alist))
+    (when index-var-alist
+      (if haskell-decl-scan-bindings-as-variables
+          (push (cons "Variables"
+                      (sort index-var-alist 'haskell-ds-imenu-label-cmp))
+                index-alist)
+        (setq index-alist (append index-alist
+                                  (sort index-var-alist 'haskell-ds-imenu-label-cmp)))))
+    (message "Sorting declarations in %s...done" bufname)
+    ;; Return the alist.
+    index-alist))
+
+(defun haskell-ds-imenu-label-cmp (el1 el2)
+  "Predicate to compare labels in lists from `haskell-ds-create-imenu-index'."
+  (string< (car el1) (car el2)))
+
+(defun haskell-ds-imenu ()
+  "Install `imenu' for Haskell scripts."
+  (setq imenu-create-index-function 'haskell-ds-create-imenu-index)
+  (when haskell-decl-scan-add-to-menubar
+    (imenu-add-to-menubar "Declarations")))
+
+;; The main functions to turn on declaration scanning.
+;;;###autoload
+(defun turn-on-haskell-decl-scan ()
+  "Unconditionally activate `haskell-decl-scan-mode'."
+  (interactive)
+  (haskell-decl-scan-mode))
+(make-obsolete 'turn-on-haskell-decl-scan
+               'haskell-decl-scan-mode
+               "2015-07-23")
+
+;;;###autoload
+(define-minor-mode haskell-decl-scan-mode
+  "Toggle Haskell declaration scanning minor mode on or off.
+With a prefix argument ARG, enable minor mode if ARG is
+positive, and disable it otherwise.  If called from Lisp, enable
+the mode if ARG is omitted or nil, and toggle it if ARG is `toggle'.
+
+See also info node `(haskell-mode)haskell-decl-scan-mode' for
+more details about this minor mode.
+
+Top-level declarations are scanned and listed in the menu item
+\"Declarations\" (if enabled via option
+`haskell-decl-scan-add-to-menubar').  Selecting an item from this
+menu will take point to the start of the declaration.
+
+\\[beginning-of-defun] and \\[end-of-defun] move forward and backward to the start of a declaration.
+
+This may link with `haskell-doc-mode'.
+
+For non-literate and LaTeX-style literate scripts, we assume the
+common convention that top-level declarations start at the first
+column.  For Bird-style literate scripts, we assume the common
+convention that top-level declarations start at the third column,
+ie. after \"> \".
+
+Anything in `font-lock-comment-face' is not considered for a
+declaration.  Therefore, using Haskell font locking with comments
+coloured in `font-lock-comment-face' improves declaration scanning.
+
+Literate Haskell scripts are supported: If the value of
+`haskell-literate' (set automatically by `literate-haskell-mode')
+is `bird', a Bird-style literate script is assumed.  If it is nil
+or `tex', a non-literate or LaTeX-style literate script is
+assumed, respectively.
+
+Invokes `haskell-decl-scan-mode-hook' on activation."
+  :group 'haskell-decl-scan
+
+  (kill-local-variable 'beginning-of-defun-function)
+  (kill-local-variable 'end-of-defun-function)
+  (kill-local-variable 'imenu-create-index-function)
+  (unless haskell-decl-scan-mode
+    ;; How can we cleanly remove the "Declarations" menu?
+    (when haskell-decl-scan-add-to-menubar
+      (local-set-key [menu-bar index] nil)))
+
+  (when haskell-decl-scan-mode
+    (setq-local beginning-of-defun-function 'haskell-ds-backward-decl)
+    (setq-local end-of-defun-function 'haskell-ds-forward-decl)
+    (haskell-ds-imenu)))
+
+
+;; Provide ourselves:
+
+(provide 'haskell-decl-scan)
+
+;;; haskell-decl-scan.el ends here