You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
619 lines
26 KiB
619 lines
26 KiB
;;; 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 |
|
|
|
;; 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-") |
|
|
|
;;;###autoload |
|
(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) |
|
|
|
;;;###autoload |
|
(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-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, ie. 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-forward-decl () |
|
"As `haskell-ds-backward-decl' but forward." |
|
(interactive) |
|
(haskell-ds-move-to-decl t (haskell-ds-bird-p) nil)) |
|
|
|
(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 |
|
(set (make-local-variable 'beginning-of-defun-function) |
|
'haskell-ds-backward-decl) |
|
(set (make-local-variable 'end-of-defun-function) |
|
'haskell-ds-forward-decl) |
|
(haskell-ds-imenu))) |
|
|
|
|
|
;; Provide ourselves: |
|
|
|
(provide 'haskell-decl-scan) |
|
|
|
;;; haskell-decl-scan.el ends here
|
|
|