158 changed files with 64 additions and 59367 deletions
			
			
		@ -1 +1 @@
					 | 
				
			||||
Good signature from 474F05837FBDEF9B GNU ELPA Signing Agent <elpasign@elpa.gnu.org> (trust undefined) created at 2015-10-23T02:05:01-0700 using DSA | 
				
			||||
Good signature from 474F05837FBDEF9B GNU ELPA Signing Agent <elpasign@elpa.gnu.org> (trust undefined) created at 2015-10-24T02:05:02-0700 using DSA | 
				
			||||
									
										
											File diff suppressed because one or more lines are too long
										
									
								
							
						@ -1,130 +0,0 @@
					 | 
				
			||||
;;; cider-apropos.el --- Apropos functionality for Clojure -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright © 2014-2015 Jeff Valk, Bozhidar Batsov | 
				
			||||
;; | 
				
			||||
;; Author: Jeff Valk <jv@jeffvalk.com> | 
				
			||||
 | 
				
			||||
;; This program 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 of the License, or | 
				
			||||
;; (at your option) any later version. | 
				
			||||
 | 
				
			||||
;; This program 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/>. | 
				
			||||
 | 
				
			||||
;; This file is not part of GNU Emacs. | 
				
			||||
 | 
				
			||||
;;; Commentary: | 
				
			||||
 | 
				
			||||
;; Apropos functionality for Clojure. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'cider-doc) | 
				
			||||
(require 'cider-util) | 
				
			||||
(require 'cider-compat) | 
				
			||||
 | 
				
			||||
(require 'cider-client) | 
				
			||||
(require 'cider-popup) | 
				
			||||
(require 'nrepl-client) | 
				
			||||
 | 
				
			||||
(require 'clojure-mode) | 
				
			||||
(require 'apropos) | 
				
			||||
(require 'button) | 
				
			||||
 | 
				
			||||
(defconst cider-apropos-buffer "*cider-apropos*") | 
				
			||||
 | 
				
			||||
(push cider-apropos-buffer cider-ancillary-buffers) | 
				
			||||
 | 
				
			||||
(defun cider-apropos-doc (button) | 
				
			||||
  "Display documentation for the symbol represented at BUTTON." | 
				
			||||
  (cider-doc-lookup (button-get button 'apropos-symbol))) | 
				
			||||
 | 
				
			||||
(defun cider-apropos-summary (query ns docs-p include-private-p case-sensitive-p) | 
				
			||||
  "Return a short description for the performed apropos search." | 
				
			||||
  (concat (if case-sensitive-p "Case-sensitive " "") | 
				
			||||
          (if docs-p "Documentation " "") | 
				
			||||
          (format "Apropos for %S" query) | 
				
			||||
          (if ns (format " in namespace %S" ns) "") | 
				
			||||
          (if include-private-p | 
				
			||||
              " (public and private symbols)" | 
				
			||||
            " (public symbols only)"))) | 
				
			||||
 | 
				
			||||
(defun cider-apropos-highlight (doc query) | 
				
			||||
  "Return the DOC string propertized to highlight QUERY matches." | 
				
			||||
  (let ((pos 0)) | 
				
			||||
    (while (string-match query doc pos) | 
				
			||||
      (setq pos (match-end 0)) | 
				
			||||
      (put-text-property (match-beginning 0) | 
				
			||||
                         (match-end 0) | 
				
			||||
                         'font-lock-face apropos-match-face doc))) | 
				
			||||
  doc) | 
				
			||||
 | 
				
			||||
(defun cider-apropos-result (result query docs-p) | 
				
			||||
  "Emit a RESULT matching QUERY into current buffer, formatted for DOCS-P." | 
				
			||||
  (nrepl-dbind-response result (name type doc) | 
				
			||||
    (let* ((label (capitalize (if (string= type "variable") "var" type))) | 
				
			||||
           (help (concat "Display doc for this " (downcase label)))) | 
				
			||||
      (cider-propertize-region (list 'apropos-symbol name | 
				
			||||
                                     'action 'cider-apropos-doc | 
				
			||||
                                     'help-echo help) | 
				
			||||
        (insert-text-button name 'type 'apropos-symbol) | 
				
			||||
        (insert "\n  ") | 
				
			||||
        (insert-text-button label 'type (intern (concat "apropos-" type))) | 
				
			||||
        (insert ": ") | 
				
			||||
        (let ((beg (point))) | 
				
			||||
          (if docs-p | 
				
			||||
              (insert (cider-apropos-highlight doc query) "\n") | 
				
			||||
            (insert doc) | 
				
			||||
            (fill-region beg (point)))) | 
				
			||||
        (insert "\n"))))) | 
				
			||||
 | 
				
			||||
(declare-function cider-mode "cider-mode") | 
				
			||||
 | 
				
			||||
(defun cider-show-apropos (summary results query docs-p) | 
				
			||||
  "Show SUMMARY and RESULTS for QUERY in a pop-up buffer, formatted for DOCS-P." | 
				
			||||
  (with-current-buffer (cider-popup-buffer cider-apropos-buffer t) | 
				
			||||
    (let ((inhibit-read-only t)) | 
				
			||||
      (set-syntax-table clojure-mode-syntax-table) | 
				
			||||
      (apropos-mode) | 
				
			||||
      (cider-mode) | 
				
			||||
      (if (boundp 'header-line-format) | 
				
			||||
          (setq-local header-line-format summary) | 
				
			||||
        (insert summary "\n\n")) | 
				
			||||
      (dolist (result results) | 
				
			||||
        (cider-apropos-result result query docs-p)) | 
				
			||||
      (goto-char (point-min))))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun cider-apropos (query &optional ns docs-p privates-p case-sensitive-p) | 
				
			||||
  "Show all symbols whose names match QUERY, a regular expression. | 
				
			||||
The search may be limited to the namespace NS, and may optionally search doc | 
				
			||||
strings, include private vars, and be case sensitive." | 
				
			||||
  (interactive | 
				
			||||
   (if current-prefix-arg | 
				
			||||
       (list (read-string "Clojure Apropos: ") | 
				
			||||
             (let ((ns (read-string "Namespace: "))) | 
				
			||||
               (if (string= ns "") nil ns)) | 
				
			||||
             (y-or-n-p "Search doc strings? ") | 
				
			||||
             (y-or-n-p "Include private symbols? ") | 
				
			||||
             (y-or-n-p "Case-sensitive? ")) | 
				
			||||
     (list (read-string "Clojure Apropos: ")))) | 
				
			||||
  (cider-ensure-op-supported "apropos") | 
				
			||||
  (if-let ((summary (cider-apropos-summary | 
				
			||||
                     query ns docs-p privates-p case-sensitive-p)) | 
				
			||||
           (results (cider-sync-request:apropos query ns docs-p privates-p case-sensitive-p))) | 
				
			||||
      (cider-show-apropos summary results query docs-p) | 
				
			||||
    (message "No apropos matches for %S" query))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun cider-apropos-documentation () | 
				
			||||
  "Shortcut for (cider-apropos <query> nil t)." | 
				
			||||
  (interactive) | 
				
			||||
  (cider-apropos (read-string "Clojure documentation Apropos: ") nil t)) | 
				
			||||
 | 
				
			||||
(provide 'cider-apropos) | 
				
			||||
@ -1,150 +0,0 @@
					 | 
				
			||||
;;; cider-browse-ns.el --- CIDER namespace browser | 
				
			||||
 | 
				
			||||
;; Copyright © 2014-2015 John Andrews | 
				
			||||
 | 
				
			||||
;; Author: John Andrews <john.m.andrews@gmail.com> | 
				
			||||
 | 
				
			||||
;; This program 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 of the License, or | 
				
			||||
;; (at your option) any later version. | 
				
			||||
 | 
				
			||||
;; This program 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/>. | 
				
			||||
 | 
				
			||||
;; This file is not part of GNU Emacs. | 
				
			||||
 | 
				
			||||
;;; Commentary: | 
				
			||||
 | 
				
			||||
;; (cider-browse-ns) | 
				
			||||
;; Display a list of all vars in a namespace. | 
				
			||||
;; Pressing <enter> will take you to the cider-doc buffer for that var. | 
				
			||||
;; Pressing ^ will take you to a list of all namespaces (akin to dired mode) | 
				
			||||
 | 
				
			||||
;; (cider-browse-ns-all) | 
				
			||||
;; Explore clojure namespaces by browsing a list of all namespaces. | 
				
			||||
;; Pressing enter expands into a list of that namespace's vars as if by | 
				
			||||
;; executing the command (cider-browse-ns "my.ns") | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'cider-repl) | 
				
			||||
(require 'cider-client) | 
				
			||||
(require 'cider-compat) | 
				
			||||
 | 
				
			||||
(defconst cider-browse-ns-buffer "*Browse NS*") | 
				
			||||
(defvar-local cider-browse-ns-current-ns nil) | 
				
			||||
 | 
				
			||||
;; Mode Definition | 
				
			||||
 | 
				
			||||
(defvar cider-browse-ns-mode-map | 
				
			||||
  (let ((map (make-sparse-keymap))) | 
				
			||||
    (set-keymap-parent map cider-popup-buffer-mode-map) | 
				
			||||
    (define-key map "d" #'cider-browse-ns--doc-at-point) | 
				
			||||
    (define-key map "s" #'cider-browse-ns--find-at-point) | 
				
			||||
    (define-key map [return] #'cider-browse-ns--doc-at-point) | 
				
			||||
    (define-key map "^" #'cider-browse-ns-all) | 
				
			||||
    (define-key map "n" #'next-line) | 
				
			||||
    (define-key map "p" #'previous-line) | 
				
			||||
    map)) | 
				
			||||
 | 
				
			||||
(defvar cider-browse-ns-mouse-map | 
				
			||||
  (let ((map (make-sparse-keymap))) | 
				
			||||
    (define-key map [mouse-1] #'cider-browse-ns--handle-mouse) | 
				
			||||
    map)) | 
				
			||||
 | 
				
			||||
(define-derived-mode cider-browse-ns-mode special-mode "browse-ns" | 
				
			||||
  "Major mode for browsing Clojure namespaces. | 
				
			||||
 | 
				
			||||
\\{cider-browse-ns-mode-map}" | 
				
			||||
  (set-syntax-table clojure-mode-syntax-table) | 
				
			||||
  (setq buffer-read-only t) | 
				
			||||
  (setq-local electric-indent-chars nil) | 
				
			||||
  (setq-local truncate-lines t) | 
				
			||||
  (setq-local cider-browse-ns-current-ns nil)) | 
				
			||||
 | 
				
			||||
(defun cider-browse-ns--properties (text) | 
				
			||||
  "Decorate TEXT with a clickable keymap and function face." | 
				
			||||
  (propertize text | 
				
			||||
              'font-lock-face 'font-lock-function-name-face | 
				
			||||
              'mouse-face 'highlight | 
				
			||||
              'keymap cider-browse-ns-mouse-map)) | 
				
			||||
 | 
				
			||||
(defun cider-browse-ns--list (buffer title items &optional ns noerase) | 
				
			||||
  "Reset contents of BUFFER.  Then display TITLE at the top and ITEMS are indented underneath. | 
				
			||||
If NS is non-nil, it is added to each item as the | 
				
			||||
`cider-browse-ns-current-ns' text property. If NOERASE is non-nil, the | 
				
			||||
contents of the buffer are not reset before inserting TITLE and ITEMS." | 
				
			||||
  (with-current-buffer buffer | 
				
			||||
    (cider-browse-ns-mode) | 
				
			||||
    (let ((inhibit-read-only t)) | 
				
			||||
      (unless noerase (erase-buffer)) | 
				
			||||
      (goto-char (point-max)) | 
				
			||||
      (insert (propertize title 'font-lock-face 'font-lock-type-face) | 
				
			||||
              "\n") | 
				
			||||
      (dolist (item items) | 
				
			||||
        (insert (propertize (concat "  " item "\n") | 
				
			||||
                            'cider-browse-ns-current-ns ns))) | 
				
			||||
      (goto-char (point-min))))) | 
				
			||||
 | 
				
			||||
;; Interactive Functions | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun cider-browse-ns (namespace) | 
				
			||||
  "List all NAMESPACE's vars in BUFFER." | 
				
			||||
  (interactive (list (completing-read "Browse namespace: " (cider-sync-request:ns-list)))) | 
				
			||||
  (with-current-buffer (cider-popup-buffer cider-browse-ns-buffer t) | 
				
			||||
    (let ((vars (cider-sync-request:ns-vars namespace))) | 
				
			||||
      (cider-browse-ns--list (current-buffer) | 
				
			||||
                             namespace | 
				
			||||
                             (mapcar (lambda (var) | 
				
			||||
                                       (format "%s" | 
				
			||||
                                               (cider-browse-ns--properties var))) | 
				
			||||
                                     vars)) | 
				
			||||
      (setq-local cider-browse-ns-current-ns namespace)))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun cider-browse-ns-all () | 
				
			||||
  "List all loaded namespaces in BUFFER." | 
				
			||||
  (interactive) | 
				
			||||
  (with-current-buffer (cider-popup-buffer cider-browse-ns-buffer t) | 
				
			||||
    (let ((names (cider-sync-request:ns-list))) | 
				
			||||
      (cider-browse-ns--list (current-buffer) | 
				
			||||
                             "All loaded namespaces" | 
				
			||||
                             (mapcar (lambda (name) | 
				
			||||
                                       (cider-browse-ns--properties name)) | 
				
			||||
                                     names)) | 
				
			||||
      (setq-local cider-browse-ns-current-ns nil)))) | 
				
			||||
 | 
				
			||||
(defun cider-browse-ns--var-at-point () | 
				
			||||
  (let ((line (thing-at-point 'line))) | 
				
			||||
    (when (string-match " +\\(.+\\)\n?" line) | 
				
			||||
      (format "%s/%s" | 
				
			||||
              (or (get-text-property (point) 'cider-browse-ns-current-ns) | 
				
			||||
                  cider-browse-ns-current-ns) | 
				
			||||
              (match-string 1 line))))) | 
				
			||||
 | 
				
			||||
(defun cider-browse-ns--doc-at-point () | 
				
			||||
  "Expand browser according to thing at current point." | 
				
			||||
  (interactive) | 
				
			||||
  (when-let ((var (cider-browse-ns--var-at-point))) | 
				
			||||
    (cider-doc-lookup var))) | 
				
			||||
 | 
				
			||||
(defun cider-browse-ns--find-at-point () | 
				
			||||
  (interactive) | 
				
			||||
  (when-let ((var (cider-browse-ns--var-at-point))) | 
				
			||||
    (cider-find-var current-prefix-arg var))) | 
				
			||||
 | 
				
			||||
(defun cider-browse-ns--handle-mouse (event) | 
				
			||||
  "Handle mouse click EVENT." | 
				
			||||
  (interactive "e") | 
				
			||||
  (cider-browse-ns--doc-at-point)) | 
				
			||||
 | 
				
			||||
(provide 'cider-browse-ns) | 
				
			||||
 | 
				
			||||
;;; cider-browse-ns.el ends here | 
				
			||||
@ -1,106 +0,0 @@
					 | 
				
			||||
;;; cider-classpath.el --- Basic Java classpath browser | 
				
			||||
 | 
				
			||||
;; Copyright © 2014-2015 Bozhidar Batsov | 
				
			||||
 | 
				
			||||
;; This program 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 of the License, or | 
				
			||||
;; (at your option) any later version. | 
				
			||||
 | 
				
			||||
;; This program 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/>. | 
				
			||||
 | 
				
			||||
;; This file is not part of GNU Emacs. | 
				
			||||
 | 
				
			||||
;;; Commentary: | 
				
			||||
 | 
				
			||||
;; Basic Java classpath browser for CIDER. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'cider-client) | 
				
			||||
(require 'cider-popup) | 
				
			||||
(require 'cider-compat) | 
				
			||||
 | 
				
			||||
(defvar cider-classpath-buffer "*Classpath*") | 
				
			||||
 | 
				
			||||
(defvar cider-classpath-mode-map | 
				
			||||
  (let ((map (make-sparse-keymap))) | 
				
			||||
    (set-keymap-parent map cider-popup-buffer-mode-map) | 
				
			||||
    (define-key map [return] #'cider-classpath-operate-on-point) | 
				
			||||
    (define-key map "n" #'next-line) | 
				
			||||
    (define-key map "p" #'previous-line) | 
				
			||||
    map)) | 
				
			||||
 | 
				
			||||
(defvar cider-classpath-mouse-map | 
				
			||||
  (let ((map (make-sparse-keymap))) | 
				
			||||
    (define-key map [mouse-1] #'cider-classpath-handle-mouse) | 
				
			||||
    map)) | 
				
			||||
 | 
				
			||||
(define-derived-mode cider-classpath-mode special-mode "classpath" | 
				
			||||
  "Major mode for browsing the entries in Java's classpath. | 
				
			||||
 | 
				
			||||
\\{cider-classpath-mode-map}" | 
				
			||||
  (setq buffer-read-only t) | 
				
			||||
  (setq-local electric-indent-chars nil) | 
				
			||||
  (setq-local truncate-lines t)) | 
				
			||||
 | 
				
			||||
(defun cider-classpath-list (buffer items) | 
				
			||||
  "Populate BUFFER with ITEMS." | 
				
			||||
  (with-current-buffer buffer | 
				
			||||
    (cider-classpath-mode) | 
				
			||||
    (let ((inhibit-read-only t)) | 
				
			||||
      (erase-buffer) | 
				
			||||
      (dolist (item items) | 
				
			||||
        (insert item "\n")) | 
				
			||||
      (goto-char (point-min))))) | 
				
			||||
 | 
				
			||||
(defun cider-classpath-properties (text) | 
				
			||||
  "Decorate TEXT with a clickable keymap and function face." | 
				
			||||
  (let ((face (cond | 
				
			||||
               ((not (file-exists-p text)) 'font-lock-warning-face) | 
				
			||||
               ((file-directory-p text) 'dired-directory) | 
				
			||||
               (t 'default)))) | 
				
			||||
    (propertize text | 
				
			||||
                'font-lock-face face | 
				
			||||
                'mouse-face 'highlight | 
				
			||||
                'keymap cider-classpath-mouse-map))) | 
				
			||||
 | 
				
			||||
(defun cider-classpath-operate-on-point () | 
				
			||||
  "Expand browser according to thing at current point." | 
				
			||||
  (interactive) | 
				
			||||
  (let* ((bol (line-beginning-position)) | 
				
			||||
         (eol (line-end-position)) | 
				
			||||
         (line (buffer-substring-no-properties bol eol))) | 
				
			||||
    (find-file-other-window line))) | 
				
			||||
 | 
				
			||||
(defun cider-classpath-handle-mouse (event) | 
				
			||||
  "Handle mouse click EVENT." | 
				
			||||
  (interactive "e") | 
				
			||||
  (cider-classpath-operate-on-point)) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun cider-classpath () | 
				
			||||
  "List all classpath entries." | 
				
			||||
  (interactive) | 
				
			||||
  (with-current-buffer (cider-popup-buffer cider-classpath-buffer t) | 
				
			||||
    (cider-classpath-list (current-buffer) | 
				
			||||
                          (mapcar (lambda (name) | 
				
			||||
                                    (cider-classpath-properties name)) | 
				
			||||
                                  (cider-sync-request:classpath))))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun cider-open-classpath-entry () | 
				
			||||
  "Open a classpath entry." | 
				
			||||
  (interactive) | 
				
			||||
  (when-let ((entry (completing-read "Classpath entries: " (cider-sync-request:classpath)))) | 
				
			||||
    (find-file-other-window entry))) | 
				
			||||
 | 
				
			||||
(provide 'cider-classpath) | 
				
			||||
 | 
				
			||||
;;; cider-classpath.el ends here | 
				
			||||
@ -1,225 +0,0 @@
					 | 
				
			||||
;;; cider-common.el --- Common use functions         -*- lexical-binding: t; -*- | 
				
			||||
 | 
				
			||||
;; Copyright (C) 2015  Artur Malabarba | 
				
			||||
 | 
				
			||||
;; Author: Artur Malabarba <bruce.connor.am@gmail.com> | 
				
			||||
 | 
				
			||||
;; This program 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 of the License, or | 
				
			||||
;; (at your option) any later version. | 
				
			||||
 | 
				
			||||
;; This program 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: | 
				
			||||
 | 
				
			||||
;; Common functions that are useful in both Clojure buffers and REPL | 
				
			||||
;; buffers. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'cider-compat) | 
				
			||||
(require 'nrepl-client) | 
				
			||||
(require 'cider-util) | 
				
			||||
 | 
				
			||||
(defcustom cider-prompt-for-symbol t | 
				
			||||
  "Controls when to prompt for symbol when a command requires one. | 
				
			||||
 | 
				
			||||
When non-nil, always prompt, and use the symbol at point as the default | 
				
			||||
value at the prompt. | 
				
			||||
 | 
				
			||||
When nil, attempt to use the symbol at point for the command, and only | 
				
			||||
prompt if that throws an error." | 
				
			||||
  :type '(choice (const :tag "always" t) | 
				
			||||
                 (const :tag "dwim" nil)) | 
				
			||||
  :group 'cider | 
				
			||||
  :package-version '(cider . "0.9.0")) | 
				
			||||
 | 
				
			||||
(defun cider--should-prompt-for-symbol (&optional invert) | 
				
			||||
  (if invert (not cider-prompt-for-symbol) cider-prompt-for-symbol)) | 
				
			||||
 | 
				
			||||
(defun cider-prompt-for-symbol-function (&optional invert) | 
				
			||||
  (if (cider--should-prompt-for-symbol invert) | 
				
			||||
      #'cider-read-symbol-name | 
				
			||||
    #'cider-try-symbol-at-point)) | 
				
			||||
 | 
				
			||||
(defun cider--kw-to-symbol (kw) | 
				
			||||
  "Convert the keyword KW to a symbol." | 
				
			||||
  (when kw | 
				
			||||
    (replace-regexp-in-string "\\`:+" "" kw))) | 
				
			||||
 | 
				
			||||
(declare-function cider-read-from-minibuffer "cider-interaction") | 
				
			||||
 | 
				
			||||
(defun cider-read-symbol-name (prompt callback) | 
				
			||||
  "Read a symbol name using PROMPT with a default of the one at point. | 
				
			||||
Use CALLBACK as the completing read var callback." | 
				
			||||
  (funcall callback (cider-read-from-minibuffer | 
				
			||||
                     prompt | 
				
			||||
                     ;; if the thing at point is a keyword we treat it as symbol | 
				
			||||
                     (cider--kw-to-symbol (cider-symbol-at-point))))) | 
				
			||||
 | 
				
			||||
(defun cider-try-symbol-at-point (prompt callback) | 
				
			||||
  "Call CALLBACK with symbol at point. | 
				
			||||
On failure, read a symbol name using PROMPT and call CALLBACK with that." | 
				
			||||
  (condition-case nil (funcall callback (cider--kw-to-symbol (cider-symbol-at-point))) | 
				
			||||
    ('error (funcall callback (cider-read-from-minibuffer prompt))))) | 
				
			||||
 | 
				
			||||
(declare-function cider-jump-to "cider-interaction") | 
				
			||||
 | 
				
			||||
(defun cider--jump-to-loc-from-info (info &optional other-window) | 
				
			||||
  "Jump to location give by INFO. | 
				
			||||
INFO object is returned by `cider-var-info' or `cider-member-info'. | 
				
			||||
OTHER-WINDOW is passed to `cider-jamp-to'." | 
				
			||||
  (let* ((line (nrepl-dict-get info "line")) | 
				
			||||
         (file (nrepl-dict-get info "file")) | 
				
			||||
         (name (nrepl-dict-get info "name")) | 
				
			||||
         (buffer (and file | 
				
			||||
                      (not (cider--tooling-file-p file)) | 
				
			||||
                      (cider-find-file file)))) | 
				
			||||
    (if buffer | 
				
			||||
        (cider-jump-to buffer (if line (cons line nil) name) other-window) | 
				
			||||
      (error "No source location")))) | 
				
			||||
 | 
				
			||||
(declare-function url-filename "url-parse" (cl-x) t) | 
				
			||||
 | 
				
			||||
(defun cider--url-to-file (url) | 
				
			||||
  "Return the filename from the resource URL. | 
				
			||||
Uses `url-generic-parse-url' to parse the url.  The filename is extracted and | 
				
			||||
then url decoded.  If the decoded filename has a Windows device letter followed | 
				
			||||
by a colon immediately after the leading '/' then the leading '/' is dropped to | 
				
			||||
create a valid path." | 
				
			||||
  (let ((filename (url-unhex-string (url-filename (url-generic-parse-url url))))) | 
				
			||||
    (if (string-match "^/\\([a-zA-Z]:/.*\\)" filename) | 
				
			||||
        (match-string 1 filename) | 
				
			||||
      filename))) | 
				
			||||
 | 
				
			||||
(defun cider-tramp-prefix (&optional buffer) | 
				
			||||
  "Use the filename for BUFFER to determine a tramp prefix. | 
				
			||||
Defaults to the current buffer. | 
				
			||||
Return the tramp prefix, or nil if BUFFER is local." | 
				
			||||
  (let* ((buffer (or buffer (current-buffer))) | 
				
			||||
         (name (or (buffer-file-name buffer) | 
				
			||||
                   (with-current-buffer buffer | 
				
			||||
                     default-directory)))) | 
				
			||||
    (when (tramp-tramp-file-p name) | 
				
			||||
      (let ((vec (tramp-dissect-file-name name))) | 
				
			||||
        (tramp-make-tramp-file-name (tramp-file-name-method vec) | 
				
			||||
                                    (tramp-file-name-user vec) | 
				
			||||
                                    (tramp-file-name-host vec) | 
				
			||||
                                    nil))))) | 
				
			||||
 | 
				
			||||
(defun cider--client-tramp-filename (name &optional buffer) | 
				
			||||
  "Return the tramp filename for path NAME relative to BUFFER. | 
				
			||||
If BUFFER has a tramp prefix, it will be added as a prefix to NAME. | 
				
			||||
If the resulting path is an existing tramp file, it returns the path, | 
				
			||||
otherwise, nil." | 
				
			||||
  (let* ((buffer (or buffer (current-buffer))) | 
				
			||||
         (name (concat (cider-tramp-prefix buffer) name))) | 
				
			||||
    (if (tramp-handle-file-exists-p name) | 
				
			||||
        name))) | 
				
			||||
 | 
				
			||||
(defun cider--server-filename (name) | 
				
			||||
  "Return the nREPL server-relative filename for NAME." | 
				
			||||
  (if (tramp-tramp-file-p name) | 
				
			||||
      (with-parsed-tramp-file-name name nil | 
				
			||||
        localname) | 
				
			||||
    name)) | 
				
			||||
 | 
				
			||||
(defvar cider-from-nrepl-filename-function | 
				
			||||
  (with-no-warnings | 
				
			||||
    (if (eq system-type 'cygwin) | 
				
			||||
        #'cygwin-convert-file-name-from-windows | 
				
			||||
      #'identity)) | 
				
			||||
  "Function to translate nREPL namestrings to Emacs filenames.") | 
				
			||||
 | 
				
			||||
(defcustom cider-prefer-local-resources nil | 
				
			||||
  "Prefer local resources to remote (tramp) ones when both are available." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'cider) | 
				
			||||
 | 
				
			||||
(defun cider--file-path (path) | 
				
			||||
  "Return PATH's local or tramp path using `cider-prefer-local-resources'. | 
				
			||||
If no local or remote file exists, return nil." | 
				
			||||
  (let* ((local-path (funcall cider-from-nrepl-filename-function path)) | 
				
			||||
         (tramp-path (and local-path (cider--client-tramp-filename local-path)))) | 
				
			||||
    (cond ((equal local-path "") "") | 
				
			||||
          ((and cider-prefer-local-resources (file-exists-p local-path)) | 
				
			||||
           local-path) | 
				
			||||
          ((and tramp-path (file-exists-p tramp-path)) | 
				
			||||
           tramp-path) | 
				
			||||
          ((and local-path (file-exists-p local-path)) | 
				
			||||
           local-path)))) | 
				
			||||
 | 
				
			||||
(declare-function archive-extract "arc-mode") | 
				
			||||
(declare-function archive-zip-extract "arc-mode") | 
				
			||||
 | 
				
			||||
(defun cider-find-file (url) | 
				
			||||
  "Return a buffer visiting the file URL if it exists, or nil otherwise. | 
				
			||||
If URL has a scheme prefix, it must represent a fully-qualified file path | 
				
			||||
or an entry within a zip/jar archive.  If URL doesn't contain a scheme | 
				
			||||
prefix and is an absolute path, it is treated as such.  Finally, if URL is | 
				
			||||
relative, it is expanded within each of the open Clojure buffers till an | 
				
			||||
existing file ending with URL has been found." | 
				
			||||
  (require 'arc-mode) | 
				
			||||
  (cond ((string-match "^file:\\(.+\\)" url) | 
				
			||||
         (when-let ((file (cider--url-to-file (match-string 1 url))) | 
				
			||||
                    (path (cider--file-path file))) | 
				
			||||
           (find-file-noselect path))) | 
				
			||||
        ((string-match "^\\(jar\\|zip\\):\\(file:.+\\)!/\\(.+\\)" url) | 
				
			||||
         (when-let ((entry (match-string 3 url)) | 
				
			||||
                    (file  (cider--url-to-file (match-string 2 url))) | 
				
			||||
                    (path  (cider--file-path file)) | 
				
			||||
                    (name  (format "%s:%s" path entry))) | 
				
			||||
           (or (find-buffer-visiting name) | 
				
			||||
               (if (tramp-tramp-file-p path) | 
				
			||||
                   (progn | 
				
			||||
                     ;; Use emacs built in archiving | 
				
			||||
                     (find-file path) | 
				
			||||
                     (goto-char (point-min)) | 
				
			||||
                     ;; Make sure the file path is followed by a newline to | 
				
			||||
                     ;; prevent eg. clj matching cljs. | 
				
			||||
                     (search-forward (concat entry "\n")) | 
				
			||||
                     ;; moves up to matching line | 
				
			||||
                     (forward-line -1) | 
				
			||||
                     (archive-extract) | 
				
			||||
                     (current-buffer)) | 
				
			||||
                 ;; Use external zip program to just extract the single file | 
				
			||||
                 (with-current-buffer (generate-new-buffer | 
				
			||||
                                       (file-name-nondirectory entry)) | 
				
			||||
                   (archive-zip-extract path entry) | 
				
			||||
                   (set-visited-file-name name) | 
				
			||||
                   (setq-local default-directory (file-name-directory path)) | 
				
			||||
                   (setq-local buffer-read-only t) | 
				
			||||
                   (set-buffer-modified-p nil) | 
				
			||||
                   (set-auto-mode) | 
				
			||||
                   (current-buffer)))))) | 
				
			||||
        (t (if-let ((path (cider--file-path url))) | 
				
			||||
               (find-file-noselect path) | 
				
			||||
             (unless (file-name-absolute-p url) | 
				
			||||
               (let ((cider-buffers (cider-util--clojure-buffers)) | 
				
			||||
                     (url (file-name-nondirectory url))) | 
				
			||||
                 (or (cl-loop for bf in cider-buffers | 
				
			||||
                              for path = (with-current-buffer bf | 
				
			||||
                                           (expand-file-name url)) | 
				
			||||
                              if (and path (file-exists-p path)) | 
				
			||||
                              return (find-file-noselect path)) | 
				
			||||
                     (cl-loop for bf in cider-buffers | 
				
			||||
                              if (string= (buffer-name bf) url) | 
				
			||||
                              return bf)))))))) | 
				
			||||
 | 
				
			||||
(defun cider--open-other-window-p (arg) | 
				
			||||
  "Test prefix value ARG to see if it indicates displaying results in other window." | 
				
			||||
  (let ((narg (prefix-numeric-value arg))) | 
				
			||||
    (pcase narg | 
				
			||||
      (-1 t) ; - | 
				
			||||
      (16 t) ; empty empty | 
				
			||||
      (_ nil)))) | 
				
			||||
 | 
				
			||||
(provide 'cider-common) | 
				
			||||
;;; cider-common.el ends here | 
				
			||||
@ -1,157 +0,0 @@
					 | 
				
			||||
;;; cider-compat.el --- Functions from newer Emacs versions for compatibility -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright © 2012-2015 Tim King, Phil Hagelberg | 
				
			||||
;; Copyright © 2013-2015 Bozhidar Batsov, Hugo Duncan, Steve Purcell | 
				
			||||
;; | 
				
			||||
 | 
				
			||||
;; This program 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 of the License, or | 
				
			||||
;; (at your option) any later version. | 
				
			||||
 | 
				
			||||
;; This program 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/>. | 
				
			||||
 | 
				
			||||
;; This file is not part of GNU Emacs. | 
				
			||||
 | 
				
			||||
;;; Commentary: | 
				
			||||
 | 
				
			||||
;; Pretty much everything here's copied from subr-x for compatibility with | 
				
			||||
;; Emacs 24.3 and 24.4. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(eval-and-compile | 
				
			||||
 | 
				
			||||
  (unless (fboundp 'thread-first ) | 
				
			||||
 | 
				
			||||
    (defmacro internal--thread-argument (first? &rest forms) | 
				
			||||
      "Internal implementation for `thread-first' and `thread-last'. | 
				
			||||
When Argument FIRST? is non-nil argument is threaded first, else | 
				
			||||
last.  FORMS are the expressions to be threaded." | 
				
			||||
      (pcase forms | 
				
			||||
        (`(,x (,f . ,args) . ,rest) | 
				
			||||
         `(internal--thread-argument | 
				
			||||
           ,first? ,(if first? `(,f ,x ,@args) `(,f ,@args ,x)) ,@rest)) | 
				
			||||
        (`(,x ,f . ,rest) `(internal--thread-argument ,first? (,f ,x) ,@rest)) | 
				
			||||
        (_ (car forms)))) | 
				
			||||
 | 
				
			||||
    (defmacro thread-first (&rest forms) | 
				
			||||
      "Thread FORMS elements as the first argument of their successor. | 
				
			||||
Example: | 
				
			||||
    (thread-first | 
				
			||||
      5 | 
				
			||||
      (+ 20) | 
				
			||||
      (/ 25) | 
				
			||||
      - | 
				
			||||
      (+ 40)) | 
				
			||||
Is equivalent to: | 
				
			||||
    (+ (- (/ (+ 5 20) 25)) 40) | 
				
			||||
Note how the single `-' got converted into a list before | 
				
			||||
threading." | 
				
			||||
      (declare (indent 1) | 
				
			||||
               (debug (form &rest [&or symbolp (sexp &rest form)]))) | 
				
			||||
      `(internal--thread-argument t ,@forms))) | 
				
			||||
 | 
				
			||||
  (unless (fboundp 'thread-last ) | 
				
			||||
 | 
				
			||||
    (defmacro thread-last (&rest forms) | 
				
			||||
      "Thread FORMS elements as the last argument of their successor. | 
				
			||||
Example: | 
				
			||||
    (thread-last | 
				
			||||
      5 | 
				
			||||
      (+ 20) | 
				
			||||
      (/ 25) | 
				
			||||
      - | 
				
			||||
      (+ 40)) | 
				
			||||
Is equivalent to: | 
				
			||||
    (+ 40 (- (/ 25 (+ 20 5)))) | 
				
			||||
Note how the single `-' got converted into a list before | 
				
			||||
threading." | 
				
			||||
      (declare (indent 1) (debug thread-first)) | 
				
			||||
      `(internal--thread-argument nil ,@forms)))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
(eval-and-compile | 
				
			||||
 | 
				
			||||
  (unless (fboundp 'internal--listify) | 
				
			||||
 | 
				
			||||
    (defsubst internal--listify (elt) | 
				
			||||
      "Wrap ELT in a list if it is not one." | 
				
			||||
      (if (not (listp elt)) | 
				
			||||
          (list elt) | 
				
			||||
        elt))) | 
				
			||||
 | 
				
			||||
  (unless (fboundp 'internal--check-binding) | 
				
			||||
 | 
				
			||||
    (defsubst internal--check-binding (binding) | 
				
			||||
      "Check BINDING is properly formed." | 
				
			||||
      (when (> (length binding) 2) | 
				
			||||
        (signal | 
				
			||||
         'error | 
				
			||||
         (cons "`let' bindings can have only one value-form" binding))) | 
				
			||||
      binding)) | 
				
			||||
 | 
				
			||||
  (unless (fboundp 'internal--build-binding-value-form) | 
				
			||||
 | 
				
			||||
    (defsubst internal--build-binding-value-form (binding prev-var) | 
				
			||||
      "Build the conditional value form for BINDING using PREV-VAR." | 
				
			||||
      `(,(car binding) (and ,prev-var ,(cadr binding))))) | 
				
			||||
 | 
				
			||||
  (unless (fboundp ' internal--build-binding) | 
				
			||||
 | 
				
			||||
    (defun internal--build-binding (binding prev-var) | 
				
			||||
      "Check and build a single BINDING with PREV-VAR." | 
				
			||||
      (thread-first | 
				
			||||
          binding | 
				
			||||
        internal--listify | 
				
			||||
        internal--check-binding | 
				
			||||
        (internal--build-binding-value-form prev-var)))) | 
				
			||||
 | 
				
			||||
  (unless (fboundp ' internal--build-bindings) | 
				
			||||
 | 
				
			||||
    (defun internal--build-bindings (bindings) | 
				
			||||
      "Check and build conditional value forms for BINDINGS." | 
				
			||||
      (let ((prev-var t)) | 
				
			||||
        (mapcar (lambda (binding) | 
				
			||||
                  (let ((binding (internal--build-binding binding prev-var))) | 
				
			||||
                    (setq prev-var (car binding)) | 
				
			||||
                    binding)) | 
				
			||||
                bindings))))) | 
				
			||||
 | 
				
			||||
(eval-and-compile | 
				
			||||
 | 
				
			||||
  (unless (fboundp 'if-let) | 
				
			||||
    (defmacro if-let (bindings then &rest else) | 
				
			||||
      "Process BINDINGS and if all values are non-nil eval THEN, else ELSE. | 
				
			||||
Argument BINDINGS is a list of tuples whose car is a symbol to be | 
				
			||||
bound and (optionally) used in THEN, and its cadr is a sexp to be | 
				
			||||
evalled to set symbol's value.  In the special case you only want | 
				
			||||
to bind a single value, BINDINGS can just be a plain tuple." | 
				
			||||
      (declare (indent 2) | 
				
			||||
               (debug ([&or (&rest (symbolp form)) (symbolp form)] form body))) | 
				
			||||
      (when (and (<= (length bindings) 2) | 
				
			||||
                 (not (listp (car bindings)))) | 
				
			||||
        ;; Adjust the single binding case | 
				
			||||
        (setq bindings (list bindings))) | 
				
			||||
      `(let* ,(internal--build-bindings bindings) | 
				
			||||
         (if ,(car (internal--listify (car (last bindings)))) | 
				
			||||
             ,then | 
				
			||||
           ,@else))) | 
				
			||||
 | 
				
			||||
    (defmacro when-let (bindings &rest body) | 
				
			||||
      "Process BINDINGS and if all values are non-nil eval BODY. | 
				
			||||
Argument BINDINGS is a list of tuples whose car is a symbol to be | 
				
			||||
bound and (optionally) used in BODY, and its cadr is a sexp to be | 
				
			||||
evalled to set symbol's value.  In the special case you only want | 
				
			||||
to bind a single value, BINDINGS can just be a plain tuple." | 
				
			||||
      (declare (indent 1) (debug if-let)) | 
				
			||||
      (list 'if-let bindings (macroexp-progn body))))) | 
				
			||||
 | 
				
			||||
(provide 'cider-compat) | 
				
			||||
;;; cider-compat.el ends here | 
				
			||||
@ -1,164 +0,0 @@
					 | 
				
			||||
;;; cider-eldoc.el --- eldoc support for Clojure -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright © 2012-2015 Tim King, Phil Hagelberg | 
				
			||||
;; Copyright © 2013-2015 Bozhidar Batsov, Hugo Duncan, Steve Purcell | 
				
			||||
;; | 
				
			||||
;; Author: Tim King <kingtim@gmail.com> | 
				
			||||
;;         Phil Hagelberg <technomancy@gmail.com> | 
				
			||||
;;         Bozhidar Batsov <bozhidar@batsov.com> | 
				
			||||
;;         Hugo Duncan <hugo@hugoduncan.org> | 
				
			||||
;;         Steve Purcell <steve@sanityinc.com> | 
				
			||||
 | 
				
			||||
;; This program 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 of the License, or | 
				
			||||
;; (at your option) any later version. | 
				
			||||
 | 
				
			||||
;; This program 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/>. | 
				
			||||
 | 
				
			||||
;; This file is not part of GNU Emacs. | 
				
			||||
 | 
				
			||||
;;; Commentary: | 
				
			||||
 | 
				
			||||
;; eldoc support for Clojure. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'cider-client) | 
				
			||||
(require 'cider-common) ; for cider-symbol-at-point | 
				
			||||
(require 'cider-compat) | 
				
			||||
(require 'cider-util) | 
				
			||||
 | 
				
			||||
(require 'cl-lib) | 
				
			||||
 | 
				
			||||
(require 'eldoc) | 
				
			||||
 | 
				
			||||
(defvar cider-extra-eldoc-commands '("yas-expand") | 
				
			||||
  "Extra commands to be added to eldoc's safe commands list.") | 
				
			||||
 | 
				
			||||
(defvar-local cider-eldoc-last-symbol nil | 
				
			||||
  "The eldoc information for the last symbol we checked.") | 
				
			||||
 | 
				
			||||
(defun cider-eldoc-format-thing (thing) | 
				
			||||
  "Format the eldoc THING." | 
				
			||||
  (propertize thing 'face 'font-lock-function-name-face)) | 
				
			||||
 | 
				
			||||
(defun cider-highlight-args (arglist pos) | 
				
			||||
  "Format the the function ARGLIST for eldoc. | 
				
			||||
POS is the index of the currently highlighted argument." | 
				
			||||
  (let* ((rest-pos (cider--find-rest-args-position arglist)) | 
				
			||||
         (i 0)) | 
				
			||||
    (mapconcat | 
				
			||||
     (lambda (arg) | 
				
			||||
       (let ((argstr (format "%s" arg))) | 
				
			||||
         (if (eq arg '&) | 
				
			||||
             argstr | 
				
			||||
           (prog1 | 
				
			||||
               (if (or (= (1+ i) pos) | 
				
			||||
                       (and rest-pos (> (+ 1 i) rest-pos) | 
				
			||||
                            (> pos rest-pos))) | 
				
			||||
                   (propertize argstr 'face | 
				
			||||
                               'eldoc-highlight-function-argument) | 
				
			||||
                 argstr) | 
				
			||||
             (setq i (1+ i)))))) arglist " "))) | 
				
			||||
 | 
				
			||||
(defun cider--find-rest-args-position (arglist) | 
				
			||||
  "Find the position of & in the ARGLIST vector." | 
				
			||||
  (cl-position '& (append arglist ()))) | 
				
			||||
 | 
				
			||||
(defun cider-highlight-arglist (arglist pos) | 
				
			||||
  "Format the ARGLIST for eldoc. | 
				
			||||
POS is the index of the argument to highlight." | 
				
			||||
  (concat "[" (cider-highlight-args arglist pos) "]")) | 
				
			||||
 | 
				
			||||
(defun cider-eldoc-format-arglist (arglist pos) | 
				
			||||
  "Format all the ARGLIST for eldoc. | 
				
			||||
POS is the index of current argument." | 
				
			||||
  (concat "(" | 
				
			||||
          (mapconcat (lambda (args) (cider-highlight-arglist args pos)) | 
				
			||||
                     arglist | 
				
			||||
                     " ") | 
				
			||||
          ")")) | 
				
			||||
 | 
				
			||||
(defun cider-eldoc-beginning-of-sexp () | 
				
			||||
  "Move to the beginning of current sexp. | 
				
			||||
 | 
				
			||||
Return the number of nested sexp the point was over or after. " | 
				
			||||
  (let ((parse-sexp-ignore-comments t) | 
				
			||||
        (num-skipped-sexps 0)) | 
				
			||||
    (condition-case _ | 
				
			||||
        (progn | 
				
			||||
          ;; First account for the case the point is directly over a | 
				
			||||
          ;; beginning of a nested sexp. | 
				
			||||
          (condition-case _ | 
				
			||||
              (let ((p (point))) | 
				
			||||
                (forward-sexp -1) | 
				
			||||
                (forward-sexp 1) | 
				
			||||
                (when (< (point) p) | 
				
			||||
                  (setq num-skipped-sexps 1))) | 
				
			||||
            (error)) | 
				
			||||
          (while | 
				
			||||
              (let ((p (point))) | 
				
			||||
                (forward-sexp -1) | 
				
			||||
                (when (< (point) p) | 
				
			||||
                  (setq num-skipped-sexps (1+ num-skipped-sexps)))))) | 
				
			||||
      (error)) | 
				
			||||
    num-skipped-sexps)) | 
				
			||||
 | 
				
			||||
(defun cider-eldoc-info-in-current-sexp () | 
				
			||||
  "Return a list of the current sexp and the current argument index." | 
				
			||||
  (save-excursion | 
				
			||||
    (let ((argument-index (1- (cider-eldoc-beginning-of-sexp)))) | 
				
			||||
      ;; If we are at the beginning of function name, this will be -1. | 
				
			||||
      (when (< argument-index 0) | 
				
			||||
        (setq argument-index 0)) | 
				
			||||
      ;; Don't do anything if current word is inside a string, vector, | 
				
			||||
      ;; hash or set literal. | 
				
			||||
      (if (member (or (char-after (1- (point))) 0) '(?\" ?\{ ?\[)) | 
				
			||||
          nil | 
				
			||||
        (list (cider-symbol-at-point) argument-index))))) | 
				
			||||
 | 
				
			||||
(defun cider-eldoc-arglist (thing) | 
				
			||||
  "Return the arglist for THING." | 
				
			||||
  (when (and (cider-nrepl-op-supported-p "eldoc") | 
				
			||||
             thing | 
				
			||||
             (not (string= thing "")) | 
				
			||||
             (not (string-prefix-p ":" thing))) | 
				
			||||
    ;; check if we can used the cached eldoc info | 
				
			||||
    (if (string= thing (car cider-eldoc-last-symbol)) | 
				
			||||
        (cdr cider-eldoc-last-symbol) | 
				
			||||
      (when-let ((eldoc-info (cider-sync-request:eldoc (substring-no-properties thing)))) | 
				
			||||
        (let ((arglist (nrepl-dict-get eldoc-info "eldoc"))) | 
				
			||||
          (setq cider-eldoc-last-symbol (cons thing arglist)) | 
				
			||||
          arglist))))) | 
				
			||||
 | 
				
			||||
(defun cider-eldoc () | 
				
			||||
  "Backend function for eldoc to show argument list in the echo area." | 
				
			||||
  (when (and (cider-connected-p) | 
				
			||||
             ;; don't clobber an error message in the minibuffer | 
				
			||||
             (not (member last-command '(next-error previous-error)))) | 
				
			||||
    (let* ((info (cider-eldoc-info-in-current-sexp)) | 
				
			||||
           (thing (car info)) | 
				
			||||
           (pos (cadr info)) | 
				
			||||
           (value (cider-eldoc-arglist thing))) | 
				
			||||
      (when value | 
				
			||||
        (format "%s: %s" | 
				
			||||
                (cider-eldoc-format-thing thing) | 
				
			||||
                (cider-eldoc-format-arglist value pos)))))) | 
				
			||||
 | 
				
			||||
(defun cider-eldoc-setup () | 
				
			||||
  "Turn on eldoc mode in the current buffer." | 
				
			||||
  (setq-local eldoc-documentation-function #'cider-eldoc) | 
				
			||||
  (apply #'eldoc-add-command cider-extra-eldoc-commands)) | 
				
			||||
 | 
				
			||||
(define-obsolete-function-alias 'cider-turn-on-eldoc-mode 'eldoc-mode) | 
				
			||||
 | 
				
			||||
(provide 'cider-eldoc) | 
				
			||||
 | 
				
			||||
;;; cider-eldoc.el ends here | 
				
			||||
@ -1,112 +0,0 @@
					 | 
				
			||||
;;; cider-grimoire.el --- Grimoire integration -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright © 2014-2015 Bozhidar Batsov | 
				
			||||
;; | 
				
			||||
;; Author: Bozhidar Batsov <bozhidar@batsov.com> | 
				
			||||
 | 
				
			||||
;; This program 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 of the License, or | 
				
			||||
;; (at your option) any later version. | 
				
			||||
 | 
				
			||||
;; This program 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/>. | 
				
			||||
 | 
				
			||||
;; This file is not part of GNU Emacs. | 
				
			||||
 | 
				
			||||
;;; Commentary: | 
				
			||||
 | 
				
			||||
;; A few commands for Grimoire documentation lookup. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'cider-client) | 
				
			||||
(require 'cider-common) | 
				
			||||
(require 'cider-compat) | 
				
			||||
(require 'cider-popup) | 
				
			||||
 | 
				
			||||
(require 'nrepl-client) | 
				
			||||
 | 
				
			||||
(require 'url-vars) | 
				
			||||
 | 
				
			||||
(defconst cider-grimoire-url "http://conj.io/") | 
				
			||||
 | 
				
			||||
(defun cider-grimoire-replace-special (name) | 
				
			||||
  "Convert the dashes in NAME to a grimoire friendly format." | 
				
			||||
  (thread-last name | 
				
			||||
    (replace-regexp-in-string "\\?" "_QMARK_") | 
				
			||||
    (replace-regexp-in-string "\\." "_DOT_") | 
				
			||||
    (replace-regexp-in-string "\\/" "_SLASH_") | 
				
			||||
    (replace-regexp-in-string "\\(\\`_\\)\\|\\(_\\'\\)" ""))) | 
				
			||||
 | 
				
			||||
(defun cider-grimoire-url (name ns) | 
				
			||||
  "Generate a grimoire search v0 url from NAME, NS." | 
				
			||||
  (let ((base-url cider-grimoire-url)) | 
				
			||||
    (when (and name ns) | 
				
			||||
      (concat base-url  "search/v0/" ns "/" (cider-grimoire-replace-special name) "/")))) | 
				
			||||
 | 
				
			||||
(defun cider-grimoire-web-lookup (symbol) | 
				
			||||
  "Look up the grimoire documentation for SYMBOL." | 
				
			||||
  (if-let ((var-info (cider-var-info symbol))) | 
				
			||||
      (let ((name (nrepl-dict-get var-info "name")) | 
				
			||||
            (ns (nrepl-dict-get var-info "ns"))) | 
				
			||||
        (browse-url (cider-grimoire-url name ns))) | 
				
			||||
    (error "Symbol %s not resolved" symbol))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun cider-grimoire-web (&optional arg) | 
				
			||||
  "Open grimoire documentation in the default web browser. | 
				
			||||
 | 
				
			||||
Prompts for the symbol to use, or uses the symbol at point, depending on | 
				
			||||
the value of `cider-prompt-for-symbol'. With prefix arg ARG, does the | 
				
			||||
opposite of what that option dictates." | 
				
			||||
  (interactive "P") | 
				
			||||
  (funcall (cider-prompt-for-symbol-function arg) | 
				
			||||
           "Grimoire doc for" | 
				
			||||
           #'cider-grimoire-web-lookup)) | 
				
			||||
 | 
				
			||||
(defun cider-create-grimoire-buffer (content) | 
				
			||||
  "Create a new grimoire buffer with CONTENT." | 
				
			||||
  (with-current-buffer (cider-popup-buffer "*cider grimoire*" t) | 
				
			||||
    (read-only-mode -1) | 
				
			||||
    (insert content) | 
				
			||||
    (read-only-mode +1) | 
				
			||||
    (goto-char (point-min)) | 
				
			||||
    (current-buffer))) | 
				
			||||
 | 
				
			||||
(defun cider-grimoire-lookup (symbol) | 
				
			||||
  "Look up the grimoire documentation for SYMBOL." | 
				
			||||
  (if-let ((var-info (cider-var-info symbol))) | 
				
			||||
      (let ((name (nrepl-dict-get var-info "name")) | 
				
			||||
            (ns (nrepl-dict-get var-info "ns")) | 
				
			||||
            (url-request-method "GET") | 
				
			||||
            (url-request-extra-headers `(("Content-Type" . "text/plain")))) | 
				
			||||
        (url-retrieve (cider-grimoire-url name ns) | 
				
			||||
                      (lambda (_status) | 
				
			||||
                        ;; we need to strip the http header | 
				
			||||
                        (goto-char (point-min)) | 
				
			||||
                        (re-search-forward "^$") | 
				
			||||
                        (delete-region (point-min) (point)) | 
				
			||||
                        (delete-blank-lines) | 
				
			||||
                        ;; and create a new buffer with whatever is left | 
				
			||||
                        (pop-to-buffer (cider-create-grimoire-buffer (buffer-string)))))) | 
				
			||||
    (error "Symbol %s not resolved" symbol))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun cider-grimoire (&optional arg) | 
				
			||||
  "Open grimoire documentation in a popup buffer. | 
				
			||||
 | 
				
			||||
Prompts for the symbol to use, or uses the symbol at point, depending on | 
				
			||||
the value of `cider-prompt-for-symbol'. With prefix arg ARG, does the | 
				
			||||
opposite of what that option dictates." | 
				
			||||
  (interactive "P") | 
				
			||||
  (funcall (cider-prompt-for-symbol-function arg) | 
				
			||||
           "Grimoire doc for" | 
				
			||||
           #'cider-grimoire-lookup)) | 
				
			||||
 | 
				
			||||
(provide 'cider-grimoire) | 
				
			||||
@ -1,309 +0,0 @@
					 | 
				
			||||
;;; cider-inspector.el --- Object inspector -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright © 2013-2015 Vital Reactor, LLC | 
				
			||||
;; Copyright © 2014-2015 Bozhidar Batsov | 
				
			||||
 | 
				
			||||
;; Author: Ian Eslick <ian@vitalreactor.com> | 
				
			||||
;;         Bozhidar Batsov <bozhidar@batsov.com> | 
				
			||||
 | 
				
			||||
;; This program 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 of the License, or | 
				
			||||
;; (at your option) any later version. | 
				
			||||
 | 
				
			||||
;; This program 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/>. | 
				
			||||
 | 
				
			||||
;; This file is not part of GNU Emacs. | 
				
			||||
 | 
				
			||||
;;; Commentary: | 
				
			||||
 | 
				
			||||
;; Clojure object inspector inspired by SLIME. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'cl-lib) | 
				
			||||
(require 'cider-interaction) | 
				
			||||
 | 
				
			||||
;; =================================== | 
				
			||||
;; Inspector Key Map and Derived Mode | 
				
			||||
;; =================================== | 
				
			||||
 | 
				
			||||
(defconst cider-inspector-buffer "*cider inspect*") | 
				
			||||
 | 
				
			||||
;;; Customization | 
				
			||||
(defgroup cider-inspector nil | 
				
			||||
  "Presentation and behaviour of the cider value inspector." | 
				
			||||
  :prefix "cider-inspector-" | 
				
			||||
  :group 'cider | 
				
			||||
  :package-version '(cider . "0.10.0")) | 
				
			||||
 | 
				
			||||
(defcustom cider-inspector-page-size 32 | 
				
			||||
  "Default page size in paginated inspector view. | 
				
			||||
The page size can be also changed interactively within the inspector." | 
				
			||||
  :type '(integer :tag "Page size" 32) | 
				
			||||
  :group 'cider-inspector | 
				
			||||
  :package-version '(cider . "0.10.0")) | 
				
			||||
 | 
				
			||||
(push cider-inspector-buffer cider-ancillary-buffers) | 
				
			||||
 | 
				
			||||
(defvar cider-inspector-mode-map | 
				
			||||
  (let ((map (make-sparse-keymap))) | 
				
			||||
    (set-keymap-parent map cider-popup-buffer-mode-map) | 
				
			||||
    (define-key map [return] #'cider-inspector-operate-on-point) | 
				
			||||
    (define-key map "\C-m"   #'cider-inspector-operate-on-point) | 
				
			||||
    (define-key map [mouse-1] #'cider-inspector-operate-on-click) | 
				
			||||
    (define-key map "l" #'cider-inspector-pop) | 
				
			||||
    (define-key map "g" #'cider-inspector-refresh) | 
				
			||||
    ;; Page-up/down | 
				
			||||
    (define-key map [next] #'cider-inspector-next-page) | 
				
			||||
    (define-key map [prior] #'cider-inspector-prev-page) | 
				
			||||
    (define-key map " " #'cider-inspector-next-page) | 
				
			||||
    (define-key map (kbd "M-SPC") #'cider-inspector-prev-page) | 
				
			||||
    (define-key map (kbd "S-SPC") #'cider-inspector-prev-page) | 
				
			||||
    (define-key map "s" #'cider-inspector-set-page-size) | 
				
			||||
    (define-key map [tab] #'cider-inspector-next-inspectable-object) | 
				
			||||
    (define-key map "\C-i" #'cider-inspector-next-inspectable-object) | 
				
			||||
    (define-key map [(shift tab)] #'cider-inspector-previous-inspectable-object) ; Emacs translates S-TAB | 
				
			||||
    (define-key map [backtab] #'cider-inspector-previous-inspectable-object) ; to BACKTAB on X. | 
				
			||||
    map)) | 
				
			||||
 | 
				
			||||
(define-derived-mode cider-inspector-mode fundamental-mode "Inspector" | 
				
			||||
  "Major mode for inspecting Clojure data structures. | 
				
			||||
 | 
				
			||||
\\{cider-inspector-mode-map}" | 
				
			||||
  (set-syntax-table clojure-mode-syntax-table) | 
				
			||||
  (setq buffer-read-only t) | 
				
			||||
  (setq-local electric-indent-chars nil) | 
				
			||||
  (setq-local truncate-lines t)) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun cider-inspect (expression) | 
				
			||||
  "Eval the string EXPRESSION and inspect the result." | 
				
			||||
  (interactive | 
				
			||||
   (list (cider-read-from-minibuffer "Inspect value: " | 
				
			||||
                                     (cider-sexp-at-point)))) | 
				
			||||
  (cider-inspect-expr expression (cider-current-ns))) | 
				
			||||
 | 
				
			||||
;; Operations | 
				
			||||
(defun cider-inspector--value-handler (_buffer value) | 
				
			||||
  (cider-make-popup-buffer cider-inspector-buffer 'cider-inspector-mode) | 
				
			||||
  (cider-irender cider-inspector-buffer value)) | 
				
			||||
 | 
				
			||||
(defun cider-inspector--out-handler (_buffer value) | 
				
			||||
  (cider-emit-interactive-eval-output value)) | 
				
			||||
 | 
				
			||||
(defun cider-inspector--err-handler (_buffer err) | 
				
			||||
  (cider-emit-interactive-eval-err-output err)) | 
				
			||||
 | 
				
			||||
(defun cider-inspector--done-handler (buffer) | 
				
			||||
  (when (get-buffer cider-inspector-buffer) | 
				
			||||
    (with-current-buffer buffer | 
				
			||||
      (cider-popup-buffer-display cider-inspector-buffer t)))) | 
				
			||||
 | 
				
			||||
(defun cider-inspector-response-handler (buffer) | 
				
			||||
  "Create an inspector response handler for BUFFER. | 
				
			||||
 | 
				
			||||
The \"value\" slot of each successive response (if it exists) will be | 
				
			||||
rendered into `cider-inspector-buffer'. Once a response is received with a | 
				
			||||
\"status\" slot containing \"done\", `cider-inspector-buffer' will be | 
				
			||||
displayed. | 
				
			||||
 | 
				
			||||
Used for all inspector nREPL ops." | 
				
			||||
  (nrepl-make-response-handler buffer | 
				
			||||
                               #'cider-inspector--value-handler | 
				
			||||
                               #'cider-inspector--out-handler | 
				
			||||
                               #'cider-inspector--err-handler | 
				
			||||
                               #'cider-inspector--done-handler)) | 
				
			||||
 | 
				
			||||
(defun cider-inspect-expr (expr ns) | 
				
			||||
  (cider--prep-interactive-eval expr) | 
				
			||||
  (cider-nrepl-send-request (append (nrepl--eval-request expr (cider-current-session) ns) | 
				
			||||
                                    (list "inspect" "true" | 
				
			||||
                                          "page-size" (or cider-inspector-page-size 32))) | 
				
			||||
                            (cider-inspector-response-handler (current-buffer)))) | 
				
			||||
 | 
				
			||||
(defun cider-inspector-pop () | 
				
			||||
  (interactive) | 
				
			||||
  (cider-nrepl-send-request (list "op" "inspect-pop" | 
				
			||||
                                  "session" (cider-current-session)) | 
				
			||||
                            (cider-inspector-response-handler (current-buffer)))) | 
				
			||||
 | 
				
			||||
(defun cider-inspector-push (idx) | 
				
			||||
  (cider-nrepl-send-request (list "op" "inspect-push" | 
				
			||||
                                  "idx" (number-to-string idx) | 
				
			||||
                                  "session" (cider-current-session)) | 
				
			||||
                            (cider-inspector-response-handler (current-buffer)))) | 
				
			||||
 | 
				
			||||
(defun cider-inspector-refresh () | 
				
			||||
  (interactive) | 
				
			||||
  (cider-nrepl-send-request (list "op" "inspect-refresh" | 
				
			||||
                                  "session" (cider-current-session)) | 
				
			||||
                            (cider-inspector-response-handler (current-buffer)))) | 
				
			||||
 | 
				
			||||
(defun cider-inspector-next-page () | 
				
			||||
  "Jump to the next page when inspecting a paginated sequence/map. | 
				
			||||
 | 
				
			||||
Does nothing if already on the last page." | 
				
			||||
  (interactive) | 
				
			||||
  (cider-nrepl-send-request (list "op" "inspect-next-page" | 
				
			||||
                                  "session" (cider-current-session)) | 
				
			||||
                            (cider-inspector-response-handler (current-buffer)))) | 
				
			||||
 | 
				
			||||
(defun cider-inspector-prev-page () | 
				
			||||
  "Jump to the previous page when expecting a paginated sequence/map. | 
				
			||||
 | 
				
			||||
Does nothing if already on the first page." | 
				
			||||
  (interactive) | 
				
			||||
  (cider-nrepl-send-request (list "op" "inspect-prev-page" | 
				
			||||
                                  "session" (cider-current-session)) | 
				
			||||
                            (cider-inspector-response-handler (current-buffer)))) | 
				
			||||
 | 
				
			||||
(defun cider-inspector-set-page-size (page-size) | 
				
			||||
  "Set the page size in pagination mode to the specified value. | 
				
			||||
 | 
				
			||||
Current page will be reset to zero." | 
				
			||||
  (interactive "nPage size:") | 
				
			||||
  (cider-nrepl-send-request (list "op" "inspect-set-page-size" | 
				
			||||
                                  "session" (cider-current-session) | 
				
			||||
                                  "page-size" page-size) | 
				
			||||
                            (cider-inspector-response-handler (current-buffer)))) | 
				
			||||
 | 
				
			||||
;; Render Inspector from Structured Values | 
				
			||||
(defun cider-irender (buffer str) | 
				
			||||
  (with-current-buffer buffer | 
				
			||||
    (cider-inspector-mode) | 
				
			||||
    (let ((inhibit-read-only t)) | 
				
			||||
      (condition-case nil | 
				
			||||
          (cider-irender* (car (read-from-string str))) | 
				
			||||
        (error (insert "\nInspector error for: " str)))) | 
				
			||||
    (goto-char (point-min)))) | 
				
			||||
 | 
				
			||||
(defun cider-irender* (elements) | 
				
			||||
  (dolist (el elements) | 
				
			||||
    (cider-irender-el* el))) | 
				
			||||
 | 
				
			||||
(defun cider-irender-el* (el) | 
				
			||||
  (cond ((symbolp el) (insert (symbol-name el))) | 
				
			||||
        ((stringp el) (insert (propertize el 'font-lock-face 'font-lock-keyword-face))) | 
				
			||||
        ((and (consp el) (eq (car el) :newline)) | 
				
			||||
         (insert "\n")) | 
				
			||||
        ((and (consp el) (eq (car el) :value)) | 
				
			||||
         (cider-irender-value (cadr el) (cl-caddr el))) | 
				
			||||
        (t (message "Unrecognized inspector object: %s" el)))) | 
				
			||||
 | 
				
			||||
(defun cider-irender-value (value idx) | 
				
			||||
  (cider-propertize-region | 
				
			||||
      (list 'cider-value-idx idx | 
				
			||||
            'mouse-face 'highlight) | 
				
			||||
    (cider-irender-el* (cider-font-lock-as-clojure value)))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
;; =================================================== | 
				
			||||
;; Inspector Navigation (lifted from SLIME inspector) | 
				
			||||
;; =================================================== | 
				
			||||
 | 
				
			||||
(defun cider-find-inspectable-object (direction limit) | 
				
			||||
  "Find the next/previous inspectable object. | 
				
			||||
DIRECTION can be either 'next or 'prev. | 
				
			||||
LIMIT is the maximum or minimum position in the current buffer. | 
				
			||||
 | 
				
			||||
Return a list of two values: If an object could be found, the | 
				
			||||
starting position of the found object and T is returned; | 
				
			||||
otherwise LIMIT and NIL is returned." | 
				
			||||
  (let ((finder (cl-ecase direction | 
				
			||||
                  (next 'next-single-property-change) | 
				
			||||
                  (prev 'previous-single-property-change)))) | 
				
			||||
    (let ((prop nil) (curpos (point))) | 
				
			||||
      (while (and (not prop) (not (= curpos limit))) | 
				
			||||
        (let ((newpos (funcall finder curpos 'cider-value-idx nil limit))) | 
				
			||||
          (setq prop (get-text-property newpos 'cider-value-idx)) | 
				
			||||
          (setq curpos newpos))) | 
				
			||||
      (list curpos (and prop t))))) | 
				
			||||
 | 
				
			||||
(defun cider-inspector-next-inspectable-object (arg) | 
				
			||||
  "Move point to the next inspectable object. | 
				
			||||
With optional ARG, move across that many objects. | 
				
			||||
If ARG is negative, move backwards." | 
				
			||||
  (interactive "p") | 
				
			||||
  (let ((maxpos (point-max)) (minpos (point-min)) | 
				
			||||
        (previously-wrapped-p nil)) | 
				
			||||
    ;; Forward. | 
				
			||||
    (while (> arg 0) | 
				
			||||
      (cl-destructuring-bind (pos foundp) | 
				
			||||
          (cider-find-inspectable-object 'next maxpos) | 
				
			||||
        (if foundp | 
				
			||||
            (progn (goto-char pos) (setq arg (1- arg)) | 
				
			||||
                   (setq previously-wrapped-p nil)) | 
				
			||||
          (if (not previously-wrapped-p) ; cycle detection | 
				
			||||
              (progn (goto-char minpos) (setq previously-wrapped-p t)) | 
				
			||||
            (error "No inspectable objects"))))) | 
				
			||||
    ;; Backward. | 
				
			||||
    (while (< arg 0) | 
				
			||||
      (cl-destructuring-bind (pos foundp) | 
				
			||||
          (cider-find-inspectable-object 'prev minpos) | 
				
			||||
        ;; CIDER-OPEN-INSPECTOR inserts the title of an inspector page | 
				
			||||
        ;; as a presentation at the beginning of the buffer; skip | 
				
			||||
        ;; that.  (Notice how this problem can not arise in ``Forward.'') | 
				
			||||
        (if (and foundp (/= pos minpos)) | 
				
			||||
            (progn (goto-char pos) (setq arg (1+ arg)) | 
				
			||||
                   (setq previously-wrapped-p nil)) | 
				
			||||
          (if (not previously-wrapped-p) ; cycle detection | 
				
			||||
              (progn (goto-char maxpos) (setq previously-wrapped-p t)) | 
				
			||||
            (error "No inspectable objects"))))))) | 
				
			||||
 | 
				
			||||
(defun cider-inspector-previous-inspectable-object (arg) | 
				
			||||
  "Move point to the previous inspectable object. | 
				
			||||
With optional ARG, move across that many objects. | 
				
			||||
If ARG is negative, move forwards." | 
				
			||||
  (interactive "p") | 
				
			||||
  (cider-inspector-next-inspectable-object (- arg))) | 
				
			||||
 | 
				
			||||
(defun cider-inspector-property-at-point () | 
				
			||||
  (let* ((properties '(cider-value-idx cider-range-button | 
				
			||||
                                       cider-action-number)) | 
				
			||||
         (find-property | 
				
			||||
          (lambda (point) | 
				
			||||
            (cl-loop for property in properties | 
				
			||||
                     for value = (get-text-property point property) | 
				
			||||
                     when value | 
				
			||||
                     return (list property value))))) | 
				
			||||
    (or (funcall find-property (point)) | 
				
			||||
        (funcall find-property (1- (point)))))) | 
				
			||||
 | 
				
			||||
(defun cider-inspector-operate-on-point () | 
				
			||||
  "Invoke the command for the text at point. | 
				
			||||
1. If point is on a value then recursivly call the inspector on | 
				
			||||
that value. | 
				
			||||
2. If point is on an action then call that action. | 
				
			||||
3. If point is on a range-button fetch and insert the range." | 
				
			||||
  (interactive) | 
				
			||||
  (cl-destructuring-bind (property value) | 
				
			||||
      (cider-inspector-property-at-point) | 
				
			||||
    (cl-case property | 
				
			||||
      (cider-value-idx | 
				
			||||
       (cider-inspector-push value)) | 
				
			||||
      ;; TODO: range and action handlers | 
				
			||||
      (t (error "No object at point"))))) | 
				
			||||
 | 
				
			||||
(defun cider-inspector-operate-on-click (event) | 
				
			||||
  "Move to EVENT's position and operate the part." | 
				
			||||
  (interactive "@e") | 
				
			||||
  (let ((point (posn-point (event-end event)))) | 
				
			||||
    (cond ((and point | 
				
			||||
                (or (get-text-property point 'cider-value-idx))) | 
				
			||||
           ;;                    (get-text-property point 'cider-range-button) | 
				
			||||
           ;;                    (get-text-property point 'cider-action-number))) | 
				
			||||
           (goto-char point) | 
				
			||||
           (cider-inspector-operate-on-point)) | 
				
			||||
          (t | 
				
			||||
           (error "No clickable part here"))))) | 
				
			||||
 | 
				
			||||
(provide 'cider-inspector) | 
				
			||||
 | 
				
			||||
;;; cider-inspector.el ends here | 
				
			||||
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						@ -1,208 +0,0 @@
					 | 
				
			||||
;;; cider-macroexpansion.el --- Macro expansion support -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright © 2012-2015 Tim King, Phil Hagelberg | 
				
			||||
;; Copyright © 2013-2015 Bozhidar Batsov, Hugo Duncan, Steve Purcell | 
				
			||||
;; | 
				
			||||
;; Author: Tim King <kingtim@gmail.com> | 
				
			||||
;;         Phil Hagelberg <technomancy@gmail.com> | 
				
			||||
;;         Bozhidar Batsov <bozhidar@batsov.com> | 
				
			||||
;;         Hugo Duncan <hugo@hugoduncan.org> | 
				
			||||
;;         Steve Purcell <steve@sanityinc.com> | 
				
			||||
 | 
				
			||||
;; This program 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 of the License, or | 
				
			||||
;; (at your option) any later version. | 
				
			||||
 | 
				
			||||
;; This program 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/>. | 
				
			||||
 | 
				
			||||
;; This file is not part of GNU Emacs. | 
				
			||||
 | 
				
			||||
;;; Commentary: | 
				
			||||
 | 
				
			||||
;; Macro expansion support. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'cider-mode) | 
				
			||||
(require 'cider-compat) | 
				
			||||
 | 
				
			||||
(defconst cider-macroexpansion-buffer "*cider-macroexpansion*") | 
				
			||||
 | 
				
			||||
(push cider-macroexpansion-buffer cider-ancillary-buffers) | 
				
			||||
 | 
				
			||||
(defcustom cider-macroexpansion-display-namespaces 'tidy | 
				
			||||
  "Determines if namespaces are displayed in the macroexpansion buffer. | 
				
			||||
Possible values are: | 
				
			||||
 | 
				
			||||
  'qualified ;=> Vars are fully-qualified in the expansion | 
				
			||||
  'none      ;=> Vars are displayed without namespace qualification | 
				
			||||
  'tidy      ;=> Vars that are :refer-ed or defined in the current namespace are | 
				
			||||
                 displayed with their simple name, non-refered vars from other | 
				
			||||
                 namespaces are refered using the alias for that namespace (if | 
				
			||||
                 defined), other vars are displayed fully qualified." | 
				
			||||
  :type '(choice (const :tag "Suppress namespaces" none) | 
				
			||||
                 (const :tag "Show fully-qualified namespaces" qualified) | 
				
			||||
                 (const :tag "Show namespace aliases" tidy)) | 
				
			||||
  :group 'cider | 
				
			||||
  :package-version '(cider . "0.7.0")) | 
				
			||||
 | 
				
			||||
(defcustom cider-macroexpansion-print-metadata nil | 
				
			||||
  "Determines if metadata is included in macroexpansion results." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'cider | 
				
			||||
  :package-version '(cider . "0.9.0")) | 
				
			||||
 | 
				
			||||
(defun cider-sync-request:macroexpand (expander expr &optional display-namespaces) | 
				
			||||
  "Macroexpand, using EXPANDER, the given EXPR. | 
				
			||||
The default for DISPLAY-NAMESPACES is taken from | 
				
			||||
`cider-macroexpansion-display-namespaces'." | 
				
			||||
  (cider-ensure-op-supported "macroexpand") | 
				
			||||
  (thread-first (list "op" "macroexpand" | 
				
			||||
                      "expander" expander | 
				
			||||
                      "code" expr | 
				
			||||
                      "ns" (cider-current-ns) | 
				
			||||
                      "display-namespaces" | 
				
			||||
                      (or display-namespaces | 
				
			||||
                          (symbol-name cider-macroexpansion-display-namespaces))) | 
				
			||||
    (append (when cider-macroexpansion-print-metadata | 
				
			||||
              (list "print-meta" "true"))) | 
				
			||||
    (cider-nrepl-send-sync-request) | 
				
			||||
    (nrepl-dict-get "expansion"))) | 
				
			||||
 | 
				
			||||
(defun cider-macroexpand-undo (&optional arg) | 
				
			||||
  "Undo the last macroexpansion, using `undo-only'. | 
				
			||||
ARG is passed along to `undo-only'." | 
				
			||||
  (interactive) | 
				
			||||
  (let ((inhibit-read-only t)) | 
				
			||||
    (undo-only arg))) | 
				
			||||
 | 
				
			||||
(defvar cider-last-macroexpand-expression nil | 
				
			||||
  "Specify the last macroexpansion preformed. | 
				
			||||
This variable specifies both what was expanded and the expander.") | 
				
			||||
 | 
				
			||||
(defun cider-macroexpand-expr (expander expr) | 
				
			||||
  "Macroexpand, use EXPANDER, the given EXPR." | 
				
			||||
  (let* ((expansion (cider-sync-request:macroexpand expander expr))) | 
				
			||||
    (setq cider-last-macroexpand-expression expr) | 
				
			||||
    (cider-initialize-macroexpansion-buffer expansion (cider-current-ns)))) | 
				
			||||
 | 
				
			||||
(defun cider-macroexpand-expr-inplace (expander) | 
				
			||||
  "Substitute the form preceding point with its macroexpansion using EXPANDER." | 
				
			||||
  (interactive) | 
				
			||||
  (let* ((expansion (cider-sync-request:macroexpand expander (cider-last-sexp))) | 
				
			||||
         (bounds (cons (save-excursion (clojure-backward-logical-sexp 1) (point)) (point)))) | 
				
			||||
    (cider-redraw-macroexpansion-buffer | 
				
			||||
     expansion (current-buffer) (car bounds) (cdr bounds)))) | 
				
			||||
 | 
				
			||||
(defun cider-macroexpand-again () | 
				
			||||
  "Repeat the last macroexpansion." | 
				
			||||
  (interactive) | 
				
			||||
  (cider-initialize-macroexpansion-buffer cider-last-macroexpand-expression (cider-current-ns))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun cider-macroexpand-1 (&optional prefix) | 
				
			||||
  "Invoke 'macroexpand-1' on the expression preceding point. | 
				
			||||
If invoked with a PREFIX argument, use 'macroexpand' instead of | 
				
			||||
'macroexpand-1'." | 
				
			||||
  (interactive "P") | 
				
			||||
  (let ((expander (if prefix "macroexpand" "macroexpand-1"))) | 
				
			||||
    (cider-macroexpand-expr expander (cider-last-sexp)))) | 
				
			||||
 | 
				
			||||
(defun cider-macroexpand-1-inplace (&optional prefix) | 
				
			||||
  "Perform inplace 'macroexpand-1' on the expression preceding point. | 
				
			||||
If invoked with a PREFIX argument, use 'macroexpand' instead of | 
				
			||||
'macroexpand-1'." | 
				
			||||
  (interactive "P") | 
				
			||||
  (let ((expander (if prefix "macroexpand" "macroexpand-1"))) | 
				
			||||
    (cider-macroexpand-expr-inplace expander))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun cider-macroexpand-all () | 
				
			||||
  "Invoke 'clojure.walk/macroexpand-all' on the expression preceding point." | 
				
			||||
  (interactive) | 
				
			||||
  (cider-macroexpand-expr "macroexpand-all" (cider-last-sexp))) | 
				
			||||
 | 
				
			||||
(defun cider-macroexpand-all-inplace () | 
				
			||||
  "Perform inplace 'clojure.walk/macroexpand-all' on the expression preceding point." | 
				
			||||
  (interactive) | 
				
			||||
  (cider-macroexpand-expr-inplace "macroexpand-all")) | 
				
			||||
 | 
				
			||||
(defun cider-initialize-macroexpansion-buffer (expansion ns) | 
				
			||||
  "Create a new Macroexpansion buffer with EXPANSION and namespace NS." | 
				
			||||
  (pop-to-buffer (cider-create-macroexpansion-buffer)) | 
				
			||||
  (setq cider-buffer-ns ns) | 
				
			||||
  (setq buffer-undo-list nil) | 
				
			||||
  (let ((inhibit-read-only t) | 
				
			||||
        (buffer-undo-list t)) | 
				
			||||
    (erase-buffer) | 
				
			||||
    (insert (format "%s" expansion)) | 
				
			||||
    (goto-char (point-max)) | 
				
			||||
    (cider--font-lock-ensure))) | 
				
			||||
 | 
				
			||||
(defun cider-redraw-macroexpansion-buffer (expansion buffer start end) | 
				
			||||
  "Redraw the macroexpansion with new EXPANSION. | 
				
			||||
Text in BUFFER from START to END is replaced with new expansion, | 
				
			||||
and point is placed after the expanded form." | 
				
			||||
  (with-current-buffer buffer | 
				
			||||
    (let ((buffer-read-only nil)) | 
				
			||||
      (goto-char start) | 
				
			||||
      (delete-region start end) | 
				
			||||
      (insert (format "%s" expansion)) | 
				
			||||
      (goto-char start) | 
				
			||||
      (indent-sexp) | 
				
			||||
      (forward-sexp)))) | 
				
			||||
 | 
				
			||||
(declare-function cider-mode "cider-mode") | 
				
			||||
 | 
				
			||||
(defun cider-create-macroexpansion-buffer () | 
				
			||||
  "Create a new macroexpansion buffer." | 
				
			||||
  (with-current-buffer (cider-popup-buffer cider-macroexpansion-buffer t) | 
				
			||||
    (clojure-mode) | 
				
			||||
    (cider-mode -1) | 
				
			||||
    (cider-macroexpansion-mode 1) | 
				
			||||
    (current-buffer))) | 
				
			||||
 | 
				
			||||
(defvar cider-macroexpansion-mode-map | 
				
			||||
  (let ((map (make-sparse-keymap))) | 
				
			||||
    (define-key map (kbd "g") #'cider-macroexpand-again) | 
				
			||||
    (define-key map (kbd "q") #'cider-popup-buffer-quit-function) | 
				
			||||
    (define-key map (kbd "d") #'cider-doc) | 
				
			||||
    (define-key map (kbd "j") #'cider-javadoc) | 
				
			||||
    (define-key map (kbd ".") #'cider-find-var) | 
				
			||||
    (easy-menu-define cider-macroexpansion-mode-menu map | 
				
			||||
      "Menu for CIDER's doc mode" | 
				
			||||
      '("Macroexpansion" | 
				
			||||
        ["Restart expansion" cider-macroexpand-again] | 
				
			||||
        ["Macroexpand-1" cider-macroexpand-1-inplace] | 
				
			||||
        ["Macroexpand-all" cider-macroexpand-all-inplace] | 
				
			||||
        ["Go to source" cider-find-var] | 
				
			||||
        ["Go to doc" cider-doc] | 
				
			||||
        ["Go to Javadoc" cider-docview-javadoc] | 
				
			||||
        ["Quit" cider-popup-buffer-quit-function])) | 
				
			||||
    (cl-labels ((redefine-key (from to) | 
				
			||||
                              (dolist (mapping (where-is-internal from cider-mode-map)) | 
				
			||||
                                (define-key map mapping to)))) | 
				
			||||
      (redefine-key 'cider-macroexpand-1 #'cider-macroexpand-1-inplace) | 
				
			||||
      (redefine-key 'cider-macroexpand-all #'cider-macroexpand-all-inplace) | 
				
			||||
      (redefine-key 'advertised-undo #'cider-macroexpand-undo) | 
				
			||||
      (redefine-key 'undo #'cider-macroexpand-undo)) | 
				
			||||
    map)) | 
				
			||||
 | 
				
			||||
(define-minor-mode cider-macroexpansion-mode | 
				
			||||
  "Minor mode for CIDER macroexpansion. | 
				
			||||
 | 
				
			||||
\\{cider-macroexpansion-mode-map}" | 
				
			||||
  nil | 
				
			||||
  " Macroexpand" | 
				
			||||
  cider-macroexpansion-mode-map) | 
				
			||||
 | 
				
			||||
(provide 'cider-macroexpansion) | 
				
			||||
 | 
				
			||||
;;; cider-macroexpansion.el ends here | 
				
			||||
@ -1,12 +0,0 @@
					 | 
				
			||||
(define-package "cider" "20151022.28" "Clojure Interactive Development Environment that Rocks" | 
				
			||||
  '((clojure-mode "5.0.0") | 
				
			||||
    (pkg-info "0.4") | 
				
			||||
    (emacs "24.3") | 
				
			||||
    (queue "0.1.1") | 
				
			||||
    (spinner "1.4") | 
				
			||||
    (seq "1.9")) | 
				
			||||
  :url "http://www.github.com/clojure-emacs/cider" :keywords | 
				
			||||
  '("languages" "clojure" "cider")) | 
				
			||||
;; Local Variables: | 
				
			||||
;; no-byte-compile: t | 
				
			||||
;; End: | 
				
			||||
@ -1,122 +0,0 @@
					 | 
				
			||||
;;; cider-popup.el --- Creating and quitting popup buffers  -*- lexical-binding: t; -*- | 
				
			||||
 | 
				
			||||
;; Copyright (C) 2015  Artur Malabarba | 
				
			||||
 | 
				
			||||
;; Author: Artur Malabarba <bruce.connor.am@gmail.com> | 
				
			||||
 | 
				
			||||
;; This program 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 of the License, or | 
				
			||||
;; (at your option) any later version. | 
				
			||||
 | 
				
			||||
;; This program 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/>. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'nrepl-client) | 
				
			||||
(require 'cider-compat) | 
				
			||||
 | 
				
			||||
(define-minor-mode cider-popup-buffer-mode | 
				
			||||
  "Mode for CIDER popup buffers" | 
				
			||||
  nil | 
				
			||||
  (" cider-tmp") | 
				
			||||
  '(("q" .  cider-popup-buffer-quit-function))) | 
				
			||||
 | 
				
			||||
(defvar-local cider-popup-buffer-quit-function #'cider-popup-buffer-quit | 
				
			||||
  "The function that is used to quit a temporary popup buffer.") | 
				
			||||
 | 
				
			||||
(defun cider-popup-buffer-quit-function (&optional kill-buffer-p) | 
				
			||||
  "Wrapper to invoke the function `cider-popup-buffer-quit-function'. | 
				
			||||
KILL-BUFFER-P is passed along." | 
				
			||||
  (interactive) | 
				
			||||
  (funcall cider-popup-buffer-quit-function kill-buffer-p)) | 
				
			||||
 | 
				
			||||
(defun cider-popup-buffer (name &optional select mode ancillary) | 
				
			||||
  "Create new popup buffer called NAME. | 
				
			||||
If SELECT is non-nil, select the newly created window. | 
				
			||||
If major MODE is non-nil, enable it for the popup buffer. | 
				
			||||
If ANCILLARY is non-nil, the buffer is added to `cider-ancillary-buffers' | 
				
			||||
and automatically removed when killed." | 
				
			||||
  (thread-first (cider-make-popup-buffer name mode ancillary) | 
				
			||||
    (cider-popup-buffer-display select))) | 
				
			||||
 | 
				
			||||
(defun cider-popup-buffer-display (buffer &optional select) | 
				
			||||
  "Display BUFFER. | 
				
			||||
If SELECT is non-nil, select the BUFFER." | 
				
			||||
  (let ((window (get-buffer-window buffer))) | 
				
			||||
    (when window | 
				
			||||
      (with-current-buffer buffer | 
				
			||||
        (set-window-point window (point)))) | 
				
			||||
    ;; If the buffer we are popping up is already displayed in the selected | 
				
			||||
    ;; window, the below `inhibit-same-window' logic will cause it to be | 
				
			||||
    ;; displayed twice - so we early out in this case. Note that we must check | 
				
			||||
    ;; `selected-window', as async request handlers are executed in the context | 
				
			||||
    ;; of the current connection buffer (i.e. `current-buffer' is dynamically | 
				
			||||
    ;; bound to that). | 
				
			||||
    (unless (eq window (selected-window)) | 
				
			||||
      ;; Non nil `inhibit-same-window' ensures that current window is not covered | 
				
			||||
      (if select | 
				
			||||
          (pop-to-buffer buffer `(nil . ((inhibit-same-window . ,pop-up-windows)))) | 
				
			||||
        (display-buffer buffer `(nil . ((inhibit-same-window . ,pop-up-windows))))))) | 
				
			||||
  buffer) | 
				
			||||
 | 
				
			||||
(defun cider-popup-buffer-quit (&optional kill) | 
				
			||||
  "Quit the current (temp) window and bury its buffer using `quit-restore-window'. | 
				
			||||
If prefix argument KILL is non-nil, kill the buffer instead of burying it." | 
				
			||||
  (interactive) | 
				
			||||
  (quit-restore-window (selected-window) (if kill 'kill 'append))) | 
				
			||||
 | 
				
			||||
(defvar-local cider-popup-output-marker nil) | 
				
			||||
 | 
				
			||||
(defvar cider-ancillary-buffers (list nrepl-message-buffer-name)) | 
				
			||||
 | 
				
			||||
(defun cider-make-popup-buffer (name &optional mode ancillary) | 
				
			||||
  "Create a temporary buffer called NAME using major MODE (if specified). | 
				
			||||
If ANCILLARY is non-nil, the buffer is added to `cider-ancillary-buffers' | 
				
			||||
and automatically removed when killed." | 
				
			||||
  (with-current-buffer (get-buffer-create name) | 
				
			||||
    (kill-all-local-variables) | 
				
			||||
    (setq buffer-read-only nil) | 
				
			||||
    (erase-buffer) | 
				
			||||
    (when mode | 
				
			||||
      (funcall mode)) | 
				
			||||
    (cider-popup-buffer-mode 1) | 
				
			||||
    (setq cider-popup-output-marker (point-marker)) | 
				
			||||
    (setq buffer-read-only t) | 
				
			||||
    (when ancillary | 
				
			||||
      (add-to-list 'cider-ancillary-buffers name) | 
				
			||||
      (add-hook 'kill-buffer-hook | 
				
			||||
                (lambda () (setq cider-ancillary-buffers (remove name cider-ancillary-buffers))) | 
				
			||||
                nil 'local)) | 
				
			||||
    (current-buffer))) | 
				
			||||
 | 
				
			||||
(defun cider-emit-into-popup-buffer (buffer value &optional face) | 
				
			||||
  "Emit into BUFFER the provided VALUE." | 
				
			||||
  ;; Long string output renders emacs unresponsive and users might intentionally | 
				
			||||
  ;; kill the frozen popup buffer. Therefore, we don't re-create the buffer and | 
				
			||||
  ;; silently ignore the output. | 
				
			||||
  (when (buffer-live-p buffer) | 
				
			||||
    (with-current-buffer buffer | 
				
			||||
      (let ((inhibit-read-only t) | 
				
			||||
            (buffer-undo-list t) | 
				
			||||
            (moving (= (point) cider-popup-output-marker))) | 
				
			||||
        (save-excursion | 
				
			||||
          (goto-char cider-popup-output-marker) | 
				
			||||
          (let ((value-str (format "%s" value))) | 
				
			||||
            (when face | 
				
			||||
              (if (fboundp 'add-face-text-property) | 
				
			||||
                  (add-face-text-property 0 (length value-str) face nil value-str) | 
				
			||||
                (add-text-properties 0 (length value-str) (list 'face face) value-str))) | 
				
			||||
            (insert value-str)) | 
				
			||||
          (indent-sexp) | 
				
			||||
          (set-marker cider-popup-output-marker (point))) | 
				
			||||
        (when moving (goto-char cider-popup-output-marker)))))) | 
				
			||||
 | 
				
			||||
(provide 'cider-popup) | 
				
			||||
;;; cider-popup.el ends here | 
				
			||||
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						@ -1,129 +0,0 @@
					 | 
				
			||||
;;; cider-resolve.el --- Resolve clojure symbols according to current nREPL connection | 
				
			||||
 | 
				
			||||
;; Copyright © 2015 Artur Malabarba | 
				
			||||
 | 
				
			||||
;; Author: Artur Malabarba <bruce.connor.am@gmail.com> | 
				
			||||
 | 
				
			||||
;; This program 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 of the License, or | 
				
			||||
;; (at your option) any later version. | 
				
			||||
 | 
				
			||||
;; This program 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: | 
				
			||||
 | 
				
			||||
;; The ns cache is a dict of namespaces stored in the connection buffer. This | 
				
			||||
;; file offers functions to easily get information about variables from this | 
				
			||||
;; cache, given the variable's name and the file's namespace.  This | 
				
			||||
;; functionality is similar to that offered by the `cider-var-info' function | 
				
			||||
;; (and others). The difference is that all functions in this file operate | 
				
			||||
;; without contacting the server (they still rely on an active connection | 
				
			||||
;; buffer, but no messages are actually exchanged). | 
				
			||||
 | 
				
			||||
;; For this reason, the functions here are well suited for very | 
				
			||||
;; performance-sentitive operations, such as font-locking or | 
				
			||||
;; indentation. Meanwhile, operations like code-jumping are better off | 
				
			||||
;; communicating with the middleware, just in the off chance that the cache is | 
				
			||||
;; outdated. | 
				
			||||
 | 
				
			||||
;; Below is a typical entry on this cache dict. Note that clojure.core symbols | 
				
			||||
;; are excluded from the refers to save space. | 
				
			||||
 | 
				
			||||
;; "cider.nrepl.middleware.track-state" | 
				
			||||
;; (dict "aliases" | 
				
			||||
;;       (dict "cljs" "cider.nrepl.middleware.util.cljs" | 
				
			||||
;;             "misc" "cider.nrepl.middleware.util.misc" | 
				
			||||
;;             "set" "clojure.set") | 
				
			||||
;;       "interns" (dict a | 
				
			||||
;;                       "assoc-state"    (dict "arglists" | 
				
			||||
;;                                              (("response" | 
				
			||||
;;                                                (dict "as" "msg" "keys" | 
				
			||||
;;                                                      ("session"))))) | 
				
			||||
;;                       "filter-core"    (dict "arglists" | 
				
			||||
;;                                              (("refers"))) | 
				
			||||
;;                       "make-transport" (dict "arglists" | 
				
			||||
;;                                              (((dict "as" "msg" "keys" | 
				
			||||
;;                                                      ("transport"))))) | 
				
			||||
;;                       "ns-as-map"      (dict "arglists" | 
				
			||||
;;                                              (("ns"))) | 
				
			||||
;;                       "ns-cache"       (dict) | 
				
			||||
;;                       "relevant-meta"  (dict "arglists" | 
				
			||||
;;                                              (("var"))) | 
				
			||||
;;                       "update-vals"    (dict "arglists" | 
				
			||||
;;                                              (("m" "f"))) | 
				
			||||
;;                       "wrap-tracker"   (dict "arglists" | 
				
			||||
;;                                              (("handler")))) | 
				
			||||
;;       "refers" (dict "set-descriptor!" "#'clojure.tools.nrepl.middleware/set-descriptor!")) | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'cider-client) | 
				
			||||
(require 'nrepl-client) | 
				
			||||
(require 'cider-util) | 
				
			||||
 | 
				
			||||
(defvar cider-repl-ns-cache) | 
				
			||||
 | 
				
			||||
(defun cider-resolve--get-in (&rest keys) | 
				
			||||
  "Return (nrepl-dict-get-in cider-repl-ns-cache KEYS)." | 
				
			||||
  (when cider-connections | 
				
			||||
    (with-current-buffer (cider-current-connection) | 
				
			||||
      (nrepl-dict-get-in cider-repl-ns-cache keys)))) | 
				
			||||
 | 
				
			||||
(defun cider-resolve-alias (ns alias) | 
				
			||||
  "Return the namespace that ALIAS refers to in namespace NS. | 
				
			||||
If it doesn't point anywhere, returns ALIAS." | 
				
			||||
  (or (cider-resolve--get-in ns "aliases" alias) | 
				
			||||
      alias)) | 
				
			||||
 | 
				
			||||
(defconst cider-resolve--prefix-regexp "\\`\\(?:#'\\)?\\([^/]+\\)/") | 
				
			||||
 | 
				
			||||
(defun cider-resolve-var (ns var) | 
				
			||||
  "Return a dict of the metadata of a clojure var VAR in namespace NS. | 
				
			||||
VAR is a string. | 
				
			||||
Return nil only if VAR cannot be resolved." | 
				
			||||
  (let* ((var-ns (when (string-match cider-resolve--prefix-regexp var) | 
				
			||||
                   (cider-resolve-alias ns (match-string 1 var)))) | 
				
			||||
         (name (replace-regexp-in-string cider-resolve--prefix-regexp "" var))) | 
				
			||||
    (or | 
				
			||||
     (cider-resolve--get-in (or var-ns ns) "interns" name) | 
				
			||||
     (unless var-ns | 
				
			||||
       ;; If the var had no prefix, it might be referred. | 
				
			||||
       (if-let ((referal (cider-resolve--get-in ns "refers" name))) | 
				
			||||
           (cider-resolve-var ns referal) | 
				
			||||
         ;; Or it might be from core. | 
				
			||||
         (unless (equal ns "clojure.core") | 
				
			||||
           (cider-resolve-var "clojure.core" name))))))) | 
				
			||||
 | 
				
			||||
(defun cider-resolve-core-ns () | 
				
			||||
  "Return a dict of the core namespace for current connection. | 
				
			||||
This will be clojure.core or cljs.core depending on `cider-repl-type'." | 
				
			||||
  (when (cider-connected-p) | 
				
			||||
    (with-current-buffer (cider-current-connection) | 
				
			||||
      (cider-resolve--get-in (if (equal cider-repl-type "cljs") | 
				
			||||
                                 "cljs.core" | 
				
			||||
                               "clojure.core"))))) | 
				
			||||
 | 
				
			||||
(defun cider-resolve-ns-symbols (ns) | 
				
			||||
  "Return a plist of all valid symbols in NS. | 
				
			||||
Each entry's value is the metadata of the var that the symbol refers to. | 
				
			||||
NS can be the namespace name, or a dict of the namespace itself." | 
				
			||||
  (when-let ((dict (if (stringp ns) | 
				
			||||
                       (cider-resolve--get-in ns) | 
				
			||||
                     ns))) | 
				
			||||
    (nrepl-dbind-response dict (interns refers aliases) | 
				
			||||
      (append (cdr interns) | 
				
			||||
              (nrepl-dict-flat-map (lambda (alias namespace) | 
				
			||||
                                     (nrepl-dict-flat-map (lambda (sym meta) | 
				
			||||
                                                            (list (concat alias "/" sym) meta)) | 
				
			||||
                                                          (cider-resolve--get-in namespace "interns"))) | 
				
			||||
                                   aliases))))) | 
				
			||||
 | 
				
			||||
(provide 'cider-resolve) | 
				
			||||
;;; cider-resolve.el ends here | 
				
			||||
@ -1,71 +0,0 @@
					 | 
				
			||||
;;; cider-scratch.el --- *scratch* buffer for Clojure -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright © 2014-2015 Bozhidar Batsov | 
				
			||||
;; | 
				
			||||
;; Author: Tim King <kingtim@gmail.com> | 
				
			||||
;;         Phil Hagelberg <technomancy@gmail.com> | 
				
			||||
;;         Bozhidar Batsov <bozhidar@batsov.com> | 
				
			||||
;;         Hugo Duncan <hugo@hugoduncan.org> | 
				
			||||
;;         Steve Purcell <steve@sanityinc.com> | 
				
			||||
 | 
				
			||||
;; This program 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 of the License, or | 
				
			||||
;; (at your option) any later version. | 
				
			||||
 | 
				
			||||
;; This program 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/>. | 
				
			||||
 | 
				
			||||
;; This file is not part of GNU Emacs. | 
				
			||||
 | 
				
			||||
;;; Commentary: | 
				
			||||
 | 
				
			||||
;; Imitate Emacs's *scratch* buffer. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'cider-interaction) | 
				
			||||
(require 'clojure-mode) | 
				
			||||
 | 
				
			||||
(defvar cider-clojure-interaction-mode-map | 
				
			||||
  (let ((map (make-sparse-keymap))) | 
				
			||||
    (set-keymap-parent map clojure-mode-map) | 
				
			||||
    (define-key map (kbd "C-j") #'cider-eval-print-last-sexp) | 
				
			||||
    map)) | 
				
			||||
 | 
				
			||||
(defconst cider-scratch-buffer-name "*cider-scratch*") | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun cider-scratch () | 
				
			||||
  "Create a scratch buffer." | 
				
			||||
  (interactive) | 
				
			||||
  (pop-to-buffer (cider-find-or-create-scratch-buffer))) | 
				
			||||
 | 
				
			||||
(defun cider-find-or-create-scratch-buffer () | 
				
			||||
  "Find or create the scratch buffer." | 
				
			||||
  (or (get-buffer cider-scratch-buffer-name) | 
				
			||||
      (cider-create-scratch-buffer))) | 
				
			||||
 | 
				
			||||
(define-derived-mode cider-clojure-interaction-mode clojure-mode "Clojure Interaction" | 
				
			||||
  "Major mode for typing and evaluating Clojure forms. | 
				
			||||
Like Lisp mode except that \\[cider-eval-print-last-sexp] evals the Lisp expression | 
				
			||||
before point, and prints its value into the buffer, advancing point. | 
				
			||||
 | 
				
			||||
\\{cider-clojure-interaction-mode-map}") | 
				
			||||
 | 
				
			||||
(defun cider-create-scratch-buffer () | 
				
			||||
  "Create a new scratch buffer." | 
				
			||||
  (with-current-buffer (get-buffer-create cider-scratch-buffer-name) | 
				
			||||
    (cider-clojure-interaction-mode) | 
				
			||||
    (insert ";; This buffer is for Clojure experiments and evaluation.\n" | 
				
			||||
            ";; Press C-j to evaluate the last expression.\n\n") | 
				
			||||
    (current-buffer))) | 
				
			||||
 | 
				
			||||
(provide 'cider-scratch) | 
				
			||||
 | 
				
			||||
;;; cider-scratch.el ends here | 
				
			||||
@ -1,154 +0,0 @@
					 | 
				
			||||
;;; cider-selector.el --- Buffer selection command inspired by SLIME's selector -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright © 2012-2015 Tim King, Phil Hagelberg | 
				
			||||
;; Copyright © 2013-2015 Bozhidar Batsov, Hugo Duncan, Steve Purcell | 
				
			||||
;; | 
				
			||||
;; Author: Tim King <kingtim@gmail.com> | 
				
			||||
;;         Phil Hagelberg <technomancy@gmail.com> | 
				
			||||
;;         Bozhidar Batsov <bozhidar@batsov.com> | 
				
			||||
;;         Hugo Duncan <hugo@hugoduncan.org> | 
				
			||||
;;         Steve Purcell <steve@sanityinc.com> | 
				
			||||
 | 
				
			||||
;; This program 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 of the License, or | 
				
			||||
;; (at your option) any later version. | 
				
			||||
 | 
				
			||||
;; This program 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/>. | 
				
			||||
 | 
				
			||||
;; This file is not part of GNU Emacs. | 
				
			||||
 | 
				
			||||
;;; Commentary: | 
				
			||||
 | 
				
			||||
;; Buffer selection command inspired by SLIME's selector. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'cider-client) | 
				
			||||
(require 'cider-interaction) | 
				
			||||
 | 
				
			||||
(defconst cider-selector-help-buffer "*Selector Help*" | 
				
			||||
  "The name of the selector's help buffer.") | 
				
			||||
 | 
				
			||||
(defvar cider-selector-methods nil | 
				
			||||
  "List of buffer-selection methods for the `cider-selector' command. | 
				
			||||
Each element is a list (KEY DESCRIPTION FUNCTION). | 
				
			||||
DESCRIPTION is a one-line description of what the key selects.") | 
				
			||||
 | 
				
			||||
(defvar cider-selector-other-window nil | 
				
			||||
  "If non-nil use `switch-to-buffer-other-window'.") | 
				
			||||
 | 
				
			||||
(defun cider--recently-visited-buffer (mode) | 
				
			||||
  "Return the most recently visited buffer whose `major-mode' is MODE. | 
				
			||||
Only considers buffers that are not already visible." | 
				
			||||
  (cl-loop for buffer in (buffer-list) | 
				
			||||
           when (and (with-current-buffer buffer (eq major-mode mode)) | 
				
			||||
                     (not (string-match-p "^ " (buffer-name buffer))) | 
				
			||||
                     (null (get-buffer-window buffer 'visible))) | 
				
			||||
           return buffer | 
				
			||||
           finally (error "Can't find unshown buffer in %S" mode))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun cider-selector (&optional other-window) | 
				
			||||
  "Select a new buffer by type, indicated by a single character. | 
				
			||||
The user is prompted for a single character indicating the method by | 
				
			||||
which to choose a new buffer.  The `?' character describes then | 
				
			||||
available methods.  OTHER-WINDOW provides an optional target. | 
				
			||||
 | 
				
			||||
See `def-cider-selector-method' for defining new methods." | 
				
			||||
  (interactive) | 
				
			||||
  (message "Select [%s]: " | 
				
			||||
           (apply #'string (mapcar #'car cider-selector-methods))) | 
				
			||||
  (let* ((cider-selector-other-window other-window) | 
				
			||||
         (ch (save-window-excursion | 
				
			||||
               (select-window (minibuffer-window)) | 
				
			||||
               (read-char))) | 
				
			||||
         (method (cl-find ch cider-selector-methods :key #'car))) | 
				
			||||
    (cond (method | 
				
			||||
           (funcall (cl-caddr method))) | 
				
			||||
          (t | 
				
			||||
           (message "No method for character: ?\\%c" ch) | 
				
			||||
           (ding) | 
				
			||||
           (sleep-for 1) | 
				
			||||
           (discard-input) | 
				
			||||
           (cider-selector))))) | 
				
			||||
 | 
				
			||||
(defmacro def-cider-selector-method (key description &rest body) | 
				
			||||
  "Define a new `cider-select' buffer selection method. | 
				
			||||
 | 
				
			||||
KEY is the key the user will enter to choose this method. | 
				
			||||
 | 
				
			||||
DESCRIPTION is a one-line sentence describing how the method | 
				
			||||
selects a buffer. | 
				
			||||
 | 
				
			||||
BODY is a series of forms which are evaluated when the selector | 
				
			||||
is chosen.  The returned buffer is selected with | 
				
			||||
`switch-to-buffer'." | 
				
			||||
  (let ((method `(lambda () | 
				
			||||
                   (let ((buffer (progn ,@body))) | 
				
			||||
                     (cond ((not (get-buffer buffer)) | 
				
			||||
                            (message "No such buffer: %S" buffer) | 
				
			||||
                            (ding)) | 
				
			||||
                           ((get-buffer-window buffer) | 
				
			||||
                            (select-window (get-buffer-window buffer))) | 
				
			||||
                           (cider-selector-other-window | 
				
			||||
                            (switch-to-buffer-other-window buffer)) | 
				
			||||
                           (t | 
				
			||||
                            (switch-to-buffer buffer))))))) | 
				
			||||
    `(setq cider-selector-methods | 
				
			||||
           (cl-sort (cons (list ,key ,description ,method) | 
				
			||||
                          (cl-remove ,key cider-selector-methods :key #'car)) | 
				
			||||
                    #'< :key #'car)))) | 
				
			||||
 | 
				
			||||
(def-cider-selector-method ?? "Selector help buffer." | 
				
			||||
  (ignore-errors (kill-buffer cider-selector-help-buffer)) | 
				
			||||
  (with-current-buffer (get-buffer-create cider-selector-help-buffer) | 
				
			||||
    (insert "CIDER Selector Methods:\n\n") | 
				
			||||
    (cl-loop for (key line nil) in cider-selector-methods | 
				
			||||
             do (insert (format "%c:\t%s\n" key line))) | 
				
			||||
    (goto-char (point-min)) | 
				
			||||
    (help-mode) | 
				
			||||
    (display-buffer (current-buffer) t)) | 
				
			||||
  (cider-selector) | 
				
			||||
  (current-buffer)) | 
				
			||||
 | 
				
			||||
(cl-pushnew (list ?4 "Select in other window" (lambda () (cider-selector t))) | 
				
			||||
            cider-selector-methods :key #'car) | 
				
			||||
 | 
				
			||||
(def-cider-selector-method ?c | 
				
			||||
  "Most recently visited clojure-mode buffer." | 
				
			||||
  (cider--recently-visited-buffer 'clojure-mode)) | 
				
			||||
 | 
				
			||||
(def-cider-selector-method ?e | 
				
			||||
  "Most recently visited emacs-lisp-mode buffer." | 
				
			||||
  (cider--recently-visited-buffer 'emacs-lisp-mode)) | 
				
			||||
 | 
				
			||||
(def-cider-selector-method ?q "Abort." | 
				
			||||
  (top-level)) | 
				
			||||
 | 
				
			||||
(def-cider-selector-method ?r | 
				
			||||
  "Current REPL buffer." | 
				
			||||
  (cider-current-repl-buffer)) | 
				
			||||
 | 
				
			||||
(def-cider-selector-method ?n | 
				
			||||
  "Connections browser buffer." | 
				
			||||
  (cider-connection-browser) | 
				
			||||
  cider--connection-browser-buffer-name) | 
				
			||||
 | 
				
			||||
(def-cider-selector-method ?m | 
				
			||||
  "*nrepl-messages* buffer." | 
				
			||||
  nrepl-message-buffer-name) | 
				
			||||
 | 
				
			||||
(def-cider-selector-method ?x | 
				
			||||
  "*cider-error* buffer." | 
				
			||||
  cider-error-buffer) | 
				
			||||
 | 
				
			||||
(provide 'cider-selector) | 
				
			||||
 | 
				
			||||
;;; cider-selector.el ends here | 
				
			||||
@ -1,610 +0,0 @@
					 | 
				
			||||
;;; cider-stacktrace.el --- Stacktrace navigator -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright © 2014-2015 Jeff Valk | 
				
			||||
 | 
				
			||||
;; Author: Jeff Valk <jv@jeffvalk.com> | 
				
			||||
 | 
				
			||||
;; This program 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 of the License, or | 
				
			||||
;; (at your option) any later version. | 
				
			||||
 | 
				
			||||
;; This program 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/>. | 
				
			||||
 | 
				
			||||
;; This file is not part of GNU Emacs. | 
				
			||||
 | 
				
			||||
;;; Commentary: | 
				
			||||
 | 
				
			||||
;; Stacktrace filtering and stack frame source navigation | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'cl-lib) | 
				
			||||
(require 'cider-popup) | 
				
			||||
(require 'button) | 
				
			||||
(require 'easymenu) | 
				
			||||
(require 'cider-common) | 
				
			||||
(require 'cider-compat) | 
				
			||||
(require 'cider-client) | 
				
			||||
(require 'cider-util) | 
				
			||||
 | 
				
			||||
(require 'seq) | 
				
			||||
 | 
				
			||||
;; Variables | 
				
			||||
 | 
				
			||||
(defgroup cider-stacktrace nil | 
				
			||||
  "Stacktrace filtering and navigation." | 
				
			||||
  :prefix "cider-stacktrace-" | 
				
			||||
  :group 'cider) | 
				
			||||
 | 
				
			||||
(defcustom cider-stacktrace-fill-column t | 
				
			||||
  "Fill column for error messages in stacktrace display. | 
				
			||||
If nil, messages will not be wrapped.  If truthy but non-numeric, | 
				
			||||
`fill-column' will be used." | 
				
			||||
  :type 'list | 
				
			||||
  :group 'cider-stacktrace | 
				
			||||
  :package-version '(cider . "0.7.0")) | 
				
			||||
 | 
				
			||||
(defcustom cider-stacktrace-default-filters '(tooling dup) | 
				
			||||
  "Frame types to omit from initial stacktrace display." | 
				
			||||
  :type 'list | 
				
			||||
  :group 'cider-stacktrace | 
				
			||||
  :package-version '(cider . "0.6.0")) | 
				
			||||
 | 
				
			||||
(defcustom cider-stacktrace-print-length 50 | 
				
			||||
  "Set the maximum length of sequences in displayed cause data. | 
				
			||||
 | 
				
			||||
This sets the value of Clojure's `*print-length*` when pretty printing the | 
				
			||||
`ex-data` map for exception causes in the stacktrace that are instances of | 
				
			||||
`IExceptionInfo`. | 
				
			||||
 | 
				
			||||
Be advised that setting this to `nil` will cause the attempted printing of | 
				
			||||
infinite data structures." | 
				
			||||
  :type '(choice integer (const nil)) | 
				
			||||
  :group 'cider-stacktrace | 
				
			||||
  :package-version '(cider . "0.9.0")) | 
				
			||||
 | 
				
			||||
(defcustom cider-stacktrace-print-level 50 | 
				
			||||
  "Set the maximum level of nesting in displayed cause data. | 
				
			||||
 | 
				
			||||
This sets the value of Clojure's `*print-level*` when pretty printing the | 
				
			||||
`ex-data` map for exception causes in the stacktrace that are instances of | 
				
			||||
`IExceptionInfo`. | 
				
			||||
 | 
				
			||||
Be advised that setting this to `nil` will cause the attempted printing of | 
				
			||||
cyclical data structures." | 
				
			||||
  :type '(choice integer (const nil)) | 
				
			||||
  :group 'cider-stacktrace | 
				
			||||
  :package-version '(cider . "0.8.0")) | 
				
			||||
 | 
				
			||||
(defvar cider-stacktrace-detail-max 2 | 
				
			||||
  "The maximum detail level for causes.") | 
				
			||||
 | 
				
			||||
(defvar-local cider-stacktrace-hidden-frame-count 0) | 
				
			||||
(defvar-local cider-stacktrace-filters nil) | 
				
			||||
(defvar-local cider-stacktrace-prior-filters nil) | 
				
			||||
(defvar-local cider-stacktrace-cause-visibility nil) | 
				
			||||
 | 
				
			||||
(defconst cider-error-buffer "*cider-error*") | 
				
			||||
(add-to-list 'cider-ancillary-buffers cider-error-buffer) | 
				
			||||
 | 
				
			||||
;; Faces | 
				
			||||
 | 
				
			||||
(defface cider-stacktrace-error-class-face | 
				
			||||
  '((t (:inherit font-lock-warning-face))) | 
				
			||||
  "Face for exception class names" | 
				
			||||
  :group 'cider-stacktrace | 
				
			||||
  :package-version '(cider . "0.6.0")) | 
				
			||||
 | 
				
			||||
(defface cider-stacktrace-error-message-face | 
				
			||||
  '((t (:inherit font-lock-doc-face))) | 
				
			||||
  "Face for exception messages" | 
				
			||||
  :group 'cider-stacktrace | 
				
			||||
  :package-version '(cider . "0.7.0")) | 
				
			||||
 | 
				
			||||
(defface cider-stacktrace-filter-shown-face | 
				
			||||
  '((t (:inherit button :underline t :weight normal))) | 
				
			||||
  "Face for filter buttons representing frames currently visible" | 
				
			||||
  :group 'cider-stacktrace | 
				
			||||
  :package-version '(cider . "0.6.0")) | 
				
			||||
 | 
				
			||||
(defface cider-stacktrace-filter-hidden-face | 
				
			||||
  '((t (:inherit button :underline nil :weight normal))) | 
				
			||||
  "Face for filter buttons representing frames currently filtered out" | 
				
			||||
  :group 'cider-stacktrace | 
				
			||||
  :package-version '(cider . "0.6.0")) | 
				
			||||
 | 
				
			||||
(defface cider-stacktrace-face | 
				
			||||
  '((t (:inherit default))) | 
				
			||||
  "Face for stack frame text" | 
				
			||||
  :group 'cider-stacktrace | 
				
			||||
  :package-version '(cider . "0.6.0")) | 
				
			||||
 | 
				
			||||
(defface cider-stacktrace-ns-face | 
				
			||||
  '((t (:inherit font-lock-comment-face))) | 
				
			||||
  "Face for stack frame namespace name" | 
				
			||||
  :group 'cider-stacktrace | 
				
			||||
  :package-version '(cider . "0.6.0")) | 
				
			||||
 | 
				
			||||
(defface cider-stacktrace-fn-face | 
				
			||||
  '((t (:inherit default :weight bold))) | 
				
			||||
  "Face for stack frame function name" | 
				
			||||
  :group 'cider-stacktrace | 
				
			||||
  :package-version '(cider . "0.6.0")) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
;; Colors & Theme Support | 
				
			||||
 | 
				
			||||
(defvar cider-stacktrace-frames-background-color | 
				
			||||
  (cider-scale-background-color) | 
				
			||||
  "Background color for stacktrace frames.") | 
				
			||||
 | 
				
			||||
(defadvice enable-theme (after cider-stacktrace-adapt-to-theme activate) | 
				
			||||
  "When theme is changed, update `cider-stacktrace-frames-background-color'." | 
				
			||||
  (setq cider-stacktrace-frames-background-color (cider-scale-background-color))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
;; Mode & key bindings | 
				
			||||
 | 
				
			||||
(defvar cider-stacktrace-mode-map | 
				
			||||
  (let ((map (make-sparse-keymap))) | 
				
			||||
    (define-key map (kbd "M-p") #'cider-stacktrace-previous-cause) | 
				
			||||
    (define-key map (kbd "M-n") #'cider-stacktrace-next-cause) | 
				
			||||
    (define-key map (kbd "M-.") #'cider-stacktrace-jump) | 
				
			||||
    (define-key map "q" #'cider-popup-buffer-quit-function) | 
				
			||||
    (define-key map "j" #'cider-stacktrace-toggle-java) | 
				
			||||
    (define-key map "c" #'cider-stacktrace-toggle-clj) | 
				
			||||
    (define-key map "r" #'cider-stacktrace-toggle-repl) | 
				
			||||
    (define-key map "t" #'cider-stacktrace-toggle-tooling) | 
				
			||||
    (define-key map "d" #'cider-stacktrace-toggle-duplicates) | 
				
			||||
    (define-key map "a" #'cider-stacktrace-toggle-all) | 
				
			||||
    (define-key map "1" #'cider-stacktrace-cycle-cause-1) | 
				
			||||
    (define-key map "2" #'cider-stacktrace-cycle-cause-2) | 
				
			||||
    (define-key map "3" #'cider-stacktrace-cycle-cause-3) | 
				
			||||
    (define-key map "4" #'cider-stacktrace-cycle-cause-4) | 
				
			||||
    (define-key map "5" #'cider-stacktrace-cycle-cause-5) | 
				
			||||
    (define-key map "0" #'cider-stacktrace-cycle-all-causes) | 
				
			||||
    (define-key map [tab] #'cider-stacktrace-cycle-current-cause) | 
				
			||||
    (define-key map [backtab] #'cider-stacktrace-cycle-all-causes) | 
				
			||||
    (easy-menu-define cider-stacktrace-mode-menu map | 
				
			||||
      "Menu for CIDER's stacktrace mode" | 
				
			||||
      '("Stacktrace" | 
				
			||||
        ["Previous cause" cider-stacktrace-previous-cause] | 
				
			||||
        ["Next cause" cider-stacktrace-next-cause] | 
				
			||||
        "--" | 
				
			||||
        ["Jump to frame source" cider-stacktrace-jump] | 
				
			||||
        "--" | 
				
			||||
        ["Cycle current cause detail" cider-stacktrace-cycle-current-cause] | 
				
			||||
        ["Cycle cause #1 detail" cider-stacktrace-cycle-cause-1] | 
				
			||||
        ["Cycle cause #2 detail" cider-stacktrace-cycle-cause-2] | 
				
			||||
        ["Cycle cause #3 detail" cider-stacktrace-cycle-cause-3] | 
				
			||||
        ["Cycle cause #4 detail" cider-stacktrace-cycle-cause-4] | 
				
			||||
        ["Cycle cause #5 detail" cider-stacktrace-cycle-cause-5] | 
				
			||||
        ["Cycle all cause detail" cider-stacktrace-cycle-all-causes] | 
				
			||||
        "--" | 
				
			||||
        ["Show/hide Java frames" cider-stacktrace-toggle-java] | 
				
			||||
        ["Show/hide Clojure frames" cider-stacktrace-toggle-clj] | 
				
			||||
        ["Show/hide REPL frames" cider-stacktrace-toggle-repl] | 
				
			||||
        ["Show/hide tooling frames" cider-stacktrace-toggle-tooling] | 
				
			||||
        ["Show/hide duplicate frames" cider-stacktrace-toggle-duplicates] | 
				
			||||
        ["Show/hide all frames" cider-stacktrace-toggle-all])) | 
				
			||||
    map)) | 
				
			||||
 | 
				
			||||
(define-derived-mode cider-stacktrace-mode special-mode "Stacktrace" | 
				
			||||
  "Major mode for filtering and navigating CIDER stacktraces. | 
				
			||||
 | 
				
			||||
\\{cider-stacktrace-mode-map}" | 
				
			||||
  (setq buffer-read-only t) | 
				
			||||
  (setq-local truncate-lines t) | 
				
			||||
  (setq-local electric-indent-chars nil) | 
				
			||||
  (setq-local cider-stacktrace-prior-filters nil) | 
				
			||||
  (setq-local cider-stacktrace-hidden-frame-count 0) | 
				
			||||
  (setq-local cider-stacktrace-filters cider-stacktrace-default-filters) | 
				
			||||
  (setq-local cider-stacktrace-cause-visibility (make-vector 10 0))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
;; Stacktrace filtering | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-indicate-filters (filters) | 
				
			||||
  "Update enabled state of filter buttons. | 
				
			||||
 | 
				
			||||
Find buttons with a 'filter property; if filter is a member of FILTERS, or | 
				
			||||
if filter is nil ('show all') and the argument list is non-nil, fontify the | 
				
			||||
button as disabled.  Upon finding text with a 'hidden-count property, stop | 
				
			||||
searching and update the hidden count text." | 
				
			||||
  (with-current-buffer cider-error-buffer | 
				
			||||
    (save-excursion | 
				
			||||
      (goto-char (point-min)) | 
				
			||||
      (let ((inhibit-read-only t) | 
				
			||||
            (get-face (lambda (hide) | 
				
			||||
                        (if hide | 
				
			||||
                            'cider-stacktrace-filter-hidden-face | 
				
			||||
                          'cider-stacktrace-filter-shown-face)))) | 
				
			||||
        ;; Toggle buttons | 
				
			||||
        (while (not (or (get-text-property (point) 'hidden-count) (eobp))) | 
				
			||||
          (let ((button (button-at (point)))) | 
				
			||||
            (when button | 
				
			||||
              (let* ((filter (button-get button 'filter)) | 
				
			||||
                     (face (funcall get-face (if filter | 
				
			||||
                                                 (member filter filters) | 
				
			||||
                                               filters)))) | 
				
			||||
                (button-put button 'face face))) | 
				
			||||
            (goto-char (or (next-property-change (point)) | 
				
			||||
                           (point-max))))) | 
				
			||||
        ;; Update hidden count | 
				
			||||
        (when (and (get-text-property (point) 'hidden-count) | 
				
			||||
                   (re-search-forward "[0-9]+" (line-end-position) t)) | 
				
			||||
          (replace-match | 
				
			||||
           (number-to-string cider-stacktrace-hidden-frame-count))))))) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-apply-filters (filters) | 
				
			||||
  "Set visibility on stack frames using FILTERS. | 
				
			||||
Update `cider-stacktrace-hidden-frame-count' and indicate filters applied. | 
				
			||||
Currently collapsed stacktraces are ignored, and do not contribute to the | 
				
			||||
hidden count." | 
				
			||||
  (with-current-buffer cider-error-buffer | 
				
			||||
    (save-excursion | 
				
			||||
      (goto-char (point-min)) | 
				
			||||
      (let ((inhibit-read-only t) | 
				
			||||
            (hidden 0)) | 
				
			||||
        (while (not (eobp)) | 
				
			||||
          (unless (get-text-property (point) 'collapsed) | 
				
			||||
            (let* ((flags (get-text-property (point) 'flags)) | 
				
			||||
                   (hide (if (seq-intersection filters flags) t nil))) | 
				
			||||
              (when hide (setq hidden (+ 1 hidden))) | 
				
			||||
              (put-text-property (point) (line-beginning-position 2) 'invisible hide))) | 
				
			||||
          (forward-line 1)) | 
				
			||||
        (setq cider-stacktrace-hidden-frame-count hidden))) | 
				
			||||
    (cider-stacktrace-indicate-filters filters))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-apply-cause-visibility () | 
				
			||||
  "Apply `cider-stacktrace-cause-visibility' to causes and reapply filters." | 
				
			||||
  (with-current-buffer cider-error-buffer | 
				
			||||
    (save-excursion | 
				
			||||
      (goto-char (point-min)) | 
				
			||||
      (cl-flet ((next-detail (end) | 
				
			||||
                             (when-let ((pos (next-single-property-change (point) 'detail))) | 
				
			||||
                               (when (< pos end) | 
				
			||||
                                 (goto-char pos))))) | 
				
			||||
        (let ((inhibit-read-only t)) | 
				
			||||
          ;; For each cause... | 
				
			||||
          (while (cider-stacktrace-next-cause) | 
				
			||||
            (let* ((num   (get-text-property (point) 'cause)) | 
				
			||||
                   (level (elt cider-stacktrace-cause-visibility num)) | 
				
			||||
                   (cause-end (cadr (cider-property-bounds 'cause)))) | 
				
			||||
              ;; For each detail level within the cause, set visibility. | 
				
			||||
              (while (next-detail cause-end) | 
				
			||||
                (let* ((detail (get-text-property (point) 'detail)) | 
				
			||||
                       (detail-end (cadr (cider-property-bounds 'detail))) | 
				
			||||
                       (hide (if (> detail level) t nil))) | 
				
			||||
                  (add-text-properties (point) detail-end | 
				
			||||
                                       (list 'invisible hide | 
				
			||||
                                             'collapsed hide)))))))) | 
				
			||||
      (cider-stacktrace-apply-filters | 
				
			||||
       cider-stacktrace-filters)))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
;; Interactive functions | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-previous-cause () | 
				
			||||
  "Move point to the previous exception cause, if one exists." | 
				
			||||
  (interactive) | 
				
			||||
  (with-current-buffer cider-error-buffer | 
				
			||||
    (when-let ((pos (previous-single-property-change (point) 'cause))) | 
				
			||||
      (goto-char pos)))) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-next-cause () | 
				
			||||
  "Move point to the next exception cause, if one exists." | 
				
			||||
  (interactive) | 
				
			||||
  (with-current-buffer cider-error-buffer | 
				
			||||
    (when-let ((pos (next-single-property-change (point) 'cause))) | 
				
			||||
      (goto-char pos)))) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-cycle-cause (num &optional level) | 
				
			||||
  "Update element NUM of `cider-stacktrace-cause-visibility', optionally to LEVEL. | 
				
			||||
If LEVEL is not specified, its current value is incremented. When it reaches 3, | 
				
			||||
it wraps to 0." | 
				
			||||
  (let ((level (or level (1+ (elt cider-stacktrace-cause-visibility num))))) | 
				
			||||
    (aset cider-stacktrace-cause-visibility num (mod level 3)) | 
				
			||||
    (cider-stacktrace-apply-cause-visibility))) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-cycle-all-causes () | 
				
			||||
  "Cycle the visibility of all exception causes." | 
				
			||||
  (interactive) | 
				
			||||
  (with-current-buffer cider-error-buffer | 
				
			||||
    (save-excursion | 
				
			||||
      ;; Find nearest cause. | 
				
			||||
      (unless (get-text-property (point) 'cause) | 
				
			||||
        (cider-stacktrace-next-cause) | 
				
			||||
        (unless (get-text-property (point) 'cause) | 
				
			||||
          (cider-stacktrace-previous-cause))) | 
				
			||||
      ;; Cycle its level, and apply that to all causes. | 
				
			||||
      (let* ((num (get-text-property (point) 'cause)) | 
				
			||||
             (level (1+ (elt cider-stacktrace-cause-visibility num)))) | 
				
			||||
        (setq-local cider-stacktrace-cause-visibility | 
				
			||||
                    (make-vector 10 (mod level 3))) | 
				
			||||
        (cider-stacktrace-apply-cause-visibility))))) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-cycle-current-cause () | 
				
			||||
  "Cycle the visibility of current exception at point, if any." | 
				
			||||
  (interactive) | 
				
			||||
  (with-current-buffer cider-error-buffer | 
				
			||||
    (when-let ((num (get-text-property (point) 'cause))) | 
				
			||||
      (cider-stacktrace-cycle-cause num)))) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-cycle-cause-1 () | 
				
			||||
  "Cycle the visibility of exception cause #1." | 
				
			||||
  (interactive) | 
				
			||||
  (cider-stacktrace-cycle-cause 1)) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-cycle-cause-2 () | 
				
			||||
  "Cycle the visibility of exception cause #2." | 
				
			||||
  (interactive) | 
				
			||||
  (cider-stacktrace-cycle-cause 2)) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-cycle-cause-3 () | 
				
			||||
  "Cycle the visibility of exception cause #3." | 
				
			||||
  (interactive) | 
				
			||||
  (cider-stacktrace-cycle-cause 3)) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-cycle-cause-4 () | 
				
			||||
  "Cycle the visibility of exception cause #4." | 
				
			||||
  (interactive) | 
				
			||||
  (cider-stacktrace-cycle-cause 4)) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-cycle-cause-5 () | 
				
			||||
  "Cycle the visibility of exception cause #5." | 
				
			||||
  (interactive) | 
				
			||||
  (cider-stacktrace-cycle-cause 5)) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-toggle-all () | 
				
			||||
  "Reset `cider-stacktrace-filters' if present; otherwise restore prior filters." | 
				
			||||
  (interactive) | 
				
			||||
  (when cider-stacktrace-filters | 
				
			||||
    (setq-local cider-stacktrace-prior-filters | 
				
			||||
                cider-stacktrace-filters)) | 
				
			||||
  (cider-stacktrace-apply-filters | 
				
			||||
   (setq cider-stacktrace-filters | 
				
			||||
         (unless cider-stacktrace-filters      ; when current filters are nil, | 
				
			||||
           cider-stacktrace-prior-filters))))  ;  reenable prior filter set | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-toggle (flag) | 
				
			||||
  "Update `cider-stacktrace-filters' to add or remove FLAG, and apply filters." | 
				
			||||
  (cider-stacktrace-apply-filters | 
				
			||||
   (setq cider-stacktrace-filters | 
				
			||||
         (if (memq flag cider-stacktrace-filters) | 
				
			||||
             (remq flag cider-stacktrace-filters) | 
				
			||||
           (cons flag cider-stacktrace-filters))))) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-toggle-java () | 
				
			||||
  "Toggle display of Java stack frames." | 
				
			||||
  (interactive) | 
				
			||||
  (cider-stacktrace-toggle 'java)) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-toggle-clj () | 
				
			||||
  "Toggle display of Clojure stack frames." | 
				
			||||
  (interactive) | 
				
			||||
  (cider-stacktrace-toggle 'clj)) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-toggle-repl () | 
				
			||||
  "Toggle display of REPL stack frames." | 
				
			||||
  (interactive) | 
				
			||||
  (cider-stacktrace-toggle 'repl)) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-toggle-tooling () | 
				
			||||
  "Toggle display of Tooling stack frames (compiler, nREPL middleware, etc)." | 
				
			||||
  (interactive) | 
				
			||||
  (cider-stacktrace-toggle 'tooling)) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-toggle-duplicates () | 
				
			||||
  "Toggle display of stack frames that are duplicates of their descendents." | 
				
			||||
  (interactive) | 
				
			||||
  (cider-stacktrace-toggle 'dup)) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
;; Text button functions | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-filter (button) | 
				
			||||
  "Apply filter(s) indicated by the BUTTON." | 
				
			||||
  (with-temp-message "Filters may also be toggled with the keyboard." | 
				
			||||
    (let ((flag (button-get button 'filter))) | 
				
			||||
      (if flag | 
				
			||||
          (cider-stacktrace-toggle flag) | 
				
			||||
        (cider-stacktrace-toggle-all))) | 
				
			||||
    (sit-for 5))) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-navigate (button) | 
				
			||||
  "Navigate to the stack frame source represented by the BUTTON." | 
				
			||||
  (let* ((var (button-get button 'var)) | 
				
			||||
         (class (button-get button 'class)) | 
				
			||||
         (method (button-get button 'method)) | 
				
			||||
         (info (or (and var (cider-var-info var)) | 
				
			||||
                   (and class method (cider-member-info class method)) | 
				
			||||
                   (nrepl-dict))) | 
				
			||||
         ;; Stacktrace returns more accurate line numbers, but if the function's | 
				
			||||
         ;; line was unreliable, then so is the stacktrace by the same amount. | 
				
			||||
         ;; Set `line-shift' to the number of lines from the beginning of defn. | 
				
			||||
         (line-shift (- (or (button-get button 'line) 0) | 
				
			||||
                        (or (nrepl-dict-get info "line") 1))) | 
				
			||||
         ;; give priority to `info` files as `info` returns full paths. | 
				
			||||
         (info (nrepl-dict-put info "file" (or (nrepl-dict-get info "file") | 
				
			||||
                                               (button-get button 'file))))) | 
				
			||||
    (cider--jump-to-loc-from-info info t) | 
				
			||||
    (forward-line line-shift) | 
				
			||||
    (back-to-indentation))) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-jump (&optional arg) | 
				
			||||
  "Like `cider-find-var', but uses the stack frame source at point, if available." | 
				
			||||
  (interactive "P") | 
				
			||||
  (let ((button (button-at (point)))) | 
				
			||||
    (if (and button (button-get button 'line)) | 
				
			||||
        (cider-stacktrace-navigate button) | 
				
			||||
      (cider-find-var arg)))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
;; Rendering | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-emit-indented (text indent &optional fill) | 
				
			||||
  "Insert TEXT, and INDENT and optionally FILL the entire block." | 
				
			||||
  (let ((beg (point))) | 
				
			||||
    (insert text) | 
				
			||||
    (goto-char beg) | 
				
			||||
    (while (not (eobp)) | 
				
			||||
      (insert indent) | 
				
			||||
      (forward-line)) | 
				
			||||
    (when (and fill cider-stacktrace-fill-column) | 
				
			||||
      (when (and (numberp cider-stacktrace-fill-column)) | 
				
			||||
        (setq-local fill-column cider-stacktrace-fill-column)) | 
				
			||||
      (setq-local fill-prefix indent) | 
				
			||||
      (fill-region beg (point))))) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-render-filters (buffer filters) | 
				
			||||
  "Emit into BUFFER toggle buttons for each of the FILTERS." | 
				
			||||
  (with-current-buffer buffer | 
				
			||||
    (insert "  Show: ") | 
				
			||||
    (dolist (filter filters) | 
				
			||||
      (insert-text-button (car filter) | 
				
			||||
                          'filter (cadr filter) | 
				
			||||
                          'follow-link t | 
				
			||||
                          'action 'cider-stacktrace-filter | 
				
			||||
                          'help-echo (format "Toggle %s stack frames" | 
				
			||||
                                             (car filter))) | 
				
			||||
      (insert " ")) | 
				
			||||
    (let ((hidden "(0 frames hidden)")) | 
				
			||||
      (put-text-property 0 (length hidden) 'hidden-count t hidden) | 
				
			||||
      (insert " " hidden "\n")))) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-render-frame (buffer frame) | 
				
			||||
  "Emit into BUFFER function call site info for the stack FRAME. | 
				
			||||
This associates text properties to enable filtering and source navigation." | 
				
			||||
  (with-current-buffer buffer | 
				
			||||
    (nrepl-dbind-response frame (file line flags class method name var ns fn) | 
				
			||||
      (let ((flags (mapcar 'intern flags))) ; strings -> symbols | 
				
			||||
        (insert-text-button (format "%30s:%5d  %s/%s" | 
				
			||||
                                    (if (member 'repl flags) "REPL" file) line | 
				
			||||
                                    (if (member 'clj flags) ns class) | 
				
			||||
                                    (if (member 'clj flags) fn method)) | 
				
			||||
                            'var var 'class class 'method method | 
				
			||||
                            'name name 'file file 'line line | 
				
			||||
                            'flags flags 'follow-link t | 
				
			||||
                            'action 'cider-stacktrace-navigate | 
				
			||||
                            'help-echo "View source at this location" | 
				
			||||
                            'face 'cider-stacktrace-face) | 
				
			||||
        (save-excursion | 
				
			||||
          (let ((p4 (point)) | 
				
			||||
                (p1 (search-backward " ")) | 
				
			||||
                (p2 (search-forward "/")) | 
				
			||||
                (p3 (search-forward-regexp "[^/$]+"))) | 
				
			||||
            (put-text-property p1 p4 'font-lock-face 'cider-stacktrace-ns-face) | 
				
			||||
            (put-text-property p2 p3 'font-lock-face 'cider-stacktrace-fn-face))) | 
				
			||||
        (insert "\n"))))) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace--create-go-to-err-button (beg message) | 
				
			||||
  "Create a button that jumps to the relevant error. | 
				
			||||
 | 
				
			||||
Buttons span over the region from BEG to current point. | 
				
			||||
MESSAGE is parsed to find line, col and buffer name to jump to." | 
				
			||||
  (when (and message | 
				
			||||
             (string-match "\\([^:]+\\):\\([^:]+\\):\\([^:]+\\):\\([^:]+\\)\\'" message)) | 
				
			||||
    (let* ((line (string-to-number (match-string 3 message))) | 
				
			||||
           (col (string-to-number (match-string 4 message))) | 
				
			||||
           (buf-name (car (last (split-string (match-string 2 message) "\\/"))))) | 
				
			||||
      (when buf-name | 
				
			||||
        (make-button (+ beg 3) | 
				
			||||
                     (point) | 
				
			||||
                     'action (lambda (_button) | 
				
			||||
                               (let ((the-buf-window (get-buffer-window buf-name))) | 
				
			||||
                                 (if the-buf-window | 
				
			||||
                                     (select-window the-buf-window) | 
				
			||||
                                   (switch-to-buffer buf-name))) | 
				
			||||
                               (goto-char (point-min)) | 
				
			||||
                               (forward-line line) | 
				
			||||
                               (move-to-column col t))))))) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-render-cause (buffer cause num note) | 
				
			||||
  "Emit into BUFFER the CAUSE NUM, exception class, message, data, and NOTE." | 
				
			||||
  (with-current-buffer buffer | 
				
			||||
    (nrepl-dbind-response cause (class message data stacktrace) | 
				
			||||
      (let ((indent "   ") | 
				
			||||
            (class-face 'cider-stacktrace-error-class-face) | 
				
			||||
            (message-face 'cider-stacktrace-error-message-face)) | 
				
			||||
        (cider-propertize-region `(cause ,num) | 
				
			||||
          ;; Detail level 0: exception class | 
				
			||||
          (cider-propertize-region '(detail 0) | 
				
			||||
            (insert (format "%d. " num) | 
				
			||||
                    (propertize note 'font-lock-face 'font-lock-comment-face) " " | 
				
			||||
                    (propertize class 'font-lock-face class-face) | 
				
			||||
                    "\n")) | 
				
			||||
          ;; Detail level 1: message + ex-data | 
				
			||||
          (cider-propertize-region '(detail 1) | 
				
			||||
            (let ((beg (point))) | 
				
			||||
              (cider-stacktrace-emit-indented | 
				
			||||
               (propertize (or message "(No message)") 'font-lock-face  message-face) indent t) | 
				
			||||
              (cider-stacktrace--create-go-to-err-button beg message)) | 
				
			||||
            (insert "\n") | 
				
			||||
            (when data | 
				
			||||
              (cider-stacktrace-emit-indented | 
				
			||||
               (cider-font-lock-as-clojure data) indent nil))) | 
				
			||||
          ;; Detail level 2: stacktrace | 
				
			||||
          (cider-propertize-region '(detail 2) | 
				
			||||
            (insert "\n") | 
				
			||||
            (let ((beg (point)) | 
				
			||||
                  (bg `(:background ,cider-stacktrace-frames-background-color))) | 
				
			||||
              (dolist (frame stacktrace) | 
				
			||||
                (cider-stacktrace-render-frame buffer frame)) | 
				
			||||
              (overlay-put (make-overlay beg (point)) 'font-lock-face bg))) | 
				
			||||
          ;; Add line break between causes, even when collapsed. | 
				
			||||
          (cider-propertize-region '(detail 0) | 
				
			||||
            (insert "\n"))))))) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-initialize (causes) | 
				
			||||
  "Set and apply CAUSES initial visibility, filters, and cursor position." | 
				
			||||
  ;; Partially display outermost cause if it's a compiler exception (the | 
				
			||||
  ;; description reports reader location of the error). | 
				
			||||
  (nrepl-dbind-response (car causes) (class) | 
				
			||||
    (when (equal class "clojure.lang.Compiler$CompilerException") | 
				
			||||
      (cider-stacktrace-cycle-cause (length causes) 1))) | 
				
			||||
  ;; Fully display innermost cause. This also applies visibility/filters. | 
				
			||||
  (cider-stacktrace-cycle-cause 1 cider-stacktrace-detail-max) | 
				
			||||
  ;; Move point to first stacktrace frame in displayed cause.  If the error | 
				
			||||
  ;; buffer is visible in a window, ensure that window is selected while moving | 
				
			||||
  ;; point, so as to move both the buffer's and the window's point. | 
				
			||||
  (with-selected-window (or (get-buffer-window cider-error-buffer) | 
				
			||||
                            (selected-window)) | 
				
			||||
    (with-current-buffer cider-error-buffer | 
				
			||||
      (goto-char (point-min)) | 
				
			||||
      (while (cider-stacktrace-next-cause)) | 
				
			||||
      (goto-char (next-single-property-change (point) 'flags))))) | 
				
			||||
 | 
				
			||||
(defun cider-stacktrace-render (buffer causes) | 
				
			||||
  "Emit into BUFFER useful stacktrace information for the CAUSES." | 
				
			||||
  (with-current-buffer buffer | 
				
			||||
    (let ((inhibit-read-only t)) | 
				
			||||
      (erase-buffer) | 
				
			||||
      (insert "\n") | 
				
			||||
      ;; Stacktrace filters | 
				
			||||
      (cider-stacktrace-render-filters | 
				
			||||
       buffer | 
				
			||||
       `(("Clojure" clj) ("Java" java) ("REPL" repl) | 
				
			||||
         ("Tooling" tooling) ("Duplicates" dup) ("All" ,nil))) | 
				
			||||
      (insert "\n") | 
				
			||||
      ;; Stacktrace exceptions & frames | 
				
			||||
      (let ((num (length causes))) | 
				
			||||
        (dolist (cause causes) | 
				
			||||
          (let ((note (if (= num (length causes)) "Unhandled" "Caused by"))) | 
				
			||||
            (cider-stacktrace-render-cause buffer cause num note) | 
				
			||||
            (setq num (1- num)))))) | 
				
			||||
    (cider-stacktrace-initialize causes) | 
				
			||||
    (font-lock-refresh-defaults))) | 
				
			||||
 | 
				
			||||
(provide 'cider-stacktrace) | 
				
			||||
 | 
				
			||||
;;; cider-stacktrace.el ends here | 
				
			||||
@ -1,499 +0,0 @@
					 | 
				
			||||
;;; cider-test.el --- Test result viewer -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright © 2014-2015 Jeff Valk | 
				
			||||
 | 
				
			||||
;; Author: Jeff Valk <jv@jeffvalk.com> | 
				
			||||
 | 
				
			||||
;; This program 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 of the License, or | 
				
			||||
;; (at your option) any later version. | 
				
			||||
 | 
				
			||||
;; This program 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/>. | 
				
			||||
 | 
				
			||||
;; This file is not part of GNU Emacs. | 
				
			||||
 | 
				
			||||
;;; Commentary: | 
				
			||||
 | 
				
			||||
;; This provides execution, reporting, and navigation support for Clojure tests, | 
				
			||||
;; specifically using the `clojure.test' machinery. This functionality replaces | 
				
			||||
;; the venerable `clojure-test-mode' (deprecated in June 2014), and relies on | 
				
			||||
;; nREPL middleware for report running and session support. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'cider-common) | 
				
			||||
(require 'cider-client) | 
				
			||||
(require 'cider-popup) | 
				
			||||
(require 'cider-stacktrace) | 
				
			||||
(require 'cider-compat) | 
				
			||||
 | 
				
			||||
(require 'button) | 
				
			||||
(require 'easymenu) | 
				
			||||
 | 
				
			||||
;;; Variables | 
				
			||||
 | 
				
			||||
(defgroup cider-test nil | 
				
			||||
  "Presentation and navigation for test results." | 
				
			||||
  :prefix "cider-test-" | 
				
			||||
  :group 'cider) | 
				
			||||
 | 
				
			||||
(defcustom cider-test-show-report-on-success nil | 
				
			||||
  "Whether to show the `*cider-test-report*` buffer on passing tests." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'cider-test | 
				
			||||
  :package-version '(cider . "0.8.0")) | 
				
			||||
 | 
				
			||||
(defcustom cider-auto-select-test-report-buffer t | 
				
			||||
  "Determines if the test-report buffer should be auto-selected." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'cider-test | 
				
			||||
  :package-version '(cider . "0.9.0")) | 
				
			||||
 | 
				
			||||
(defvar cider-test-last-test-ns nil | 
				
			||||
  "The namespace for which tests were last run.") | 
				
			||||
 | 
				
			||||
(defvar cider-test-last-results nil | 
				
			||||
  "The results of the last run test.") | 
				
			||||
 | 
				
			||||
(defconst cider-test-report-buffer "*cider-test-report*" | 
				
			||||
  "Buffer name in which to display test reports.") | 
				
			||||
(add-to-list 'cider-ancillary-buffers cider-test-report-buffer) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
;;; Faces | 
				
			||||
;; These are as defined in clojure-test-mode. | 
				
			||||
 | 
				
			||||
(defface cider-test-failure-face | 
				
			||||
  '((((class color) (background light)) | 
				
			||||
     :background "orange red") | 
				
			||||
    (((class color) (background dark)) | 
				
			||||
     :background "firebrick")) | 
				
			||||
  "Face for failed tests." | 
				
			||||
  :group 'cider-test | 
				
			||||
  :package-version '(cider . "0.7.0")) | 
				
			||||
 | 
				
			||||
(defface cider-test-error-face | 
				
			||||
  '((((class color) (background light)) | 
				
			||||
     :background "orange1") | 
				
			||||
    (((class color) (background dark)) | 
				
			||||
     :background "orange4")) | 
				
			||||
  "Face for erring tests." | 
				
			||||
  :group 'cider-test | 
				
			||||
  :package-version '(cider . "0.7.0")) | 
				
			||||
 | 
				
			||||
(defface cider-test-success-face | 
				
			||||
  '((((class color) (background light)) | 
				
			||||
     :foreground "black" | 
				
			||||
     :background "green") | 
				
			||||
    (((class color) (background dark)) | 
				
			||||
     :foreground "black" | 
				
			||||
     :background "green")) | 
				
			||||
  "Face for passing tests." | 
				
			||||
  :group 'cider-test | 
				
			||||
  :package-version '(cider . "0.7.0")) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
;;; Report mode & key bindings | 
				
			||||
;; The primary mode of interacting with test results is the report buffer, which | 
				
			||||
;; allows navigation among tests, jumping to test definitions, expected/actual | 
				
			||||
;; diff-ing, and cause/stacktrace inspection for test errors. | 
				
			||||
 | 
				
			||||
(defvar cider-test-report-mode-map | 
				
			||||
  (let ((map (make-sparse-keymap))) | 
				
			||||
    (define-key map (kbd "C-c ,") #'cider-test-run-tests) | 
				
			||||
    (define-key map (kbd "C-c C-,") #'cider-test-rerun-tests) | 
				
			||||
    (define-key map (kbd "C-c M-,") #'cider-test-run-test) | 
				
			||||
    (define-key map (kbd "M-p") #'cider-test-previous-result) | 
				
			||||
    (define-key map (kbd "M-n") #'cider-test-next-result) | 
				
			||||
    (define-key map (kbd "M-.") #'cider-test-jump) | 
				
			||||
    (define-key map (kbd "<backtab>") #'cider-test-previous-result) | 
				
			||||
    (define-key map (kbd "TAB") #'cider-test-next-result) | 
				
			||||
    (define-key map (kbd "RET") #'cider-test-jump) | 
				
			||||
    (define-key map (kbd "t") #'cider-test-jump) | 
				
			||||
    (define-key map (kbd "d") #'cider-test-ediff) | 
				
			||||
    (define-key map (kbd "e") #'cider-test-stacktrace) | 
				
			||||
    (define-key map "q" #'cider-popup-buffer-quit-function) | 
				
			||||
    (easy-menu-define cider-test-report-mode-menu map | 
				
			||||
      "Menu for CIDER's test result mode" | 
				
			||||
      '("Test-Report" | 
				
			||||
        ["Previous result" cider-test-previous-result] | 
				
			||||
        ["Next result" cider-test-next-result] | 
				
			||||
        "--" | 
				
			||||
        ["Rerun current test" cider-test-run-test] | 
				
			||||
        ["Rerun failed/erring tests" cider-test-rerun-tests] | 
				
			||||
        ["Rerun all tests" cider-test-run-tests] | 
				
			||||
        "--" | 
				
			||||
        ["Jump to test definition" cider-test-jump] | 
				
			||||
        ["Display test error" cider-test-stacktrace] | 
				
			||||
        ["Display expected/actual diff" cider-test-ediff])) | 
				
			||||
    map)) | 
				
			||||
 | 
				
			||||
(define-derived-mode cider-test-report-mode fundamental-mode "Test Report" | 
				
			||||
  "Major mode for presenting Clojure test results. | 
				
			||||
 | 
				
			||||
\\{cider-test-report-mode-map}" | 
				
			||||
  (setq buffer-read-only t) | 
				
			||||
  (setq-local truncate-lines t) | 
				
			||||
  (setq-local electric-indent-chars nil)) | 
				
			||||
 | 
				
			||||
;; Report navigation | 
				
			||||
 | 
				
			||||
(defun cider-test-show-report () | 
				
			||||
  "Show the test report buffer, if one exists." | 
				
			||||
  (interactive) | 
				
			||||
  (if-let ((report-buffer (get-buffer cider-test-report-buffer))) | 
				
			||||
      (switch-to-buffer report-buffer) | 
				
			||||
    (message "No test report buffer"))) | 
				
			||||
 | 
				
			||||
(defun cider-test-previous-result () | 
				
			||||
  "Move point to the previous test result, if one exists." | 
				
			||||
  (interactive) | 
				
			||||
  (with-current-buffer (get-buffer cider-test-report-buffer) | 
				
			||||
    (when-let ((pos (previous-single-property-change (point) 'type))) | 
				
			||||
      (goto-char pos)))) | 
				
			||||
 | 
				
			||||
(defun cider-test-next-result () | 
				
			||||
  "Move point to the next test result, if one exists." | 
				
			||||
  (interactive) | 
				
			||||
  (with-current-buffer (get-buffer cider-test-report-buffer) | 
				
			||||
    (when-let ((pos (next-single-property-change (point) 'type))) | 
				
			||||
      (goto-char pos)))) | 
				
			||||
 | 
				
			||||
(defun cider-test-jump (&optional arg) | 
				
			||||
  "Like `cider-find-var', but uses the test at point's definition, if available." | 
				
			||||
  (interactive "P") | 
				
			||||
  (let ((ns   (get-text-property (point) 'ns)) | 
				
			||||
        (var  (get-text-property (point) 'var)) | 
				
			||||
        (line (get-text-property (point) 'line))) | 
				
			||||
    (if (and ns var) | 
				
			||||
        (cider-find-var arg (concat ns "/" var) line) | 
				
			||||
      (cider-find-var arg)))) | 
				
			||||
 | 
				
			||||
;;; Error stacktraces | 
				
			||||
 | 
				
			||||
(defvar cider-auto-select-error-buffer) | 
				
			||||
 | 
				
			||||
(defun cider-test-stacktrace-for (ns var index) | 
				
			||||
  "Display stacktrace for the erring NS VAR test with the assertion INDEX." | 
				
			||||
  (let (causes) | 
				
			||||
    (cider-nrepl-send-request | 
				
			||||
     (append | 
				
			||||
      (list "op" "test-stacktrace" "session" (cider-current-session) | 
				
			||||
            "ns" ns "var" var "index" index) | 
				
			||||
      (when cider-stacktrace-print-length | 
				
			||||
        (list "print-length" cider-stacktrace-print-length)) | 
				
			||||
      (when cider-stacktrace-print-level | 
				
			||||
        (list "print-level" cider-stacktrace-print-level))) | 
				
			||||
     (lambda (response) | 
				
			||||
       (nrepl-dbind-response response (class status) | 
				
			||||
         (cond (class  (setq causes (cons response causes))) | 
				
			||||
               (status (when causes | 
				
			||||
                         (cider-stacktrace-render | 
				
			||||
                          (cider-popup-buffer cider-error-buffer | 
				
			||||
                                              cider-auto-select-error-buffer) | 
				
			||||
                          (reverse causes)))))))))) | 
				
			||||
 | 
				
			||||
(defun cider-test-stacktrace () | 
				
			||||
  "Display stacktrace for the erring test at point." | 
				
			||||
  (interactive) | 
				
			||||
  (let ((ns    (get-text-property (point) 'ns)) | 
				
			||||
        (var   (get-text-property (point) 'var)) | 
				
			||||
        (index (get-text-property (point) 'index)) | 
				
			||||
        (err   (get-text-property (point) 'error))) | 
				
			||||
    (if (and err ns var index) | 
				
			||||
        (cider-test-stacktrace-for ns var index) | 
				
			||||
      (message "No test error at point")))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
;;; Expected vs actual diffing | 
				
			||||
 | 
				
			||||
(defvar cider-test-ediff-buffers nil | 
				
			||||
  "The expected/actual buffers used to display diff.") | 
				
			||||
 | 
				
			||||
(defun cider-test-ediff () | 
				
			||||
  "Show diff of the expected vs actual value for the test at point. | 
				
			||||
With the actual value, the outermost '(not ...)' s-expression is removed." | 
				
			||||
  (interactive) | 
				
			||||
  (let ((expected (get-text-property (point) 'expected)) | 
				
			||||
        (actual   (get-text-property (point) 'actual))) | 
				
			||||
    (if (and expected actual) | 
				
			||||
        (let ((expected-buffer (generate-new-buffer " *expected*")) | 
				
			||||
              (actual-buffer   (generate-new-buffer " *actual*"))) | 
				
			||||
          (with-current-buffer expected-buffer | 
				
			||||
            (insert expected) | 
				
			||||
            (clojure-mode)) | 
				
			||||
          (with-current-buffer actual-buffer | 
				
			||||
            (insert actual) | 
				
			||||
            (goto-char (point-min)) | 
				
			||||
            (forward-char) | 
				
			||||
            (forward-sexp) | 
				
			||||
            (forward-whitespace 1) | 
				
			||||
            (let ((beg (point))) | 
				
			||||
              (forward-sexp) | 
				
			||||
              (let ((actual* (buffer-substring beg (point)))) | 
				
			||||
                (erase-buffer) | 
				
			||||
                (insert actual*))) | 
				
			||||
            (clojure-mode)) | 
				
			||||
          (apply 'ediff-buffers | 
				
			||||
                 (setq cider-test-ediff-buffers | 
				
			||||
                       (list (buffer-name expected-buffer) | 
				
			||||
                             (buffer-name actual-buffer))))) | 
				
			||||
      (message "No test failure at point")))) | 
				
			||||
 | 
				
			||||
(defun cider-test-ediff-cleanup () | 
				
			||||
  "Cleanup expected/actual buffers used for diff." | 
				
			||||
  (interactive) | 
				
			||||
  (mapc (lambda (b) (when (get-buffer b) (kill-buffer b))) | 
				
			||||
        cider-test-ediff-buffers)) | 
				
			||||
 | 
				
			||||
(add-hook 'ediff-cleanup-hook #'cider-test-ediff-cleanup) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
;;; Report rendering | 
				
			||||
 | 
				
			||||
(defun cider-test-type-face (type) | 
				
			||||
  "Return the font lock face for the test result TYPE." | 
				
			||||
  (pcase type | 
				
			||||
    ("pass"  'cider-test-success-face) | 
				
			||||
    ("fail"  'cider-test-failure-face) | 
				
			||||
    ("error" 'cider-test-error-face) | 
				
			||||
    (_       'default))) | 
				
			||||
 | 
				
			||||
(defun cider-test-render-summary (buffer summary) | 
				
			||||
  "Emit into BUFFER the report SUMMARY statistics." | 
				
			||||
  (with-current-buffer buffer | 
				
			||||
    (nrepl-dbind-response summary (var test pass fail error) | 
				
			||||
      (insert (format "Ran %d tests, in %d test functions\n" test var)) | 
				
			||||
      (unless (zerop fail) | 
				
			||||
        (cider-insert (format "%d failures" fail) 'cider-test-failure-face t)) | 
				
			||||
      (unless (zerop error) | 
				
			||||
        (cider-insert (format "%d errors" error) 'cider-test-error-face t)) | 
				
			||||
      (when (zerop (+ fail error)) | 
				
			||||
        (cider-insert (format "%d passed" pass) 'cider-test-success-face t)) | 
				
			||||
      (insert "\n\n")))) | 
				
			||||
 | 
				
			||||
(defun cider-test-render-assertion (buffer test) | 
				
			||||
  "Emit into BUFFER report detail for the TEST assertion." | 
				
			||||
  (with-current-buffer buffer | 
				
			||||
    (nrepl-dbind-response test (var context type message expected actual error) | 
				
			||||
      (cider-propertize-region (cider-intern-keys (cdr test)) | 
				
			||||
        (cider-insert (capitalize type) (cider-test-type-face type) nil " in ") | 
				
			||||
        (cider-insert var 'font-lock-function-name-face t) | 
				
			||||
        (when context  (cider-insert context 'font-lock-doc-face t)) | 
				
			||||
        (when message  (cider-insert message 'font-lock-doc-string-face t)) | 
				
			||||
        (when expected (cider-insert "expected: " 'font-lock-comment-face nil | 
				
			||||
                                     (cider-font-lock-as-clojure expected))) | 
				
			||||
        (when actual   (cider-insert "  actual: " 'font-lock-comment-face) | 
				
			||||
              (if error | 
				
			||||
                  (progn (insert-text-button | 
				
			||||
                          error | 
				
			||||
                          'follow-link t | 
				
			||||
                          'action '(lambda (_button) (cider-test-stacktrace)) | 
				
			||||
                          'help-echo "View causes and stacktrace") | 
				
			||||
                         (insert "\n")) | 
				
			||||
                (insert (cider-font-lock-as-clojure actual))))) | 
				
			||||
      (insert "\n")))) | 
				
			||||
 | 
				
			||||
(defun cider-test-render-report (buffer ns summary results) | 
				
			||||
  "Emit into BUFFER the report for the NS, SUMMARY, and test RESULTS." | 
				
			||||
  (with-current-buffer buffer | 
				
			||||
    (let ((inhibit-read-only t)) | 
				
			||||
      (cider-test-report-mode) | 
				
			||||
      (cider-insert "Test Summary" 'bold t) | 
				
			||||
      (cider-insert ns 'font-lock-function-name-face t "\n") | 
				
			||||
      (cider-test-render-summary buffer summary) | 
				
			||||
      (nrepl-dbind-response summary (fail error) | 
				
			||||
        (unless (zerop (+ fail error)) | 
				
			||||
          (cider-insert "Results" 'bold t "\n") | 
				
			||||
          (nrepl-dict-map | 
				
			||||
           (lambda (_var tests) | 
				
			||||
             (dolist (test tests) | 
				
			||||
               (nrepl-dbind-response test (type) | 
				
			||||
                 (unless (equal "pass" type) | 
				
			||||
                   (cider-test-render-assertion buffer test))))) | 
				
			||||
           results))) | 
				
			||||
      (goto-char (point-min)) | 
				
			||||
      (current-buffer)))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
;;; Summary echo | 
				
			||||
 | 
				
			||||
(defun cider-test-echo-summary (summary) | 
				
			||||
  "Echo SUMMARY statistics for a test run." | 
				
			||||
  (nrepl-dbind-response summary (test fail error) | 
				
			||||
    (message | 
				
			||||
     (propertize | 
				
			||||
      (format "Ran %s tests. %s failures, %s errors." test fail error) | 
				
			||||
      'face (cond ((not (zerop error)) 'cider-test-error-face) | 
				
			||||
                  ((not (zerop fail))  'cider-test-failure-face) | 
				
			||||
                  (t                   'cider-test-success-face)))))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
;;; Test definition highlighting | 
				
			||||
;; On receipt of test results, failing/erring test definitions are highlighted. | 
				
			||||
;; Highlights are cleared on the next report run, and may be cleared manually | 
				
			||||
;; by the user. | 
				
			||||
 | 
				
			||||
;; NOTE If keybindings specific to test sources are desired, it would be | 
				
			||||
;; straightforward to turn this into a `cider-test-mode' minor mode, which we | 
				
			||||
;; enable on test sources, much like the legacy `clojure-test-mode'. At present, | 
				
			||||
;; though, there doesn't seem to be much value in this, since the report buffer | 
				
			||||
;; provides the primary means of interacting with test results. | 
				
			||||
 | 
				
			||||
(defun cider-test-highlight-problem (buffer test) | 
				
			||||
  "Highlight the BUFFER test definition for the non-passing TEST." | 
				
			||||
  (with-current-buffer buffer | 
				
			||||
    (nrepl-dbind-response test (type file line message expected actual) | 
				
			||||
      ;; we have to watch out for vars without proper location metadata | 
				
			||||
      ;; right now everything evaluated interactively lacks this data | 
				
			||||
      ;; TODO: Figure out what to do when the metadata is missing | 
				
			||||
      (when (and file line (not (cider--tooling-file-p file))) | 
				
			||||
        (save-excursion | 
				
			||||
          (goto-char (point-min)) | 
				
			||||
          (forward-line (1- line)) | 
				
			||||
          (forward-whitespace 1) | 
				
			||||
          (forward-char) | 
				
			||||
          (let ((beg (point))) | 
				
			||||
            (forward-sexp) | 
				
			||||
            (let ((overlay (make-overlay beg (point)))) | 
				
			||||
              (overlay-put overlay 'font-lock-face (cider-test-type-face type)) | 
				
			||||
              (overlay-put overlay 'type type) | 
				
			||||
              (overlay-put overlay 'help-echo message) | 
				
			||||
              (overlay-put overlay 'message message) | 
				
			||||
              (overlay-put overlay 'expected expected) | 
				
			||||
              (overlay-put overlay 'actual actual)))))))) | 
				
			||||
 | 
				
			||||
(defun cider-find-var-file (var) | 
				
			||||
  "Return the buffer visiting the file in which VAR is defined, or nil if | 
				
			||||
not found." | 
				
			||||
  (cider-ensure-op-supported "info") | 
				
			||||
  (when-let ((info (cider-var-info var)) | 
				
			||||
             (file (nrepl-dict-get info "file"))) | 
				
			||||
    (cider-find-file file))) | 
				
			||||
 | 
				
			||||
(defun cider-test-highlight-problems (ns results) | 
				
			||||
  "Highlight all non-passing tests in the NS test RESULTS." | 
				
			||||
  (nrepl-dict-map | 
				
			||||
   (lambda (var tests) | 
				
			||||
     (when-let ((buffer (cider-find-var-file (concat ns "/" var)))) | 
				
			||||
       (dolist (test tests) | 
				
			||||
         (nrepl-dbind-response test (type) | 
				
			||||
           (unless (equal "pass" type) | 
				
			||||
             (cider-test-highlight-problem buffer test)))))) | 
				
			||||
   results)) | 
				
			||||
 | 
				
			||||
(defun cider-test-clear-highlights () | 
				
			||||
  "Clear highlighting of non-passing tests from the last test run." | 
				
			||||
  (interactive) | 
				
			||||
  (when-let ((ns cider-test-last-test-ns)) | 
				
			||||
    (dolist (var (nrepl-dict-keys cider-test-last-results)) | 
				
			||||
      (when-let ((buffer (cider-find-var-file (concat ns "/" var)))) | 
				
			||||
        (with-current-buffer buffer | 
				
			||||
          (remove-overlays)))))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
;;; Test namespaces | 
				
			||||
;; Test namespace inference exists to enable DWIM test running functions: the | 
				
			||||
;; same "run-tests" function should be able to be used in a source file, and in | 
				
			||||
;; its corresponding test namespace. To provide this, we need to map the | 
				
			||||
;; relationship between those namespaces. | 
				
			||||
 | 
				
			||||
(defcustom cider-test-infer-test-ns 'cider-test-default-test-ns-fn | 
				
			||||
  "Function to infer the test namespace for NS. | 
				
			||||
The default implementation uses the simple Leiningen convention of appending | 
				
			||||
'-test' to the namespace name." | 
				
			||||
  :type 'symbol | 
				
			||||
  :group 'cider-test | 
				
			||||
  :package-version '(cider . "0.7.0")) | 
				
			||||
 | 
				
			||||
(defun cider-test-default-test-ns-fn (ns) | 
				
			||||
  "For a NS, return the test namespace, which may be the argument itself. | 
				
			||||
This uses the Leiningen convention of appending '-test' to the namespace name." | 
				
			||||
  (when ns | 
				
			||||
    (let ((suffix "-test")) | 
				
			||||
      ;; string-suffix-p is only available in Emacs 24.4+ | 
				
			||||
      (if (string-match-p (rx-to-string `(: ,suffix eos) t) ns) | 
				
			||||
          ns | 
				
			||||
        (concat ns suffix))))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
;;; Test execution | 
				
			||||
 | 
				
			||||
(declare-function cider-emit-interactive-eval-output "cider-interaction") | 
				
			||||
(declare-function cider-emit-interactive-eval-err-output "cider-interaction") | 
				
			||||
 | 
				
			||||
(defun cider-test-execute (ns &optional retest tests) | 
				
			||||
  "Run tests for NS; optionally RETEST failures or run only specified TESTS. | 
				
			||||
Upon test completion, results are echoed and a test report is optionally | 
				
			||||
displayed. When test failures/errors occur, their sources are highlighted." | 
				
			||||
  (cider-test-clear-highlights) | 
				
			||||
  (message "Testing...") | 
				
			||||
  (cider-nrepl-send-request | 
				
			||||
   (list "ns" ns "op" (if retest "retest" "test") | 
				
			||||
         "tests" tests "session" (cider-current-session)) | 
				
			||||
   (lambda (response) | 
				
			||||
     (nrepl-dbind-response response (summary results status out err) | 
				
			||||
       (cond ((member "namespace-not-found" status) | 
				
			||||
              (message "No tests namespace: %s" ns)) | 
				
			||||
             (out (cider-emit-interactive-eval-output out)) | 
				
			||||
             (err (cider-emit-interactive-eval-err-output err)) | 
				
			||||
             (results | 
				
			||||
              (nrepl-dbind-response summary (error fail) | 
				
			||||
                (setq cider-test-last-test-ns ns) | 
				
			||||
                (setq cider-test-last-results results) | 
				
			||||
                (cider-test-highlight-problems ns results) | 
				
			||||
                (cider-test-echo-summary summary) | 
				
			||||
                (when (or (not (zerop (+ error fail))) | 
				
			||||
                          cider-test-show-report-on-success) | 
				
			||||
                  (cider-test-render-report | 
				
			||||
                   (cider-popup-buffer cider-test-report-buffer | 
				
			||||
                                       cider-auto-select-test-report-buffer) | 
				
			||||
                   ns summary results))))))))) | 
				
			||||
 | 
				
			||||
(defun cider-test-rerun-tests () | 
				
			||||
  "Rerun failed and erring tests from the last tested namespace." | 
				
			||||
  (interactive) | 
				
			||||
  (if-let ((ns cider-test-last-test-ns)) | 
				
			||||
      (cider-test-execute ns t) | 
				
			||||
    (message "No namespace to retest"))) | 
				
			||||
 | 
				
			||||
(defun cider-test-run-tests (suppress-inference) | 
				
			||||
  "Run all tests for the current Clojure source or test report context. | 
				
			||||
 | 
				
			||||
With a prefix arg SUPPRESS-INFERENCE it will try to run the tests in the | 
				
			||||
current ns." | 
				
			||||
  (interactive "P") | 
				
			||||
  (if-let ((ns (if suppress-inference | 
				
			||||
                   (clojure-find-ns) | 
				
			||||
                 (or (funcall cider-test-infer-test-ns (clojure-find-ns)) | 
				
			||||
                     (when (eq major-mode 'cider-test-report-mode) | 
				
			||||
                       cider-test-last-test-ns))))) | 
				
			||||
      (cider-test-execute ns nil) | 
				
			||||
    (message "No namespace to test in current context"))) | 
				
			||||
 | 
				
			||||
(defun cider-test-run-test () | 
				
			||||
  "Run the test at point. | 
				
			||||
The test ns/var exist as text properties on report items and on highlighted | 
				
			||||
failed/erred test definitions. When not found, a test definition at point | 
				
			||||
is searched." | 
				
			||||
  (interactive) | 
				
			||||
  (let ((ns  (get-text-property (point) 'ns)) | 
				
			||||
        (var (get-text-property (point) 'var))) | 
				
			||||
    (if (and ns var) | 
				
			||||
        (cider-test-execute ns nil (list var)) | 
				
			||||
      (let ((ns  (clojure-find-ns)) | 
				
			||||
            (def (clojure-find-def))) | 
				
			||||
        (if (and ns (member (car def) '("deftest" "defspec"))) | 
				
			||||
            (cider-test-execute ns nil (cdr def)) | 
				
			||||
          (message "No test at point")))))) | 
				
			||||
 | 
				
			||||
(provide 'cider-test) | 
				
			||||
 | 
				
			||||
;;; cider-test.el ends here | 
				
			||||
@ -1,383 +0,0 @@
					 | 
				
			||||
;;; cider-util.el --- Common utility functions that don't belong anywhere else -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright © 2012-2015 Tim King, Phil Hagelberg | 
				
			||||
;; Copyright © 2013-2015 Bozhidar Batsov, Hugo Duncan, Steve Purcell | 
				
			||||
;; | 
				
			||||
;; Author: Tim King <kingtim@gmail.com> | 
				
			||||
;;         Phil Hagelberg <technomancy@gmail.com> | 
				
			||||
;;         Bozhidar Batsov <bozhidar@batsov.com> | 
				
			||||
;;         Hugo Duncan <hugo@hugoduncan.org> | 
				
			||||
;;         Steve Purcell <steve@sanityinc.com> | 
				
			||||
 | 
				
			||||
;; This program 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 of the License, or | 
				
			||||
;; (at your option) any later version. | 
				
			||||
 | 
				
			||||
;; This program 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/>. | 
				
			||||
 | 
				
			||||
;; This file is not part of GNU Emacs. | 
				
			||||
 | 
				
			||||
;;; Commentary: | 
				
			||||
 | 
				
			||||
;; Common utility functions that don't belong anywhere else. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'seq) | 
				
			||||
(require 'cl-lib) | 
				
			||||
(require 'clojure-mode) | 
				
			||||
 | 
				
			||||
(defalias 'cider-pop-back 'pop-tag-mark) | 
				
			||||
(define-obsolete-function-alias 'cider-jump-back 'cider-pop-back "0.10.0") | 
				
			||||
 | 
				
			||||
(defcustom cider-font-lock-max-length 10000 | 
				
			||||
  "The max length of strings to fontify in `cider-font-lock-as'. | 
				
			||||
 | 
				
			||||
Setting this to nil removes the fontification restriction." | 
				
			||||
  :group 'cider | 
				
			||||
  :type 'boolean | 
				
			||||
  :package-version '(cider . "0.10.0")) | 
				
			||||
 | 
				
			||||
(defun cider-util--hash-keys (hashtable) | 
				
			||||
  "Return a list of keys in HASHTABLE." | 
				
			||||
  (let ((keys '())) | 
				
			||||
    (maphash (lambda (k _v) (setq keys (cons k keys))) hashtable) | 
				
			||||
    keys)) | 
				
			||||
 | 
				
			||||
(defun cider-util--clojure-buffers () | 
				
			||||
  "Return a list of all existing `clojure-mode' buffers." | 
				
			||||
  (seq-filter | 
				
			||||
   (lambda (buffer) (with-current-buffer buffer (derived-mode-p 'clojure-mode))) | 
				
			||||
   (buffer-list))) | 
				
			||||
 | 
				
			||||
(defun cider-current-dir () | 
				
			||||
  "Return the directory of the current buffer." | 
				
			||||
  (if buffer-file-name | 
				
			||||
      (file-name-directory buffer-file-name) | 
				
			||||
    default-directory)) | 
				
			||||
 | 
				
			||||
(defun cider-in-string-p () | 
				
			||||
  "Return true if point is in a string." | 
				
			||||
  (let ((beg (save-excursion (beginning-of-defun) (point)))) | 
				
			||||
    (nth 3 (parse-partial-sexp beg (point))))) | 
				
			||||
 | 
				
			||||
(defun cider-in-comment-p () | 
				
			||||
  "Return true if point is in a comment." | 
				
			||||
  (let ((beg (save-excursion (beginning-of-defun) (point)))) | 
				
			||||
    (nth 4 (parse-partial-sexp beg (point))))) | 
				
			||||
 | 
				
			||||
(defun cider--tooling-file-p (file-name) | 
				
			||||
  "Return t if FILE-NAME is not a 'real' source file. | 
				
			||||
Currently, only check if the relative file name starts with 'form-init' | 
				
			||||
which nREPL uses for temporary evaluation file names." | 
				
			||||
  (let ((fname (file-name-nondirectory file-name))) | 
				
			||||
    (string-match-p "^form-init" fname))) | 
				
			||||
 | 
				
			||||
;;; Thing at point | 
				
			||||
(defun cider-defun-at-point () | 
				
			||||
  "Return the text of the top-level sexp at point." | 
				
			||||
  (apply #'buffer-substring-no-properties | 
				
			||||
         (cider--region-for-defun-at-point))) | 
				
			||||
 | 
				
			||||
(defun cider--region-for-defun-at-point () | 
				
			||||
  "Return the start and end position of defun at point." | 
				
			||||
  (save-excursion | 
				
			||||
    (save-match-data | 
				
			||||
      (end-of-defun) | 
				
			||||
      (let ((end (point))) | 
				
			||||
        (beginning-of-defun) | 
				
			||||
        (list (point) end))))) | 
				
			||||
 | 
				
			||||
(defun cider-defun-at-point-start-pos () | 
				
			||||
  "Return the starting position of the current defun." | 
				
			||||
  (car (cider--region-for-defun-at-point))) | 
				
			||||
 | 
				
			||||
(defun cider-ns-form () | 
				
			||||
  "Retrieve the ns form." | 
				
			||||
  (when (clojure-find-ns) | 
				
			||||
    (save-excursion | 
				
			||||
      (goto-char (match-beginning 0)) | 
				
			||||
      (cider-defun-at-point)))) | 
				
			||||
 | 
				
			||||
(defun cider-bounds-of-sexp-at-point () | 
				
			||||
  "Return the bounds sexp at point as a pair (or nil)." | 
				
			||||
  (or (and (equal (char-after) ?\() | 
				
			||||
           (member (char-before) '(?\' ?\, ?\@)) | 
				
			||||
           ;; hide stuff before ( to avoid quirks with '( etc. | 
				
			||||
           (save-restriction | 
				
			||||
             (narrow-to-region (point) (point-max)) | 
				
			||||
             (bounds-of-thing-at-point 'sexp))) | 
				
			||||
      (bounds-of-thing-at-point 'sexp))) | 
				
			||||
 | 
				
			||||
(defun cider-map-indexed (f list) | 
				
			||||
  "Return a list of (F index item) for each item in LIST." | 
				
			||||
  (let ((i 0) | 
				
			||||
        (out)) | 
				
			||||
    (dolist (it list (nreverse out)) | 
				
			||||
      (push (funcall f i it) out) | 
				
			||||
      (setq i (1+ i))))) | 
				
			||||
 | 
				
			||||
(defun cider-symbol-at-point () | 
				
			||||
  "Return the name of the symbol at point, otherwise nil." | 
				
			||||
  (let ((str (or (thing-at-point 'symbol) ""))) | 
				
			||||
    (if (text-property-any 0 (length str) 'field 'cider-repl-prompt str) | 
				
			||||
        "" | 
				
			||||
      str))) | 
				
			||||
 | 
				
			||||
(defun cider-sexp-at-point () | 
				
			||||
  "Return the sexp at point as a string, otherwise nil." | 
				
			||||
  (let ((bounds (cider-bounds-of-sexp-at-point))) | 
				
			||||
    (if bounds | 
				
			||||
        (buffer-substring-no-properties (car bounds) | 
				
			||||
                                        (cdr bounds))))) | 
				
			||||
 | 
				
			||||
(defun cider-sexp-at-point-with-bounds () | 
				
			||||
  "Return a list containing the sexp at point and its bounds." | 
				
			||||
  (let ((bounds (cider-bounds-of-sexp-at-point))) | 
				
			||||
    (if bounds | 
				
			||||
        (let ((start (car bounds)) | 
				
			||||
              (end (cdr bounds))) | 
				
			||||
          (list (buffer-substring-no-properties start end) | 
				
			||||
                (cons (set-marker (make-marker) start) | 
				
			||||
                      (set-marker (make-marker) end))))))) | 
				
			||||
 | 
				
			||||
(defun cider-last-sexp (&optional bounds) | 
				
			||||
  "Return the sexp preceding the point. | 
				
			||||
If BOUNDS is non-nil, return a list of its starting and ending position | 
				
			||||
instead." | 
				
			||||
  (apply (if bounds #'list #'buffer-substring-no-properties) | 
				
			||||
         (save-excursion | 
				
			||||
           (clojure-backward-logical-sexp 1) | 
				
			||||
           (list (point) | 
				
			||||
                 (progn (clojure-forward-logical-sexp 1) | 
				
			||||
                        (point)))))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
;;; Text properties | 
				
			||||
 | 
				
			||||
(defun cider-maybe-intern (name) | 
				
			||||
  "If NAME is a symbol, return it; otherwise, intern it." | 
				
			||||
  (if (symbolp name) name (intern name))) | 
				
			||||
 | 
				
			||||
(defun cider-intern-keys (props) | 
				
			||||
  "Copy plist-style PROPS with any non-symbol keys replaced with symbols." | 
				
			||||
  (cider-map-indexed (lambda (i x) (if (cl-oddp i) x (cider-maybe-intern x))) props)) | 
				
			||||
 | 
				
			||||
(defmacro cider-propertize-region (props &rest body) | 
				
			||||
  "Execute BODY and add PROPS to all the text it inserts. | 
				
			||||
More precisely, PROPS are added to the region between the point's | 
				
			||||
positions before and after executing BODY." | 
				
			||||
  (declare (indent 1)) | 
				
			||||
  (let ((start (cl-gensym))) | 
				
			||||
    `(let ((,start (point))) | 
				
			||||
       (prog1 (progn ,@body) | 
				
			||||
         (add-text-properties ,start (point) ,props))))) | 
				
			||||
 | 
				
			||||
(put 'cider-propertize-region 'lisp-indent-function 1) | 
				
			||||
 | 
				
			||||
(defun cider-property-bounds (prop) | 
				
			||||
  "Return the the positions of the previous and next change to PROP. | 
				
			||||
PROP is the name of a text property." | 
				
			||||
  (let ((end (next-single-char-property-change (point) prop))) | 
				
			||||
    (list (previous-single-char-property-change end prop) end))) | 
				
			||||
 | 
				
			||||
(defun cider-insert (text &optional face break more-text) | 
				
			||||
  "Insert TEXT with FACE, optionally followed by a line BREAK and MORE-TEXT." | 
				
			||||
  (insert (if face (propertize text 'font-lock-face face) text)) | 
				
			||||
  (when more-text (insert more-text)) | 
				
			||||
  (when break (insert "\n"))) | 
				
			||||
 | 
				
			||||
;;; Font lock | 
				
			||||
 | 
				
			||||
(defun cider--font-lock-ensure () | 
				
			||||
  "Call `font-lock-ensure' or `font-lock-fontify-buffer', as appropriate." | 
				
			||||
  (if (fboundp 'font-lock-ensure) | 
				
			||||
      (font-lock-ensure) | 
				
			||||
    (with-no-warnings | 
				
			||||
      (font-lock-fontify-buffer)))) | 
				
			||||
 | 
				
			||||
(defvar cider--mode-buffers nil | 
				
			||||
  "A list of buffers for different major modes.") | 
				
			||||
 | 
				
			||||
(defun cider--make-buffer-for-mode (mode) | 
				
			||||
  "Return a temp buffer using major-mode MODE. | 
				
			||||
This buffer is not designed to display anything to the user. For that, use | 
				
			||||
`cider-make-popup-buffer' instead." | 
				
			||||
  (or (cdr (assq mode cider--mode-buffers)) | 
				
			||||
      (let ((b (generate-new-buffer (format " *cider-temp %s*" mode)))) | 
				
			||||
        (push (cons mode b) cider--mode-buffers) | 
				
			||||
        (with-current-buffer b | 
				
			||||
          ;; suppress major mode hooks as we care only about their font-locking | 
				
			||||
          ;; otherwise modes like whitespace-mode and paredit might interfere | 
				
			||||
          (setq-local delay-mode-hooks t) | 
				
			||||
          (setq delayed-mode-hooks nil) | 
				
			||||
          (funcall mode)) | 
				
			||||
        b))) | 
				
			||||
 | 
				
			||||
(defun cider-font-lock-as (mode string) | 
				
			||||
  "Use MODE to font-lock the STRING." | 
				
			||||
  (if (or (null cider-font-lock-max-length) | 
				
			||||
          (< (length string) cider-font-lock-max-length)) | 
				
			||||
      (with-current-buffer (cider--make-buffer-for-mode mode) | 
				
			||||
        (erase-buffer) | 
				
			||||
        (insert string) | 
				
			||||
        (font-lock-fontify-region (point-min) (point-max)) | 
				
			||||
        (buffer-string)) | 
				
			||||
    string)) | 
				
			||||
 | 
				
			||||
(defun cider-font-lock-region-as (mode beg end &optional buffer) | 
				
			||||
  "Use MODE to font-lock text between BEG and END. | 
				
			||||
 | 
				
			||||
Unless you specify a BUFFER it will default to the current one." | 
				
			||||
  (with-current-buffer (or buffer (current-buffer)) | 
				
			||||
    (let ((text (buffer-substring beg end))) | 
				
			||||
      (delete-region beg end) | 
				
			||||
      (goto-char beg) | 
				
			||||
      (insert (cider-font-lock-as mode text))))) | 
				
			||||
 | 
				
			||||
(defun cider-font-lock-as-clojure (string) | 
				
			||||
  "Font-lock STRING as Clojure code." | 
				
			||||
  (cider-font-lock-as 'clojure-mode string)) | 
				
			||||
 | 
				
			||||
;;; Colors | 
				
			||||
 | 
				
			||||
(defun cider-scale-color (color scale) | 
				
			||||
  "For a COLOR hex string or name, adjust intensity of RGB components by SCALE." | 
				
			||||
  (let* ((rgb (color-values color)) | 
				
			||||
         (scaled-rgb (mapcar (lambda (n) | 
				
			||||
                               (format "%04x" (round (+ n (* scale 65535))))) | 
				
			||||
                             rgb))) | 
				
			||||
    (apply #'concat "#" scaled-rgb))) | 
				
			||||
 | 
				
			||||
(defun cider-scale-background-color () | 
				
			||||
  "Scale the current background color to get a slighted muted version." | 
				
			||||
  (let ((color (frame-parameter nil 'background-color)) | 
				
			||||
        (dark (eq (frame-parameter nil 'background-mode) 'dark))) | 
				
			||||
    (cider-scale-color color (if dark 0.05 -0.05)))) | 
				
			||||
 | 
				
			||||
(autoload 'pkg-info-version-info "pkg-info.el") | 
				
			||||
 | 
				
			||||
(defvar cider-version) | 
				
			||||
 | 
				
			||||
(defun cider--version () | 
				
			||||
  "Retrieve CIDER's version." | 
				
			||||
  (condition-case nil | 
				
			||||
      (pkg-info-version-info 'cider) | 
				
			||||
    (error cider-version))) | 
				
			||||
 | 
				
			||||
;;; Strings | 
				
			||||
 | 
				
			||||
(defun cider-string-join (strings &optional separator) | 
				
			||||
  "Join all STRINGS using SEPARATOR." | 
				
			||||
  (mapconcat #'identity strings separator)) | 
				
			||||
 | 
				
			||||
(defun cider-join-into-alist (candidates &optional separator) | 
				
			||||
  "Make an alist from CANDIDATES. | 
				
			||||
The keys are the elements joined with SEPARATOR and values are the original | 
				
			||||
elements.  Useful for `completing-read' when candidates are complex | 
				
			||||
objects." | 
				
			||||
  (mapcar (lambda (el) | 
				
			||||
            (if (listp el) | 
				
			||||
                (cons (cider-string-join el (or separator ":")) el) | 
				
			||||
              (cons el el))) | 
				
			||||
          candidates)) | 
				
			||||
 | 
				
			||||
(defun cider-namespace-qualified-p (sym) | 
				
			||||
  "Return t if SYM is namespace-qualified." | 
				
			||||
  (string-match-p "[^/]+/" sym)) | 
				
			||||
 | 
				
			||||
(defun cider--readme-button (label section-id) | 
				
			||||
  "Return a button string that links to the online readme. | 
				
			||||
LABEL is the displayed string, and SECTION-ID is where it points | 
				
			||||
to." | 
				
			||||
  (with-temp-buffer | 
				
			||||
    (insert-text-button | 
				
			||||
     label | 
				
			||||
     'follow-link t | 
				
			||||
     'action (lambda (&rest _) (interactive) | 
				
			||||
               (browse-url (concat "https://github.com/clojure-emacs/cider#" | 
				
			||||
                                   section-id)))) | 
				
			||||
    (buffer-string))) | 
				
			||||
 | 
				
			||||
(defun cider--project-name (dir) | 
				
			||||
  "Extracts a project name from DIR, possibly nil. | 
				
			||||
The project name is the final component of DIR if not nil." | 
				
			||||
  (when dir | 
				
			||||
    (file-name-nondirectory (directory-file-name dir)))) | 
				
			||||
 | 
				
			||||
;;; Vectors | 
				
			||||
(defun cider--deep-vector-to-list (x) | 
				
			||||
  "Convert vectors in X to lists. | 
				
			||||
If X is a sequence, return a list of `cider--deep-vector-to-list' applied to | 
				
			||||
each of its elements. | 
				
			||||
Any other value is just returned." | 
				
			||||
  (if (sequencep x) | 
				
			||||
      (mapcar #'cider--deep-vector-to-list x) | 
				
			||||
    x)) | 
				
			||||
 | 
				
			||||
;;; Words of inspiration | 
				
			||||
(defun cider-user-first-name () | 
				
			||||
  "Find the current user's first name." | 
				
			||||
  (let ((name (if (string= (user-full-name) "") | 
				
			||||
                  (user-login-name) | 
				
			||||
                (user-full-name)))) | 
				
			||||
    (string-match "^[^ ]*" name) | 
				
			||||
    (capitalize (match-string 0 name)))) | 
				
			||||
 | 
				
			||||
(defvar cider-words-of-inspiration | 
				
			||||
  `("The best way to predict the future is to invent it. -Alan Kay" | 
				
			||||
    "A point of view is worth 80 IQ points. -Alan Kay" | 
				
			||||
    "Lisp isn't a language, it's a building material. -Alan Kay" | 
				
			||||
    "Simple things should be simple, complex things should be possible. -Alan Kay" | 
				
			||||
    "Everything should be as simple as possible, but not simpler. -Albert Einstein" | 
				
			||||
    "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. -Bill Gates" | 
				
			||||
    "Controlling complexity is the essence of computer programming. -Brian Kernighan" | 
				
			||||
    "The unavoidable price of reliability is simplicity. -C.A.R. Hoare" | 
				
			||||
    "You're bound to be unhappy if you optimize everything. -Donald Knuth" | 
				
			||||
    "Simplicity is prerequisite for reliability. -Edsger W. Dijkstra" | 
				
			||||
    "Elegance is not a dispensable luxury but a quality that decides between success and failure. -Edsger W. Dijkstra" | 
				
			||||
    "Deleted code is debugged code. -Jeff Sickel" | 
				
			||||
    "The key to performance is elegance, not battalions of special cases. -Jon Bentley and Doug McIlroy" | 
				
			||||
    "First, solve the problem. Then, write the code. -John Johnson" | 
				
			||||
    "Simplicity is the ultimate sophistication. -Leonardo da Vinci" | 
				
			||||
    "Programming is not about typing... it's about thinking. -Rich Hickey" | 
				
			||||
    "Design is about pulling things apart. -Rich Hickey" | 
				
			||||
    "Programmers know the benefits of everything and the tradeoffs of nothing. -Rich Hickey" | 
				
			||||
    "Code never lies, comments sometimes do. -Ron Jeffries" | 
				
			||||
    "The true delight is in the finding out rather than in the knowing.  -Isaac Asimov" | 
				
			||||
    "If paredit is not for you, then you need to become the sort of person that paredit is for. -Phil Hagelberg" | 
				
			||||
    "Express Yourself. -Madonna" | 
				
			||||
    "Take this REPL, fellow hacker, and may it serve you well." | 
				
			||||
    "Let the hacking commence!" | 
				
			||||
    "Hacks and glory await!" | 
				
			||||
    "Hack and be merry!" | 
				
			||||
    "Your hacking starts... NOW!" | 
				
			||||
    "May the Source be with you!" | 
				
			||||
    "May the Source shine upon thy REPL!" | 
				
			||||
    "Code long and prosper!" | 
				
			||||
    "Happy hacking!" | 
				
			||||
    "nREPL server is up, CIDER REPL is online!" | 
				
			||||
    "CIDER REPL operational!" | 
				
			||||
    "Your imagination is the only limit to what you can do with this REPL!" | 
				
			||||
    "This REPL is yours to command!" | 
				
			||||
    "Fame is but a hack away!" | 
				
			||||
    "The REPL is not enough, but it is such a perfect place to start..." | 
				
			||||
    ,(format "%s, this could be the start of a beautiful program." | 
				
			||||
             (cider-user-first-name))) | 
				
			||||
  "Scientifically-proven optimal words of hackerish encouragement.") | 
				
			||||
 | 
				
			||||
(defun cider-random-words-of-inspiration () | 
				
			||||
  "Select a random entry from `cider-words-of-inspiration'." | 
				
			||||
  (eval (nth (random (length cider-words-of-inspiration)) | 
				
			||||
             cider-words-of-inspiration))) | 
				
			||||
 | 
				
			||||
(provide 'cider-util) | 
				
			||||
 | 
				
			||||
;;; cider-util.el ends here | 
				
			||||
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						@ -1 +0,0 @@
					 | 
				
			||||
(define-package "flycheck-clojure" "20150831.631" "Flycheck: Clojure support" '((cider "0.8.1") (flycheck "0.22alpha1") (let-alist "1.0.1") (emacs "24")) :url "https://github.com/clojure-emacs/squiggly-clojure") | 
				
			||||
@ -1,205 +0,0 @@
					 | 
				
			||||
;;; flycheck-clojure.el --- Flycheck: Clojure support    -*- lexical-binding: t; -*- | 
				
			||||
 | 
				
			||||
;; Copyright © 2014 Peter Fraenkel | 
				
			||||
;; Copyright (C) 2014 Sebastian Wiesner <swiesner@lunaryorn.com> | 
				
			||||
;; | 
				
			||||
;; Author: Peter Fraenkel <pnf@podsnap.com> | 
				
			||||
;;     Sebastian Wiesner <swiesner@lunaryorn.com> | 
				
			||||
;; Maintainer: Peter Fraenkel <pnf@podsnap.com> | 
				
			||||
;; URL: https://github.com/clojure-emacs/squiggly-clojure | 
				
			||||
;; Package-Version: 20150831.631 | 
				
			||||
;; Version: 1.1.0 | 
				
			||||
;; Package-Requires: ((cider "0.8.1") (flycheck "0.22-cvs1") (let-alist "1.0.1") (emacs "24")) | 
				
			||||
 | 
				
			||||
;; This file is not part of GNU Emacs. | 
				
			||||
 | 
				
			||||
;; This program 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 of the License, or | 
				
			||||
;; (at your option) any later version. | 
				
			||||
 | 
				
			||||
;; This program 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: | 
				
			||||
 | 
				
			||||
;; Add Clojure support to Flycheck. | 
				
			||||
;; | 
				
			||||
;; Provide syntax checkers to check Clojure code using a running Cider repl. | 
				
			||||
;; | 
				
			||||
;; Installation: | 
				
			||||
;; | 
				
			||||
;; (eval-after-load 'flycheck '(flycheck-clojure-setup)) | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'cider-client) | 
				
			||||
(require 'flycheck) | 
				
			||||
(require 'json) | 
				
			||||
(require 'url-parse) | 
				
			||||
(eval-when-compile (require 'let-alist)) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun flycheck-clojure-parse-cider-errors (value checker) | 
				
			||||
  "Parse cider errors from JSON VALUE from CHECKER. | 
				
			||||
 | 
				
			||||
Return a list of parsed `flycheck-error' objects." | 
				
			||||
  ;; Parse the nested JSON from Cider.  The outer JSON contains the return value | 
				
			||||
  ;; from Cider, and the inner JSON the errors returned by the individual | 
				
			||||
  ;; checker. | 
				
			||||
  (let ((error-objects (json-read-from-string (json-read-from-string value)))) | 
				
			||||
    (mapcar (lambda (o) | 
				
			||||
              (let-alist o | 
				
			||||
                ;; Use the file name reported by the syntax checker, but only if | 
				
			||||
                ;; its absolute, because typed reports relative file names that | 
				
			||||
                ;; are hard to expand correctly, since they are relative to the | 
				
			||||
                ;; source directory (not the project directory). | 
				
			||||
                (let* ((parsed-file (when .file | 
				
			||||
                                      (url-filename | 
				
			||||
                                       (url-generic-parse-url .file)))) | 
				
			||||
                       (filename (if (and parsed-file | 
				
			||||
                                          (file-name-absolute-p parsed-file)) | 
				
			||||
                                     parsed-file | 
				
			||||
                                   (buffer-file-name)))) | 
				
			||||
                  (flycheck-error-new-at .line .column (intern .level) .msg | 
				
			||||
                                         :checker checker | 
				
			||||
                                         :filename filename)))) | 
				
			||||
            error-objects))) | 
				
			||||
 | 
				
			||||
(defun cider-flycheck-eval (input callback) | 
				
			||||
  "Send the request INPUT and register the CALLBACK as the response handler. | 
				
			||||
Uses the tooling session, with no specified namespace." | 
				
			||||
  (cider-nrepl-request:eval input callback)) | 
				
			||||
 | 
				
			||||
(defun flycheck-clojure-may-use-cider-checker () | 
				
			||||
  "Determine whether a cider checker may be used. | 
				
			||||
 | 
				
			||||
Checks for `cider-mode', and a current nREPL connection. | 
				
			||||
 | 
				
			||||
Standard predicate for cider checkers." | 
				
			||||
  (let ((connection-buffer (cider-default-connection :no-error))) | 
				
			||||
    (and (bound-and-true-p cider-mode) | 
				
			||||
         connection-buffer | 
				
			||||
         (buffer-live-p (get-buffer connection-buffer)) | 
				
			||||
         (clojure-find-ns)))) | 
				
			||||
 | 
				
			||||
(defun flycheck-clojure-start-cider (checker callback) | 
				
			||||
  "Start a cider syntax CHECKER with CALLBACK." | 
				
			||||
  (let ((ns (clojure-find-ns)) | 
				
			||||
        (form (get checker 'flycheck-clojure-form))) | 
				
			||||
    (cider-flycheck-eval | 
				
			||||
     (funcall form ns) | 
				
			||||
     (nrepl-make-response-handler | 
				
			||||
      (current-buffer) | 
				
			||||
      (lambda (buffer value) | 
				
			||||
        (funcall callback 'finished | 
				
			||||
                 (with-current-buffer buffer | 
				
			||||
                   (flycheck-clojure-parse-cider-errors value checker)))) | 
				
			||||
      nil                               ; stdout | 
				
			||||
      nil                               ; stderr | 
				
			||||
      (lambda (_) | 
				
			||||
        ;; If the evaluation completes without returning any value, there has | 
				
			||||
        ;; gone something wrong.  Ideally, we'd report *what* was wrong, but | 
				
			||||
        ;; `nrepl-make-response-handler' is close to useless for this :(, | 
				
			||||
        ;; because it just `message's for many status codes that are errors for | 
				
			||||
        ;; us :( | 
				
			||||
        (funcall callback 'errored "Done with no errors")) | 
				
			||||
      (lambda (_buffer ex _rootex _sess) | 
				
			||||
        (funcall callback 'errored | 
				
			||||
                 (format "Form %s of checker %s failed: %s" | 
				
			||||
                         form checker ex)))))) | 
				
			||||
) | 
				
			||||
 | 
				
			||||
(defun flycheck-clojure-define-cider-checker (name docstring &rest properties) | 
				
			||||
  "Define a Cider syntax checker with NAME, DOCSTRING and PROPERTIES. | 
				
			||||
 | 
				
			||||
NAME, DOCSTRING, and PROPERTIES are like for | 
				
			||||
`flycheck-define-generic-checker', except that `:start' and | 
				
			||||
`:modes' are invalid PROPERTIES.  A syntax checker defined with | 
				
			||||
this function will always check in `clojure-mode', and only if | 
				
			||||
`cider-mode' is enabled. | 
				
			||||
 | 
				
			||||
Instead of `:start', this syntax checker requires a `:form | 
				
			||||
FUNCTION' property.  FUNCTION takes the current Clojure namespace | 
				
			||||
as single argument, and shall return a string containing a | 
				
			||||
Clojure form to be sent to Cider to check the current buffer." | 
				
			||||
  (declare (indent 1) | 
				
			||||
           (doc-string 2)) | 
				
			||||
  (let* ((form (plist-get properties :form)) | 
				
			||||
         (orig-predicate (plist-get properties :predicate))) | 
				
			||||
 | 
				
			||||
    (when (plist-get :start properties) | 
				
			||||
      (error "Checker %s may not have :start" name)) | 
				
			||||
    (when (plist-get :modes properties) | 
				
			||||
      (error "Checker %s may not have :modes" name)) | 
				
			||||
    (unless (functionp form) | 
				
			||||
      (error ":form %s of %s not a valid function" form name)) | 
				
			||||
    (apply #'flycheck-define-generic-checker | 
				
			||||
           name docstring | 
				
			||||
           :start #'flycheck-clojure-start-cider | 
				
			||||
           :modes '(clojure-mode) | 
				
			||||
           :predicate (if orig-predicate | 
				
			||||
                          (lambda () | 
				
			||||
                            (and (flycheck-clojure-may-use-cider-checker) | 
				
			||||
                                 (funcall orig-predicate))) | 
				
			||||
                        #'flycheck-clojure-may-use-cider-checker) | 
				
			||||
           properties) | 
				
			||||
 | 
				
			||||
    (put name 'flycheck-clojure-form form))) | 
				
			||||
 | 
				
			||||
(flycheck-clojure-define-cider-checker 'clojure-cider-eastwood | 
				
			||||
  "A syntax checker for Clojure, using Eastwood in Cider. | 
				
			||||
 | 
				
			||||
See URL `https://github.com/jonase/eastwood' and URL | 
				
			||||
`https://github.com/clojure-emacs/cider/' for more information." | 
				
			||||
  :form (lambda (ns) | 
				
			||||
          (format "(do (require 'squiggly-clojure.core) (squiggly-clojure.core/check-ew '%s))" | 
				
			||||
                  ns)) | 
				
			||||
  :next-checkers '(clojure-cider-kibit clojure-cider-typed)) | 
				
			||||
 | 
				
			||||
(flycheck-clojure-define-cider-checker 'clojure-cider-kibit | 
				
			||||
  "A syntax checker for Clojure, using Kibit in Cider. | 
				
			||||
 | 
				
			||||
See URL `https://github.com/jonase/kibit' and URL | 
				
			||||
`https://github.com/clojure-emacs/cider/' for more information." | 
				
			||||
  :form (lambda (ns) | 
				
			||||
          (format | 
				
			||||
           "(do (require 'squiggly-clojure.core) (squiggly-clojure.core/check-kb '%s %s))" | 
				
			||||
           ns | 
				
			||||
           ;; Escape file name for Clojure | 
				
			||||
           (flycheck-sexp-to-string (buffer-file-name)))) | 
				
			||||
  :predicate (lambda () (buffer-file-name)) | 
				
			||||
  :next-checkers '(clojure-cider-typed)) | 
				
			||||
 | 
				
			||||
(flycheck-clojure-define-cider-checker 'clojure-cider-typed | 
				
			||||
  "A syntax checker for Clojure, using Typed Clojure in Cider. | 
				
			||||
 | 
				
			||||
See URL `https://github.com/clojure-emacs/cider/' and URL | 
				
			||||
`https://github.com/clojure/core.typed' for more information." | 
				
			||||
  :form (lambda (ns) | 
				
			||||
          (format | 
				
			||||
           "(do (require 'squiggly-clojure.core) (squiggly-clojure.core/check-tc '%s))" | 
				
			||||
           ns))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun flycheck-clojure-setup () | 
				
			||||
  "Setup Flycheck for Clojure." | 
				
			||||
  (interactive) | 
				
			||||
  ;; Add checkers in reverse order, because `add-to-list' adds to front. | 
				
			||||
  (dolist (checker '(clojure-cider-typed | 
				
			||||
                     clojure-cider-kibit | 
				
			||||
                     clojure-cider-eastwood)) | 
				
			||||
    (add-to-list 'flycheck-checkers checker))) | 
				
			||||
 | 
				
			||||
(provide 'flycheck-clojure) | 
				
			||||
 | 
				
			||||
;; Local Variables: | 
				
			||||
;; indent-tabs-mode: nil | 
				
			||||
;; End: | 
				
			||||
 | 
				
			||||
;;; flycheck-clojure.el ends here | 
				
			||||
@ -1,11 +0,0 @@
					 | 
				
			||||
(define-package "flycheck-haskell" "20151010.340" "Flycheck: Cabal projects and sandboxes" | 
				
			||||
  '((emacs "24.1") | 
				
			||||
    (flycheck "0.22") | 
				
			||||
    (haskell-mode "13.7") | 
				
			||||
    (dash "2.4.0") | 
				
			||||
    (let-alist "1.0.1")) | 
				
			||||
  :url "https://github.com/flycheck/flycheck-haskell" :keywords | 
				
			||||
  '("tools" "convenience")) | 
				
			||||
;; Local Variables: | 
				
			||||
;; no-byte-compile: t | 
				
			||||
;; End: | 
				
			||||
@ -1,209 +0,0 @@
					 | 
				
			||||
-- Copyright (C) 2014, 2015 Sebastian Wiesner <swiesner@lunaryorn.com> | 
				
			||||
-- Copyright (C) 2014 Gracjan Polak <gracjanpolak@gmail.com> | 
				
			||||
-- Copyright (C) 2015 Michael Alan Dorman <mdorman@ironicdesign.com> | 
				
			||||
 | 
				
			||||
-- This file is not part of GNU Emacs. | 
				
			||||
 | 
				
			||||
-- This program 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 of the License, or (at your option) any later | 
				
			||||
-- version. | 
				
			||||
 | 
				
			||||
-- This program 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/>. | 
				
			||||
 | 
				
			||||
{-# LANGUAGE CPP                  #-} | 
				
			||||
{-# LANGUAGE TypeSynonymInstances #-} | 
				
			||||
{-# LANGUAGE FlexibleInstances #-} | 
				
			||||
 | 
				
			||||
import Control.Arrow (second) | 
				
			||||
import Data.List (nub, isPrefixOf) | 
				
			||||
import Data.Maybe (listToMaybe) | 
				
			||||
#ifdef USE_COMPILER_ID | 
				
			||||
import Distribution.Compiler | 
				
			||||
       (CompilerFlavor(GHC), CompilerId(CompilerId), buildCompilerFlavor) | 
				
			||||
#else | 
				
			||||
import Distribution.Compiler | 
				
			||||
       (AbiTag(NoAbiTag), CompilerFlavor(GHC), CompilerId(CompilerId), | 
				
			||||
        CompilerInfo, buildCompilerFlavor, unknownCompilerInfo) | 
				
			||||
#endif | 
				
			||||
import Distribution.Package | 
				
			||||
       (PackageName(..), PackageIdentifier(..), Dependency(..)) | 
				
			||||
import Distribution.PackageDescription | 
				
			||||
       (PackageDescription(..), allBuildInfo, BuildInfo(..), | 
				
			||||
        usedExtensions, allLanguages, hcOptions, exeName, testEnabled, | 
				
			||||
        condTestSuites, benchmarkEnabled, condBenchmarks) | 
				
			||||
import Distribution.PackageDescription.Configuration | 
				
			||||
       (finalizePackageDescription, mapTreeData) | 
				
			||||
import Distribution.PackageDescription.Parse (readPackageDescription) | 
				
			||||
import Distribution.Simple.BuildPaths (defaultDistPref) | 
				
			||||
import Distribution.System (buildPlatform) | 
				
			||||
import Distribution.Verbosity (silent) | 
				
			||||
import Language.Haskell.Extension (Extension(..),Language(..)) | 
				
			||||
import System.Environment (getArgs) | 
				
			||||
import System.Exit (exitFailure) | 
				
			||||
import System.FilePath ((</>),dropFileName,normalise) | 
				
			||||
import System.Info (compilerVersion) | 
				
			||||
 | 
				
			||||
data Sexp | 
				
			||||
    = SList [Sexp] | 
				
			||||
    | SString String | 
				
			||||
    | SSymbol String | 
				
			||||
 | 
				
			||||
sym :: String -> Sexp | 
				
			||||
sym = SSymbol | 
				
			||||
 | 
				
			||||
instance Show Sexp where | 
				
			||||
    show (SSymbol s) = s | 
				
			||||
    show (SString s) = show s     -- Poor man's escaping | 
				
			||||
    show (SList s) = "(" ++ unwords (map show s) ++ ")" | 
				
			||||
 | 
				
			||||
class ToSexp a  where | 
				
			||||
    toSexp :: a -> Sexp | 
				
			||||
 | 
				
			||||
instance ToSexp String where | 
				
			||||
    toSexp = SString | 
				
			||||
 | 
				
			||||
instance ToSexp Extension where | 
				
			||||
    toSexp (EnableExtension ext) = toSexp (show ext) | 
				
			||||
    toSexp (DisableExtension ext) = toSexp ("No" ++ show ext) | 
				
			||||
    toSexp (UnknownExtension ext) = toSexp ext | 
				
			||||
 | 
				
			||||
instance ToSexp Language where | 
				
			||||
    toSexp (UnknownLanguage lang) = toSexp lang | 
				
			||||
    toSexp lang = toSexp (show lang) | 
				
			||||
 | 
				
			||||
instance ToSexp Dependency where | 
				
			||||
    toSexp (Dependency (PackageName dependency) _) = toSexp dependency | 
				
			||||
 | 
				
			||||
instance ToSexp Sexp where | 
				
			||||
    toSexp = id | 
				
			||||
 | 
				
			||||
cons :: (ToSexp a, ToSexp b) => a -> [b] -> Sexp | 
				
			||||
cons h t = SList (toSexp h : map toSexp t) | 
				
			||||
 | 
				
			||||
getBuildDirectories :: PackageDescription -> FilePath -> [String] | 
				
			||||
getBuildDirectories pkgDesc cabalDir = | 
				
			||||
    case library pkgDesc of | 
				
			||||
        Just _ -> buildDir : buildDirs | 
				
			||||
        Nothing -> buildDirs | 
				
			||||
  where | 
				
			||||
    distDir = cabalDir </> defaultDistPref | 
				
			||||
    buildDir = distDir </> "build" | 
				
			||||
    autogenDir = buildDir </> "autogen" | 
				
			||||
    executableBuildDir e = buildDir </> exeName e </> (exeName e ++ "-tmp") | 
				
			||||
    buildDirs = autogenDir : map executableBuildDir (executables pkgDesc) | 
				
			||||
 | 
				
			||||
getSourceDirectories :: [BuildInfo] -> FilePath -> [String] | 
				
			||||
getSourceDirectories buildInfo cabalDir = | 
				
			||||
    map (cabalDir </>) (concatMap hsSourceDirs buildInfo) | 
				
			||||
 | 
				
			||||
allowedOptions :: [String] | 
				
			||||
allowedOptions = | 
				
			||||
    [ "-W" | 
				
			||||
    , "-w" | 
				
			||||
    , "-Wall" | 
				
			||||
    , "-fglasgow-exts" | 
				
			||||
    , "-fpackage-trust" | 
				
			||||
    , "-fhelpful-errors" | 
				
			||||
    , "-F" | 
				
			||||
    , "-cpp"] | 
				
			||||
 | 
				
			||||
allowedOptionPrefixes :: [String] | 
				
			||||
allowedOptionPrefixes = | 
				
			||||
    [ "-fwarn-" | 
				
			||||
    , "-fno-warn-" | 
				
			||||
    , "-fcontext-stack=" | 
				
			||||
    , "-firrefutable-tuples" | 
				
			||||
    , "-D" | 
				
			||||
    , "-U" | 
				
			||||
    , "-I" | 
				
			||||
    , "-fplugin=" | 
				
			||||
    , "-fplugin-opt=" | 
				
			||||
    , "-pgm" | 
				
			||||
    , "-opt"] | 
				
			||||
 | 
				
			||||
isAllowedOption :: String -> Bool | 
				
			||||
isAllowedOption opt = | 
				
			||||
    elem opt allowedOptions || any (`isPrefixOf` opt) allowedOptionPrefixes | 
				
			||||
 | 
				
			||||
dumpPackageDescription :: PackageDescription -> FilePath -> Sexp | 
				
			||||
dumpPackageDescription pkgDesc cabalFile = | 
				
			||||
    SList | 
				
			||||
        [ cons (sym "build-directories") buildDirs | 
				
			||||
        , cons (sym "source-directories") sourceDirs | 
				
			||||
        , cons (sym "extensions") exts | 
				
			||||
        , cons (sym "languages") langs | 
				
			||||
        , cons (sym "dependencies") deps | 
				
			||||
        , cons (sym "other-options") otherOptions] | 
				
			||||
  where | 
				
			||||
    cabalDir = dropFileName cabalFile | 
				
			||||
    buildInfo = allBuildInfo pkgDesc | 
				
			||||
    buildDirs = nub (map normalise (getBuildDirectories pkgDesc cabalDir)) | 
				
			||||
    sourceDirs = nub (map normalise (getSourceDirectories buildInfo cabalDir)) | 
				
			||||
    exts = nub (concatMap usedExtensions buildInfo) | 
				
			||||
    langs = nub (concatMap allLanguages buildInfo) | 
				
			||||
    thisPackage = (pkgName . package) pkgDesc | 
				
			||||
    deps = | 
				
			||||
        nub | 
				
			||||
            (filter | 
				
			||||
                 (\(Dependency name _) -> | 
				
			||||
                       name /= thisPackage) | 
				
			||||
                 (buildDepends pkgDesc)) | 
				
			||||
    otherOptions = | 
				
			||||
        nub (filter isAllowedOption (concatMap (hcOptions GHC) buildInfo)) | 
				
			||||
 | 
				
			||||
dumpCabalConfiguration :: String -> IO () | 
				
			||||
dumpCabalConfiguration cabalFile = do | 
				
			||||
    genericDesc <- readPackageDescription silent cabalFile | 
				
			||||
    -- This let block is eerily like one in Cabal.Distribution.Simple.Configure | 
				
			||||
    let enableTest t = | 
				
			||||
            t | 
				
			||||
            { testEnabled = True | 
				
			||||
            } | 
				
			||||
        flaggedTests = | 
				
			||||
            map (second (mapTreeData enableTest)) (condTestSuites genericDesc) | 
				
			||||
        enableBenchmark bm = | 
				
			||||
            bm | 
				
			||||
            { benchmarkEnabled = True | 
				
			||||
            } | 
				
			||||
        flaggedBenchmarks = | 
				
			||||
            map | 
				
			||||
                (second (mapTreeData enableBenchmark)) | 
				
			||||
                (condBenchmarks genericDesc) | 
				
			||||
        genericDesc' = | 
				
			||||
            genericDesc | 
				
			||||
            { condTestSuites = flaggedTests | 
				
			||||
            , condBenchmarks = flaggedBenchmarks | 
				
			||||
            } | 
				
			||||
    case finalizePackageDescription | 
				
			||||
             [] | 
				
			||||
             (const True) | 
				
			||||
             buildPlatform | 
				
			||||
             buildCompilerId | 
				
			||||
             [] | 
				
			||||
             genericDesc' of | 
				
			||||
        Left e -> putStrLn $ "Issue with package configuration\n" ++ show e | 
				
			||||
        Right (pkgDesc,_) -> print (dumpPackageDescription pkgDesc cabalFile) | 
				
			||||
 | 
				
			||||
#ifdef USE_COMPILER_ID | 
				
			||||
buildCompilerId :: CompilerId | 
				
			||||
buildCompilerId = CompilerId buildCompilerFlavor compilerVersion | 
				
			||||
#else | 
				
			||||
buildCompilerId :: CompilerInfo | 
				
			||||
buildCompilerId = | 
				
			||||
    unknownCompilerInfo | 
				
			||||
        (CompilerId buildCompilerFlavor compilerVersion) | 
				
			||||
        NoAbiTag | 
				
			||||
#endif | 
				
			||||
 | 
				
			||||
main :: IO () | 
				
			||||
main = do | 
				
			||||
    args <- getArgs | 
				
			||||
    let cabalFile = listToMaybe args | 
				
			||||
    maybe exitFailure dumpCabalConfiguration cabalFile | 
				
			||||
@ -1,48 +0,0 @@
					 | 
				
			||||
-- Copyright (C) 2015 Michael Alan Dorman <mdorman@ironicdesign.com> | 
				
			||||
 | 
				
			||||
-- This file is not part of GNU Emacs. | 
				
			||||
 | 
				
			||||
-- This program 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 of the License, or (at your option) any later | 
				
			||||
-- version. | 
				
			||||
 | 
				
			||||
-- This program 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/>. | 
				
			||||
 | 
				
			||||
import Data.Version (Version (Version)) | 
				
			||||
import Distribution.Simple.Utils (cabalVersion) | 
				
			||||
import System.Environment (getArgs) | 
				
			||||
 | 
				
			||||
data Mode | 
				
			||||
  = GHC | 
				
			||||
  | HLint | 
				
			||||
 | 
				
			||||
define :: Mode -> String -> String | 
				
			||||
define GHC def = "-D" ++ def | 
				
			||||
define HLint def = "--cpp-define=" ++ def | 
				
			||||
 | 
				
			||||
legacyFlags :: Mode -> [String] | 
				
			||||
legacyFlags mode = [define mode "USE_COMPILER_ID"] | 
				
			||||
 | 
				
			||||
isLegacyCabal :: Bool | 
				
			||||
isLegacyCabal = cabalVersion < Version [1, 22] [] | 
				
			||||
 | 
				
			||||
getMode :: [String] -> Mode | 
				
			||||
getMode ("hlint":_) = HLint | 
				
			||||
getMode _ = GHC | 
				
			||||
 | 
				
			||||
main :: IO () | 
				
			||||
main = do | 
				
			||||
    args <- getArgs | 
				
			||||
    mapM_ putStrLn (flags (getMode args)) | 
				
			||||
  where | 
				
			||||
    flags mode = | 
				
			||||
        if isLegacyCabal | 
				
			||||
            then legacyFlags mode | 
				
			||||
            else [] | 
				
			||||
@ -1 +0,0 @@
					 | 
				
			||||
(define-package "flycheck-pos-tip" "20140606.510" "Flycheck errors display in tooltip" '((flycheck "0.18") (popup "0.5.0")) :url "https://github.com/flycheck/flycheck-pos-tip" :keywords '("tools" "convenience")) | 
				
			||||
@ -1,61 +0,0 @@
					 | 
				
			||||
;;; flycheck-pos-tip.el --- Flycheck errors display in tooltip | 
				
			||||
 | 
				
			||||
;; Copyright (C) 2014  Akiha Senda | 
				
			||||
 | 
				
			||||
;; Author: Akiha Senda <senda.akiha@gmail.com> | 
				
			||||
;; URL: https://github.com/flycheck/flycheck-pos-tip | 
				
			||||
;; Package-Version: 20140606.510 | 
				
			||||
;; Keywords: tools, convenience | 
				
			||||
;; Version: 0.0.1 | 
				
			||||
;; Package-Requires: ((flycheck "0.18") (popup "0.5.0")) | 
				
			||||
 | 
				
			||||
;; This file is not part of GNU Emacs. | 
				
			||||
 | 
				
			||||
;; This program 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 of the License, or | 
				
			||||
;; (at your option) any later version. | 
				
			||||
 | 
				
			||||
;; This program 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: | 
				
			||||
 | 
				
			||||
;; Add error message display method for Flycheck. | 
				
			||||
 | 
				
			||||
;;;; Setup | 
				
			||||
 | 
				
			||||
;; (eval-after-load 'flycheck | 
				
			||||
;;   '(custom-set-variables | 
				
			||||
;;    '(flycheck-display-errors-function #'flycheck-pos-tip-error-messages))) | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'flycheck) | 
				
			||||
(require 'popup) | 
				
			||||
 | 
				
			||||
(defgroup flycheck-pos-tip nil | 
				
			||||
  "Flycheck errors display in tooltip" | 
				
			||||
  :prefix "flycheck-pos-tip-" | 
				
			||||
  :group 'flycheck | 
				
			||||
  :link '(url-link :tag "Github" "https://github.com/flycheck/flycheck-pos-tip")) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun flycheck-pos-tip-error-messages (errors) | 
				
			||||
  "Display the tooltip that the messages of ERRORS. | 
				
			||||
 | 
				
			||||
Concatenate all non-nil messages of ERRORS separated by empty | 
				
			||||
lines, and display them with `pos-tip-show-no-propertize', which shows | 
				
			||||
 the messages in tooltip, depending on the number of lines." | 
				
			||||
  (-when-let (messages (-keep #'flycheck-error-message errors)) | 
				
			||||
    (popup-tip | 
				
			||||
     (mapconcat 'identity messages "\n")))) | 
				
			||||
 | 
				
			||||
(provide 'flycheck-pos-tip) | 
				
			||||
 | 
				
			||||
;;; flycheck-pos-tip.el ends here | 
				
			||||
@ -1 +0,0 @@
					 | 
				
			||||
(define-package "flycheck-rust" "20150609.1248" "Flycheck: Rust additions and Cargo support" '((emacs "24.1") (flycheck "0.20") (dash "2.4.0")) :url "https://github.com/flycheck/flycheck-rust" :keywords '("tools" "convenience")) | 
				
			||||
@ -1,121 +0,0 @@
					 | 
				
			||||
;;; flycheck-rust.el --- Flycheck: Rust additions and Cargo support  -*- lexical-binding: t; -*- | 
				
			||||
 | 
				
			||||
;; Copyright (C) 2014, 2015  Sebastian Wiesner <swiesner@lunaryorn.com> | 
				
			||||
 | 
				
			||||
;; Author: Sebastian Wiesner <swiesner@lunaryorn.com> | 
				
			||||
;; URL: https://github.com/flycheck/flycheck-rust | 
				
			||||
;; Package-Version: 20150609.1248 | 
				
			||||
;; Keywords: tools, convenience | 
				
			||||
;; Version: 0.1-cvs | 
				
			||||
;; Package-Requires: ((emacs "24.1") (flycheck "0.20") (dash "2.4.0")) | 
				
			||||
 | 
				
			||||
;; This file is not part of GNU Emacs. | 
				
			||||
 | 
				
			||||
;; This program 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 of the License, or | 
				
			||||
;; (at your option) any later version. | 
				
			||||
 | 
				
			||||
;; This program 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: | 
				
			||||
 | 
				
			||||
;; This Flycheck extension configures Flycheck automatically for the current | 
				
			||||
;; Cargo project. | 
				
			||||
;; | 
				
			||||
;; # Setup | 
				
			||||
;; | 
				
			||||
;;     (add-hook 'flycheck-mode-hook #'flycheck-rust-setup) | 
				
			||||
;; | 
				
			||||
;; # Usage | 
				
			||||
;; | 
				
			||||
;; Just use Flycheck as usual in your Rust/Cargo projects. | 
				
			||||
;; | 
				
			||||
;; Note: You must run `cargo build` initially to install all dependencies.  If | 
				
			||||
;; you add new dependencies to `Cargo.toml` you need to run `cargo build` | 
				
			||||
;; again. Otherwise you will see spurious errors about missing crates. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'dash) | 
				
			||||
(require 'flycheck) | 
				
			||||
 | 
				
			||||
(defun flycheck-rust-executable-p (rel-name) | 
				
			||||
  "Whether REL-NAME denotes an executable. | 
				
			||||
 | 
				
			||||
REL-NAME is the file relative to the Cargo.toml file." | 
				
			||||
  (or (string= "src/main.rs" rel-name) | 
				
			||||
      (string-prefix-p "src/bin/" rel-name))) | 
				
			||||
 | 
				
			||||
(defun flycheck-rust-test-p (rel-name) | 
				
			||||
  "Whether REL-NAME denotes a test. | 
				
			||||
 | 
				
			||||
REL-NAME is the file relative to the Cargo.toml file." | 
				
			||||
  (string-prefix-p "tests/" rel-name)) | 
				
			||||
 | 
				
			||||
(defun flycheck-rust-bench-p (rel-name) | 
				
			||||
  "Whether REL-NAME denotes a bench. | 
				
			||||
 | 
				
			||||
REL-NAME is the file relative to the Cargo.toml file." | 
				
			||||
  (string-prefix-p "benches/" rel-name)) | 
				
			||||
 | 
				
			||||
(defun flycheck-rust-example-p (rel-name) | 
				
			||||
  "Whether REL-NAME denotes an example. | 
				
			||||
 | 
				
			||||
REL-NAME is the file relative to the Cargo.toml file." | 
				
			||||
  (string-prefix-p "examples/" rel-name)) | 
				
			||||
 | 
				
			||||
(defun flycheck-rust-project-root () | 
				
			||||
  "Get the project root for the current buffer. | 
				
			||||
 | 
				
			||||
Return the directory containing the Cargo file, or nil if there | 
				
			||||
is none." | 
				
			||||
  (locate-dominating-file (buffer-file-name) "Cargo.toml")) | 
				
			||||
 | 
				
			||||
(defun flycheck-rust-find-crate-root () | 
				
			||||
  "Get the crate root (the nearest lib.rs or main.rs) | 
				
			||||
relative to the current file." | 
				
			||||
  (-if-let (lib-crate-dir (locate-dominating-file (buffer-file-name) "lib.rs")) | 
				
			||||
      (expand-file-name "lib.rs" lib-crate-dir) | 
				
			||||
    (-when-let (exe-crate-dir (locate-dominating-file (buffer-file-name) "main.rs")) | 
				
			||||
      (expand-file-name "main.rs" exe-crate-dir)))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun flycheck-rust-setup () | 
				
			||||
  "Setup Rust in Flycheck. | 
				
			||||
 | 
				
			||||
If the current file is part of a Cargo project, configure | 
				
			||||
Flycheck according to the Cargo project layout." | 
				
			||||
  (interactive) | 
				
			||||
  (when (buffer-file-name) | 
				
			||||
    (-when-let (root (flycheck-rust-project-root)) | 
				
			||||
      (let ((rel-name (file-relative-name (buffer-file-name) root))) | 
				
			||||
        ;; These are valid crate roots as by Cargo's layout | 
				
			||||
        (unless (or (flycheck-rust-executable-p rel-name) | 
				
			||||
                    (flycheck-rust-test-p rel-name) | 
				
			||||
                    (flycheck-rust-bench-p rel-name) | 
				
			||||
                    (flycheck-rust-example-p rel-name) | 
				
			||||
                    (string= "src/lib.rs" rel-name)) | 
				
			||||
          ;; For other files, the library is either the default library or the | 
				
			||||
          ;; executable | 
				
			||||
          (setq-local flycheck-rust-crate-root (flycheck-rust-find-crate-root))) | 
				
			||||
        ;; Check tests in libraries and integration tests | 
				
			||||
        (setq-local flycheck-rust-check-tests | 
				
			||||
                    (not (flycheck-rust-executable-p rel-name))) | 
				
			||||
        ;; Set the crate type | 
				
			||||
        (setq-local flycheck-rust-crate-type | 
				
			||||
                    (if (flycheck-rust-executable-p rel-name) "bin" "lib")) | 
				
			||||
        ;; Find build libraries | 
				
			||||
        (setq-local flycheck-rust-library-path | 
				
			||||
                    (list (expand-file-name "target/debug" root) | 
				
			||||
                          (expand-file-name "target/debug/deps" root))))))) | 
				
			||||
 | 
				
			||||
(provide 'flycheck-rust) | 
				
			||||
 | 
				
			||||
;;; flycheck-rust.el ends here | 
				
			||||
@ -1 +0,0 @@
					 | 
				
			||||
(define-package "gitignore-mode" "20150330.1048" "Major mode for editing .gitignore files" 'nil :url "https://github.com/magit/git-modes" :keywords '("convenience" "vc" "git")) | 
				
			||||
@ -1,61 +0,0 @@
					 | 
				
			||||
;;; gitignore-mode.el --- Major mode for editing .gitignore files -*- lexical-binding: t; -*- | 
				
			||||
 | 
				
			||||
;; Copyright (c) 2012-2013  Sebastian Wiesner | 
				
			||||
;; Copyright (C) 2012-2015  The Magit Project Developers | 
				
			||||
 | 
				
			||||
;; Author: Sebastian Wiesner <lunaryorn@gmail.com> | 
				
			||||
;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> | 
				
			||||
;; Homepage: https://github.com/magit/git-modes | 
				
			||||
;; Keywords: convenience vc git | 
				
			||||
;; Package-Version: 20150330.1048 | 
				
			||||
 | 
				
			||||
;; 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 file.  If not, see <http://www.gnu.org/licenses/>. | 
				
			||||
 | 
				
			||||
;;; Commentary: | 
				
			||||
 | 
				
			||||
;; A major mode for editing .gitignore files. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'conf-mode) | 
				
			||||
 | 
				
			||||
(defvar gitignore-mode-font-lock-keywords | 
				
			||||
  '(("^\\s<.*$"   . font-lock-comment-face) | 
				
			||||
    ("^!"         . font-lock-negation-char-face) ; Negative pattern | 
				
			||||
    ("/"          . font-lock-constant-face)      ; Directory separators | 
				
			||||
    ("[*?]"       . font-lock-keyword-face)       ; Glob patterns | 
				
			||||
    ("\\[.+?\\]"  . font-lock-keyword-face)))     ; Ranged glob patterns | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(define-derived-mode gitignore-mode conf-unix-mode "Gitignore" | 
				
			||||
  "A major mode for editing .gitignore files." | 
				
			||||
  (conf-mode-initialize "#") | 
				
			||||
  ;; Disable syntactic font locking, because comments are only valid at | 
				
			||||
  ;; beginning of line. | 
				
			||||
  (setq font-lock-defaults '(gitignore-mode-font-lock-keywords t t)) | 
				
			||||
  (set (make-local-variable 'conf-assignment-sign) nil)) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(dolist (pattern (list "/\\.gitignore\\'" | 
				
			||||
                       "/\\.git/info/exclude\\'" | 
				
			||||
                       "/git/ignore\\'")) | 
				
			||||
  (add-to-list 'auto-mode-alist (cons pattern 'gitignore-mode))) | 
				
			||||
 | 
				
			||||
(provide 'gitignore-mode) | 
				
			||||
;; Local Variables: | 
				
			||||
;; indent-tabs-mode: nil | 
				
			||||
;; End: | 
				
			||||
;;; gitignore-mode.el ends here | 
				
			||||
									
										Binary file not shown.
									
								
							
						
									
										Binary file not shown.
									
								
							
						@ -1,438 +0,0 @@
					 | 
				
			||||
Haskell Mode NEWS                                -*- org -*- | 
				
			||||
 | 
				
			||||
This file uses Org mode. Some useful (default) key-bindings: | 
				
			||||
 - Use "C-c C-n"/"C-c C-p" to jump to next/prev heading | 
				
			||||
 - Use "<tab>" to expand/collapse nodes | 
				
			||||
 - Use "<backtab>" to cycle visibility of all nodes | 
				
			||||
 - Use "C-c C-o" to open links | 
				
			||||
 | 
				
			||||
* Changes in 13.12 | 
				
			||||
 | 
				
			||||
- Added haskell-bot.el | 
				
			||||
 | 
				
			||||
- Added support for cabal repl build targets | 
				
			||||
 | 
				
			||||
- Automatically add import lines via Hoogle | 
				
			||||
 | 
				
			||||
- Automatically add package to cabal file | 
				
			||||
 | 
				
			||||
- Added w3m-haddock.el | 
				
			||||
 | 
				
			||||
- Added debugger mode | 
				
			||||
 | 
				
			||||
- Added preliminary :present support | 
				
			||||
 | 
				
			||||
- Added haskell-sort-imports | 
				
			||||
 | 
				
			||||
- Added haskell-complete-module | 
				
			||||
 | 
				
			||||
- Support if and multi-way if in indentation | 
				
			||||
 | 
				
			||||
- Add support to generate tags on windows | 
				
			||||
 | 
				
			||||
- Add haskell-language-extensions variable | 
				
			||||
 | 
				
			||||
- Improve haskell-simple-indent mode | 
				
			||||
 | 
				
			||||
- Improve test cases | 
				
			||||
 | 
				
			||||
* Changes in 13.10 | 
				
			||||
 | 
				
			||||
- Small fix for haskell-simple-indent: Certain indentation situations | 
				
			||||
  cause valname-string to be nil, which haskell-trim did not handle | 
				
			||||
  gracefully (naturally, since nil != ""). | 
				
			||||
 | 
				
			||||
- Luke Hoersten's Shnippet merged in under snippets/. | 
				
			||||
 | 
				
			||||
- haskell-presentation-mode is now a haskell-mode derived mode. | 
				
			||||
 | 
				
			||||
- Small improvement to haskell-process-do-info (works on constructors | 
				
			||||
  now and underscored names). | 
				
			||||
 | 
				
			||||
- Add haskell-indent-spaces configuration variable. | 
				
			||||
 | 
				
			||||
- The command string to run cabal commands is slightly more | 
				
			||||
  configurable. See: C-h f haskell-process-do-cabal-format-string | 
				
			||||
 | 
				
			||||
* Changes in 13.8 | 
				
			||||
 | 
				
			||||
See also [[https://github.com/haskell/haskell-mode/compare/v13.07...v13.08][detailed Git history]]. | 
				
			||||
 | 
				
			||||
- Make `haskell-simple-indent-mode' a proper minor mode with `SInd` as | 
				
			||||
  mode-line lighter | 
				
			||||
 | 
				
			||||
- Support popular "λ> " prompt in inf-haskell by default | 
				
			||||
 | 
				
			||||
- Hide internal `*print-haskell-mode*' buffers | 
				
			||||
  (used when `haskell-interactive-mode-eval-mode' is active) | 
				
			||||
 | 
				
			||||
- Add tab-completion support for haskell-interactive-mode | 
				
			||||
  (requires `:complete' command support in GHCi) | 
				
			||||
 | 
				
			||||
- Add support to `haskell-process-do-info` to perform `:browse!` query | 
				
			||||
  on module name when called on import statement line | 
				
			||||
 | 
				
			||||
- `haskell-decl-scan-mode': | 
				
			||||
  - New customize group `haskell-decl-scan' | 
				
			||||
  - New flag `haskell-decl-scan-bindings-as-variables' for controlling | 
				
			||||
    whether to put value bindings into the "Variables" category. | 
				
			||||
  - New flag `haskell-decl-scan-add-to-menubar' for controlling | 
				
			||||
    whether to add "Declarations" menu entry to menu bar. | 
				
			||||
  - New manual section node `(haskell-mode)haskell-decl-scan-mode' | 
				
			||||
 | 
				
			||||
- Add support for [[http://www.haskell.org/ghc/docs/latest/html/users_guide/syntax-extns.html#lambda-case][LambdaCase]] syntax extension to `haskell-indentation` | 
				
			||||
 | 
				
			||||
- Change `haskell-indentation-mode' to never jump back a whole line | 
				
			||||
  when pressing DEL.  The old behavior can be restored by setting | 
				
			||||
  `haskell-indentation-delete-backward-jump-line' to t | 
				
			||||
 | 
				
			||||
- New convenience function `haskell-cabal-visit-file' for locating and | 
				
			||||
  visiting most likely `.cabal` file associated with current buffer | 
				
			||||
 | 
				
			||||
- Add support for [[http://www.haskell.org/ghc/docs/latest/html/users_guide/syntax-extns.html#package-import][PackageImports]] and [[http://www.haskell.org/ghc/docs/latest/html/users_guide/syntax-extns.html#safe-imports-ext][SafeHaskell]] syntax extensions to | 
				
			||||
  `haskell-decl-scan-mode' parser | 
				
			||||
 | 
				
			||||
- Add `turn-{on,off}-haskell-doc' commands as aliases for the existing | 
				
			||||
  `turn-{on,off}-haskell-doc-mode' commands | 
				
			||||
 | 
				
			||||
- Add support for "cabal repl" process type to `haskell-interactive-mode' | 
				
			||||
 | 
				
			||||
- Add new Haskell compilation sub-mode and associated `haskell-compile' | 
				
			||||
  command | 
				
			||||
 | 
				
			||||
* Changes in 13.7 | 
				
			||||
 | 
				
			||||
See also [[https://github.com/haskell/haskell-mode/compare/v13.06...v13.07][detailed Git history]]. | 
				
			||||
 | 
				
			||||
- Convert NEWS (this file) to Org mode style and include NEWS file in | 
				
			||||
  package and add command for visiting NEWS file | 
				
			||||
  (M-x haskell-mode-view-news) | 
				
			||||
 | 
				
			||||
- Officially drop support for versions prior to Emacs 23 | 
				
			||||
 | 
				
			||||
- New work-in-progress Info manual for haskell-mode | 
				
			||||
 | 
				
			||||
- Remove deprecated `haskell-{hugs,ghci}' modules | 
				
			||||
 | 
				
			||||
- Font-locking changes: | 
				
			||||
  - Remove deprecated `turn-on-haskell-font-lock` function | 
				
			||||
  - Improve font-locking of type-signatures in presence of newlines | 
				
			||||
  - Use `font-lock-preprocessor-face' instead of the previously used | 
				
			||||
    `font-lock-warning-face` for CPP directives | 
				
			||||
  - Use `font-lock-warning-face` instead  of the previously used | 
				
			||||
    `font-lock-preprocessor-face` for Git merge conflict annotations. | 
				
			||||
 | 
				
			||||
- Improvements to `haskell-move-nested' module: | 
				
			||||
  - Add support for operating on active regions | 
				
			||||
  - New interactive commands `haskell-move-nested-{left,right}` which | 
				
			||||
    support numeric prefix arguments for controlling the amount of | 
				
			||||
    shifting to apply. | 
				
			||||
 | 
				
			||||
- Add `haskell-unicode-input-method.el` to distribution | 
				
			||||
  (enable with `turn-on-haskell-unicode-input-method`) | 
				
			||||
 | 
				
			||||
- Fix all byte-compilation warnings | 
				
			||||
 | 
				
			||||
- Build-system: | 
				
			||||
  - For in-place installation, `haskell-site-file.el' is renamed | 
				
			||||
    to `haskell-mode-autoloads.el` | 
				
			||||
  - Auto-generate ELPA compatible README file by extracting header of | 
				
			||||
    haskell-mode.el | 
				
			||||
  - New "make check" target | 
				
			||||
  - Add Travis-CI build jobs for testing byte-compilation with | 
				
			||||
    multiple Emacs versions | 
				
			||||
 | 
				
			||||
- Reorganize customize settings | 
				
			||||
  - Add new convenience function for browsing all Haskell Mode settings | 
				
			||||
    (M-x haskell-customize) | 
				
			||||
  - Add `:link' keywords pointing to the new Info manual | 
				
			||||
  - Add `:group' keywords to modes to make (M-x customize-mode) work | 
				
			||||
  - Create new customization groups `haskell-interactive' and `inferior-haskell' | 
				
			||||
    to clean up namespace | 
				
			||||
  - Create new customization group `ghc-core` containing the two new | 
				
			||||
    customization variables `ghc-core-program` and `ghc-core-program-args`. | 
				
			||||
 | 
				
			||||
- Improvements to haskell-interactive-mode | 
				
			||||
  - Add support for deleting compile messages superseded by recompile/reloads | 
				
			||||
    (M-x customize-variable RET haskell-interactive-mode-delete-superseded-errors) | 
				
			||||
  - Fix `C-u M-x haskell-process-do-type` inserting bad signatures | 
				
			||||
  - Integrate with Emacs' `next-error` subsystem | 
				
			||||
  - Add "C-c C-f" binding to REPL keymap for enabling `next-error-follow-minor-mode' | 
				
			||||
  - Add support for `-ferror-spans`-style compile messages | 
				
			||||
  - Add `-ferror-spans` as default for `haskell-process-args-ghci` | 
				
			||||
  - Add optional argument to | 
				
			||||
    `haskell-session-{all,installed,project}-modules' to suppress | 
				
			||||
    session-creation. This is useful for yasnippet usage, see commit | 
				
			||||
    517fd7e for an example. | 
				
			||||
  - Change default for `haskell-process-path-ghci` to a static "ghci" | 
				
			||||
  - Fix `haskell-interactive-switch` not selecting the REPL window | 
				
			||||
  - Make `*haskell-process-log*` buffer configurable | 
				
			||||
    (controlled via new `haskell-process-log` customize option) | 
				
			||||
 | 
				
			||||
* Changes in 13.6 | 
				
			||||
 | 
				
			||||
See also [[https://github.com/haskell/haskell-mode/compare/2_9_1...v13.06][detailed Git history]]. | 
				
			||||
 | 
				
			||||
- Switch to new versioning scheme | 
				
			||||
 | 
				
			||||
- Switch to MELPA/Marmalade based packaging | 
				
			||||
 | 
				
			||||
- Cleanup/refactor build-system | 
				
			||||
 | 
				
			||||
- Enhance `M-x haskell-version` to report more detailed versioning | 
				
			||||
  information | 
				
			||||
 | 
				
			||||
- Make haskell-interactive-mode emulate comint/eshell history navigation | 
				
			||||
  (see commit 0e96843 for more details) | 
				
			||||
 | 
				
			||||
- Improvements to haskell-interactive-mode | 
				
			||||
  - Improve killing/restarting haskell-interactive sessions | 
				
			||||
  - Improve directory prompting and resolution | 
				
			||||
  - Fix redundant-import suggest trigger to support qualified imports | 
				
			||||
  - Detect all abbreviations of an user-inputted ":quit" | 
				
			||||
  - Fix regexps for recent GHC 7.x compiler messages | 
				
			||||
  - Customizable commandline args for GHCi | 
				
			||||
    (M-x customize-variable RET haskell-process-args-ghci) | 
				
			||||
  - New command to load or reload via prefix argument | 
				
			||||
    (M-x haskell-process-load-or-reload) | 
				
			||||
  - Fix haskell-interactive-mode prompt detection | 
				
			||||
  - Add cabal-ghci as supported process mode | 
				
			||||
  - Add a customization option for the visibility of multi-line errors | 
				
			||||
    (M-x customize-variable RET haskell-interactive-mode-hide-multi-line-errors) | 
				
			||||
 | 
				
			||||
- Add forward declarations to reduce Elisp bytecompile warnings | 
				
			||||
 | 
				
			||||
- Improvements to `haskell-indentation` | 
				
			||||
  - Add support for the UnicodeSyntax tokens `→`, `←`, and `∷`. | 
				
			||||
  - Indent "=" following data/type/newtype declarations. | 
				
			||||
  - Align "->"/"→" arrows in types under "::"/"∷" | 
				
			||||
  - Make customizable whether "<backspace>" deletes indentation too | 
				
			||||
    (via `haskell-indentation-delete-backward-indentation` and | 
				
			||||
     `haskell-indentation-delete-indentation`) | 
				
			||||
  - Properly indent 'rec' keyword, same as 'mdo' | 
				
			||||
  - Minor optimizations. | 
				
			||||
 | 
				
			||||
- Add support for "'"-prefixed constructors (-> DataKinds) to font-locking | 
				
			||||
 | 
				
			||||
- New experimental haskell session menu mode (M-x haskell-menu) | 
				
			||||
 | 
				
			||||
- Various minor cleanups/fixes/improvements... | 
				
			||||
 | 
				
			||||
* Changes in 2.9.1 | 
				
			||||
 | 
				
			||||
See also [[https://github.com/haskell/haskell-mode/compare/2_9_0...2_9_1][detailed Git history]]. | 
				
			||||
 | 
				
			||||
- Bugfix release adding missing autoload declaration | 
				
			||||
 | 
				
			||||
* Changes in 2.9.0 | 
				
			||||
 | 
				
			||||
See also [[https://github.com/haskell/haskell-mode/compare/2_8_0...2_9_0][detailed Git history]]. | 
				
			||||
 | 
				
			||||
- This is the first release after haskell-mode was migrated to GitHub | 
				
			||||
 | 
				
			||||
- New experimental `haskell-interactive-mode' module implementing a | 
				
			||||
  new REPL interaction mode for GHCi sessions to eventually replace | 
				
			||||
  the existing "inf-haskell" mode. | 
				
			||||
 | 
				
			||||
- New `haskell-process-cabal' command for interaction with cabal-install | 
				
			||||
 | 
				
			||||
- New `haskell-checkers' module | 
				
			||||
 | 
				
			||||
- Update haskell-cabal-mode font-lock keywords | 
				
			||||
 | 
				
			||||
- Improve scrolling of hoogle output (haskell-mode.el) | 
				
			||||
 | 
				
			||||
- Derive `haskell-mode` from `prog-mode` for Emacs 24+ | 
				
			||||
 | 
				
			||||
- Add new binding for "<backtab>" to haskell-mode's keymap which | 
				
			||||
  unindents current line | 
				
			||||
 | 
				
			||||
- New modules `haskell-navigate-imports`, `haskell-sort-imports' and | 
				
			||||
  `haskell-align-imports' for operating on module import lines in | 
				
			||||
  Haskell source code | 
				
			||||
 | 
				
			||||
- Add new binding for "C-c C-." to haskell-mode's keymap to sort and | 
				
			||||
  realign Haskell module imports | 
				
			||||
 | 
				
			||||
- Add new binding for "C-c i" to haskell-mode's keymap to jump back and | 
				
			||||
  forth from/to the current Haskell module's module import section. | 
				
			||||
 | 
				
			||||
- New `inferior-haskell-kind' function for querying kind via GHCi's ":kind" | 
				
			||||
 | 
				
			||||
- New `inferior-haskell-send-decl' for sending declarations to GHCi | 
				
			||||
  (bound to "C-x C-d" by default) | 
				
			||||
 | 
				
			||||
- Add new `haskell-doc-use-inf-haskell` customization variable | 
				
			||||
 | 
				
			||||
- Add support for bird-style literate haskell editing and a new | 
				
			||||
  related customization variable | 
				
			||||
  `haskell-indentation-birdtrack-extra-space' | 
				
			||||
 | 
				
			||||
- Font locking improvements | 
				
			||||
  - Add support for Git's merge annotation | 
				
			||||
    (with `font-lock-preprocessor-face') | 
				
			||||
  - Improve `import', `foreign import' and `foreign export' font | 
				
			||||
    locking | 
				
			||||
  - Add support for `rec', `proc' and `mdo` as keywords | 
				
			||||
  - Make whitespace within `-- |' and `{- |' optional when possible | 
				
			||||
 | 
				
			||||
- New `haskell-move-nested` module providing utilities for | 
				
			||||
  interactively {in,de}denting nested "hanging" blocks. | 
				
			||||
 | 
				
			||||
- Add stylish-haskell support | 
				
			||||
  (enable via `haskell-stylish-on-save` customization variable) | 
				
			||||
 | 
				
			||||
- Add support for generating tags on save | 
				
			||||
  (enable via `haskell-tags-on-save' customization variable) | 
				
			||||
 | 
				
			||||
- Set sensible dabbrev defaults in haskell-mode | 
				
			||||
 | 
				
			||||
- Added `SCC` pragma insert/delete commands | 
				
			||||
  (`haskell-mode-insert-scc-at-point` and `haskell-mode-kill-scc-at-point') | 
				
			||||
 | 
				
			||||
- New experimental `haskell-mode-contextual-space' command | 
				
			||||
 | 
				
			||||
- And a couple more cleanups/fixes/improvements... | 
				
			||||
 | 
				
			||||
* Changes in 2.8.0 (since 2.7.0) | 
				
			||||
 | 
				
			||||
See also [[https://github.com/haskell/haskell-mode/compare/2_7_0...2_8_0][detailed Git history]]. | 
				
			||||
 | 
				
			||||
- Minimal indentation support for arrow syntax | 
				
			||||
 | 
				
			||||
- Avoid opening a new inf-haskell window if one is already visible. | 
				
			||||
  Windows on other virtual desktops or iconified frames don't count. | 
				
			||||
 | 
				
			||||
- Force comint-process-echoes to nil | 
				
			||||
 | 
				
			||||
- Autolaunch haskell-mode for files starting with #!/usr/bin/runghc | 
				
			||||
  and similar | 
				
			||||
 | 
				
			||||
- Added minimal major mode for parsing GHC core files, courtesy of Johan Tibell. | 
				
			||||
  There is a corresponding Haskell menu entry. | 
				
			||||
 | 
				
			||||
- Allow configuration of where-clause indentation; M-x customize-group | 
				
			||||
  haskell-indentation. | 
				
			||||
 | 
				
			||||
* Changes since 2.6.4 | 
				
			||||
 | 
				
			||||
- fill-paragraph (M-q) now only affects comments, and correctly | 
				
			||||
  handles Haddock commentary. adaptive-fill-mode is turned off, as it | 
				
			||||
  was interfering. | 
				
			||||
 | 
				
			||||
- Yet more unicode symbols | 
				
			||||
 | 
				
			||||
- Better support for unicode encoding of haskell source files | 
				
			||||
 | 
				
			||||
- mdo correctly indented | 
				
			||||
 | 
				
			||||
- Indentation fixes, fixes to the fixes, and fixes to the fixes to the | 
				
			||||
  fixes | 
				
			||||
 | 
				
			||||
- New command: M-x haskell-check, calls (by default) hlint on the | 
				
			||||
  current file. Also bound to C-c C-v. | 
				
			||||
 | 
				
			||||
  You can also use the flymake minor mode with this. | 
				
			||||
 | 
				
			||||
* Changes since 2.5.1 | 
				
			||||
 | 
				
			||||
- Parser corrections for haskell-indentation and haskell-decl-scan | 
				
			||||
 | 
				
			||||
- haskell-indentation: Pressing tab in the rightmost position now | 
				
			||||
  moves to the leftmost, by default with a warning. | 
				
			||||
 | 
				
			||||
- Typo fix: One haskell-indentation variable had ended up in the | 
				
			||||
  haskell-ntation customize group. | 
				
			||||
 | 
				
			||||
- haskell-hoogle aliased to hoogle, haskell-hayoo aliased to hayoo | 
				
			||||
 | 
				
			||||
- Courtesy of Alex Ott: | 
				
			||||
  - Additional unicode symbols for font-lock-symbols: () == /= >= <= !! && || sqrt | 
				
			||||
  - M-x haskell-hayoo search added, opens using browse-url | 
				
			||||
  - Bug-fix for inferior-haskell-type | 
				
			||||
 | 
				
			||||
- If haskell-indentation errors out, it now fail-safes to inserting | 
				
			||||
  a literal newline or deleting one character, for return and | 
				
			||||
  backspace respectively. | 
				
			||||
 | 
				
			||||
* Changes since 2.4: | 
				
			||||
 | 
				
			||||
- haskell-indentation, a new minor mode for indentation. | 
				
			||||
 | 
				
			||||
* Changes since 2.3: | 
				
			||||
 | 
				
			||||
- Update license to GPLv3. | 
				
			||||
 | 
				
			||||
- New derived major mode for .hsc files. | 
				
			||||
 | 
				
			||||
- Removed the C-c C-r binding to reload a file.  You can still call | 
				
			||||
  inferior-haskell-reload-file (and/or bind it to your favorite key, | 
				
			||||
  including C-c C-r) or you can now use C-u C-c C-l. | 
				
			||||
 | 
				
			||||
- C-c C-d looks up the symbol at point in the Haddock docs. | 
				
			||||
 | 
				
			||||
- Haddock comments are highlighted with font-lock-doc-face if it exists. | 
				
			||||
 | 
				
			||||
- Use `tex' rather than `latex' for haskell-literate. | 
				
			||||
 | 
				
			||||
- inf-haskell.el tries to find the root of the module hierarchy to determine | 
				
			||||
  the root of a project (either by looking for a Cabal file or relying on | 
				
			||||
  the `module' declaration line).  If all works well, this will make C-c C-l | 
				
			||||
  automatically switch to the root dir, so that dependencies in other | 
				
			||||
  directories are automatically found.  If it doesn't, complain and/or set | 
				
			||||
  inferior-haskell-find-project-root to nil. | 
				
			||||
 | 
				
			||||
- The new command haskell-hoogle helps you query Hoogle from Emacs. | 
				
			||||
 | 
				
			||||
* Changes since 2.2: | 
				
			||||
 | 
				
			||||
- Trivial support for Cabal package description files. | 
				
			||||
 | 
				
			||||
- Minor bug fixes. | 
				
			||||
 | 
				
			||||
* Changes since 2.1: | 
				
			||||
 | 
				
			||||
- There are now commands to find type and info of identifiers by querying an | 
				
			||||
  inferior haskell process.  Available under C-c C-t, C-c C-i, and C-c M-. | 
				
			||||
 | 
				
			||||
- Indentation now looks back further, until a line that has no indentation. | 
				
			||||
  To recover the earlier behavior of stopping at the first empty line | 
				
			||||
  instead, configure haskell-indent-look-past-empty-line. | 
				
			||||
 | 
				
			||||
- inf-haskell can wait until a file load completes and jump directly to the | 
				
			||||
  first error, like haskell-ghci and haskell-hugs used to do.  See the var | 
				
			||||
  inferior-haskell-wait-and-jump. | 
				
			||||
 | 
				
			||||
* Changes since 2.0: | 
				
			||||
 | 
				
			||||
- inf-haskell uses ghci if hugs is absent. | 
				
			||||
 | 
				
			||||
- Fix up some binding conflicts (C-c C-o in haskell-doc) | 
				
			||||
 | 
				
			||||
- Many (hopefully minor) changes to the indentation. | 
				
			||||
 | 
				
			||||
- New symbols in haskell-font-lock-symbols-alist. | 
				
			||||
 | 
				
			||||
* Changes since 1.45: | 
				
			||||
 | 
				
			||||
- keybindings C-c <char> have been replaced by C-c C-<char> so as not | 
				
			||||
  to collide with minor modes. | 
				
			||||
 | 
				
			||||
- The following modules are now automatically activated without having to | 
				
			||||
  add anything to haskell-mode-hook: | 
				
			||||
  haskell-font-lock (just turn on global-font-lock-mode). | 
				
			||||
  haskell-decl-scan (just bind `imenu' to some key). | 
				
			||||
 | 
				
			||||
- In recent Emacsen, haskell-doc hooks into eldoc-mode. | 
				
			||||
 | 
				
			||||
- haskell-hugs and haskell-ghci are superceded by inf-haskell. | 
				
			||||
 | 
				
			||||
- Indentation rules have been improved when using layout inside parens/braces. | 
				
			||||
 | 
				
			||||
- Symbols like -> and \ can be displayed as actual arrows and lambdas. | 
				
			||||
  See haskell-font-lock-symbols. | 
				
			||||
 | 
				
			||||
- Tweaks to the font-lock settings.  Among other things paren-matching | 
				
			||||
  with things like \(x,y) should work correctly now. | 
				
			||||
 | 
				
			||||
- New maintainer <monnier@gnu.org>. | 
				
			||||
@ -1,125 +0,0 @@
					 | 
				
			||||
;;; ghc-core.el --- Syntax highlighting module for GHC Core -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright (C) 2010  Johan Tibell | 
				
			||||
 | 
				
			||||
;; Author: Johan Tibell <johan.tibell@gmail.com> | 
				
			||||
 | 
				
			||||
;; 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 GNU Emacs; see the file COPYING.  If not, write to | 
				
			||||
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 
				
			||||
;; Boston, MA 02110-1301, USA. | 
				
			||||
 | 
				
			||||
;;; Commentary: | 
				
			||||
 | 
				
			||||
;; Purpose: | 
				
			||||
;; | 
				
			||||
;; To make it easier to read GHC Core output by providing highlighting | 
				
			||||
;; and removal of commonly ignored annotations. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
(require 'haskell-mode) | 
				
			||||
(require 'haskell-font-lock) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defgroup ghc-core nil | 
				
			||||
  "Major mode for viewing pretty printed GHC Core output." | 
				
			||||
  :link '(custom-manual "(haskell-mode)") | 
				
			||||
  :group 'haskell | 
				
			||||
  :prefix "ghc-core-") | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom ghc-core-program | 
				
			||||
  "ghc" | 
				
			||||
  "Name of the GHC executable (excluding any arguments)." | 
				
			||||
  :type 'string | 
				
			||||
  :group 'ghc-core) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom ghc-core-program-args | 
				
			||||
  '("-O2") | 
				
			||||
  "Additional options to be passed to GHC when generating core output. | 
				
			||||
GHC (see variable `ghc-core-program') is invoked with the basic | 
				
			||||
command line options \"-ddump-simpl -c <source-file>\" | 
				
			||||
followed by the additional options defined here. | 
				
			||||
 | 
				
			||||
The following `-ddump-simpl` options might be of interest: | 
				
			||||
 | 
				
			||||
 - `-dsuppress-all' | 
				
			||||
 - `-dsuppress-uniques' | 
				
			||||
 - `-dsuppress-idinfo' | 
				
			||||
 - `-dsuppress-module-prefixes' | 
				
			||||
 - `-dsuppress-type-signatures' | 
				
			||||
 - `-dsuppress-type-applications' | 
				
			||||
 - `-dsuppress-coercions' | 
				
			||||
 | 
				
			||||
See `M-x manual-entry RET ghc' for more details." | 
				
			||||
  :type '(repeat (string :tag "Argument")) | 
				
			||||
  :group 'ghc-core) | 
				
			||||
 | 
				
			||||
(define-obsolete-variable-alias 'ghc-core-create-options 'ghc-core-program-args | 
				
			||||
  "haskell-mode 13.7") | 
				
			||||
 | 
				
			||||
(defun ghc-core-clean-region (start end) | 
				
			||||
  "Remove commonly ignored annotations and namespace prefixes | 
				
			||||
in the region between START and END." | 
				
			||||
  (interactive "r") | 
				
			||||
  (save-restriction | 
				
			||||
    (narrow-to-region start end) | 
				
			||||
    (goto-char (point-min)) | 
				
			||||
    (while (search-forward-regexp "GHC\.[^\.]*\." nil t) | 
				
			||||
      (replace-match "" nil t)) | 
				
			||||
    (goto-char (point-min)) | 
				
			||||
    (while (flush-lines "^ *GblId *$" nil)) | 
				
			||||
    (goto-char (point-min)) | 
				
			||||
    (while (flush-lines "^ *LclId *$" nil)) | 
				
			||||
    (goto-char (point-min)) | 
				
			||||
    (while (flush-lines (concat "^ *\\[\\(?:Arity [0-9]+\\|NoCafRefs\\|" | 
				
			||||
                                "Str: DmdType\\|Worker \\)" | 
				
			||||
                                "\\([^]]*\\n?\\).*\\] *$") nil)) | 
				
			||||
    (goto-char (point-min)) | 
				
			||||
    (while (search-forward "Main." nil t) (replace-match "" nil t)))) | 
				
			||||
 | 
				
			||||
(defun ghc-core-clean-buffer () | 
				
			||||
  "Remove commonly ignored annotations and namespace prefixes | 
				
			||||
in the current buffer." | 
				
			||||
  (interactive) | 
				
			||||
  (ghc-core-clean-region (point-min) (point-max))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun ghc-core-create-core () | 
				
			||||
  "Compile and load the current buffer as tidy core." | 
				
			||||
  (interactive) | 
				
			||||
  (save-buffer) | 
				
			||||
  (let* ((core-buffer (generate-new-buffer "ghc-core")) | 
				
			||||
         (neh (lambda () (kill-buffer core-buffer)))) | 
				
			||||
    (add-hook 'next-error-hook neh) | 
				
			||||
    (apply #'call-process ghc-core-program nil core-buffer nil | 
				
			||||
           "-ddump-simpl" "-c" (buffer-file-name) ghc-core-program-args) | 
				
			||||
    (display-buffer core-buffer) | 
				
			||||
    (with-current-buffer core-buffer | 
				
			||||
      (ghc-core-mode)) | 
				
			||||
    (remove-hook 'next-error-hook neh))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(add-to-list 'auto-mode-alist '("\\.hcr\\'" . ghc-core-mode)) | 
				
			||||
;;;###autoload | 
				
			||||
(add-to-list 'auto-mode-alist '("\\.dump-simpl\\'" . ghc-core-mode)) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(define-derived-mode ghc-core-mode haskell-mode "GHC-Core" | 
				
			||||
  "Major mode for GHC Core files.") | 
				
			||||
 | 
				
			||||
(provide 'ghc-core) | 
				
			||||
;;; ghc-core.el ends here | 
				
			||||
@ -1,68 +0,0 @@
					 | 
				
			||||
;;; ghci-script-mode.el --- GHCi scripts major mode -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright (c) 2014 Chris Done. All rights reserved. | 
				
			||||
 | 
				
			||||
;; 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/>. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'haskell) | 
				
			||||
 | 
				
			||||
(defvar ghci-script-mode-keywords | 
				
			||||
  ;; The comment syntax can't be described simply in syntax-table. | 
				
			||||
  ;; We could use font-lock-syntactic-keywords, but is it worth it? | 
				
			||||
  '(("^[ \t]*--.*" . font-lock-comment-face) | 
				
			||||
    ("^ *\\([^ \t:]+\\):" (1 font-lock-keyword-face)) | 
				
			||||
    ("^:[a-z{]+ *\\+" . font-lock-keyword-face) | 
				
			||||
    ("^:[a-z{]+ " . font-lock-keyword-face))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(define-derived-mode ghci-script-mode text-mode "GHCi-Script" | 
				
			||||
  "Major mode for working with .ghci files." | 
				
			||||
  (set (make-local-variable 'adaptive-fill-mode) nil) | 
				
			||||
  (set (make-local-variable 'comment-start) "-- ") | 
				
			||||
  (set (make-local-variable 'comment-padding) 0) | 
				
			||||
  (set (make-local-variable 'comment-start-skip) "[-{]-[ \t]*") | 
				
			||||
  (set (make-local-variable 'comment-end) "") | 
				
			||||
  (set (make-local-variable 'comment-end-skip) "[ \t]*\\(-}\\|\\s>\\)") | 
				
			||||
  (set (make-local-variable 'indent-line-function) 'haskell-mode-suggest-indent-choice) | 
				
			||||
  (set (make-local-variable 'font-lock-defaults) | 
				
			||||
       '(ghci-script-mode-keywords t t nil nil)) | 
				
			||||
  (set (make-local-variable 'indent-tabs-mode) nil) | 
				
			||||
  (set (make-local-variable 'tab-width) 8) | 
				
			||||
  (when (boundp 'electric-indent-inhibit) | 
				
			||||
    (setq electric-indent-inhibit t)) | 
				
			||||
  (set (make-local-variable 'dabbrev-case-fold-search) nil) | 
				
			||||
  (set (make-local-variable 'dabbrev-case-distinction) nil) | 
				
			||||
  (set (make-local-variable 'dabbrev-case-replace) nil) | 
				
			||||
  (set (make-local-variable 'dabbrev-abbrev-char-regexp) "\\sw\\|[.]") | 
				
			||||
  (setq haskell-literate nil)) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(add-to-list 'auto-mode-alist '("\\.ghci\\'" . ghci-script-mode)) | 
				
			||||
 | 
				
			||||
(define-key ghci-script-mode-map (kbd "C-c C-l") 'ghci-script-mode-load) | 
				
			||||
 | 
				
			||||
(defun ghci-script-mode-load () | 
				
			||||
  "Load the current script file into the GHCi session." | 
				
			||||
  (interactive) | 
				
			||||
  (let ((buffer (haskell-session-interactive-buffer (haskell-session))) | 
				
			||||
        (filename (buffer-file-name))) | 
				
			||||
    (save-buffer) | 
				
			||||
    (with-current-buffer buffer | 
				
			||||
      (set-marker haskell-interactive-mode-prompt-start (point-max)) | 
				
			||||
      (haskell-interactive-mode-run-expr | 
				
			||||
       (concat ":script " filename))))) | 
				
			||||
 | 
				
			||||
(provide 'ghci-script-mode) | 
				
			||||
@ -1,231 +0,0 @@
					 | 
				
			||||
;;; haskell-align-imports.el --- Align the import lines in a Haskell file -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright (C) 2010  Chris Done | 
				
			||||
 | 
				
			||||
;; Author: Chris Done <chrisdone@gmail.com> | 
				
			||||
 | 
				
			||||
;; This file is not part of GNU Emacs. | 
				
			||||
 | 
				
			||||
;; This program 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 of | 
				
			||||
;; the License, or (at your option) any later version. | 
				
			||||
 | 
				
			||||
;; This program 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: | 
				
			||||
 | 
				
			||||
;; Consider the following imports list: | 
				
			||||
;; | 
				
			||||
;; import One | 
				
			||||
;; import Two as A | 
				
			||||
;; import qualified Three | 
				
			||||
;; import qualified Four as PRELUDE | 
				
			||||
;; import Five (A) | 
				
			||||
;; import Six (A,B) | 
				
			||||
;; import qualified Seven (A,B) | 
				
			||||
;; import "abc" Eight | 
				
			||||
;; import "abc" Nine as TWO | 
				
			||||
;; import qualified "abc" Ten | 
				
			||||
;; import qualified "defg" Eleven as PRELUDE | 
				
			||||
;; import "barmu" Twelve (A) | 
				
			||||
;; import "zotconpop" Thirteen (A,B) | 
				
			||||
;; import qualified "z" Fourteen (A,B) | 
				
			||||
;; import Fifteen hiding (A) | 
				
			||||
;; import Sixteen as TWO hiding (A) | 
				
			||||
;; import qualified Seventeen hiding (A) | 
				
			||||
;; import qualified Eighteen as PRELUDE hiding (A) | 
				
			||||
;; import "abc" Nineteen hiding (A) | 
				
			||||
;; import "abc" Twenty as TWO hiding (A) | 
				
			||||
;; | 
				
			||||
;; When haskell-align-imports is run within the same buffer, the | 
				
			||||
;; import list is transformed to: | 
				
			||||
;; | 
				
			||||
;; import "abc"            Eight | 
				
			||||
;; import qualified        Eighteen as PRELUDE hiding (A) | 
				
			||||
;; import qualified "defg" Eleven as PRELUDE | 
				
			||||
;; import                  Fifteen hiding (A) | 
				
			||||
;; import                  Five (A) | 
				
			||||
;; import qualified        Four as PRELUDE | 
				
			||||
;; import qualified "z"    Fourteen  (A,B) | 
				
			||||
;; import "abc"            Nine as TWO | 
				
			||||
;; import "abc"            Nineteen hiding (A) | 
				
			||||
;; import                  One | 
				
			||||
;; import qualified        Seven (A,B) | 
				
			||||
;; import qualified        Seventeen hiding (A) | 
				
			||||
;; import                  Six (A,B) | 
				
			||||
;; import                  Sixteen as TWO hiding (A) | 
				
			||||
;; import qualified "abc"  Ten | 
				
			||||
;; import "zotconpop"      Thirteen (A,B) | 
				
			||||
;; import qualified        Three | 
				
			||||
;; import "barmu"          Twelve (A) | 
				
			||||
;; import "abc"            Twenty as TWO hiding (A) | 
				
			||||
;; import                  Two as A | 
				
			||||
;; | 
				
			||||
;; If you want everything after module names to be padded out, too, | 
				
			||||
;; customize `haskell-align-imports-pad-after-name', and you'll get: | 
				
			||||
;; | 
				
			||||
;; import                  One | 
				
			||||
;; import                  Two       as A | 
				
			||||
;; import qualified        Three | 
				
			||||
;; import qualified        Four      as PRELUDE | 
				
			||||
;; import                  Five      (A) | 
				
			||||
;; import                  Six       (A,B) | 
				
			||||
;; import qualified        Seven     (A,B) | 
				
			||||
;; import "abc"            Eight | 
				
			||||
;; import "abc"            Nine      as TWO | 
				
			||||
;; import qualified "abc"  Ten | 
				
			||||
;; import qualified "defg" Eleven    as PRELUDE | 
				
			||||
;; import "barmu"          Twelve    (A) | 
				
			||||
;; import "zotconpop"      Thirteen  (A,B) | 
				
			||||
;; import qualified "z"    Fourteen  (A,B) | 
				
			||||
;; import                  Fifteen   hiding (A) | 
				
			||||
;; import                  Sixteen   as TWO hiding (A) | 
				
			||||
;; import qualified        Seventeen hiding (A) | 
				
			||||
;; import qualified        Eighteen  as PRELUDE hiding (A) | 
				
			||||
;; import "abc"            Nineteen  hiding (A) | 
				
			||||
;; import "abc"            Twenty    as TWO hiding (A) | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'cl-lib) | 
				
			||||
 | 
				
			||||
(defvar haskell-align-imports-regexp | 
				
			||||
  (concat "^\\(import[ ]+\\)" | 
				
			||||
          "\\(qualified \\)?" | 
				
			||||
          "[ ]*\\(\"[^\"]*\" \\)?" | 
				
			||||
          "[ ]*\\([A-Za-z0-9_.']+\\)" | 
				
			||||
          "[ ]*\\([ ]*as [A-Z][^ ]*\\)?" | 
				
			||||
          "[ ]*\\((.*)\\)?" | 
				
			||||
          "\\([ ]*hiding (.*)\\)?" | 
				
			||||
          "\\( -- .*\\)?[ ]*$") | 
				
			||||
  "Regex used for matching components of an import.") | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-align-imports-pad-after-name | 
				
			||||
  nil | 
				
			||||
  "Pad layout after the module name also." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-align-imports () | 
				
			||||
  "Align all the imports in the buffer." | 
				
			||||
  (interactive) | 
				
			||||
  (when (haskell-align-imports-line-match) | 
				
			||||
    (save-excursion | 
				
			||||
      (goto-char (point-min)) | 
				
			||||
      (let* ((imports (haskell-align-imports-collect)) | 
				
			||||
             (padding (haskell-align-imports-padding imports))) | 
				
			||||
        (mapc (lambda (x) | 
				
			||||
                (goto-char (cdr x)) | 
				
			||||
                (delete-region (point) (line-end-position)) | 
				
			||||
                (insert (haskell-align-imports-chomp | 
				
			||||
                         (haskell-align-imports-fill padding (car x))))) | 
				
			||||
              imports)))) | 
				
			||||
  nil) | 
				
			||||
 | 
				
			||||
(defun haskell-align-imports-line-match () | 
				
			||||
  "Try to match the current line as a regexp." | 
				
			||||
  (let ((line (buffer-substring-no-properties (line-beginning-position) | 
				
			||||
                                              (line-end-position)))) | 
				
			||||
    (if (string-match "^import " line) | 
				
			||||
        line | 
				
			||||
      nil))) | 
				
			||||
 | 
				
			||||
(defun haskell-align-imports-collect () | 
				
			||||
  "Collect a list of mark / import statement pairs." | 
				
			||||
  (let ((imports '())) | 
				
			||||
    (while (not (or (equal (point) (point-max)) (haskell-align-imports-after-imports-p))) | 
				
			||||
      (let ((line (haskell-align-imports-line-match-it))) | 
				
			||||
        (when line | 
				
			||||
          (let ((match | 
				
			||||
                 (haskell-align-imports-merge-parts | 
				
			||||
                  (cl-loop for i from 1 to 8 | 
				
			||||
                           collect (haskell-align-imports-chomp (match-string i line)))))) | 
				
			||||
            (setq imports (cons (cons match (line-beginning-position)) | 
				
			||||
                                imports))))) | 
				
			||||
      (forward-line)) | 
				
			||||
    imports)) | 
				
			||||
 | 
				
			||||
(defun haskell-align-imports-merge-parts (l) | 
				
			||||
  "Merge together parts of an import statement that shouldn't be separated." | 
				
			||||
  (let ((parts (apply #'vector l)) | 
				
			||||
        (join (lambda (ls) | 
				
			||||
                (cl-reduce (lambda (a b) | 
				
			||||
                             (concat a | 
				
			||||
                                     (if (and (> (length a) 0) | 
				
			||||
                                              (> (length b) 0)) | 
				
			||||
                                         " " | 
				
			||||
                                       "") | 
				
			||||
                                     b)) | 
				
			||||
                           ls)))) | 
				
			||||
    (if haskell-align-imports-pad-after-name | 
				
			||||
        (list (funcall join (list (aref parts 0) | 
				
			||||
                                  (aref parts 1) | 
				
			||||
                                  (aref parts 2))) | 
				
			||||
              (aref parts 3) | 
				
			||||
              (funcall join (list (aref parts 4) | 
				
			||||
                                  (aref parts 5) | 
				
			||||
                                  (aref parts 6))) | 
				
			||||
              (aref parts 7)) | 
				
			||||
      (list (funcall join (list (aref parts 0) | 
				
			||||
                                (aref parts 1) | 
				
			||||
                                (aref parts 2))) | 
				
			||||
            (funcall join (list (aref parts 3) | 
				
			||||
                                (aref parts 4) | 
				
			||||
                                (aref parts 5) | 
				
			||||
                                (aref parts 6) | 
				
			||||
                                (aref parts 7))))))) | 
				
			||||
 | 
				
			||||
(defun haskell-align-imports-chomp (str) | 
				
			||||
  "Chomp leading and tailing whitespace from STR." | 
				
			||||
  (if str | 
				
			||||
      (replace-regexp-in-string "\\(^[[:space:]\n]*\\|[[:space:]\n]*$\\)" "" | 
				
			||||
                                str) | 
				
			||||
    "")) | 
				
			||||
 | 
				
			||||
(defun haskell-align-imports-padding (imports) | 
				
			||||
  "Find the padding for each part of the import statements." | 
				
			||||
  (if (null imports) | 
				
			||||
      imports | 
				
			||||
    (cl-reduce (lambda (a b) (cl-mapcar #'max a b)) | 
				
			||||
               (mapcar (lambda (x) (mapcar #'length (car x))) | 
				
			||||
                       imports)))) | 
				
			||||
 | 
				
			||||
(defun haskell-align-imports-fill (padding line) | 
				
			||||
  "Fill an import line using the padding worked out from all statements." | 
				
			||||
  (mapconcat #'identity | 
				
			||||
             (cl-mapcar (lambda (pad part) | 
				
			||||
                          (if (> (length part) 0) | 
				
			||||
                              (concat part (make-string (- pad (length part)) ? )) | 
				
			||||
                            (make-string pad ? ))) | 
				
			||||
                        padding | 
				
			||||
                        line) | 
				
			||||
             " ")) | 
				
			||||
 | 
				
			||||
(defun haskell-align-imports-line-match-it () | 
				
			||||
  "Try to match the current line as a regexp." | 
				
			||||
  (let ((line (buffer-substring-no-properties (line-beginning-position) | 
				
			||||
                                              (line-end-position)))) | 
				
			||||
    (if (string-match haskell-align-imports-regexp line) | 
				
			||||
        line | 
				
			||||
      nil))) | 
				
			||||
 | 
				
			||||
(defun haskell-align-imports-after-imports-p () | 
				
			||||
  "Are we after the imports list?" | 
				
			||||
  (save-excursion | 
				
			||||
    (goto-char (line-beginning-position)) | 
				
			||||
    (not (not (search-forward-regexp "\\( = \\|\\<instance\\>\\| :: \\| ∷ \\)" | 
				
			||||
                                     (line-end-position) t 1))))) | 
				
			||||
 | 
				
			||||
(provide 'haskell-align-imports) | 
				
			||||
 | 
				
			||||
;;; haskell-align-imports.el ends here | 
				
			||||
@ -1,974 +0,0 @@
					 | 
				
			||||
;;; haskell-cabal.el --- Support for Cabal packages -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright (C) 2007, 2008  Stefan Monnier | 
				
			||||
 | 
				
			||||
;; Author: Stefan Monnier <monnier@iro.umontreal.ca> | 
				
			||||
 | 
				
			||||
;; 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 GNU Emacs; see the file COPYING.  If not, write to | 
				
			||||
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 
				
			||||
;; Boston, MA 02110-1301, USA. | 
				
			||||
 | 
				
			||||
;;; Commentary: | 
				
			||||
 | 
				
			||||
;; Todo: | 
				
			||||
 | 
				
			||||
;; - distinguish continued lines from indented lines. | 
				
			||||
;; - indent-line-function. | 
				
			||||
;; - outline-minor-mode. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
;; (defun haskell-cabal-extract-fields-from-doc () | 
				
			||||
;;   (require 'xml) | 
				
			||||
;;   (with-no-warnings (require 'cl)) | 
				
			||||
;;   (let ((section (completing-read | 
				
			||||
;;                   "Section: " | 
				
			||||
;;                   '("general-fields" "library" "executable" "buildinfo")))) | 
				
			||||
;;     (goto-char (point-min)) | 
				
			||||
;;     (search-forward (concat "<sect3 id=\"" section "\">"))) | 
				
			||||
;;   (let* ((xml (xml-parse-region | 
				
			||||
;;                (progn (search-forward "<variablelist>") (match-beginning 0)) | 
				
			||||
;;                (progn (search-forward "</variablelist>") (point)))) | 
				
			||||
;;          (varlist (remove-if-not 'consp (cddar xml))) | 
				
			||||
;;          (syms (mapcar (lambda (entry) (caddr (assq 'literal (assq 'term entry)))) | 
				
			||||
;;                        varlist)) | 
				
			||||
;;          (fields (mapcar (lambda (sym) (substring-no-properties sym 0 -1)) syms))) | 
				
			||||
;;     fields)) | 
				
			||||
 | 
				
			||||
(require 'cl-lib) | 
				
			||||
(require 'haskell-utils) | 
				
			||||
 | 
				
			||||
(defconst haskell-cabal-general-fields | 
				
			||||
  ;; Extracted with (haskell-cabal-extract-fields-from-doc "general-fields") | 
				
			||||
  '("name" "version" "cabal-version" "license" "license-file" "copyright" | 
				
			||||
    "author" "maintainer" "stability" "homepage" "package-url" "synopsis" | 
				
			||||
    "description" "category" "tested-with" "build-depends" "data-files" | 
				
			||||
    "extra-source-files" "extra-tmp-files")) | 
				
			||||
 | 
				
			||||
(defconst haskell-cabal-library-fields | 
				
			||||
  ;; Extracted with (haskell-cabal-extract-fields-from-doc "library") | 
				
			||||
  '("exposed-modules")) | 
				
			||||
 | 
				
			||||
(defconst haskell-cabal-executable-fields | 
				
			||||
  ;; Extracted with (haskell-cabal-extract-fields-from-doc "executable") | 
				
			||||
  '("executable" "main-is")) | 
				
			||||
 | 
				
			||||
(defconst haskell-cabal-buildinfo-fields | 
				
			||||
  ;; Extracted with (haskell-cabal-extract-fields-from-doc "buildinfo") | 
				
			||||
  '("buildable" "other-modules" "hs-source-dirs" "extensions" "ghc-options" | 
				
			||||
    "ghc-prof-options" "hugs-options" "nhc-options" "includes" | 
				
			||||
    "install-includes" "include-dirs" "c-sources" "extra-libraries" | 
				
			||||
    "extra-lib-dirs" "cc-options" "ld-options" "frameworks")) | 
				
			||||
 | 
				
			||||
(defvar haskell-cabal-mode-syntax-table | 
				
			||||
  (let ((st (make-syntax-table))) | 
				
			||||
    ;; The comment syntax can't be described simply in syntax-table. | 
				
			||||
    ;; We could use font-lock-syntactic-keywords, but is it worth it? | 
				
			||||
    ;; (modify-syntax-entry ?-  ". 12" st) | 
				
			||||
    (modify-syntax-entry ?\n ">" st) | 
				
			||||
    (modify-syntax-entry ?. "w"  st) | 
				
			||||
    (modify-syntax-entry ?- "w"  st) | 
				
			||||
    st)) | 
				
			||||
 | 
				
			||||
(defvar haskell-cabal-font-lock-keywords | 
				
			||||
  ;; The comment syntax can't be described simply in syntax-table. | 
				
			||||
  ;; We could use font-lock-syntactic-keywords, but is it worth it? | 
				
			||||
  '(("^[ \t]*--.*" . font-lock-comment-face) | 
				
			||||
    ("^ *\\([^ \t:]+\\):" (1 font-lock-keyword-face)) | 
				
			||||
    ("^\\(Library\\)[ \t]*\\({\\|$\\)" (1 font-lock-keyword-face)) | 
				
			||||
    ("^\\(Executable\\|Test-Suite\\|Benchmark\\)[ \t]+\\([^\n \t]*\\)" | 
				
			||||
     (1 font-lock-keyword-face) (2 font-lock-function-name-face)) | 
				
			||||
    ("^\\(Flag\\)[ \t]+\\([^\n \t]*\\)" | 
				
			||||
     (1 font-lock-keyword-face) (2 font-lock-constant-face)) | 
				
			||||
    ("^\\(Source-Repository\\)[ \t]+\\(head\\|this\\)" | 
				
			||||
     (1 font-lock-keyword-face) (2 font-lock-constant-face)) | 
				
			||||
    ("^ *\\(if\\)[ \t]+.*\\({\\|$\\)" (1 font-lock-keyword-face)) | 
				
			||||
    ("^ *\\(}[ \t]*\\)?\\(else\\)[ \t]*\\({\\|$\\)" | 
				
			||||
     (2 font-lock-keyword-face)))) | 
				
			||||
 | 
				
			||||
(defvar haskell-cabal-buffers nil | 
				
			||||
  "List of Cabal buffers.") | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-buffers-clean (&optional buffer) | 
				
			||||
  (let ((bufs ())) | 
				
			||||
    (dolist (buf haskell-cabal-buffers) | 
				
			||||
      (if (and (buffer-live-p buf) (not (eq buf buffer)) | 
				
			||||
               (with-current-buffer buf (derived-mode-p 'haskell-cabal-mode))) | 
				
			||||
          (push buf bufs))) | 
				
			||||
    (setq haskell-cabal-buffers bufs))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-unregister-buffer () | 
				
			||||
  (haskell-cabal-buffers-clean (current-buffer))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(add-to-list 'auto-mode-alist '("\\.cabal\\'" . haskell-cabal-mode)) | 
				
			||||
 | 
				
			||||
(defvar haskell-cabal-mode-map | 
				
			||||
  (let ((map (make-sparse-keymap))) | 
				
			||||
    (define-key map (kbd "C-c C-s") 'haskell-cabal-subsection-arrange-lines) | 
				
			||||
    (define-key map (kbd "C-M-n") 'haskell-cabal-next-section) | 
				
			||||
    (define-key map (kbd "C-M-p") 'haskell-cabal-previous-section) | 
				
			||||
    (define-key map (kbd "M-n") 'haskell-cabal-next-subsection) | 
				
			||||
    (define-key map (kbd "M-p") 'haskell-cabal-previous-subsection) | 
				
			||||
    (define-key map (kbd "C-<down>") 'haskell-cabal-next-subsection) | 
				
			||||
    (define-key map (kbd "C-<up>") 'haskell-cabal-previous-subsection) | 
				
			||||
    (define-key map (kbd "C-c C-f") 'haskell-cabal-find-or-create-source-file) | 
				
			||||
    (define-key map (kbd "M-g l") 'haskell-cabal-goto-library-section) | 
				
			||||
    (define-key map (kbd "M-g e") 'haskell-cabal-goto-executable-section) | 
				
			||||
    (define-key map (kbd "M-g b") 'haskell-cabal-goto-benchmark-section) | 
				
			||||
    (define-key map (kbd "M-g t") 'haskell-cabal-goto-test-suite-section) | 
				
			||||
    map)) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(define-derived-mode haskell-cabal-mode fundamental-mode "Haskell-Cabal" | 
				
			||||
  "Major mode for Cabal package description files." | 
				
			||||
  (set (make-local-variable 'font-lock-defaults) | 
				
			||||
       '(haskell-cabal-font-lock-keywords t t nil nil)) | 
				
			||||
  (add-to-list 'haskell-cabal-buffers (current-buffer)) | 
				
			||||
  (add-hook 'change-major-mode-hook 'haskell-cabal-unregister-buffer nil 'local) | 
				
			||||
  (add-hook 'kill-buffer-hook 'haskell-cabal-unregister-buffer nil 'local) | 
				
			||||
  (set (make-local-variable 'comment-start) "-- ") | 
				
			||||
  (set (make-local-variable 'comment-start-skip) "\\(^[ \t]*\\)--[ \t]*") | 
				
			||||
  (set (make-local-variable 'comment-end) "") | 
				
			||||
  (set (make-local-variable 'comment-end-skip) "[ \t]*\\(\\s>\\|\n\\)") | 
				
			||||
  (set (make-local-variable 'indent-line-function) 'haskell-cabal-indent-line) | 
				
			||||
  (setq indent-tabs-mode nil) | 
				
			||||
  ) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-get-setting (name) | 
				
			||||
  (save-excursion | 
				
			||||
    (let ((case-fold-search t)) | 
				
			||||
      (goto-char (point-min)) | 
				
			||||
      (when (re-search-forward | 
				
			||||
             (concat "^[ \t]*" (regexp-quote name) | 
				
			||||
                     ":[ \t]*\\(.*\\(\n[ \t]+[ \t\n].*\\)*\\)") | 
				
			||||
             nil t) | 
				
			||||
        (let ((val (match-string 1)) | 
				
			||||
              (start 1)) | 
				
			||||
          (when (match-end 2)             ;Multiple lines. | 
				
			||||
            ;; The documentation is not very precise about what to do about | 
				
			||||
            ;; the \n and the indentation: are they part of the value or | 
				
			||||
            ;; the encoding?  I take the point of view that \n is part of | 
				
			||||
            ;; the value (so that values can span multiple lines as well), | 
				
			||||
            ;; and that only the first char in the indentation is part of | 
				
			||||
            ;; the encoding, the rest is part of the value (otherwise, lines | 
				
			||||
            ;; in the value cannot start with spaces or tabs). | 
				
			||||
            (while (string-match "^[ \t]\\(?:\\.$\\)?" val start) | 
				
			||||
              (setq start (1+ (match-beginning 0))) | 
				
			||||
              (setq val (replace-match "" t t val)))) | 
				
			||||
          val))))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-cabal-guess-setting (name) | 
				
			||||
  "Guess the specified setting of this project. | 
				
			||||
If there is no valid .cabal file to get the setting from (or | 
				
			||||
there is no corresponding setting with that name in the .cabal | 
				
			||||
file), then this function returns nil." | 
				
			||||
  (interactive) | 
				
			||||
  (when (and name buffer-file-name) | 
				
			||||
    (let ((cabal-file (haskell-cabal-find-file))) | 
				
			||||
      (when (and cabal-file (file-readable-p cabal-file)) | 
				
			||||
        (with-temp-buffer | 
				
			||||
          (insert-file-contents cabal-file) | 
				
			||||
          (haskell-cabal-get-setting name)))))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-cabal-get-dir (&optional use-defaults) | 
				
			||||
  "Get the Cabal dir for a new project. Various ways of figuring this out, | 
				
			||||
   and indeed just prompting the user. Do them all." | 
				
			||||
  (let* ((file (haskell-cabal-find-file)) | 
				
			||||
         (dir (if file (file-name-directory file) default-directory))) | 
				
			||||
    (if use-defaults | 
				
			||||
	dir | 
				
			||||
	(haskell-utils-read-directory-name | 
				
			||||
	 (format "Cabal dir%s: " (if file (format " (guessed from %s)" (file-relative-name file)) "")) | 
				
			||||
	 dir)))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-compute-checksum (dir) | 
				
			||||
  "Compute MD5 checksum of package description file in DIR. | 
				
			||||
Return nil if no Cabal description file could be located via | 
				
			||||
`haskell-cabal-find-pkg-desc'." | 
				
			||||
  (let ((cabal-file (haskell-cabal-find-pkg-desc dir))) | 
				
			||||
    (when cabal-file | 
				
			||||
      (with-temp-buffer | 
				
			||||
        (insert-file-contents cabal-file) | 
				
			||||
        (md5 (buffer-string)))))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-find-file (&optional dir) | 
				
			||||
  "Search for package description file upwards starting from DIR. | 
				
			||||
If DIR is nil, `default-directory' is used as starting point for | 
				
			||||
directory traversal.  Upward traversal is aborted if file owner | 
				
			||||
changes.  Uses`haskell-cabal-find-pkg-desc' internally." | 
				
			||||
  (let ((use-dir (or dir default-directory))) | 
				
			||||
    (while (and use-dir (not (file-directory-p use-dir))) | 
				
			||||
      (setq use-dir (file-name-directory (directory-file-name use-dir)))) | 
				
			||||
    (when use-dir | 
				
			||||
      (catch 'found | 
				
			||||
        (let ((user (nth 2 (file-attributes use-dir))) | 
				
			||||
              ;; Abbreviate, so as to stop when we cross ~/. | 
				
			||||
              (root (abbreviate-file-name use-dir))) | 
				
			||||
          ;; traverse current dir up to root as long as file owner doesn't change | 
				
			||||
          (while (and root (equal user (nth 2 (file-attributes root)))) | 
				
			||||
            (let ((cabal-file (haskell-cabal-find-pkg-desc root))) | 
				
			||||
              (when cabal-file | 
				
			||||
                (throw 'found cabal-file))) | 
				
			||||
 | 
				
			||||
            (let ((proot (file-name-directory (directory-file-name root)))) | 
				
			||||
              (if (equal proot root) ;; fix-point reached? | 
				
			||||
                  (throw 'found nil) | 
				
			||||
                (setq root proot)))) | 
				
			||||
          nil))))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-find-pkg-desc (dir &optional allow-multiple) | 
				
			||||
  "Find a package description file in the directory DIR. | 
				
			||||
Returns nil if none or multiple \".cabal\" files were found.  If | 
				
			||||
ALLOW-MULTIPLE is non nil, in case of multiple \".cabal\" files, | 
				
			||||
a list is returned instead of failing with a nil result." | 
				
			||||
  ;; This is basically a port of Cabal's | 
				
			||||
  ;; Distribution.Simple.Utils.findPackageDesc function | 
				
			||||
  ;;  http://hackage.haskell.org/packages/archive/Cabal/1.16.0.3/doc/html/Distribution-Simple-Utils.html | 
				
			||||
  ;; but without the exception throwing. | 
				
			||||
  (let* ((cabal-files | 
				
			||||
          (cl-remove-if 'file-directory-p | 
				
			||||
                        (cl-remove-if-not 'file-exists-p | 
				
			||||
                                          (directory-files dir t ".\\.cabal\\'"))))) | 
				
			||||
    (cond | 
				
			||||
     ((= (length cabal-files) 1) (car cabal-files)) ;; exactly one candidate found | 
				
			||||
     (allow-multiple cabal-files) ;; pass-thru multiple candidates | 
				
			||||
     (t nil)))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-find-dir (&optional dir) | 
				
			||||
  "Like `haskell-cabal-find-file' but returns directory instead. | 
				
			||||
See `haskell-cabal-find-file' for meaning of DIR argument." | 
				
			||||
  (let ((cabal-file (haskell-cabal-find-file dir))) | 
				
			||||
    (when cabal-file | 
				
			||||
      (file-name-directory cabal-file)))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-cabal-visit-file (other-window) | 
				
			||||
  "Locate and visit package description file for file visited by current buffer. | 
				
			||||
This uses `haskell-cabal-find-file' to locate the closest | 
				
			||||
\".cabal\" file and open it.  This command assumes a common Cabal | 
				
			||||
project structure where the \".cabal\" file is in the top-folder | 
				
			||||
of the project, and all files related to the project are in or | 
				
			||||
below the top-folder.  If called with non-nil prefix argument | 
				
			||||
OTHER-WINDOW use `find-file-other-window'." | 
				
			||||
  (interactive "P") | 
				
			||||
  ;; Note: We aren't allowed to rely on haskell-session here (which, | 
				
			||||
  ;; in pathological cases, can have a different .cabal file | 
				
			||||
  ;; associated with the current buffer) | 
				
			||||
  (if buffer-file-name | 
				
			||||
      (let ((cabal-file (haskell-cabal-find-file (file-name-directory buffer-file-name)))) | 
				
			||||
        (if cabal-file | 
				
			||||
            (if other-window | 
				
			||||
                (find-file-other-window cabal-file) | 
				
			||||
              (find-file cabal-file)) | 
				
			||||
          (error "Could not locate \".cabal\" file for %S" buffer-file-name))) | 
				
			||||
    (error "Cannot locate \".cabal\" file for buffers not visiting any file"))) | 
				
			||||
 | 
				
			||||
(defvar haskell-cabal-commands | 
				
			||||
  '("install" | 
				
			||||
    "update" | 
				
			||||
    "list" | 
				
			||||
    "info" | 
				
			||||
    "upgrade" | 
				
			||||
    "fetch" | 
				
			||||
    "unpack" | 
				
			||||
    "check" | 
				
			||||
    "sdist" | 
				
			||||
    "upload" | 
				
			||||
    "report" | 
				
			||||
    "init" | 
				
			||||
    "configure" | 
				
			||||
    "build" | 
				
			||||
    "copy" | 
				
			||||
    "haddock" | 
				
			||||
    "clean" | 
				
			||||
    "hscolour" | 
				
			||||
    "register" | 
				
			||||
    "test" | 
				
			||||
    "help" | 
				
			||||
    "run")) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defgroup haskell-cabal nil | 
				
			||||
  "Haskell cabal files" | 
				
			||||
  :group 'haskell | 
				
			||||
) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-cabal-list-comma-position | 
				
			||||
  'before | 
				
			||||
  "Where to put the comma in lists" | 
				
			||||
  :safe t | 
				
			||||
  :group 'haskell-cabal | 
				
			||||
  :type '(choice (const before) | 
				
			||||
                 (const after))) | 
				
			||||
 | 
				
			||||
(defconst haskell-cabal-section-header-regexp "^[[:alnum:]]" ) | 
				
			||||
(defconst haskell-cabal-subsection-header-regexp "^[ \t]*[[:alnum:]]\\w*:") | 
				
			||||
(defconst haskell-cabal-comment-regexp "^[ \t]*--") | 
				
			||||
(defconst haskell-cabal-empty-regexp "^[ \t]*$") | 
				
			||||
(defconst haskell-cabal-conditional-regexp "^[ \t]*\\(\\if\\|else\\|}\\)") | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-classify-line () | 
				
			||||
  "Classify the current line into 'section-header 'subsection-header 'section-data 'comment and 'empty '" | 
				
			||||
  (save-excursion | 
				
			||||
    (beginning-of-line) | 
				
			||||
    (cond | 
				
			||||
     ((looking-at haskell-cabal-subsection-header-regexp ) 'subsection-header) | 
				
			||||
     ((looking-at haskell-cabal-section-header-regexp) 'section-header) | 
				
			||||
     ((looking-at haskell-cabal-comment-regexp) 'comment) | 
				
			||||
     ((looking-at haskell-cabal-empty-regexp ) 'empty) | 
				
			||||
     ((looking-at haskell-cabal-conditional-regexp ) 'conditional) | 
				
			||||
     (t 'section-data)))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-header-p () | 
				
			||||
  "Is the current line a section or subsection header?" | 
				
			||||
  (cl-case (haskell-cabal-classify-line) | 
				
			||||
    ((section-header subsection-header) t))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-section-header-p () | 
				
			||||
  "Is the current line a section or subsection header?" | 
				
			||||
  (cl-case (haskell-cabal-classify-line) | 
				
			||||
    ((section-header) t))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-section-beginning () | 
				
			||||
  "Find the beginning of the current section" | 
				
			||||
  (save-excursion | 
				
			||||
    (while (not (or (bobp) (haskell-cabal-section-header-p))) | 
				
			||||
      (forward-line -1)) | 
				
			||||
    (point))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-beginning-of-section () | 
				
			||||
  "go to the beginning of the section" | 
				
			||||
  (interactive) | 
				
			||||
  (goto-char (haskell-cabal-section-beginning)) | 
				
			||||
) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-section-end () | 
				
			||||
  "Find the end of the current section" | 
				
			||||
  (interactive) | 
				
			||||
  (save-excursion | 
				
			||||
    (if  (re-search-forward "\n\\([ \t]*\n\\)*[[:alnum:]]" nil t) | 
				
			||||
         (match-beginning 0) | 
				
			||||
         (point-max)))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-end-of-section () | 
				
			||||
  "go to the end of the section" | 
				
			||||
  (interactive) | 
				
			||||
  (goto-char (haskell-cabal-section-end))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-next-section () | 
				
			||||
  "Go to the next extion" | 
				
			||||
  (interactive) | 
				
			||||
  (when (haskell-cabal-section-header-p) (forward-line)) | 
				
			||||
  (while (not (or (eobp) (haskell-cabal-section-header-p))) | 
				
			||||
    (forward-line))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-previous-section () | 
				
			||||
  "Go to the next extion" | 
				
			||||
  (interactive) | 
				
			||||
  (when (haskell-cabal-section-header-p) (forward-line -1)) | 
				
			||||
  (while (not (or (bobp) (haskell-cabal-section-header-p))) | 
				
			||||
    (forward-line -1))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-subsection-end () | 
				
			||||
  "find the end of the current subsection" | 
				
			||||
  (save-excursion | 
				
			||||
    (haskell-cabal-beginning-of-subsection) | 
				
			||||
    (forward-line) | 
				
			||||
    (while (and (not (eobp)) | 
				
			||||
                (member (haskell-cabal-classify-line) '(empty section-data))) | 
				
			||||
      (forward-line)) | 
				
			||||
    (unless (eobp) (forward-line -1)) | 
				
			||||
    (while (and (equal  (haskell-cabal-classify-line) 'empty) | 
				
			||||
                (not (bobp))) | 
				
			||||
      (forward-line -1)) | 
				
			||||
    (end-of-line) | 
				
			||||
    (point))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-end-of-subsection () | 
				
			||||
  "go to the end of the current subsection" | 
				
			||||
  (interactive) | 
				
			||||
  (goto-char (haskell-cabal-subsection-end))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-section () | 
				
			||||
  "Get the name and data of the associated section" | 
				
			||||
  (save-excursion | 
				
			||||
    (haskell-cabal-beginning-of-section) | 
				
			||||
    (when (and (haskell-cabal-section-header-p) | 
				
			||||
               (looking-at "^\\(\\w+\\)[ \t]*\\(.*\\)$")) | 
				
			||||
      (list :name (match-string-no-properties 1) | 
				
			||||
            :value (match-string-no-properties 2) | 
				
			||||
            :beginning (match-beginning 0) | 
				
			||||
            :end (haskell-cabal-section-end))))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-subsection () | 
				
			||||
  "Get the name and bounds of of the current subsection" | 
				
			||||
  (save-excursion | 
				
			||||
    (haskell-cabal-beginning-of-subsection) | 
				
			||||
    (when  (looking-at "\\([ \t]*\\(\\w*\\):\\)[ \t]*") | 
				
			||||
      (list :name (match-string-no-properties 2) | 
				
			||||
            :beginning (match-end 0) | 
				
			||||
            :end (save-match-data (haskell-cabal-subsection-end)) | 
				
			||||
            :data-start-column (save-excursion (goto-char (match-end 0)) | 
				
			||||
                                               (current-column) | 
				
			||||
                                               ))))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-section-name (section) | 
				
			||||
  (plist-get section :name)) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-section-value (section) | 
				
			||||
  (plist-get section :value)) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-section-start (section) | 
				
			||||
  (plist-get section :beginning)) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-section-data-start-column (section) | 
				
			||||
  (plist-get section :data-start-column)) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-enum-targets () | 
				
			||||
  "Enumerate .cabal targets." | 
				
			||||
  (let ((cabal-file (haskell-cabal-find-file))) | 
				
			||||
    (when (and cabal-file (file-readable-p cabal-file)) | 
				
			||||
      (with-temp-buffer | 
				
			||||
	(insert-file-contents cabal-file) | 
				
			||||
	(haskell-cabal-mode) | 
				
			||||
	(let (matches) | 
				
			||||
	  (goto-char (point-min)) | 
				
			||||
	  (haskell-cabal-next-section) | 
				
			||||
	  (while (not (eobp)) | 
				
			||||
	    (if (haskell-cabal-source-section-p (haskell-cabal-section)) | 
				
			||||
		(let ((val (haskell-cabal-section-value (haskell-cabal-section)))) | 
				
			||||
		  (if (string= val "") | 
				
			||||
		      (push "library" matches) | 
				
			||||
		    (push val matches)))) | 
				
			||||
	    (haskell-cabal-next-section)) | 
				
			||||
	  (reverse matches)))))) | 
				
			||||
 | 
				
			||||
(defmacro haskell-cabal-with-subsection (subsection replace &rest funs) | 
				
			||||
  "Copy subsection data into a temporary buffer, save indentation | 
				
			||||
and execute FORMS | 
				
			||||
 | 
				
			||||
If REPLACE is non-nil the subsection data is replaced with the | 
				
			||||
resultung buffer-content" | 
				
			||||
  (let ((section (make-symbol "section")) | 
				
			||||
        (beg (make-symbol "beg")) | 
				
			||||
        (end (make-symbol "end")) | 
				
			||||
        (start-col (make-symbol "start-col")) | 
				
			||||
        (section-data (make-symbol "section-data"))) | 
				
			||||
    `(let* ((,section ,subsection) | 
				
			||||
            (,beg (plist-get ,section :beginning)) | 
				
			||||
            (,end (plist-get  ,section :end)) | 
				
			||||
            (,start-col (plist-get  ,section :data-start-column)) | 
				
			||||
            (,section-data (buffer-substring ,beg ,end))) | 
				
			||||
       (save-excursion | 
				
			||||
         (prog1 | 
				
			||||
             (with-temp-buffer | 
				
			||||
               (setq indent-tabs-mode nil) | 
				
			||||
               (indent-to ,start-col) | 
				
			||||
               (insert ,section-data) | 
				
			||||
               (goto-char (point-min)) | 
				
			||||
               (prog1 | 
				
			||||
                   (progn (haskell-cabal-save-indentation ,@funs)) | 
				
			||||
                 (goto-char (point-min)) | 
				
			||||
                 (when (looking-at (format "[ ]\\{0,%d\\}" (1+ ,start-col))) | 
				
			||||
                     (replace-match "")) | 
				
			||||
 | 
				
			||||
                 (setq ,section-data (buffer-substring (point-min) (point-max))))) | 
				
			||||
           ,@(when replace | 
				
			||||
               `((delete-region ,beg ,end) | 
				
			||||
                 (goto-char ,beg) | 
				
			||||
                 (insert ,section-data)))))))) | 
				
			||||
 | 
				
			||||
(defmacro haskell-cabal-each-line (&rest fun) | 
				
			||||
  "Execute FOMRS on each line" | 
				
			||||
  `(save-excursion | 
				
			||||
     (while (< (point) (point-max)) | 
				
			||||
       ,@fun | 
				
			||||
       (forward-line)))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-chomp-line () | 
				
			||||
  "Remove leading and trailing whitespaces from current line" | 
				
			||||
  (beginning-of-line) | 
				
			||||
  (when (looking-at "^[ \t]*\\([^ \t]\\|\\(?:[^ \t].*[^ \t]\\)\\)[ \t]*$") | 
				
			||||
    (replace-match (match-string 1) nil t) | 
				
			||||
    t)) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-min-indentation (&optional beg end) | 
				
			||||
  "Compute largest common whitespace prefix of each line in between BEG and END" | 
				
			||||
  (save-excursion | 
				
			||||
    (goto-char (or beg (point-min))) | 
				
			||||
    (let ((min-indent nil)) | 
				
			||||
      (while (< (point) (or end (point-max))) | 
				
			||||
        (let ((indent (current-indentation))) | 
				
			||||
          (if (and (not (haskell-cabal-ignore-line-p)) | 
				
			||||
                   (or (not min-indent) | 
				
			||||
                       (< indent min-indent))) | 
				
			||||
              (setq min-indent indent))) | 
				
			||||
        (forward-line)) | 
				
			||||
      min-indent))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-ignore-line-p () | 
				
			||||
  "Does line only contain whitespaces and comments?" | 
				
			||||
  (save-excursion | 
				
			||||
    (beginning-of-line) | 
				
			||||
    (looking-at "^[ \t]*\\(?:--.*\\)?$"))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-kill-indentation () | 
				
			||||
  "Remove longest common whitespace prefix from each line" | 
				
			||||
  (goto-char (point-min)) | 
				
			||||
  (let ((indent (haskell-cabal-min-indentation))) | 
				
			||||
    (haskell-cabal-each-line (unless (haskell-cabal-ignore-line-p) | 
				
			||||
                               (delete-char indent)) ) | 
				
			||||
    indent)) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-add-indentation (indent) | 
				
			||||
  (goto-char (point-min)) | 
				
			||||
  (haskell-cabal-each-line | 
				
			||||
   (unless (haskell-cabal-ignore-line-p) | 
				
			||||
     (indent-to indent)))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
(defmacro haskell-cabal-save-indentation (&rest funs) | 
				
			||||
  "Strip indentation from each line, execute FORMS and reinstate indentation | 
				
			||||
   so that the indentation of the FIRST LINE matches" | 
				
			||||
  (let ((old-l1-indent (make-symbol "new-l1-indent")) | 
				
			||||
        (new-l1-indent (make-symbol "old-l1-indent"))) | 
				
			||||
    `(let ( (,old-l1-indent (save-excursion | 
				
			||||
                              (goto-char (point-min)) | 
				
			||||
                              (current-indentation)))) | 
				
			||||
       (unwind-protect | 
				
			||||
           (progn | 
				
			||||
             (haskell-cabal-kill-indentation) | 
				
			||||
             ,@funs) | 
				
			||||
         (progn | 
				
			||||
           (goto-char (point-min)) | 
				
			||||
           (let ((,new-l1-indent (current-indentation))) | 
				
			||||
             (haskell-cabal-add-indentation (- ,old-l1-indent | 
				
			||||
                                           ,new-l1-indent)))))))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-strip-list () | 
				
			||||
  "strip commas from comma-seperated list" | 
				
			||||
  (goto-char (point-min)) | 
				
			||||
;; split list items on single line | 
				
			||||
  (while (re-search-forward | 
				
			||||
          "\\([^ \t,\n]\\)[ \t]*,[ \t]*\\([^ \t,\n]\\)"  nil t) | 
				
			||||
    (replace-match "\\1\n\\2" nil nil)) | 
				
			||||
  (goto-char (point-min)) | 
				
			||||
  (while (re-search-forward "^\\([ \t]*\\),\\([ \t]*\\)" nil t) | 
				
			||||
    (replace-match "" nil nil)) | 
				
			||||
  (goto-char (point-min)) | 
				
			||||
  (while (re-search-forward ",[ \t]*$" nil t) | 
				
			||||
    (replace-match "" nil nil)) | 
				
			||||
  (goto-char (point-min)) | 
				
			||||
  (haskell-cabal-each-line (haskell-cabal-chomp-line))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-listify () | 
				
			||||
  "Add commas so that buffer contains a comma-seperated list" | 
				
			||||
  (cl-case haskell-cabal-list-comma-position | 
				
			||||
    ('before | 
				
			||||
     (goto-char (point-min)) | 
				
			||||
     (while (haskell-cabal-ignore-line-p) (forward-line)) | 
				
			||||
     (indent-to 2) | 
				
			||||
     (forward-line) | 
				
			||||
     (haskell-cabal-each-line | 
				
			||||
      (unless (haskell-cabal-ignore-line-p) | 
				
			||||
        (insert ", ")))) | 
				
			||||
    ('after | 
				
			||||
     (goto-char (point-max)) | 
				
			||||
     (while (not (bobp)) | 
				
			||||
       (unless (haskell-cabal-ignore-line-p) | 
				
			||||
         (forward-line -1) | 
				
			||||
         (end-of-line) | 
				
			||||
         (insert ",") | 
				
			||||
         (beginning-of-line)))))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
(defmacro haskell-cabal-with-cs-list (&rest funs) | 
				
			||||
  "format buffer so that each line contains a list element " | 
				
			||||
  `(progn | 
				
			||||
    (save-excursion (haskell-cabal-strip-list)) | 
				
			||||
    (unwind-protect (progn ,@funs) | 
				
			||||
      (haskell-cabal-listify)))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-sort-lines-key-fun () | 
				
			||||
  (when (looking-at "[ \t]*--[ \t,]*") | 
				
			||||
    (goto-char (match-end 0))) | 
				
			||||
  nil) | 
				
			||||
 | 
				
			||||
(defmacro haskell-cabal-save-position (&rest forms) | 
				
			||||
  "Save position as mark, execute FORMs and go back to mark" | 
				
			||||
  `(prog2 | 
				
			||||
       (haskell-cabal-mark) | 
				
			||||
       (progn ,@forms) | 
				
			||||
     (haskell-cabal-goto-mark) | 
				
			||||
     (haskell-cabal-remove-mark))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-subsection-arrange-lines () | 
				
			||||
  "Sort lines of current subsection" | 
				
			||||
  (interactive) | 
				
			||||
  (haskell-cabal-save-position | 
				
			||||
   (haskell-cabal-with-subsection | 
				
			||||
    (haskell-cabal-subsection) t | 
				
			||||
    (haskell-cabal-with-cs-list | 
				
			||||
     (sort-subr nil 'forward-line 'end-of-line | 
				
			||||
                'haskell-cabal-sort-lines-key-fun) | 
				
			||||
     )))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-subsection-beginning () | 
				
			||||
  "find the beginning of the current subsection" | 
				
			||||
  (save-excursion | 
				
			||||
    (while (and (not (bobp)) | 
				
			||||
                (not (haskell-cabal-header-p))) | 
				
			||||
      (forward-line -1)) | 
				
			||||
    (back-to-indentation) | 
				
			||||
    (point))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-beginning-of-subsection () | 
				
			||||
  "go to the beginniing of the current subsection" | 
				
			||||
  (interactive) | 
				
			||||
  (goto-char (haskell-cabal-subsection-beginning))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-next-subsection () | 
				
			||||
  "go to the next subsection" | 
				
			||||
  (interactive) | 
				
			||||
  (if (haskell-cabal-header-p) (forward-line)) | 
				
			||||
  (while (and (not (eobp)) | 
				
			||||
              (not (haskell-cabal-header-p))) | 
				
			||||
    (forward-line)) | 
				
			||||
  (haskell-cabal-forward-to-line-entry)) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-previous-subsection () | 
				
			||||
  "go to the next subsection" | 
				
			||||
  (interactive) | 
				
			||||
  (if (haskell-cabal-header-p) (forward-line -1)) | 
				
			||||
  (while (and (not (bobp)) | 
				
			||||
              (not (haskell-cabal-header-p))) | 
				
			||||
    (forward-line -1)) | 
				
			||||
  (haskell-cabal-forward-to-line-entry) | 
				
			||||
  ) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-find-subsection-by (section pred) | 
				
			||||
  "Find sunsection with name NAME" | 
				
			||||
  (save-excursion | 
				
			||||
    (when section (goto-char (haskell-cabal-section-start section))) | 
				
			||||
    (let* ((end (if section (haskell-cabal-section-end) (point-max))) | 
				
			||||
           (found nil)) | 
				
			||||
      (while (and (< (point) end) | 
				
			||||
                  (not found)) | 
				
			||||
        (let ((subsection (haskell-cabal-subsection))) | 
				
			||||
          (when (and subsection (funcall pred subsection)) | 
				
			||||
            (setq found subsection))) | 
				
			||||
        (haskell-cabal-next-subsection)) | 
				
			||||
      found))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-find-subsection (section name) | 
				
			||||
  "Find sunsection with name NAME" | 
				
			||||
  (let ((downcase-name (downcase name))) | 
				
			||||
    (haskell-cabal-find-subsection-by | 
				
			||||
     section | 
				
			||||
     `(lambda (subsection) | 
				
			||||
        (string= (downcase (haskell-cabal-section-name subsection)) | 
				
			||||
                 ,downcase-name))))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-goto-subsection (name) | 
				
			||||
  (let ((subsection (haskell-cabal-find-subsection (haskell-cabal-section) name))) | 
				
			||||
    (when subsection | 
				
			||||
      (goto-char (haskell-cabal-section-start subsection))))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-goto-exposed-modules () | 
				
			||||
  (interactive) | 
				
			||||
  (haskell-cabal-goto-subsection "exposed-modules")) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-subsection-entry-list (section name) | 
				
			||||
  "Get the data of a subsection as a list" | 
				
			||||
  (let ((subsection (haskell-cabal-find-subsection section name))) | 
				
			||||
    (when subsection | 
				
			||||
      (haskell-cabal-with-subsection | 
				
			||||
       subsection nil | 
				
			||||
       (haskell-cabal-with-cs-list | 
				
			||||
        (delete-matching-lines | 
				
			||||
         (format "\\(?:%s\\)\\|\\(?:%s\\)" | 
				
			||||
                 haskell-cabal-comment-regexp | 
				
			||||
                 haskell-cabal-empty-regexp) | 
				
			||||
         (point-min) (point-max)) | 
				
			||||
        (split-string (buffer-substring-no-properties (point-min) (point-max)) | 
				
			||||
                      "\n" t)))))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-remove-mark () | 
				
			||||
  (remove-list-of-text-properties (point-min) (point-max) | 
				
			||||
                                  '(haskell-cabal-marker))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-mark () | 
				
			||||
  "Mark the current position with the text property haskell-cabal-marker" | 
				
			||||
  (haskell-cabal-remove-mark) | 
				
			||||
  (put-text-property (line-beginning-position) (line-end-position) | 
				
			||||
                     'haskell-cabal-marker 'marked-line) | 
				
			||||
  (put-text-property (point) (1+ (point)) | 
				
			||||
                     'haskell-cabal-marker 'marked)) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-goto-mark () | 
				
			||||
  "Go to marked line" | 
				
			||||
  (let ((marked-pos (text-property-any (point-min) (point-max) | 
				
			||||
                                       'haskell-cabal-marker | 
				
			||||
                                       'marked)) | 
				
			||||
        (marked-line (text-property-any (point-min) (point-max) | 
				
			||||
                                       'haskell-cabal-marker | 
				
			||||
                                       'marked-line) ) | 
				
			||||
        ) | 
				
			||||
    (cond (marked-pos (goto-char marked-pos)) | 
				
			||||
          (marked-line (goto-char marked-line))))) | 
				
			||||
 | 
				
			||||
(defmacro haskell-cabal-with-subsection-line (replace &rest forms) | 
				
			||||
  "Mark line and " | 
				
			||||
  `(progn | 
				
			||||
     (haskell-cabal-mark) | 
				
			||||
     (unwind-protect | 
				
			||||
         (haskell-cabal-with-subsection (haskell-cabal-subsection) ,replace | 
				
			||||
          (haskell-cabal-goto-mark) | 
				
			||||
          ,@forms) | 
				
			||||
       (haskell-cabal-remove-mark)))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-get-line-content () | 
				
			||||
  (haskell-cabal-with-subsection-line | 
				
			||||
   nil | 
				
			||||
   (haskell-cabal-with-cs-list | 
				
			||||
    (haskell-cabal-goto-mark) | 
				
			||||
    (buffer-substring-no-properties (line-beginning-position) | 
				
			||||
                                    (line-end-position))))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-module-to-filename (module) | 
				
			||||
  (concat  (replace-regexp-in-string "[.]" "/" module ) ".hs")) | 
				
			||||
 | 
				
			||||
(defconst haskell-cabal-module-sections '("exposed-modules" "other-modules") | 
				
			||||
  "List of sections that contain module names" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
(defconst haskell-cabal-file-sections | 
				
			||||
  '("main-is" "c-sources" "data-files" "extra-source-files" | 
				
			||||
    "extra-doc-files" "extra-tmp-files" ) | 
				
			||||
  "List of subsections that contain filenames" | 
				
			||||
  ) | 
				
			||||
 | 
				
			||||
(defconst haskell-cabal-source-bearing-sections | 
				
			||||
  '("library" "executable" "test-suite" "benchmark")) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-source-section-p (section) | 
				
			||||
  (not (not  (member (downcase (haskell-cabal-section-name section)) | 
				
			||||
                      haskell-cabal-source-bearing-sections)))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-line-filename () | 
				
			||||
  "Expand filename in current line according to the subsection type | 
				
			||||
 | 
				
			||||
Module names in exposed-modules and other-modules are expanded by replacing each dot (.) in the module name with a foward slash (/) and appending \".hs\" | 
				
			||||
 | 
				
			||||
Example: Foo.Bar.Quux ==> Foo/Bar/Quux.hs | 
				
			||||
 | 
				
			||||
Source names from main-is and c-sources sections are left untouched | 
				
			||||
 | 
				
			||||
" | 
				
			||||
  (let ((entry (haskell-cabal-get-line-content)) | 
				
			||||
        (subsection (downcase (haskell-cabal-section-name | 
				
			||||
                               (haskell-cabal-subsection))))) | 
				
			||||
    (cond ((member subsection haskell-cabal-module-sections) | 
				
			||||
           (haskell-cabal-module-to-filename entry)) | 
				
			||||
          ((member subsection haskell-cabal-file-sections) entry)))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-join-paths (&rest args) | 
				
			||||
  "Crude hack to replace f-join" | 
				
			||||
  (mapconcat 'identity args "/") | 
				
			||||
) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-find-or-create-source-file () | 
				
			||||
  "Open the source file this line refers to" | 
				
			||||
  (interactive) | 
				
			||||
  (let* ((src-dirs (append (haskell-cabal-subsection-entry-list | 
				
			||||
                            (haskell-cabal-section) "hs-source-dirs") | 
				
			||||
                           '(""))) | 
				
			||||
         (base-dir (file-name-directory (buffer-file-name))) | 
				
			||||
         (filename (haskell-cabal-line-filename))) | 
				
			||||
    (when filename | 
				
			||||
      (let ((candidates | 
				
			||||
             (delq nil (mapcar | 
				
			||||
                        (lambda (dir) | 
				
			||||
                          (let ((file (haskell-cabal-join-paths base-dir dir filename))) | 
				
			||||
                            (when (and (file-readable-p file) | 
				
			||||
                                       (not (file-directory-p file))) | 
				
			||||
                              file))) | 
				
			||||
                        src-dirs)))) | 
				
			||||
        (if (null candidates) | 
				
			||||
            (let* ((src-dir (haskell-cabal-join-paths base-dir (or (car src-dirs) ""))) | 
				
			||||
                   (newfile (haskell-cabal-join-paths src-dir filename)) | 
				
			||||
                   (do-create-p (y-or-n-p (format "Create file %s ?" newfile)))) | 
				
			||||
              (when do-create-p | 
				
			||||
                (find-file-other-window newfile ))) | 
				
			||||
          (find-file-other-window (car candidates))))))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-find-section-type (type &optional wrap) | 
				
			||||
  (save-excursion | 
				
			||||
    (haskell-cabal-next-section) | 
				
			||||
    (while | 
				
			||||
        (not | 
				
			||||
         (or | 
				
			||||
          (eobp) | 
				
			||||
          (string= | 
				
			||||
           (downcase type) | 
				
			||||
           (downcase  (haskell-cabal-section-name (haskell-cabal-section)))))) | 
				
			||||
      (haskell-cabal-next-section)) | 
				
			||||
    (if (eobp) | 
				
			||||
      (if wrap (progn | 
				
			||||
                 (goto-char (point-min)) | 
				
			||||
                 (haskell-cabal-find-section-type type nil) ) | 
				
			||||
        nil) | 
				
			||||
      (point)))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-goto-section-type  (type) | 
				
			||||
  (let ((section (haskell-cabal-find-section-type type t))) | 
				
			||||
    (if section (goto-char section) | 
				
			||||
      (message "No %s section found" type)))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-goto-library-section () | 
				
			||||
  (interactive) | 
				
			||||
  (haskell-cabal-goto-section-type "library")) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-goto-test-suite-section () | 
				
			||||
  (interactive) | 
				
			||||
  (haskell-cabal-goto-section-type "test-suite")) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-goto-executable-section () | 
				
			||||
  (interactive) | 
				
			||||
  (haskell-cabal-goto-section-type "executable")) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-goto-benchmark-section () | 
				
			||||
  (interactive) | 
				
			||||
  (haskell-cabal-goto-section-type "benchmark")) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-line-entry-column () | 
				
			||||
  "Column at which the line entry starts" | 
				
			||||
  (save-excursion | 
				
			||||
    (cl-case (haskell-cabal-classify-line) | 
				
			||||
      (section-data (beginning-of-line) | 
				
			||||
                    (when (looking-at "[ ]*\\(?:,[ ]*\\)?") | 
				
			||||
                      (goto-char  (match-end 0)) | 
				
			||||
                      (current-column))) | 
				
			||||
      (subsection-header | 
				
			||||
       (haskell-cabal-section-data-start-column (haskell-cabal-subsection)))))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-forward-to-line-entry () | 
				
			||||
  "go forward to the beginning of the line entry (but never move backwards)" | 
				
			||||
  (let ((col (haskell-cabal-line-entry-column))) | 
				
			||||
    (when (and col (< (current-column) col)) | 
				
			||||
      (beginning-of-line) | 
				
			||||
      (forward-char col)))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-indent-line () | 
				
			||||
  "Indent current line according to subsection" | 
				
			||||
  (interactive) | 
				
			||||
  (cl-case (haskell-cabal-classify-line) | 
				
			||||
    (section-data | 
				
			||||
     (save-excursion | 
				
			||||
       (let ((indent (haskell-cabal-section-data-start-column | 
				
			||||
                      (haskell-cabal-subsection)))) | 
				
			||||
         (indent-line-to indent) | 
				
			||||
         (beginning-of-line) | 
				
			||||
         (when (looking-at "[ ]*\\([ ]\\{2\\},[ ]*\\)") | 
				
			||||
           (replace-match ", " t t nil 1))))) | 
				
			||||
    (empty | 
				
			||||
     (indent-relative))) | 
				
			||||
  (haskell-cabal-forward-to-line-entry)) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-map-sections (fun) | 
				
			||||
  "Execute fun over each section, collecting the result" | 
				
			||||
  (save-excursion | 
				
			||||
    (goto-char (point-min)) | 
				
			||||
    (let ((results nil)) | 
				
			||||
        (while (not (eobp)) | 
				
			||||
          (let* ((section (haskell-cabal-section)) | 
				
			||||
                 (result (and section (funcall fun (haskell-cabal-section))))) | 
				
			||||
            (when section (setq results (cons result results)))) | 
				
			||||
          (haskell-cabal-next-section)) | 
				
			||||
        (nreverse  results)))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-section-add-build-dependency (dependency &optional sort sec) | 
				
			||||
  "Add a build dependency to the build-depends section" | 
				
			||||
  (let* ((section (or sec (haskell-cabal-section))) | 
				
			||||
         (subsection (and section | 
				
			||||
                          (haskell-cabal-find-subsection section "build-depends")))) | 
				
			||||
    (when subsection | 
				
			||||
      (haskell-cabal-with-subsection | 
				
			||||
       subsection t | 
				
			||||
       (haskell-cabal-with-cs-list | 
				
			||||
        (insert dependency) | 
				
			||||
        (insert "\n") | 
				
			||||
        (when sort | 
				
			||||
          (goto-char (point-min)) | 
				
			||||
          (sort-subr nil 'forward-line 'end-of-line | 
				
			||||
                     'haskell-cabal-sort-lines-key-fun))))))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-add-build-dependency (dependency &optional sort silent) | 
				
			||||
  "Add a build dependencies to sections" | 
				
			||||
  (haskell-cabal-map-sections | 
				
			||||
   (lambda (section) | 
				
			||||
     (when (haskell-cabal-source-section-p section) | 
				
			||||
       (when (or silent | 
				
			||||
                 (y-or-n-p (format  "Add dependency %s to %s section %s?" | 
				
			||||
                                    dependency | 
				
			||||
                                    (haskell-cabal-section-name section) | 
				
			||||
                                    (haskell-cabal-section-value section)))) | 
				
			||||
         (haskell-cabal-section-add-build-dependency dependency sort section) | 
				
			||||
         nil))))) | 
				
			||||
 | 
				
			||||
(defun haskell-cabal-add-dependency (package &optional version no-prompt | 
				
			||||
                                                   sort silent) | 
				
			||||
  "Add PACKAGE (and optionally suffix -VERSION) to the cabal | 
				
			||||
file. Prompts the user before doing so. | 
				
			||||
 | 
				
			||||
If VERSION is non-nil it will be appended as a minimum version. | 
				
			||||
If NO-PROMPT is nil the minimum-version is read from the minibuffer | 
				
			||||
When SORT is non-nil the package entries are sorted afterwards | 
				
			||||
If SILENT ist nil the user is prompted for each source-section | 
				
			||||
" | 
				
			||||
  (interactive | 
				
			||||
   (list (read-from-minibuffer "Package entry: ") | 
				
			||||
         nil t t nil)) | 
				
			||||
  (save-window-excursion | 
				
			||||
    (find-file-other-window (haskell-cabal-find-file)) | 
				
			||||
    (let ((entry (if no-prompt package | 
				
			||||
                   (read-from-minibuffer | 
				
			||||
                    "Package entry: " | 
				
			||||
                    (concat package (if version (concat " >= " version) "")))))) | 
				
			||||
      (haskell-cabal-add-build-dependency entry sort silent) | 
				
			||||
      (when (or silent (y-or-n-p "Save cabal file?")) | 
				
			||||
        (save-buffer))))) | 
				
			||||
 | 
				
			||||
(provide 'haskell-cabal) | 
				
			||||
 | 
				
			||||
;;; haskell-cabal.el ends here | 
				
			||||
@ -1,184 +0,0 @@
					 | 
				
			||||
;;; haskell-checkers.el --- Emacs interface to haskell lint and style checkers -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright (C) 2009-2011  Alex Ott, Liam O'Reilly | 
				
			||||
;; | 
				
			||||
;; Author: Alex Ott <alexott@gmail.com>, Liam O'Reilly <csliam@swansea.ac.uk> | 
				
			||||
;; Keywords: haskell, lint, hlint, style scanner | 
				
			||||
;; Requirements: hlint, scan, haskell | 
				
			||||
 | 
				
			||||
;; This file is not part of GNU Emacs. | 
				
			||||
 | 
				
			||||
;; This program 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 2 of the License, or | 
				
			||||
;; (at your option) any later version. | 
				
			||||
 | 
				
			||||
;; This program 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: | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'compile) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defgroup haskell-checkers nil | 
				
			||||
  "Run HLint as inferior of Emacs, parse error messages." | 
				
			||||
  :group 'haskell) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-lint-command "hlint" | 
				
			||||
  "The default lint command for \\[hlint]." | 
				
			||||
  :type 'string | 
				
			||||
  :group 'haskell-checkers) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-scan-command "scan" | 
				
			||||
  "The default scan command for \\[haskell-scan]." | 
				
			||||
  :type 'string | 
				
			||||
  :group 'haskell-checkers) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-scan-options "" | 
				
			||||
  "The default options for \\[haskell-scan]." | 
				
			||||
  :type 'string | 
				
			||||
  :group 'haskell-checkers) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-lint-options "" | 
				
			||||
  "The default options for \\[hlint]." | 
				
			||||
  :type 'string | 
				
			||||
  :group 'haskell-checkers) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-checkers-save-files t | 
				
			||||
  "Save modified files when run checker or not (ask user)" | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-checkers) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-checkers-replace-with-suggestions nil | 
				
			||||
  "Replace user's code with suggested replacements (hlint only)" | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-checkers) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-checkers-replace-without-ask nil | 
				
			||||
  "Replace user's code with suggested replacements automatically (hlint only)" | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-checkers) | 
				
			||||
 | 
				
			||||
;; regex for replace HLint's suggestions | 
				
			||||
;; | 
				
			||||
;; ^\(.*?\):\([0-9]+\):\([0-9]+\): .* | 
				
			||||
;; Found: | 
				
			||||
;; \s +\(.*\) | 
				
			||||
;; Why not: | 
				
			||||
;; \s +\(.*\) | 
				
			||||
 | 
				
			||||
(defvar haskell-lint-regex | 
				
			||||
  "^\\(.*?\\):\\([0-9]+\\):\\([0-9]+\\): .*[\n\C-m]Found:[\n\C-m]\\s +\\(.*\\)[\n\C-m]Why not:[\n\C-m]\\s +\\(.*\\)[\n\C-m]" | 
				
			||||
  "Regex for HLint messages") | 
				
			||||
 | 
				
			||||
(defun haskell-checkers-make-short-string (str maxlen) | 
				
			||||
  (if (< (length str) maxlen) | 
				
			||||
      str | 
				
			||||
    (concat (substring str 0 (- maxlen 3)) "..."))) | 
				
			||||
 | 
				
			||||
;; TODO: check, is it possible to adopt it for haskell-scan? | 
				
			||||
(defun haskell-lint-replace-suggestions () | 
				
			||||
  "Perform actual replacement of HLint's suggestions" | 
				
			||||
  (goto-char (point-min)) | 
				
			||||
  (while (re-search-forward haskell-lint-regex nil t) | 
				
			||||
    (let* ((fname (match-string 1)) | 
				
			||||
           (fline (string-to-number (match-string 2))) | 
				
			||||
           (old-code (match-string 4)) | 
				
			||||
           (new-code (match-string 5)) | 
				
			||||
           (msg (concat "Replace '" (haskell-checkers-make-short-string old-code 30) | 
				
			||||
                        "' with '" (haskell-checkers-make-short-string new-code 30) "'")) | 
				
			||||
           (bline 0) | 
				
			||||
           (eline 0) | 
				
			||||
           (spos 0) | 
				
			||||
           (new-old-code "")) | 
				
			||||
      (save-excursion | 
				
			||||
        (switch-to-buffer (get-file-buffer fname)) | 
				
			||||
        (goto-char (point-min)) | 
				
			||||
        (forward-line (1- fline)) | 
				
			||||
        (beginning-of-line) | 
				
			||||
        (setq bline (point)) | 
				
			||||
        (when (or haskell-checkers-replace-without-ask | 
				
			||||
                  (yes-or-no-p msg)) | 
				
			||||
          (end-of-line) | 
				
			||||
          (setq eline (point)) | 
				
			||||
          (beginning-of-line) | 
				
			||||
          (setq old-code (regexp-quote old-code)) | 
				
			||||
          (while (string-match "\\\\ " old-code spos) | 
				
			||||
            (setq new-old-code (concat new-old-code | 
				
			||||
                                       (substring old-code spos (match-beginning 0)) | 
				
			||||
                                       "\\ *")) | 
				
			||||
            (setq spos (match-end 0))) | 
				
			||||
          (setq new-old-code (concat new-old-code (substring old-code spos))) | 
				
			||||
          (remove-text-properties bline eline '(composition nil)) | 
				
			||||
          (when (re-search-forward new-old-code eline t) | 
				
			||||
            (replace-match new-code nil t))))))) | 
				
			||||
 | 
				
			||||
(defun haskell-lint-finish-hook (_buf _msg) | 
				
			||||
  "Function, that is executed at the end of HLint or scan execution" | 
				
			||||
  (if haskell-checkers-replace-with-suggestions | 
				
			||||
      (haskell-lint-replace-suggestions) | 
				
			||||
    (next-error 1 t))) | 
				
			||||
 | 
				
			||||
(defun haskell-scan-finish-hook (_buf _msg) | 
				
			||||
  "Function, that is executed at the end of haskell-scan execution" | 
				
			||||
  (next-error 1 t)) | 
				
			||||
 | 
				
			||||
(defun haskell-scan-make-command (file) | 
				
			||||
  "Generates command line for scan" | 
				
			||||
  (concat haskell-scan-command " " haskell-scan-options " \"" file "\"")) | 
				
			||||
 | 
				
			||||
(defun haskell-lint-make-command (file) | 
				
			||||
  "Generates command line for scan" | 
				
			||||
  (concat haskell-lint-command  " \"/" file "/\"" " " haskell-lint-options)) | 
				
			||||
 | 
				
			||||
(defmacro haskell-checkers-setup (type name) | 
				
			||||
  "Performs setup of corresponding checker. Receives two arguments: | 
				
			||||
type - checker's type (lint or scan) that is expanded into functions and hooks names | 
				
			||||
name - user visible name for this mode" | 
				
			||||
  (let ((nm (concat "haskell-" (symbol-name type)))) | 
				
			||||
    `(progn | 
				
			||||
;;;###autoload | 
				
			||||
       (defvar ,(intern (concat nm "-setup-hook")) nil | 
				
			||||
         ,(concat "Hook, that will executed before running " name)) | 
				
			||||
       (defun ,(intern (concat nm "-process-setup")) () | 
				
			||||
         "Setup compilation variables and buffer for `hlint'." | 
				
			||||
         (run-hooks ',(intern (concat nm "-setup-hook")))) | 
				
			||||
;;;###autoload | 
				
			||||
       (define-compilation-mode ,(intern (concat nm "-mode")) ,name | 
				
			||||
         ,(concat "Mode to check Haskell source code using " name) | 
				
			||||
         (set (make-local-variable 'compilation-process-setup-function) | 
				
			||||
              ',(intern (concat nm "-process-setup"))) | 
				
			||||
         (set (make-local-variable 'compilation-disable-input) t) | 
				
			||||
         (set (make-local-variable 'compilation-scroll-output) nil) | 
				
			||||
         (set (make-local-variable 'compilation-finish-functions) | 
				
			||||
              (list ',(intern (concat nm "-finish-hook"))))) | 
				
			||||
;;;###autoload | 
				
			||||
       (defun ,(intern nm) () | 
				
			||||
         ,(concat "Run " name " for current buffer with haskell source") | 
				
			||||
         (interactive) | 
				
			||||
         (save-some-buffers haskell-checkers-save-files) | 
				
			||||
         (compilation-start (,(intern (concat nm "-make-command")) buffer-file-name) | 
				
			||||
                            ',(intern (concat nm "-mode"))))) | 
				
			||||
    )) | 
				
			||||
 | 
				
			||||
(haskell-checkers-setup lint "HLint") | 
				
			||||
(haskell-checkers-setup scan "HScan") | 
				
			||||
 | 
				
			||||
(provide 'haskell-checkers) | 
				
			||||
 | 
				
			||||
;;; haskell-checkers.el ends here | 
				
			||||
@ -1,65 +0,0 @@
					 | 
				
			||||
;;; haskell-collapse.el --- Collapse expressions -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright (c) 2014 Chris Done. All rights reserved. | 
				
			||||
 | 
				
			||||
;; 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/>. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(define-button-type 'haskell-collapse-toggle-button | 
				
			||||
  'action 'haskell-collapse-toggle-button-callback | 
				
			||||
  'follow-link t | 
				
			||||
  'help-echo "Click to expand…") | 
				
			||||
 | 
				
			||||
(defun haskell-collapse (beg end) | 
				
			||||
  "Collapse." | 
				
			||||
  (interactive "r") | 
				
			||||
  (goto-char end) | 
				
			||||
  (let ((break nil)) | 
				
			||||
    (while (and (not break) | 
				
			||||
                (search-backward-regexp "[[({]" beg t 1)) | 
				
			||||
      (unless (elt (syntax-ppss) 3) | 
				
			||||
        (let ((orig (point))) | 
				
			||||
          (haskell-collapse-sexp) | 
				
			||||
          (goto-char orig) | 
				
			||||
          (forward-char -1) | 
				
			||||
          (when (= (point) orig) | 
				
			||||
            (setq break t))))))) | 
				
			||||
 | 
				
			||||
(defun haskell-collapse-sexp () | 
				
			||||
  "Collapse the sexp starting at point." | 
				
			||||
  (let ((beg (point))) | 
				
			||||
    (forward-sexp) | 
				
			||||
    (let ((end (point))) | 
				
			||||
      (let ((o (make-overlay beg end))) | 
				
			||||
        (overlay-put o 'invisible t) | 
				
			||||
        (let ((start (point))) | 
				
			||||
          (insert "…") | 
				
			||||
          (let ((button (make-text-button start (point) | 
				
			||||
                                          :type 'haskell-collapse-toggle-button))) | 
				
			||||
            (button-put button 'overlay o) | 
				
			||||
            (button-put button 'hide-on-click t))))))) | 
				
			||||
 | 
				
			||||
(defun haskell-collapse-toggle-button-callback (btn) | 
				
			||||
  "The callback to toggle the overlay visibility." | 
				
			||||
  (let ((overlay (button-get btn 'overlay))) | 
				
			||||
    (when overlay | 
				
			||||
      (overlay-put overlay | 
				
			||||
                   'invisible | 
				
			||||
                   (not (overlay-get overlay | 
				
			||||
                                     'invisible))))) | 
				
			||||
  (button-put btn 'invisible t) | 
				
			||||
  (delete-region (button-start btn) (button-end btn))) | 
				
			||||
 | 
				
			||||
(provide 'haskell-collapse) | 
				
			||||
@ -1,944 +0,0 @@
					 | 
				
			||||
;;; haskell-commands.el --- Commands that can be run on the process -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;;; Commentary: | 
				
			||||
 | 
				
			||||
;;; This module provides varoius `haskell-mode' and `haskell-interactive-mode' | 
				
			||||
;;; specific commands such as show type signature, show info, haskell process | 
				
			||||
;;; commands and etc. | 
				
			||||
 | 
				
			||||
;; Copyright (c) 2014 Chris Done. All rights reserved. | 
				
			||||
 | 
				
			||||
;; 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/>. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'cl-lib) | 
				
			||||
(require 'etags) | 
				
			||||
(require 'haskell-compat) | 
				
			||||
(require 'haskell-process) | 
				
			||||
(require 'haskell-font-lock) | 
				
			||||
(require 'haskell-interactive-mode) | 
				
			||||
(require 'haskell-session) | 
				
			||||
(require 'haskell-presentation-mode) | 
				
			||||
(require 'haskell-utils) | 
				
			||||
(require 'highlight-uses-mode) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-process-restart () | 
				
			||||
  "Restart the inferior Haskell process." | 
				
			||||
  (interactive) | 
				
			||||
  (haskell-process-reset (haskell-interactive-process)) | 
				
			||||
  (haskell-process-set (haskell-interactive-process) 'command-queue nil) | 
				
			||||
  (haskell-process-start (haskell-interactive-session))) | 
				
			||||
 | 
				
			||||
(defun haskell-process-start (session) | 
				
			||||
  "Start the inferior Haskell process with a given SESSION. | 
				
			||||
You can create new session using function `haskell-session-make'." | 
				
			||||
  (let ((existing-process (get-process (haskell-session-name (haskell-interactive-session))))) | 
				
			||||
    (when (processp existing-process) | 
				
			||||
      (haskell-interactive-mode-echo session "Restarting process ...") | 
				
			||||
      (haskell-process-set (haskell-session-process session) 'is-restarting t) | 
				
			||||
      (delete-process existing-process))) | 
				
			||||
  (let ((process (or (haskell-session-process session) | 
				
			||||
                     (haskell-process-make (haskell-session-name session)))) | 
				
			||||
        (old-queue (haskell-process-get (haskell-session-process session) | 
				
			||||
                                        'command-queue))) | 
				
			||||
    (haskell-session-set-process session process) | 
				
			||||
    (haskell-process-set-session process session) | 
				
			||||
    (haskell-process-set-cmd process nil) | 
				
			||||
    (haskell-process-set (haskell-session-process session) 'is-restarting nil) | 
				
			||||
    (let ((default-directory (haskell-session-cabal-dir session)) | 
				
			||||
          (log-and-command (haskell-process-compute-process-log-and-command session (haskell-process-type)))) | 
				
			||||
      (haskell-session-prompt-set-current-dir session (not haskell-process-load-or-reload-prompt)) | 
				
			||||
      (haskell-process-set-process | 
				
			||||
       process | 
				
			||||
       (progn | 
				
			||||
         (haskell-process-log (propertize (format "%S" log-and-command))) | 
				
			||||
         (apply #'start-process (cdr log-and-command))))) | 
				
			||||
    (progn (set-process-sentinel (haskell-process-process process) 'haskell-process-sentinel) | 
				
			||||
           (set-process-filter (haskell-process-process process) 'haskell-process-filter)) | 
				
			||||
    (haskell-process-send-startup process) | 
				
			||||
    (unless (or (eq 'cabal-repl (haskell-process-type)) | 
				
			||||
                   (eq 'stack-ghci (haskell-process-type))) ;; Both "cabal repl" and "stack ghci" set the proper CWD. | 
				
			||||
      (haskell-process-change-dir session | 
				
			||||
                                  process | 
				
			||||
                                  (haskell-session-current-dir session))) | 
				
			||||
    (haskell-process-set process 'command-queue | 
				
			||||
                         (append (haskell-process-get (haskell-session-process session) | 
				
			||||
                                                      'command-queue) | 
				
			||||
                                 old-queue)) | 
				
			||||
    process)) | 
				
			||||
 | 
				
			||||
(defun haskell-process-send-startup (process) | 
				
			||||
  "Send the necessary start messages to haskell PROCESS." | 
				
			||||
  (haskell-process-queue-command | 
				
			||||
   process | 
				
			||||
   (make-haskell-command | 
				
			||||
    :state process | 
				
			||||
 | 
				
			||||
    :go (lambda (process) | 
				
			||||
          ;; We must set the prompt last, so that this command as a | 
				
			||||
          ;; whole produces only one prompt marker as a response. | 
				
			||||
          (haskell-process-send-string process "Prelude.putStrLn \"\"") | 
				
			||||
          (haskell-process-send-string process ":set -v1") | 
				
			||||
          (haskell-process-send-string process ":set prompt \"\\4\"")) | 
				
			||||
 | 
				
			||||
    :live (lambda (process buffer) | 
				
			||||
            (when (haskell-process-consume | 
				
			||||
                   process | 
				
			||||
                   "^\*\*\* WARNING: \\(.+\\) is writable by someone else, IGNORING!$") | 
				
			||||
              (let ((path (match-string 1 buffer))) | 
				
			||||
                (haskell-session-modify | 
				
			||||
                 (haskell-process-session process) | 
				
			||||
                 'ignored-files | 
				
			||||
                 (lambda (files) | 
				
			||||
                   (cl-remove-duplicates (cons path files) :test 'string=))) | 
				
			||||
                (haskell-interactive-mode-compile-warning | 
				
			||||
                 (haskell-process-session process) | 
				
			||||
                 (format "GHCi is ignoring: %s (run M-x haskell-process-unignore)" | 
				
			||||
                         path))))) | 
				
			||||
 | 
				
			||||
    :complete (lambda (process _) | 
				
			||||
                (haskell-interactive-mode-echo | 
				
			||||
                 (haskell-process-session process) | 
				
			||||
                 (concat (nth (random (length haskell-process-greetings)) | 
				
			||||
                              haskell-process-greetings) | 
				
			||||
                         (when haskell-process-show-debug-tips | 
				
			||||
                           " | 
				
			||||
If I break, you can: | 
				
			||||
  1. Restart:           M-x haskell-process-restart | 
				
			||||
  2. Configure logging: C-h v haskell-process-log (useful for debugging) | 
				
			||||
  3. General config:    M-x customize-mode | 
				
			||||
  4. Hide these tips:   C-h v haskell-process-show-debug-tips"))))))) | 
				
			||||
 | 
				
			||||
(defun haskell-commands-process () | 
				
			||||
  "Get the Haskell session, throws an error if not available." | 
				
			||||
  (or (haskell-session-process (haskell-session-maybe)) | 
				
			||||
      (error "No Haskell session/process associated with this | 
				
			||||
      buffer. Maybe run M-x haskell-session-change?"))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-process-clear () | 
				
			||||
  "Clear the current process." | 
				
			||||
  (interactive) | 
				
			||||
  (haskell-process-reset (haskell-commands-process)) | 
				
			||||
  (haskell-process-set (haskell-commands-process) 'command-queue nil)) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-process-interrupt () | 
				
			||||
  "Interrupt the process (SIGINT)." | 
				
			||||
  (interactive) | 
				
			||||
  (interrupt-process (haskell-process-process (haskell-commands-process)))) | 
				
			||||
 | 
				
			||||
(defun haskell-process-reload-with-fbytecode (process module-buffer) | 
				
			||||
  "Query a PROCESS to reload MODULE-BUFFER with -fbyte-code set. | 
				
			||||
Restores -fobject-code after reload finished. | 
				
			||||
MODULE-BUFFER is the actual Emacs buffer of the module being loaded." | 
				
			||||
  (haskell-process-queue-without-filters process ":set -fbyte-code") | 
				
			||||
  ;; We prefix the module's filename with a "*", which asks ghci to | 
				
			||||
  ;; ignore any existing object file and interpret the module. | 
				
			||||
  ;; Dependencies will still use their object files as usual. | 
				
			||||
  (haskell-process-queue-without-filters | 
				
			||||
   process | 
				
			||||
   (format ":load \"*%s\"" | 
				
			||||
           (replace-regexp-in-string | 
				
			||||
            "\"" | 
				
			||||
            "\\\\\"" | 
				
			||||
            (buffer-file-name module-buffer)))) | 
				
			||||
  (haskell-process-queue-without-filters process ":set -fobject-code")) | 
				
			||||
 | 
				
			||||
(defvar url-http-response-status) | 
				
			||||
(defvar url-http-end-of-headers) | 
				
			||||
(defvar haskell-cabal-targets-history nil | 
				
			||||
  "History list for session targets.") | 
				
			||||
 | 
				
			||||
(defun haskell-process-hayoo-ident (ident) | 
				
			||||
  ;; FIXME Obsolete doc string, CALLBACK is not used. | 
				
			||||
  "Hayoo for IDENT, return a list of modules asyncronously through CALLBACK." | 
				
			||||
  ;; We need a real/simulated closure, because otherwise these | 
				
			||||
  ;; variables will be unbound when the url-retrieve callback is | 
				
			||||
  ;; called. | 
				
			||||
  ;; TODO: Remove when this code is converted to lexical bindings by | 
				
			||||
  ;; default (Emacs 24.1+) | 
				
			||||
  (let ((url (format haskell-process-hayoo-query-url (url-hexify-string ident)))) | 
				
			||||
    (with-current-buffer (url-retrieve-synchronously url) | 
				
			||||
      (if (= 200 url-http-response-status) | 
				
			||||
          (progn | 
				
			||||
            (goto-char url-http-end-of-headers) | 
				
			||||
            (let* ((res (json-read)) | 
				
			||||
                   (results (assoc-default 'result res))) | 
				
			||||
              ;; TODO: gather packages as well, and when we choose a | 
				
			||||
              ;; given import, check that we have the package in the | 
				
			||||
              ;; cabal file as well. | 
				
			||||
              (cl-mapcan (lambda (r) | 
				
			||||
                           ;; append converts from vector -> list | 
				
			||||
                           (append (assoc-default 'resultModules r) nil)) | 
				
			||||
                         results))) | 
				
			||||
        (warn "HTTP error %s fetching %s" url-http-response-status url))))) | 
				
			||||
 | 
				
			||||
(defun haskell-process-hoogle-ident (ident) | 
				
			||||
  "Hoogle for IDENT, return a list of modules." | 
				
			||||
  (with-temp-buffer | 
				
			||||
    (let ((hoogle-error (call-process "hoogle" nil t nil "search" "--exact" ident))) | 
				
			||||
      (goto-char (point-min)) | 
				
			||||
      (unless (or (/= 0 hoogle-error) | 
				
			||||
                  (looking-at "^No results found") | 
				
			||||
                  (looking-at "^package ")) | 
				
			||||
        (while (re-search-forward "^\\([^ ]+\\).*$" nil t) | 
				
			||||
          (replace-match "\\1" nil nil)) | 
				
			||||
        (cl-remove-if (lambda (a) (string= "" a)) | 
				
			||||
                      (split-string (buffer-string) | 
				
			||||
                                    "\n")))))) | 
				
			||||
 | 
				
			||||
(defun haskell-process-haskell-docs-ident (ident) | 
				
			||||
  "Search with haskell-docs for IDENT, return a list of modules." | 
				
			||||
  (cl-remove-if-not | 
				
			||||
   (lambda (a) (string-match "^[[:upper:]][[:alnum:]_'.]+$" a)) | 
				
			||||
   (split-string | 
				
			||||
      (with-output-to-string | 
				
			||||
        (with-current-buffer | 
				
			||||
            standard-output | 
				
			||||
          (call-process "haskell-docs" | 
				
			||||
                        nil             ; no infile | 
				
			||||
                        t               ; output to current buffer (that is string) | 
				
			||||
                        nil             ; do not redisplay | 
				
			||||
                        "--modules" ident))) | 
				
			||||
      "\n"))) | 
				
			||||
 | 
				
			||||
(defun haskell-process-import-modules (process modules) | 
				
			||||
  "Query PROCESS `:m +' command to import MODULES." | 
				
			||||
  (when haskell-process-auto-import-loaded-modules | 
				
			||||
    (haskell-process-queue-command | 
				
			||||
     process | 
				
			||||
     (make-haskell-command | 
				
			||||
      :state (cons process modules) | 
				
			||||
      :go (lambda (state) | 
				
			||||
            (haskell-process-send-string | 
				
			||||
             (car state) | 
				
			||||
             (format ":m + %s" (mapconcat 'identity (cdr state) " ")))))))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-describe (ident) | 
				
			||||
  "Describe the given identifier IDENT." | 
				
			||||
  (interactive (list (read-from-minibuffer "Describe identifier: " | 
				
			||||
                                           (haskell-ident-at-point)))) | 
				
			||||
  (let ((results (read (shell-command-to-string | 
				
			||||
                        (concat "haskell-docs --sexp " | 
				
			||||
                                ident))))) | 
				
			||||
    (help-setup-xref (list #'haskell-describe ident) | 
				
			||||
                     (called-interactively-p 'interactive)) | 
				
			||||
    (save-excursion | 
				
			||||
      (with-help-window (help-buffer) | 
				
			||||
        (with-current-buffer (help-buffer) | 
				
			||||
          (if results | 
				
			||||
              (cl-loop for result in results | 
				
			||||
                       do (insert (propertize ident 'font-lock-face | 
				
			||||
                                              '((:inherit font-lock-type-face | 
				
			||||
                                                          :underline t))) | 
				
			||||
                                  " is defined in " | 
				
			||||
                                  (let ((module (cadr (assoc 'module result)))) | 
				
			||||
                                    (if module | 
				
			||||
                                        (concat module " ") | 
				
			||||
                                      "")) | 
				
			||||
                                  (cadr (assoc 'package result)) | 
				
			||||
                                  "\n\n") | 
				
			||||
                       do (let ((type (cadr (assoc 'type result)))) | 
				
			||||
                            (when type | 
				
			||||
                              (insert (haskell-fontify-as-mode type 'haskell-mode) | 
				
			||||
                                      "\n"))) | 
				
			||||
                       do (let ((args (cadr (assoc 'type results)))) | 
				
			||||
                            (cl-loop for arg in args | 
				
			||||
                                     do (insert arg "\n")) | 
				
			||||
                            (insert "\n")) | 
				
			||||
                       do (insert (cadr (assoc 'documentation result))) | 
				
			||||
                       do (insert "\n\n")) | 
				
			||||
            (insert "No results for " ident))))))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-rgrep (&optional prompt) | 
				
			||||
  "Grep the effective project for the symbol at point. | 
				
			||||
Very useful for codebase navigation. | 
				
			||||
 | 
				
			||||
Prompts for an arbitrary regexp given a prefix arg PROMPT." | 
				
			||||
  (interactive "P") | 
				
			||||
  (let ((sym (if prompt | 
				
			||||
                 (read-from-minibuffer "Look for: ") | 
				
			||||
               (haskell-ident-at-point)))) | 
				
			||||
    (rgrep sym | 
				
			||||
           "*.hs" ;; TODO: common Haskell extensions. | 
				
			||||
           (haskell-session-current-dir (haskell-interactive-session))))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-process-do-info (&optional prompt-value) | 
				
			||||
  "Print info on the identifier at point. | 
				
			||||
If PROMPT-VALUE is non-nil, request identifier via mini-buffer." | 
				
			||||
  (interactive "P") | 
				
			||||
  (let ((at-point (haskell-ident-at-point))) | 
				
			||||
    (when (or prompt-value at-point) | 
				
			||||
      (let* ((ident (replace-regexp-in-string | 
				
			||||
                     "^!\\([A-Z_a-z]\\)" | 
				
			||||
                     "\\1" | 
				
			||||
                     (if prompt-value | 
				
			||||
                         (read-from-minibuffer "Info: " at-point) | 
				
			||||
                       at-point))) | 
				
			||||
             (modname (unless prompt-value | 
				
			||||
                        (haskell-utils-parse-import-statement-at-point))) | 
				
			||||
             (command (cond | 
				
			||||
                       (modname | 
				
			||||
                        (format ":browse! %s" modname)) | 
				
			||||
                       ((string= ident "") ; For the minibuffer input case | 
				
			||||
                        nil) | 
				
			||||
                       (t (format (if (string-match "^[a-zA-Z_]" ident) | 
				
			||||
                                      ":info %s" | 
				
			||||
                                    ":info (%s)") | 
				
			||||
                                  (or ident | 
				
			||||
                                      at-point)))))) | 
				
			||||
        (when command | 
				
			||||
          (haskell-process-show-repl-response command)))))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-process-do-type (&optional insert-value) | 
				
			||||
  ;; FIXME insert value functionallity seems to be missing. | 
				
			||||
  "Print the type of the given expression. | 
				
			||||
 | 
				
			||||
Given INSERT-VALUE prefix indicates that result type signature | 
				
			||||
should be inserted." | 
				
			||||
  (interactive "P") | 
				
			||||
  (if insert-value | 
				
			||||
      (haskell-process-insert-type) | 
				
			||||
    (let* ((expr | 
				
			||||
            (if (use-region-p) | 
				
			||||
                (buffer-substring-no-properties (region-beginning) (region-end)) | 
				
			||||
              (haskell-ident-at-point))) | 
				
			||||
           (expr-okay (and expr | 
				
			||||
                         (not (string-match-p "\\`[[:space:]]*\\'" expr)) | 
				
			||||
                         (not (string-match-p "\n" expr))))) | 
				
			||||
      ;; No newlines in expressions, and surround with parens if it | 
				
			||||
      ;; might be a slice expression | 
				
			||||
      (when expr-okay | 
				
			||||
        (haskell-process-show-repl-response | 
				
			||||
         (format | 
				
			||||
          (if (or (string-match-p "\\`(" expr) | 
				
			||||
                  (string-match-p "\\`[_[:alpha:]]" expr)) | 
				
			||||
              ":type %s" | 
				
			||||
            ":type (%s)") | 
				
			||||
          expr)))))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-mode-jump-to-def-or-tag (&optional _next-p) | 
				
			||||
  ;; FIXME NEXT-P arg is not used | 
				
			||||
  "Jump to the definition. | 
				
			||||
Jump to definition of identifier at point by consulting GHCi, or | 
				
			||||
tag table as fallback. | 
				
			||||
 | 
				
			||||
Remember: If GHCi is busy doing something, this will delay, but | 
				
			||||
it will always be accurate, in contrast to tags, which always | 
				
			||||
work but are not always accurate. | 
				
			||||
If the definition or tag is found, the location from which you jumped | 
				
			||||
will be pushed onto `xref--marker-ring', so you can return to that | 
				
			||||
position with `xref-pop-marker-stack'." | 
				
			||||
  (interactive "P") | 
				
			||||
  (let ((initial-loc (point-marker)) | 
				
			||||
        (loc (haskell-mode-find-def (haskell-ident-at-point)))) | 
				
			||||
    (if loc | 
				
			||||
        (haskell-mode-handle-generic-loc loc) | 
				
			||||
      (call-interactively 'haskell-mode-tag-find)) | 
				
			||||
    (unless (equal initial-loc (point-marker)) | 
				
			||||
      (xref-push-marker-stack initial-loc)))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-mode-goto-loc () | 
				
			||||
  "Go to the location of the thing at point. | 
				
			||||
Requires the :loc-at command from GHCi." | 
				
			||||
  (interactive) | 
				
			||||
  (let ((loc (haskell-mode-loc-at))) | 
				
			||||
    (when loc | 
				
			||||
      (haskell-mode-goto-span loc)))) | 
				
			||||
 | 
				
			||||
(defun haskell-mode-goto-span (span) | 
				
			||||
  "Jump to the SPAN, whatever file and line and column it needs to get there." | 
				
			||||
  (xref-push-marker-stack) | 
				
			||||
  (find-file (expand-file-name (plist-get span :path) | 
				
			||||
                               (haskell-session-cabal-dir (haskell-interactive-session)))) | 
				
			||||
  (goto-char (point-min)) | 
				
			||||
  (forward-line (1- (plist-get span :start-line))) | 
				
			||||
  (forward-char (plist-get span :start-col))) | 
				
			||||
 | 
				
			||||
(defun haskell-process-insert-type () | 
				
			||||
  "Get the identifer at the point and insert its type. | 
				
			||||
Use GHCi's :type if it's possible." | 
				
			||||
  (let ((ident (haskell-ident-at-point))) | 
				
			||||
    (when ident | 
				
			||||
      (let ((process (haskell-interactive-process)) | 
				
			||||
            (query (format (if (string-match "^[_[:lower:][:upper:]]" ident) | 
				
			||||
                               ":type %s" | 
				
			||||
                             ":type (%s)") | 
				
			||||
                           ident))) | 
				
			||||
        (haskell-process-queue-command | 
				
			||||
         process | 
				
			||||
         (make-haskell-command | 
				
			||||
          :state (list process query (current-buffer)) | 
				
			||||
          :go (lambda (state) | 
				
			||||
                (haskell-process-send-string (nth 0 state) | 
				
			||||
                                             (nth 1 state))) | 
				
			||||
          :complete (lambda (state response) | 
				
			||||
                      (cond | 
				
			||||
                       ;; TODO: Generalize this into a function. | 
				
			||||
                       ((or (string-match "^Top level" response) | 
				
			||||
                            (string-match "^<interactive>" response)) | 
				
			||||
                        (message response)) | 
				
			||||
                       (t | 
				
			||||
                        (with-current-buffer (nth 2 state) | 
				
			||||
                          (goto-char (line-beginning-position)) | 
				
			||||
                          (insert (format "%s\n" (replace-regexp-in-string "\n$" "" response))))))))))))) | 
				
			||||
 | 
				
			||||
(defun haskell-mode-find-def (ident) | 
				
			||||
  ;; TODO Check if it possible to exploit `haskell-process-do-info' | 
				
			||||
  "Find definition location of identifier IDENT. | 
				
			||||
Uses the GHCi process to find the location.  Returns nil if it | 
				
			||||
can't find the identifier or the identifier isn't a string. | 
				
			||||
 | 
				
			||||
Returns: | 
				
			||||
 | 
				
			||||
    (library <package> <module>) | 
				
			||||
    (file <path> <line> <col>) | 
				
			||||
    (module <name>) | 
				
			||||
    nil" | 
				
			||||
  (when (stringp ident) | 
				
			||||
    (let ((reply (haskell-process-queue-sync-request | 
				
			||||
                  (haskell-interactive-process) | 
				
			||||
                  (format (if (string-match "^[a-zA-Z_]" ident) | 
				
			||||
                              ":info %s" | 
				
			||||
                            ":info (%s)") | 
				
			||||
                          ident)))) | 
				
			||||
      (let ((match (string-match "-- Defined \\(at\\|in\\) \\(.+\\)$" reply))) | 
				
			||||
        (when match | 
				
			||||
          (let ((defined (match-string 2 reply))) | 
				
			||||
            (let ((match (string-match "\\(.+?\\):\\([0-9]+\\):\\([0-9]+\\)$" defined))) | 
				
			||||
              (cond | 
				
			||||
               (match | 
				
			||||
                (list 'file | 
				
			||||
                      (expand-file-name (match-string 1 defined) | 
				
			||||
                                        (haskell-session-current-dir (haskell-interactive-session))) | 
				
			||||
                      (string-to-number (match-string 2 defined)) | 
				
			||||
                      (string-to-number (match-string 3 defined)))) | 
				
			||||
               (t | 
				
			||||
                (let ((match (string-match "`\\(.+?\\):\\(.+?\\)'$" defined))) | 
				
			||||
                  (if match | 
				
			||||
                      (list 'library | 
				
			||||
                            (match-string 1 defined) | 
				
			||||
                            (match-string 2 defined)) | 
				
			||||
                    (let ((match (string-match "`\\(.+?\\)'$" defined))) | 
				
			||||
                      (if match | 
				
			||||
                          (list 'module | 
				
			||||
                                (match-string 1 defined))))))))))))))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-mode-jump-to-def (ident) | 
				
			||||
  "Jump to definition of identifier IDENT at point." | 
				
			||||
  (interactive (list (haskell-ident-at-point))) | 
				
			||||
  (let ((loc (haskell-mode-find-def ident))) | 
				
			||||
    (when loc | 
				
			||||
      (haskell-mode-handle-generic-loc loc)))) | 
				
			||||
 | 
				
			||||
(defun haskell-mode-handle-generic-loc (loc) | 
				
			||||
  "Either jump to or echo a generic location LOC. | 
				
			||||
Either a file or a library." | 
				
			||||
  (cl-case (car loc) | 
				
			||||
    (file (haskell-mode-jump-to-loc (cdr loc))) | 
				
			||||
    (library (message "Defined in `%s' (%s)." | 
				
			||||
                      (elt loc 2) | 
				
			||||
                      (elt loc 1))) | 
				
			||||
    (module (message "Defined in `%s'." | 
				
			||||
                     (elt loc 1))))) | 
				
			||||
 | 
				
			||||
(defun haskell-mode-loc-at () | 
				
			||||
  "Get the location at point. | 
				
			||||
Requires the :loc-at command from GHCi." | 
				
			||||
  (let ((pos (or (when (region-active-p) | 
				
			||||
                   (cons (region-beginning) | 
				
			||||
                         (region-end))) | 
				
			||||
                 (haskell-spanable-pos-at-point) | 
				
			||||
                 (cons (point) | 
				
			||||
                       (point))))) | 
				
			||||
    (when pos | 
				
			||||
      (let ((reply (haskell-process-queue-sync-request | 
				
			||||
                    (haskell-interactive-process) | 
				
			||||
                    (save-excursion | 
				
			||||
                      (format ":loc-at %s %d %d %d %d %s" | 
				
			||||
                              (buffer-file-name) | 
				
			||||
                              (progn (goto-char (car pos)) | 
				
			||||
                                     (line-number-at-pos)) | 
				
			||||
                              (1+ (current-column)) ;; GHC uses 1-based columns. | 
				
			||||
                              (progn (goto-char (cdr pos)) | 
				
			||||
                                     (line-number-at-pos)) | 
				
			||||
                              (1+ (current-column)) ;; GHC uses 1-based columns. | 
				
			||||
                              (buffer-substring-no-properties (car pos) | 
				
			||||
                                                              (cdr pos))))))) | 
				
			||||
        (if reply | 
				
			||||
            (if (string-match "\\(.*?\\):(\\([0-9]+\\),\\([0-9]+\\))-(\\([0-9]+\\),\\([0-9]+\\))" | 
				
			||||
                              reply) | 
				
			||||
                (list :path (match-string 1 reply) | 
				
			||||
                      :start-line (string-to-number (match-string 2 reply)) | 
				
			||||
                      ;; ;; GHC uses 1-based columns. | 
				
			||||
                      :start-col (1- (string-to-number (match-string 3 reply))) | 
				
			||||
                      :end-line (string-to-number (match-string 4 reply)) | 
				
			||||
                      ;; GHC uses 1-based columns. | 
				
			||||
                      :end-col (1- (string-to-number (match-string 5 reply)))) | 
				
			||||
              (error (propertize reply 'face 'compilation-error))) | 
				
			||||
          (error (propertize "No reply. Is :loc-at supported?" | 
				
			||||
                             'face 'compilation-error))))))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-process-cd (&optional _not-interactive) | 
				
			||||
  ;; FIXME optional arg is not used | 
				
			||||
  "Change directory." | 
				
			||||
  (interactive) | 
				
			||||
  (let* ((session (haskell-interactive-session)) | 
				
			||||
         (dir (haskell-session-prompt-set-current-dir session))) | 
				
			||||
    (haskell-process-log | 
				
			||||
     (propertize (format "Changing directory to %s ...\n" dir) | 
				
			||||
                 'face font-lock-comment-face)) | 
				
			||||
    (haskell-process-change-dir session | 
				
			||||
                                (haskell-interactive-process) | 
				
			||||
                                dir))) | 
				
			||||
 | 
				
			||||
(defun haskell-session-buffer-default-dir (session &optional buffer) | 
				
			||||
  "Try to deduce a sensible default directory for SESSION and BUFFER, | 
				
			||||
of which the latter defaults to the current buffer." | 
				
			||||
  (or (haskell-session-get session 'current-dir) | 
				
			||||
      (haskell-session-get session 'cabal-dir) | 
				
			||||
      (if (buffer-file-name buffer) | 
				
			||||
	  (file-name-directory (buffer-file-name buffer)) | 
				
			||||
	  "~/"))) | 
				
			||||
 | 
				
			||||
(defun haskell-session-prompt-set-current-dir (session &optional use-default) | 
				
			||||
  "Prompt for the current directory. | 
				
			||||
Return current working directory for SESSION." | 
				
			||||
  (let ((default (haskell-session-buffer-default-dir session))) | 
				
			||||
    (haskell-session-set-current-dir | 
				
			||||
     session | 
				
			||||
     (if use-default | 
				
			||||
	 default | 
				
			||||
	 (haskell-utils-read-directory-name "Set current directory: " default)))) | 
				
			||||
  (haskell-session-get session 'current-dir)) | 
				
			||||
 | 
				
			||||
(defun haskell-process-change-dir (session process dir) | 
				
			||||
  "Change SESSION's current directory. | 
				
			||||
Query PROCESS to `:cd` to directory DIR." | 
				
			||||
  (haskell-process-queue-command | 
				
			||||
   process | 
				
			||||
   (make-haskell-command | 
				
			||||
    :state (list session process dir) | 
				
			||||
    :go | 
				
			||||
    (lambda (state) | 
				
			||||
      (haskell-process-send-string | 
				
			||||
       (cadr state) (format ":cd %s" (cl-caddr state)))) | 
				
			||||
 | 
				
			||||
    :complete | 
				
			||||
    (lambda (state _) | 
				
			||||
      (haskell-session-set-current-dir (car state) (cl-caddr state)) | 
				
			||||
      (haskell-interactive-mode-echo (car state) | 
				
			||||
                                     (format "Changed directory: %s" | 
				
			||||
                                             (cl-caddr state))))))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-process-cabal-macros () | 
				
			||||
  "Send the cabal macros string." | 
				
			||||
  (interactive) | 
				
			||||
  (haskell-process-queue-without-filters (haskell-interactive-process) | 
				
			||||
                                         ":set -optP-include -optPdist/build/autogen/cabal_macros.h")) | 
				
			||||
 | 
				
			||||
(defun haskell-process-do-try-info (sym) | 
				
			||||
  "Get info of SYM and echo in the minibuffer." | 
				
			||||
  (let ((process (haskell-interactive-process))) | 
				
			||||
    (haskell-process-queue-command | 
				
			||||
     process | 
				
			||||
     (make-haskell-command | 
				
			||||
      :state (cons process sym) | 
				
			||||
      :go (lambda (state) | 
				
			||||
            (haskell-process-send-string | 
				
			||||
             (car state) | 
				
			||||
             (if (string-match "^[A-Za-z_]" (cdr state)) | 
				
			||||
                 (format ":info %s" (cdr state)) | 
				
			||||
               (format ":info (%s)" (cdr state))))) | 
				
			||||
      :complete (lambda (_state response) | 
				
			||||
                  (unless (or (string-match "^Top level" response) | 
				
			||||
                              (string-match "^<interactive>" response)) | 
				
			||||
                    (haskell-mode-message-line response))))))) | 
				
			||||
 | 
				
			||||
(defun haskell-process-do-try-type (sym) | 
				
			||||
  "Get type of SYM and echo in the minibuffer." | 
				
			||||
  (let ((process (haskell-interactive-process))) | 
				
			||||
    (haskell-process-queue-command | 
				
			||||
     process | 
				
			||||
     (make-haskell-command | 
				
			||||
      :state (cons process sym) | 
				
			||||
      :go (lambda (state) | 
				
			||||
            (haskell-process-send-string | 
				
			||||
             (car state) | 
				
			||||
             (if (string-match "^[A-Za-z_]" (cdr state)) | 
				
			||||
                 (format ":type %s" (cdr state)) | 
				
			||||
               (format ":type (%s)" (cdr state))))) | 
				
			||||
      :complete (lambda (_state response) | 
				
			||||
                  (unless (or (string-match "^Top level" response) | 
				
			||||
                              (string-match "^<interactive>" response)) | 
				
			||||
                    (haskell-mode-message-line response))))))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-mode-show-type-at (&optional insert-value) | 
				
			||||
  "Show type of the thing at point or within active region asynchronously. | 
				
			||||
This function requires GHCi-ng and `:set +c` option enabled by | 
				
			||||
default (please follow GHCi-ng README available at URL | 
				
			||||
`https://github.com/chrisdone/ghci-ng'). | 
				
			||||
 | 
				
			||||
\\<haskell-interactive-mode-map> | 
				
			||||
To make this function works sometimes you need to load the file in REPL | 
				
			||||
first using command `haskell-process-load-or-reload' bound to | 
				
			||||
\\[haskell-process-load-or-reload]. | 
				
			||||
 | 
				
			||||
Optional argument INSERT-VALUE indicates that | 
				
			||||
recieved type signature should be inserted (but only if nothing | 
				
			||||
happened since function invocation)." | 
				
			||||
  (interactive "P") | 
				
			||||
  (let* ((pos (haskell-command-capture-expr-bounds)) | 
				
			||||
         (req (haskell-utils-compose-type-at-command pos)) | 
				
			||||
         (process (haskell-interactive-process)) | 
				
			||||
         (buf (current-buffer)) | 
				
			||||
         (pos-reg (cons pos (region-active-p)))) | 
				
			||||
    (haskell-process-queue-command | 
				
			||||
     process | 
				
			||||
     (make-haskell-command | 
				
			||||
      :state (list process req buf insert-value pos-reg) | 
				
			||||
      :go | 
				
			||||
      (lambda (state) | 
				
			||||
        (let* ((prc (car state)) | 
				
			||||
               (req (nth 1 state))) | 
				
			||||
          (haskell-utils-async-watch-changes) | 
				
			||||
          (haskell-process-send-string prc req))) | 
				
			||||
      :complete | 
				
			||||
      (lambda (state response) | 
				
			||||
        (let* ((init-buffer (nth 2 state)) | 
				
			||||
               (insert-value (nth 3 state)) | 
				
			||||
               (pos-reg (nth 4 state)) | 
				
			||||
               (wrap (cdr pos-reg)) | 
				
			||||
               (min-pos (caar pos-reg)) | 
				
			||||
               (max-pos (cdar pos-reg)) | 
				
			||||
               (sig (haskell-utils-reduce-string response)) | 
				
			||||
               (res-type (haskell-utils-parse-repl-response sig))) | 
				
			||||
 | 
				
			||||
          (cl-case res-type | 
				
			||||
            ;; neither popup presentation buffer | 
				
			||||
            ;; nor insert response in error case | 
				
			||||
            ('unknown-command | 
				
			||||
             (message | 
				
			||||
              (concat | 
				
			||||
               "This command requires GHCi-ng. " | 
				
			||||
               "Please read command description for details."))) | 
				
			||||
            ('option-missing | 
				
			||||
             (message | 
				
			||||
              (concat | 
				
			||||
               "Could not infer type signature. " | 
				
			||||
               "You need to load file first. " | 
				
			||||
               "Also :set +c is required. " | 
				
			||||
               "Please read command description for details."))) | 
				
			||||
            ('interactive-error (message "Wrong REPL response: %s" sig)) | 
				
			||||
            (otherwise | 
				
			||||
             (if insert-value | 
				
			||||
                 ;; Only insert type signature and do not present it | 
				
			||||
                 (if (= (length haskell-utils-async-post-command-flag) 1) | 
				
			||||
                     (if wrap | 
				
			||||
                         ;; Handle region case | 
				
			||||
                         (progn | 
				
			||||
                           (deactivate-mark) | 
				
			||||
                           (save-excursion | 
				
			||||
                             (delete-region min-pos max-pos) | 
				
			||||
                             (goto-char min-pos) | 
				
			||||
                             (insert (concat "(" sig ")")))) | 
				
			||||
                       ;; Non-region cases | 
				
			||||
                       (haskell-command-insert-type-signature sig)) | 
				
			||||
                   ;; Some commands registered, prevent insertion | 
				
			||||
                   (let* ((rev (reverse haskell-utils-async-post-command-flag)) | 
				
			||||
                          (cs (format "%s" (cdr rev)))) | 
				
			||||
                     (message | 
				
			||||
                      (concat | 
				
			||||
                       "Type signature insertion was prevented. " | 
				
			||||
                       "These commands were registered:" | 
				
			||||
                       cs)))) | 
				
			||||
               ;; Present the result only when response is valid and not asked | 
				
			||||
               ;; to insert result | 
				
			||||
               (haskell-command-echo-or-present response))) | 
				
			||||
 | 
				
			||||
            (haskell-utils-async-stop-watching-changes init-buffer)))))))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-process-generate-tags (&optional and-then-find-this-tag) | 
				
			||||
  "Regenerate the TAGS table. | 
				
			||||
If optional AND-THEN-FIND-THIS-TAG argument is present it is used with | 
				
			||||
function `xref-find-definitions' after new table was generated." | 
				
			||||
  (interactive) | 
				
			||||
  (let ((process (haskell-interactive-process))) | 
				
			||||
    (haskell-process-queue-command | 
				
			||||
     process | 
				
			||||
     (make-haskell-command | 
				
			||||
      :state (cons process and-then-find-this-tag) | 
				
			||||
      :go (lambda (state) | 
				
			||||
            (if (eq system-type 'windows-nt) | 
				
			||||
                (haskell-process-send-string | 
				
			||||
                 (car state) | 
				
			||||
                 (format ":!hasktags --output=\"%s\\TAGS\" -x -e \"%s\"" | 
				
			||||
                            (haskell-session-cabal-dir (haskell-process-session (car state))) | 
				
			||||
                            (haskell-session-cabal-dir (haskell-process-session (car state))))) | 
				
			||||
              (haskell-process-send-string | 
				
			||||
               (car state) | 
				
			||||
               (format ":!cd %s && %s | %s" | 
				
			||||
                       (haskell-session-cabal-dir | 
				
			||||
                        (haskell-process-session (car state))) | 
				
			||||
                       "find . -type f \\( -name '*.hs' -or -name '*.lhs' -or -name '*.hsc' \\) -not \\( -name '#*' -or -name '.*' \\) -print0" | 
				
			||||
                       "xargs -0 hasktags -e -x")))) | 
				
			||||
      :complete (lambda (state _response) | 
				
			||||
                  (when (cdr state) | 
				
			||||
                    (let ((session-tags | 
				
			||||
                          (haskell-session-tags-filename | 
				
			||||
                           (haskell-process-session (car state))))) | 
				
			||||
                      (add-to-list 'tags-table-list session-tags) | 
				
			||||
                      (setq tags-file-name nil)) | 
				
			||||
                    (xref-find-definitions (cdr state))) | 
				
			||||
                  (haskell-mode-message-line "Tags generated.")))))) | 
				
			||||
 | 
				
			||||
(defun haskell-process-add-cabal-autogen () | 
				
			||||
  "Add cabal's autogen dir to the GHCi search path. | 
				
			||||
Add <cabal-project-dir>/dist/build/autogen/ to GHCi seatch path. | 
				
			||||
This allows modules such as 'Path_...', generated by cabal, to be | 
				
			||||
loaded by GHCi." | 
				
			||||
  (unless (eq 'cabal-repl (haskell-process-type)) ;; redundant with "cabal repl" | 
				
			||||
    (let* | 
				
			||||
        ((session       (haskell-interactive-session)) | 
				
			||||
         (cabal-dir     (haskell-session-cabal-dir session)) | 
				
			||||
         (ghci-gen-dir  (format "%sdist/build/autogen/" cabal-dir))) | 
				
			||||
      (haskell-process-queue-without-filters | 
				
			||||
       (haskell-interactive-process) | 
				
			||||
       (format ":set -i%s" ghci-gen-dir))))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-process-unignore () | 
				
			||||
  "Unignore any ignored files. | 
				
			||||
Do not ignore files that were specified as being ignored by the | 
				
			||||
inferior GHCi process." | 
				
			||||
  (interactive) | 
				
			||||
  (let ((session (haskell-interactive-session)) | 
				
			||||
        (changed nil)) | 
				
			||||
    (if (null (haskell-session-get session | 
				
			||||
                                   'ignored-files)) | 
				
			||||
        (message "Nothing to unignore!") | 
				
			||||
      (cl-loop for file in (haskell-session-get session | 
				
			||||
                                                'ignored-files) | 
				
			||||
               do (cl-case (read-event | 
				
			||||
                            (propertize (format "Set permissions? %s (y, n, v: stop and view file)" | 
				
			||||
                                                file) | 
				
			||||
                                        'face 'minibuffer-prompt)) | 
				
			||||
                    (?y | 
				
			||||
                     (haskell-process-unignore-file session file) | 
				
			||||
                     (setq changed t)) | 
				
			||||
                    (?v | 
				
			||||
                     (find-file file) | 
				
			||||
                     (cl-return)))) | 
				
			||||
      (when (and changed | 
				
			||||
                 (y-or-n-p "Restart GHCi process now? ")) | 
				
			||||
        (haskell-process-restart))))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-session-change-target (target) | 
				
			||||
  "Set the build TARGET for cabal REPL." | 
				
			||||
  (interactive | 
				
			||||
   (list | 
				
			||||
     (completing-read "New build target: " (haskell-cabal-enum-targets) | 
				
			||||
		      nil nil nil 'haskell-cabal-targets-history))) | 
				
			||||
  (let* ((session haskell-session) | 
				
			||||
         (old-target (haskell-session-get session 'target))) | 
				
			||||
    (when session | 
				
			||||
      (haskell-session-set-target session target) | 
				
			||||
      (when (and (not (string= old-target target)) | 
				
			||||
                 (y-or-n-p "Target changed, restart haskell process?")) | 
				
			||||
        (haskell-process-start session))))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-mode-stylish-buffer () | 
				
			||||
  "Apply stylish-haskell to the current buffer." | 
				
			||||
  (interactive) | 
				
			||||
  (let ((column (current-column)) | 
				
			||||
        (line (line-number-at-pos))) | 
				
			||||
    (haskell-mode-buffer-apply-command "stylish-haskell") | 
				
			||||
    (goto-char (point-min)) | 
				
			||||
    (forward-line (1- line)) | 
				
			||||
    (goto-char (+ column (point))))) | 
				
			||||
 | 
				
			||||
(defun haskell-mode-buffer-apply-command (cmd) | 
				
			||||
  "Execute shell command CMD with current buffer as input and output. | 
				
			||||
Use buffer as input and replace the whole buffer with the | 
				
			||||
output.  If CMD fails the buffer remains unchanged." | 
				
			||||
  (set-buffer-modified-p t) | 
				
			||||
  (let* ((chomp (lambda (str) | 
				
			||||
                  (while (string-match "\\`\n+\\|^\\s-+\\|\\s-+$\\|\n+\\'" str) | 
				
			||||
                    (setq str (replace-match "" t t str))) | 
				
			||||
                  str)) | 
				
			||||
         (_errout (lambda (fmt &rest args) | 
				
			||||
		    (let* ((warning-fill-prefix "    ")) | 
				
			||||
		      (display-warning cmd (apply 'format fmt args) :warning)))) | 
				
			||||
         (filename (buffer-file-name (current-buffer))) | 
				
			||||
         (cmd-prefix (replace-regexp-in-string " .*" "" cmd)) | 
				
			||||
         (tmp-file (make-temp-file cmd-prefix)) | 
				
			||||
         (err-file (make-temp-file cmd-prefix)) | 
				
			||||
         (default-directory (if (and (boundp 'haskell-session) | 
				
			||||
                                     haskell-session) | 
				
			||||
                                (haskell-session-cabal-dir haskell-session) | 
				
			||||
                              default-directory)) | 
				
			||||
         (_errcode (with-temp-file tmp-file | 
				
			||||
		     (call-process cmd filename | 
				
			||||
				   (list (current-buffer) err-file) nil))) | 
				
			||||
         (stderr-output | 
				
			||||
          (with-temp-buffer | 
				
			||||
            (insert-file-contents err-file) | 
				
			||||
            (funcall chomp (buffer-substring-no-properties (point-min) (point-max))))) | 
				
			||||
         (stdout-output | 
				
			||||
          (with-temp-buffer | 
				
			||||
            (insert-file-contents tmp-file) | 
				
			||||
            (buffer-substring-no-properties (point-min) (point-max))))) | 
				
			||||
    (if (string= "" stderr-output) | 
				
			||||
        (if (string= "" stdout-output) | 
				
			||||
            (message "Error: %s produced no output, leaving buffer alone" cmd) | 
				
			||||
          (save-restriction | 
				
			||||
            (widen) | 
				
			||||
            ;; command successful, insert file with replacement to preserve | 
				
			||||
            ;; markers. | 
				
			||||
            (insert-file-contents tmp-file nil nil nil t))) | 
				
			||||
      (progn | 
				
			||||
        ;; non-null stderr, command must have failed | 
				
			||||
        (message "Error: %s ended with errors, leaving buffer alone" cmd) | 
				
			||||
        ;; use (warning-minimum-level :debug) to see this | 
				
			||||
        (display-warning cmd stderr-output :debug))) | 
				
			||||
    (delete-file tmp-file) | 
				
			||||
    (delete-file err-file))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-mode-find-uses () | 
				
			||||
  "Find use cases of the identifier at point and highlight them all." | 
				
			||||
  (interactive) | 
				
			||||
  (let ((spans (haskell-mode-uses-at))) | 
				
			||||
    (unless (null spans) | 
				
			||||
      (highlight-uses-mode 1) | 
				
			||||
      (cl-loop for span in spans | 
				
			||||
               do (haskell-mode-make-use-highlight span))))) | 
				
			||||
 | 
				
			||||
(defun haskell-mode-make-use-highlight (span) | 
				
			||||
  "Make a highlight overlay at the given SPAN." | 
				
			||||
  (save-window-excursion | 
				
			||||
    (save-excursion | 
				
			||||
      (haskell-mode-goto-span span) | 
				
			||||
      (save-excursion | 
				
			||||
        (highlight-uses-mode-highlight | 
				
			||||
         (progn | 
				
			||||
           (goto-char (point-min)) | 
				
			||||
           (forward-line (1- (plist-get span :start-line))) | 
				
			||||
           (forward-char (plist-get span :start-col)) | 
				
			||||
           (point)) | 
				
			||||
         (progn | 
				
			||||
           (goto-char (point-min)) | 
				
			||||
           (forward-line (1- (plist-get span :end-line))) | 
				
			||||
           (forward-char (plist-get span :end-col)) | 
				
			||||
           (point))))))) | 
				
			||||
 | 
				
			||||
(defun haskell-mode-uses-at () | 
				
			||||
  "Get the locations of use cases for the ident at point. | 
				
			||||
Requires the :uses command from GHCi." | 
				
			||||
  (let ((pos (or (when (region-active-p) | 
				
			||||
                   (cons (region-beginning) | 
				
			||||
                         (region-end))) | 
				
			||||
                 (haskell-ident-pos-at-point) | 
				
			||||
                 (cons (point) | 
				
			||||
                       (point))))) | 
				
			||||
    (when pos | 
				
			||||
      (let ((reply (haskell-process-queue-sync-request | 
				
			||||
                    (haskell-interactive-process) | 
				
			||||
                    (save-excursion | 
				
			||||
                      (format ":uses %s %d %d %d %d %s" | 
				
			||||
                              (buffer-file-name) | 
				
			||||
                              (progn (goto-char (car pos)) | 
				
			||||
                                     (line-number-at-pos)) | 
				
			||||
                              (1+ (current-column)) ;; GHC uses 1-based columns. | 
				
			||||
                              (progn (goto-char (cdr pos)) | 
				
			||||
                                     (line-number-at-pos)) | 
				
			||||
                              (1+ (current-column)) ;; GHC uses 1-based columns. | 
				
			||||
                              (buffer-substring-no-properties (car pos) | 
				
			||||
                                                              (cdr pos))))))) | 
				
			||||
        (if reply | 
				
			||||
            (let ((lines (split-string reply "\n" t))) | 
				
			||||
              (cl-remove-if | 
				
			||||
               #'null | 
				
			||||
               (mapcar (lambda (line) | 
				
			||||
                         (if (string-match "\\(.*?\\):(\\([0-9]+\\),\\([0-9]+\\))-(\\([0-9]+\\),\\([0-9]+\\))" | 
				
			||||
                                           line) | 
				
			||||
                             (list :path (match-string 1 line) | 
				
			||||
                                   :start-line (string-to-number (match-string 2 line)) | 
				
			||||
                                   ;; ;; GHC uses 1-based columns. | 
				
			||||
                                   :start-col (1- (string-to-number (match-string 3 line))) | 
				
			||||
                                   :end-line (string-to-number (match-string 4 line)) | 
				
			||||
                                   ;; GHC uses 1-based columns. | 
				
			||||
                                   :end-col (1- (string-to-number (match-string 5 line)))) | 
				
			||||
                           (error (propertize line 'face 'compilation-error)))) | 
				
			||||
                       lines))) | 
				
			||||
          (error (propertize "No reply. Is :uses supported?" | 
				
			||||
                             'face 'compilation-error))))))) | 
				
			||||
 | 
				
			||||
(defun haskell-command-echo-or-present (msg) | 
				
			||||
  "Present message in some manner depending on configuration. | 
				
			||||
If variable `haskell-process-use-presentation-mode' is NIL it will output | 
				
			||||
modified message MSG to echo area." | 
				
			||||
  (if haskell-process-use-presentation-mode | 
				
			||||
      (let ((session (haskell-process-session (haskell-interactive-process)))) | 
				
			||||
        (haskell-presentation-present session msg)) | 
				
			||||
    (let ((m (haskell-utils-reduce-string msg))) | 
				
			||||
      (message m)))) | 
				
			||||
 | 
				
			||||
(defun haskell-command-capture-expr-bounds () | 
				
			||||
  "Capture position bounds of expression at point. | 
				
			||||
If there is an active region then it returns region | 
				
			||||
bounds.  Otherwise it uses `haskell-spanable-pos-at-point` to | 
				
			||||
capture identifier bounds.  If latter function returns NIL this function | 
				
			||||
will return cons cell where min and max positions both are equal | 
				
			||||
to point." | 
				
			||||
  (or (when (region-active-p) | 
				
			||||
        (cons (region-beginning) | 
				
			||||
              (region-end))) | 
				
			||||
      (haskell-spanable-pos-at-point) | 
				
			||||
      (cons (point) (point)))) | 
				
			||||
 | 
				
			||||
(defun haskell-command-insert-type-signature (signature) | 
				
			||||
  "Insert type signature. | 
				
			||||
In case of active region is present, wrap it by parentheses and | 
				
			||||
append SIGNATURE to original expression.  Otherwise tries to | 
				
			||||
carefully insert SIGNATURE above identifier at point.  Removes | 
				
			||||
newlines and extra whitespace in signature before insertion." | 
				
			||||
  (let* ((ident-pos (or (haskell-ident-pos-at-point) | 
				
			||||
                        (cons (point) (point)))) | 
				
			||||
         (min-pos (car ident-pos)) | 
				
			||||
         (sig (haskell-utils-reduce-string signature))) | 
				
			||||
    (save-excursion | 
				
			||||
      (goto-char min-pos) | 
				
			||||
      (let ((col (current-column))) | 
				
			||||
        (insert sig "\n") | 
				
			||||
        (indent-to col))))) | 
				
			||||
 | 
				
			||||
(provide 'haskell-commands) | 
				
			||||
;;; haskell-commands.el ends here | 
				
			||||
@ -1,70 +0,0 @@
					 | 
				
			||||
;;; haskell-compat.el --- legacy/compatibility backports for haskell-mode -*- lexical-binding: t -*- | 
				
			||||
;; | 
				
			||||
;; Filename: haskell-compat.el | 
				
			||||
;; Description: legacy/compatibility backports for haskell-mode | 
				
			||||
 | 
				
			||||
;; 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: | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
(require 'etags) | 
				
			||||
(require 'ring) | 
				
			||||
(require 'outline) | 
				
			||||
(require 'xref nil t) | 
				
			||||
 | 
				
			||||
(eval-when-compile | 
				
			||||
  (setq byte-compile-warnings '(not cl-functions obsolete))) | 
				
			||||
 | 
				
			||||
;; Missing in Emacs23, stolen from Emacs24's `subr.el' | 
				
			||||
(unless (fboundp 'process-live-p) | 
				
			||||
  (defun process-live-p (process) | 
				
			||||
    "Returns non-nil if PROCESS is alive. | 
				
			||||
A process is considered alive if its status is `run', `open', | 
				
			||||
`listen', `connect' or `stop'." | 
				
			||||
    (memq (process-status process) | 
				
			||||
	  '(run open listen connect stop)))) | 
				
			||||
 | 
				
			||||
;; Cross-referencing commands have been replaced since Emacs 25.1. | 
				
			||||
;; These aliases are required to provide backward compatibility. | 
				
			||||
(unless (fboundp 'xref-push-marker-stack) | 
				
			||||
  (defalias 'xref-pop-marker-stack 'pop-tag-mark) | 
				
			||||
 | 
				
			||||
  (defun xref-push-marker-stack (&optional m) | 
				
			||||
    "Add point to the marker stack." | 
				
			||||
    (ring-insert find-tag-marker-ring (or m (point-marker))))) | 
				
			||||
 | 
				
			||||
(unless (fboundp 'outline-hide-sublevels) | 
				
			||||
  (defalias 'outline-hide-sublevels 'hide-sublevels)) | 
				
			||||
 | 
				
			||||
(unless (fboundp 'outline-show-subtree) | 
				
			||||
  (defalias 'outline-show-subtree 'show-subtree)) | 
				
			||||
 | 
				
			||||
(unless (fboundp 'outline-hide-sublevels) | 
				
			||||
  (defalias 'outline-hide-sublevels 'hide-sublevels)) | 
				
			||||
 | 
				
			||||
(unless (fboundp 'outline-show-subtree) | 
				
			||||
  (defalias 'outline-show-subtree 'show-subtree)) | 
				
			||||
 | 
				
			||||
(unless (fboundp 'xref-find-definitions) | 
				
			||||
  (defun xref-find-definitions (ident) | 
				
			||||
    (let ((next-p (and (boundp 'xref-prompt-for-identifier) | 
				
			||||
                       xref-prompt-for-identifier))) | 
				
			||||
      (find-tag ident next-p)))) | 
				
			||||
 | 
				
			||||
(provide 'haskell-compat) | 
				
			||||
 | 
				
			||||
;;; haskell-compat.el ends here | 
				
			||||
@ -1,163 +0,0 @@
					 | 
				
			||||
;;; haskell-compile.el --- Haskell/GHC compilation sub-mode -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright (C) 2013  Herbert Valerio Riedel | 
				
			||||
 | 
				
			||||
;; Author: Herbert Valerio Riedel <hvr@gnu.org> | 
				
			||||
 | 
				
			||||
;; 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 of the License, 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: | 
				
			||||
 | 
				
			||||
;; Simple GHC-centric compilation sub-mode; see info node | 
				
			||||
;; `(haskell-mode)compilation' for more information | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'compile) | 
				
			||||
(require 'haskell-cabal) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defgroup haskell-compile nil | 
				
			||||
  "Settings for Haskell compilation mode" | 
				
			||||
  :link '(custom-manual "(haskell-mode)compilation") | 
				
			||||
  :group 'haskell) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-compile-cabal-build-command | 
				
			||||
  "cd %s && cabal build --ghc-option=-ferror-spans" | 
				
			||||
  "Default build command to use for `haskell-cabal-build' when a cabal file is detected. | 
				
			||||
The `%s' placeholder is replaced by the cabal package top folder." | 
				
			||||
  :group 'haskell-compile | 
				
			||||
  :type 'string) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-compile-cabal-build-alt-command | 
				
			||||
  "cd %s && cabal clean -s && cabal build --ghc-option=-ferror-spans" | 
				
			||||
  "Alternative build command to use when `haskell-cabal-build' is called with a negative prefix argument. | 
				
			||||
The `%s' placeholder is replaced by the cabal package top folder." | 
				
			||||
  :group 'haskell-compile | 
				
			||||
  :type 'string) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-compile-command | 
				
			||||
  "ghc -Wall -ferror-spans -fforce-recomp -c %s" | 
				
			||||
  "Default build command to use for `haskell-cabal-build' when no cabal file is detected. | 
				
			||||
The `%s' placeholder is replaced by the current buffer's filename." | 
				
			||||
  :group 'haskell-compile | 
				
			||||
  :type 'string) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-compile-ghc-filter-linker-messages | 
				
			||||
  t | 
				
			||||
  "Filter out unremarkable \"Loading package...\" linker messages during compilation." | 
				
			||||
  :group 'haskell-compile | 
				
			||||
  :type 'boolean) | 
				
			||||
 | 
				
			||||
(defconst haskell-compilation-error-regexp-alist | 
				
			||||
  `((,(concat | 
				
			||||
       "^ *\\(?1:[^ \t\r\n]+?\\):" | 
				
			||||
       "\\(?:" | 
				
			||||
       "\\(?2:[0-9]+\\):\\(?4:[0-9]+\\)\\(?:-\\(?5:[0-9]+\\)\\)?" ;; "121:1" & "12:3-5" | 
				
			||||
       "\\|" | 
				
			||||
       "(\\(?2:[0-9]+\\),\\(?4:[0-9]+\\))-(\\(?3:[0-9]+\\),\\(?5:[0-9]+\\))" ;; "(289,5)-(291,36)" | 
				
			||||
       "\\)" | 
				
			||||
       ":\\(?6: Warning:\\)?") | 
				
			||||
     1 (2 . 3) (4 . 5) (6 . nil)) ;; error/warning locus | 
				
			||||
 | 
				
			||||
    ;; multiple declarations | 
				
			||||
    ("^    \\(?:Declared at:\\|            \\) \\(?1:[^ \t\r\n]+\\):\\(?2:[0-9]+\\):\\(?4:[0-9]+\\)$" | 
				
			||||
     1 2 4 0) ;; info locus | 
				
			||||
 | 
				
			||||
    ;; this is the weakest pattern as it's subject to line wrapping et al. | 
				
			||||
    (" at \\(?1:[^ \t\r\n]+\\):\\(?2:[0-9]+\\):\\(?4:[0-9]+\\)\\(?:-\\(?5:[0-9]+\\)\\)?[)]?$" | 
				
			||||
     1 2 (4 . 5) 0)) ;; info locus | 
				
			||||
  "Regexps used for matching GHC compile messages. | 
				
			||||
See `compilation-error-regexp-alist' for semantics.") | 
				
			||||
 | 
				
			||||
(defvar haskell-compilation-mode-map | 
				
			||||
  (let ((map (make-sparse-keymap))) | 
				
			||||
    (set-keymap-parent map compilation-mode-map)) | 
				
			||||
  "Keymap for `haskell-compilation-mode' buffers. | 
				
			||||
This is a child of `compilation-mode-map'.") | 
				
			||||
 | 
				
			||||
(defun haskell-compilation-filter-hook () | 
				
			||||
  "Local `compilation-filter-hook' for `haskell-compilation-mode'." | 
				
			||||
 | 
				
			||||
  (when haskell-compile-ghc-filter-linker-messages | 
				
			||||
    (delete-matching-lines "^ *Loading package [^ \t\r\n]+ [.]+ linking [.]+ done\\.$" | 
				
			||||
                           (if (boundp 'compilation-filter-start) ;; available since Emacs 24.2 | 
				
			||||
                               (save-excursion (goto-char compilation-filter-start) | 
				
			||||
                                               (line-beginning-position)) | 
				
			||||
                             (point-min)) | 
				
			||||
                           (point)))) | 
				
			||||
 | 
				
			||||
(define-compilation-mode haskell-compilation-mode "HsCompilation" | 
				
			||||
  "Haskell/GHC specific `compilation-mode' derivative. | 
				
			||||
This mode provides support for GHC 7.[46]'s compile | 
				
			||||
messages. Specifically, also the `-ferror-spans` source location | 
				
			||||
format is supported, as well as info-locations within compile | 
				
			||||
messages pointing to additional source locations. | 
				
			||||
 | 
				
			||||
See Info node `(haskell-mode)compilation' for more details." | 
				
			||||
  (set (make-local-variable 'compilation-error-regexp-alist) | 
				
			||||
       haskell-compilation-error-regexp-alist) | 
				
			||||
 | 
				
			||||
  (add-hook 'compilation-filter-hook | 
				
			||||
            'haskell-compilation-filter-hook nil t) | 
				
			||||
  ) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-compile (&optional edit-command) | 
				
			||||
  "Compile the Haskell program including the current buffer. | 
				
			||||
Tries to locate the next cabal description in current or parent | 
				
			||||
folders via `haskell-cabal-find-dir' and if found, invoke | 
				
			||||
`haskell-compile-cabal-build-command' from the cabal package root | 
				
			||||
folder. If no cabal package could be detected, | 
				
			||||
`haskell-compile-command' is used instead. | 
				
			||||
 | 
				
			||||
If prefix argument EDIT-COMMAND is non-nil (and not a negative | 
				
			||||
prefix `-'), `haskell-compile' prompts for custom compile | 
				
			||||
command. | 
				
			||||
 | 
				
			||||
If EDIT-COMMAND contains the negative prefix argument `-', | 
				
			||||
`haskell-compile' calls the alternative command defined in | 
				
			||||
`haskell-compile-cabal-build-alt-command' if a cabal package was | 
				
			||||
detected. | 
				
			||||
 | 
				
			||||
`haskell-compile' uses `haskell-compilation-mode' which is | 
				
			||||
derived from `compilation-mode'. See Info | 
				
			||||
node `(haskell-mode)compilation' for more details." | 
				
			||||
  (interactive "P") | 
				
			||||
  (save-some-buffers (not compilation-ask-about-save) | 
				
			||||
                     (if (boundp 'compilation-save-buffers-predicate) ;; since Emacs 24.1(?) | 
				
			||||
                         compilation-save-buffers-predicate)) | 
				
			||||
  (let* ((cabdir (haskell-cabal-find-dir)) | 
				
			||||
         (command1 (if (eq edit-command '-) | 
				
			||||
                       haskell-compile-cabal-build-alt-command | 
				
			||||
                     haskell-compile-cabal-build-command)) | 
				
			||||
         (srcname (buffer-file-name)) | 
				
			||||
         (command (if cabdir | 
				
			||||
                      (format command1 cabdir) | 
				
			||||
                    (if (and srcname (derived-mode-p 'haskell-mode)) | 
				
			||||
                        (format haskell-compile-command srcname) | 
				
			||||
                      command1)))) | 
				
			||||
    (when (and edit-command (not (eq edit-command '-))) | 
				
			||||
      (setq command (compilation-read-command command))) | 
				
			||||
 | 
				
			||||
    (compilation-start command 'haskell-compilation-mode))) | 
				
			||||
 | 
				
			||||
(provide 'haskell-compile) | 
				
			||||
;;; haskell-compile.el ends here | 
				
			||||
@ -1,133 +0,0 @@
					 | 
				
			||||
;;; haskell-complete-module.el --- A fast way to complete Haskell module names -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright (c) 2014 Chris Done. All rights reserved. | 
				
			||||
 | 
				
			||||
;; 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/>. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'cl-lib) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-complete-module-preferred | 
				
			||||
  '() | 
				
			||||
  "Override ordering of module results by specifying preferred modules." | 
				
			||||
  :group 'haskell | 
				
			||||
  :type '(repeat string)) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-complete-module-max-display | 
				
			||||
  10 | 
				
			||||
  "Maximum items to display in minibuffer." | 
				
			||||
  :group 'haskell | 
				
			||||
  :type 'number) | 
				
			||||
 | 
				
			||||
(defun haskell-complete-module-read (prompt candidates) | 
				
			||||
  "Interactively auto-complete from a list of candidates." | 
				
			||||
  (let ((stack (list)) | 
				
			||||
        (pattern "") | 
				
			||||
        (result nil)) | 
				
			||||
    (delete-dups candidates) | 
				
			||||
    (setq candidates | 
				
			||||
          (sort candidates | 
				
			||||
                (lambda (a b) | 
				
			||||
                  (let ((a-mem (member a haskell-complete-module-preferred)) | 
				
			||||
                        (b-mem (member b haskell-complete-module-preferred))) | 
				
			||||
                    (cond | 
				
			||||
                     ((and a-mem (not b-mem)) | 
				
			||||
                      t) | 
				
			||||
                     ((and b-mem (not a-mem)) | 
				
			||||
                      nil) | 
				
			||||
                     (t | 
				
			||||
                      (string< a b))))))) | 
				
			||||
    (while (not result) | 
				
			||||
      (let ((key | 
				
			||||
             (key-description | 
				
			||||
              (vector | 
				
			||||
               (read-key | 
				
			||||
                (concat (propertize prompt 'face 'minibuffer-prompt) | 
				
			||||
                        (propertize pattern 'face 'font-lock-type-face) | 
				
			||||
                        "{" | 
				
			||||
                        (mapconcat #'identity | 
				
			||||
                                   (let* ((i 0)) | 
				
			||||
                                     (cl-loop for candidate in candidates | 
				
			||||
                                              while (<= i haskell-complete-module-max-display) | 
				
			||||
                                              do (cl-incf i) | 
				
			||||
                                              collect (cond ((> i haskell-complete-module-max-display) | 
				
			||||
                                                             "...") | 
				
			||||
                                                            ((= i 1) | 
				
			||||
                                                             (propertize candidate 'face 'ido-first-match-face)) | 
				
			||||
                                                            (t candidate)))) | 
				
			||||
                                   " | ") | 
				
			||||
                        "}")))))) | 
				
			||||
        (cond | 
				
			||||
         ((string= key "C-g") | 
				
			||||
          (keyboard-quit)) | 
				
			||||
         ((string= key "DEL") | 
				
			||||
          (unless (null stack) | 
				
			||||
            (setq candidates (pop stack))) | 
				
			||||
          (unless (string= "" pattern) | 
				
			||||
            (setq pattern (substring pattern 0 -1)))) | 
				
			||||
         ((string= key "RET") | 
				
			||||
          (setq result (or (car candidates) | 
				
			||||
                           pattern))) | 
				
			||||
         ((string= key "<left>") | 
				
			||||
          (setq candidates | 
				
			||||
                (append (last candidates) | 
				
			||||
                        (butlast candidates)))) | 
				
			||||
         ((string= key "<right>") | 
				
			||||
          (setq candidates | 
				
			||||
                (append (cdr candidates) | 
				
			||||
                        (list (car candidates))))) | 
				
			||||
         (t | 
				
			||||
          (when (string-match "[A-Za-z0-9_'.]+" key) | 
				
			||||
            (push candidates stack) | 
				
			||||
            (setq pattern (concat pattern key)) | 
				
			||||
            (setq candidates (haskell-complete-module pattern candidates))))))) | 
				
			||||
    result)) | 
				
			||||
 | 
				
			||||
(defun haskell-complete-module (pattern candidates) | 
				
			||||
  "Filter the CANDIDATES using PATTERN." | 
				
			||||
  (let ((case-fold-search t)) | 
				
			||||
    (cl-loop for candidate in candidates | 
				
			||||
             when (haskell-complete-module-match pattern candidate) | 
				
			||||
             collect candidate))) | 
				
			||||
 | 
				
			||||
(defun haskell-complete-module-match (pattern text) | 
				
			||||
  "Match PATTERN against TEXT." | 
				
			||||
  (string-match (haskell-complete-module-regexp pattern) | 
				
			||||
                text)) | 
				
			||||
 | 
				
			||||
(defun haskell-complete-module-regexp (pattern) | 
				
			||||
  "Make a regular expression for the given module pattern. Example: | 
				
			||||
 | 
				
			||||
\"c.m.s\" -> \"^c[^.]*\\.m[^.]*\\.s[^.]*\" | 
				
			||||
 | 
				
			||||
" | 
				
			||||
  (let ((components (mapcar #'haskell-complete-module-component | 
				
			||||
                            (split-string pattern "\\." t)))) | 
				
			||||
    (concat "^" | 
				
			||||
            (mapconcat #'identity | 
				
			||||
                       components | 
				
			||||
                       "\\.")))) | 
				
			||||
 | 
				
			||||
(defun haskell-complete-module-component (component) | 
				
			||||
  "Make a regular expression for the given component. Example: | 
				
			||||
 | 
				
			||||
\"co\" -> \"c[^.]*o[^.]*\" | 
				
			||||
 | 
				
			||||
" | 
				
			||||
  (replace-regexp-in-string "\\(.\\)" "\\1[^.]*" component)) | 
				
			||||
 | 
				
			||||
(provide 'haskell-complete-module) | 
				
			||||
@ -1,266 +0,0 @@
					 | 
				
			||||
;;; haskell-completions.el --- Haskell Completion package -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright © 2015 Athur Fayzrakhmanov. All rights reserved. | 
				
			||||
 | 
				
			||||
;; This file is part of haskell-mode package. | 
				
			||||
;; You can contact with authors using GitHub issue tracker: | 
				
			||||
;; https://github.com/haskell/haskell-mode/issues | 
				
			||||
 | 
				
			||||
;; 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 GNU Emacs; see the file COPYING.  If not, write to | 
				
			||||
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 
				
			||||
;; Boston, MA 02110-1301, USA. | 
				
			||||
 | 
				
			||||
;;; Commentary: | 
				
			||||
 | 
				
			||||
;; This package provides completions related functionality for | 
				
			||||
;; Haskell Mode such grab completion prefix at point, and etc.. | 
				
			||||
 | 
				
			||||
;; Some description | 
				
			||||
;; ================ | 
				
			||||
;; | 
				
			||||
;; For major use function `haskell-completions-grab-prefix' is supposed, and | 
				
			||||
;; other prefix grabbing functions are used internally by it.  So, only this | 
				
			||||
;; funciton have prefix minimal length functionality and invokes predicate | 
				
			||||
;; function `haskell-completions-can-grab-prefix'. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'haskell-mode) | 
				
			||||
(require 'haskell-process) | 
				
			||||
(require 'haskell-interactive-mode) | 
				
			||||
 | 
				
			||||
(defvar haskell-completions-pragma-names | 
				
			||||
  (list "DEPRECATED" | 
				
			||||
        "INCLUDE" | 
				
			||||
        "INCOHERENT" | 
				
			||||
        "INLINABLE" | 
				
			||||
        "INLINE" | 
				
			||||
        "LANGUAGE" | 
				
			||||
        "LINE" | 
				
			||||
        "MINIMAL" | 
				
			||||
        "NOINLINE" | 
				
			||||
        "NOUNPACK" | 
				
			||||
        "OPTIONS" | 
				
			||||
        "OPTIONS_GHC" | 
				
			||||
        "OVERLAPPABLE" | 
				
			||||
        "OVERLAPPING" | 
				
			||||
        "OVERLAPS" | 
				
			||||
        "RULES" | 
				
			||||
        "SOURCE" | 
				
			||||
        "SPECIALIZE" | 
				
			||||
        "UNPACK" | 
				
			||||
        "WARNING") | 
				
			||||
  "A list of supported pragmas. | 
				
			||||
This list comes from GHC documentation (URL | 
				
			||||
`https://downloads.haskell.org/~ghc/7.10.1/docs/html/users_guide/pragmas.html'. | 
				
			||||
") | 
				
			||||
 | 
				
			||||
(defun haskell-completions-can-grab-prefix () | 
				
			||||
  "Check if the case is appropriate for grabbing completion prefix. | 
				
			||||
Returns t if point is either at whitespace character, or at | 
				
			||||
punctuation, or at line end and preceeding character is not a | 
				
			||||
whitespace or new line, otherwise returns nil. | 
				
			||||
 | 
				
			||||
  Returns nil in presense of active region." | 
				
			||||
  (when (not (region-active-p)) | 
				
			||||
    (when (looking-at-p (rx (| space line-end punct))) | 
				
			||||
      (when (not (bobp)) | 
				
			||||
        (save-excursion | 
				
			||||
          (backward-char) | 
				
			||||
          (not (looking-at-p (rx (| space line-end))))))))) | 
				
			||||
 | 
				
			||||
(defun haskell-completions-grab-pragma-prefix () | 
				
			||||
  "Grab completion prefix for pragma completions. | 
				
			||||
Returns a list of form '(prefix-start-position | 
				
			||||
prefix-end-position prefix-value prefix-type) for pramga names | 
				
			||||
such as WARNING, DEPRECATED, LANGUAGE and etc.  Also returns | 
				
			||||
completion prefixes for options in case OPTIONS_GHC pragma, or | 
				
			||||
language extensions in case of LANGUAGE pragma.  Obsolete OPTIONS | 
				
			||||
pragma is supported also." | 
				
			||||
  (when (nth 4 (syntax-ppss)) | 
				
			||||
    ;; We're inside comment | 
				
			||||
    (let ((p (point)) | 
				
			||||
          (comment-start (nth 8 (syntax-ppss))) | 
				
			||||
          (case-fold-search nil) | 
				
			||||
          prefix-start | 
				
			||||
          prefix-end | 
				
			||||
          prefix-type | 
				
			||||
          prefix-value) | 
				
			||||
      (save-excursion | 
				
			||||
        (goto-char comment-start) | 
				
			||||
        (when (looking-at (rx "{-#" (1+ (| space "\n")))) | 
				
			||||
          (let ((pragma-start (match-end 0))) | 
				
			||||
            (when (> p pragma-start) | 
				
			||||
              ;; point stands after `{-#` | 
				
			||||
              (goto-char pragma-start) | 
				
			||||
              (when (looking-at (rx (1+ (| upper "_")))) | 
				
			||||
                ;; found suitable sequence for pragma name | 
				
			||||
                (let ((pragma-end (match-end 0)) | 
				
			||||
                      (pragma-value (match-string-no-properties 0))) | 
				
			||||
                  (if (eq p pragma-end) | 
				
			||||
                      ;; point is at the end of (in)complete pragma name | 
				
			||||
                      ;; prepare resulting values | 
				
			||||
                      (progn | 
				
			||||
                        (setq prefix-start pragma-start) | 
				
			||||
                        (setq prefix-end pragma-end) | 
				
			||||
                        (setq prefix-value pragma-value) | 
				
			||||
                        (setq prefix-type | 
				
			||||
                              'haskell-completions-pragma-name-prefix)) | 
				
			||||
                    (when (and (> p pragma-end) | 
				
			||||
                               (or (equal "OPTIONS_GHC" pragma-value) | 
				
			||||
                                   (equal "OPTIONS" pragma-value) | 
				
			||||
                                   (equal "LANGUAGE" pragma-value))) | 
				
			||||
                      ;; point is after pragma name, so we need to check | 
				
			||||
                      ;; special cases of `OPTIONS_GHC` and `LANGUAGE` pragmas | 
				
			||||
                      ;; and provide a completion prefix for possible ghc | 
				
			||||
                      ;; option or language extension. | 
				
			||||
                      (goto-char pragma-end) | 
				
			||||
                      (when (re-search-forward | 
				
			||||
                             (rx (* anything) | 
				
			||||
                                 (1+ (regexp "\\S-"))) | 
				
			||||
                             p | 
				
			||||
                             t) | 
				
			||||
                        (let* ((str (match-string-no-properties 0)) | 
				
			||||
                               (split (split-string str (rx (| space "\n")) t)) | 
				
			||||
                               (val (car (last split))) | 
				
			||||
                               (end (point))) | 
				
			||||
                          (when (and (equal p end) | 
				
			||||
                                     (not (string-match-p "#" val))) | 
				
			||||
                            (setq prefix-value val) | 
				
			||||
                            (backward-char (length val)) | 
				
			||||
                            (setq prefix-start (point)) | 
				
			||||
                            (setq prefix-end end) | 
				
			||||
                            (setq | 
				
			||||
                             prefix-type | 
				
			||||
                             (if (not (equal "LANGUAGE" pragma-value)) | 
				
			||||
                                 'haskell-completions-ghc-option-prefix | 
				
			||||
                               'haskell-completions-language-extension-prefix | 
				
			||||
                               ))))))))))))) | 
				
			||||
      (when prefix-value | 
				
			||||
        (list prefix-start prefix-end prefix-value prefix-type))))) | 
				
			||||
 | 
				
			||||
(defun haskell-completions-grab-identifier-prefix () | 
				
			||||
  "Grab completion prefix for identifier at point. | 
				
			||||
Returns a list of form '(prefix-start-position | 
				
			||||
prefix-end-position prefix-value prefix-type) for haskell | 
				
			||||
identifier at point depending on result of function | 
				
			||||
`haskell-ident-pos-at-point'." | 
				
			||||
  (let ((pos-at-point (haskell-ident-pos-at-point)) | 
				
			||||
        (p (point))) | 
				
			||||
    (when pos-at-point | 
				
			||||
      (let* ((start (car pos-at-point)) | 
				
			||||
             (end (cdr pos-at-point)) | 
				
			||||
             (type 'haskell-completions-identifier-prefix) | 
				
			||||
             (case-fold-search nil) | 
				
			||||
             value) | 
				
			||||
        ;; we need end position of result, becase of | 
				
			||||
        ;; `haskell-ident-pos-at-point' ignores trailing whitespace, e.g. the | 
				
			||||
        ;; result will be same for `map|` and `map  |` invocations. | 
				
			||||
        (when (<= p end) | 
				
			||||
          (setq end p) | 
				
			||||
          (setq value (buffer-substring-no-properties start end)) | 
				
			||||
          (when (string-match-p (rx bos upper) value) | 
				
			||||
            ;; we need to check if found identifier is a module name | 
				
			||||
            (save-excursion | 
				
			||||
              (goto-char (line-beginning-position)) | 
				
			||||
              (when (re-search-forward | 
				
			||||
                     (rx "import" | 
				
			||||
                         (? (1+ space) "qualified") | 
				
			||||
                         (1+ space) | 
				
			||||
                         upper | 
				
			||||
                         (1+ (| alnum "."))) | 
				
			||||
                     p    ;; bound | 
				
			||||
                     t)   ;; no-error | 
				
			||||
                (if (equal p (point)) | 
				
			||||
                    (setq type 'haskell-completions-module-name-prefix) | 
				
			||||
                  (when (re-search-forward | 
				
			||||
                         (rx (| " as " "(")) | 
				
			||||
                         start | 
				
			||||
                         t) | 
				
			||||
                    ;; but uppercase ident could occur after `as` keyword, or in | 
				
			||||
                    ;; module imports after opening parenthesis, in this case | 
				
			||||
                    ;; restore identifier type again, it's neccessary to | 
				
			||||
                    ;; distinguish the means of completions retrieval | 
				
			||||
                    (setq type 'haskell-completions-identifier-prefix)))))) | 
				
			||||
          (when (nth 8 (syntax-ppss)) | 
				
			||||
            ;; eighth element of syntax-ppss result is string or comment start, | 
				
			||||
            ;; so when it's not nil word at point is inside string or comment, | 
				
			||||
            ;; return special literal prefix type | 
				
			||||
            (setq type 'haskell-completions-general-prefix)) | 
				
			||||
          ;; finally take in account minlen if given and return the result | 
				
			||||
          (when value (list start end value type))))))) | 
				
			||||
 | 
				
			||||
(defun haskell-completions-grab-prefix (&optional minlen) | 
				
			||||
   "Grab prefix at point for possible completion. | 
				
			||||
Returns a list of form '(prefix-start-position | 
				
			||||
prefix-end-position prefix-value prefix-type) depending on | 
				
			||||
situation, e.g. is it needed to complete pragma, module name, | 
				
			||||
arbitrary identifier, and etc.  Rerurns nil in case it is | 
				
			||||
impossible to grab prefix. | 
				
			||||
 | 
				
			||||
If provided optional MINLEN parameter this function will return | 
				
			||||
result only if prefix length is not less than MINLEN." | 
				
			||||
   (when (haskell-completions-can-grab-prefix) | 
				
			||||
     (let ((prefix (cond | 
				
			||||
                    ((haskell-completions-grab-pragma-prefix)) | 
				
			||||
                    ((haskell-completions-grab-identifier-prefix))))) | 
				
			||||
       (cond ((and minlen prefix) | 
				
			||||
              (when (>= (length (nth 2 prefix)) minlen) | 
				
			||||
                prefix)) | 
				
			||||
             (prefix prefix))))) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
(defun haskell-completions-sync-completions-at-point () | 
				
			||||
  "A `completion-at-point' function using the current haskell process. | 
				
			||||
Returns nil if no completions available." | 
				
			||||
  (let ((prefix-data (haskell-completions-grab-prefix))) | 
				
			||||
    (when prefix-data | 
				
			||||
      (cl-destructuring-bind (beg end pfx typ) prefix-data | 
				
			||||
        (let ((imp (eql typ 'haskell-completions-module-name-prefix)) | 
				
			||||
              lst) | 
				
			||||
          (setq lst | 
				
			||||
                (cl-case typ | 
				
			||||
                  ;; non-interactive completions first | 
				
			||||
                  ('haskell-completions-pragma-name-prefix | 
				
			||||
                   haskell-completions-pragma-names) | 
				
			||||
                  ('haskell-completions-ghc-option-prefix | 
				
			||||
                   haskell-ghc-supported-options) | 
				
			||||
                  ('haskell-completions-language-extension-prefix | 
				
			||||
                   haskell-ghc-supported-extensions) | 
				
			||||
                  (otherwise | 
				
			||||
                   (when (and | 
				
			||||
                          (not (eql typ 'haskell-completions-general-prefix)) | 
				
			||||
                          (haskell-session-maybe) | 
				
			||||
                          (not | 
				
			||||
                           (haskell-process-cmd (haskell-interactive-process)))) | 
				
			||||
                     ;; if REPL is available and not busy try to query it | 
				
			||||
                     ;; for completions list in case of module name or | 
				
			||||
                     ;; identifier prefixes | 
				
			||||
                     (haskell-completions-sync-complete-repl pfx imp))))) | 
				
			||||
          (when lst | 
				
			||||
            (list beg end lst))))))) | 
				
			||||
 | 
				
			||||
(defun haskell-completions-sync-complete-repl (prefix &optional import) | 
				
			||||
  "Return completion list for given PREFIX quering REPL synchronously. | 
				
			||||
When optional IMPORT argument is non-nil complete PREFIX | 
				
			||||
prepending \"import \" keyword (useful for module names).  This | 
				
			||||
function is supposed for internal use." | 
				
			||||
  (haskell-process-get-repl-completions | 
				
			||||
   (haskell-interactive-process) | 
				
			||||
   (if import | 
				
			||||
       (concat "import " prefix) | 
				
			||||
     prefix))) | 
				
			||||
 | 
				
			||||
(provide 'haskell-completions) | 
				
			||||
;;; haskell-completions.el ends here | 
				
			||||
@ -1,429 +0,0 @@
					 | 
				
			||||
;;; haskell-customize.el --- Customization settings -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright (c) 2014 Chris Done. All rights reserved. | 
				
			||||
 | 
				
			||||
;; 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/>. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'cl-lib) | 
				
			||||
 | 
				
			||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 
				
			||||
;; Customization variables | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-load-or-reload-prompt nil | 
				
			||||
  "Nil means there will be no prompts on starting REPL. Defaults will be accepted." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defgroup haskell nil | 
				
			||||
  "Major mode for editing Haskell programs." | 
				
			||||
  :link '(custom-manual "(haskell-mode)") | 
				
			||||
  :group 'languages | 
				
			||||
  :prefix "haskell-") | 
				
			||||
 | 
				
			||||
(defvar haskell-mode-pkg-base-dir (file-name-directory load-file-name) | 
				
			||||
  "Package base directory of installed `haskell-mode'. | 
				
			||||
Used for locating additional package data files.") | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-completing-read-function 'ido-completing-read | 
				
			||||
  "Default function to use for completion." | 
				
			||||
  :group 'haskell | 
				
			||||
  :type '(choice | 
				
			||||
          (function-item :tag "ido" :value ido-completing-read) | 
				
			||||
          (function-item :tag "helm" :value helm--completing-read-default) | 
				
			||||
          (function-item :tag "completing-read" :value completing-read) | 
				
			||||
          (function :tag "Custom function"))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-type | 
				
			||||
  'auto | 
				
			||||
  "The inferior Haskell process type to use. | 
				
			||||
 | 
				
			||||
When set to 'auto (the default), the directory contents and | 
				
			||||
available programs will be used to make a best guess at the | 
				
			||||
process type: | 
				
			||||
 | 
				
			||||
If the project directory or one of its parents contains a | 
				
			||||
\"cabal.sandbox.config\" file, then cabal-repl will be used. | 
				
			||||
 | 
				
			||||
If there's a \"stack.yaml\" file and the \"stack\" executable can | 
				
			||||
be located, then stack-ghci will be used. | 
				
			||||
 | 
				
			||||
Otherwise if there's a *.cabal file, cabal-repl will be used. | 
				
			||||
 | 
				
			||||
If none of the above apply, ghci will be used." | 
				
			||||
  :type '(choice (const auto) (const ghci) (const cabal-repl) (const stack-ghci)) | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-wrapper-function | 
				
			||||
  #'identity | 
				
			||||
  "Wrap or transform haskell process commands using this function. | 
				
			||||
 | 
				
			||||
Can be set to a custom function which takes a list of arguments | 
				
			||||
and returns a possibly-modified list. | 
				
			||||
 | 
				
			||||
The following example function arranges for all haskell process | 
				
			||||
commands to be started in the current nix-shell environment: | 
				
			||||
 | 
				
			||||
  (lambda (argv) (append (list \"nix-shell\" \"-I\" \".\" \"--command\" ) | 
				
			||||
                    (list (mapconcat 'identity argv \" \")))) | 
				
			||||
 | 
				
			||||
See Info Node `(emacs)Directory Variables' for a way to set this option on | 
				
			||||
a per-project basis." | 
				
			||||
  :group 'haskell-interactive | 
				
			||||
  :type '(choice | 
				
			||||
          (function-item :tag "None" :value identity) | 
				
			||||
          (function :tag "Custom function"))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-ask-also-kill-buffers | 
				
			||||
  t | 
				
			||||
  "Ask whether to kill all associated buffers when a session | 
				
			||||
 process is killed." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 
				
			||||
;; Configuration | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-doc-prettify-types t | 
				
			||||
  "Replace some parts of types with Unicode characters like \"∷\" | 
				
			||||
when showing type information about symbols." | 
				
			||||
  :group 'haskell-doc | 
				
			||||
  :type 'boolean | 
				
			||||
  :safe 'booleanp) | 
				
			||||
 | 
				
			||||
(defvar haskell-process-end-hook nil | 
				
			||||
  "Hook for when the haskell process ends.") | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defgroup haskell-interactive nil | 
				
			||||
  "Settings for REPL interaction via `haskell-interactive-mode'" | 
				
			||||
  :link '(custom-manual "(haskell-mode)haskell-interactive-mode") | 
				
			||||
  :group 'haskell) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-path-ghci | 
				
			||||
  "ghci" | 
				
			||||
  "The path for starting ghci." | 
				
			||||
  :group 'haskell-interactive | 
				
			||||
  :type '(choice string (repeat string))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-path-cabal | 
				
			||||
  "cabal" | 
				
			||||
  "Path to the `cabal' executable." | 
				
			||||
  :group 'haskell-interactive | 
				
			||||
  :type '(choice string (repeat string))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-path-stack | 
				
			||||
  "stack" | 
				
			||||
  "The path for starting stack." | 
				
			||||
  :group 'haskell-interactive | 
				
			||||
  :type '(choice string (repeat string))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-args-ghci | 
				
			||||
  '("-ferror-spans") | 
				
			||||
  "Any arguments for starting ghci." | 
				
			||||
  :group 'haskell-interactive | 
				
			||||
  :type '(repeat (string :tag "Argument"))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-args-cabal-repl | 
				
			||||
  '("--ghc-option=-ferror-spans") | 
				
			||||
  "Additional arguments for `cabal repl' invocation. | 
				
			||||
Note: The settings in `haskell-process-path-ghci' and | 
				
			||||
`haskell-process-args-ghci' are not automatically reused as `cabal repl' | 
				
			||||
currently invokes `ghc --interactive'. Use | 
				
			||||
`--with-ghc=<path-to-executable>' if you want to use a different | 
				
			||||
interactive GHC frontend; use `--ghc-option=<ghc-argument>' to | 
				
			||||
pass additional flags to `ghc'." | 
				
			||||
  :group 'haskell-interactive | 
				
			||||
  :type '(repeat (string :tag "Argument"))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-args-stack-ghci | 
				
			||||
  '("--ghc-options=-ferror-spans") | 
				
			||||
  "Additional arguments for `stack ghci' invocation." | 
				
			||||
  :group 'haskell-interactive | 
				
			||||
  :type '(repeat (string :tag "Argument"))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-do-cabal-format-string | 
				
			||||
  ":!cd %s && %s" | 
				
			||||
  "The way to run cabal comands. It takes two arguments -- the directory and the command. | 
				
			||||
See `haskell-process-do-cabal' for more details." | 
				
			||||
  :group 'haskell-interactive | 
				
			||||
  :type 'string) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-log | 
				
			||||
  nil | 
				
			||||
  "Enable debug logging to \"*haskell-process-log*\" buffer." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-show-debug-tips | 
				
			||||
  t | 
				
			||||
  "Show debugging tips when starting the process." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-notify-p | 
				
			||||
  nil | 
				
			||||
  "Notify using notifications.el (if loaded)?" | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-suggest-no-warn-orphans | 
				
			||||
  t | 
				
			||||
  "Suggest adding -fno-warn-orphans pragma to file when getting orphan warnings." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-suggest-hoogle-imports | 
				
			||||
  nil | 
				
			||||
  "Suggest to add import statements using Hoogle as a backend." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-suggest-hayoo-imports | 
				
			||||
  nil | 
				
			||||
  "Suggest to add import statements using Hayoo as a backend." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-hayoo-query-url | 
				
			||||
  "http://hayoo.fh-wedel.de/json/?query=%s" | 
				
			||||
  "Query url for json hayoo results." | 
				
			||||
  :type 'string | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-suggest-haskell-docs-imports | 
				
			||||
  nil | 
				
			||||
  "Suggest to add import statements using haskell-docs as a backend." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-suggest-add-package | 
				
			||||
  t | 
				
			||||
  "Suggest to add packages to your .cabal file when Cabal says it | 
				
			||||
is a member of the hidden package, blah blah." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-suggest-language-pragmas | 
				
			||||
  t | 
				
			||||
  "Suggest adding LANGUAGE pragmas recommended by GHC." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-suggest-remove-import-lines | 
				
			||||
  nil | 
				
			||||
  "Suggest removing import lines as warned by GHC." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-suggest-overloaded-strings | 
				
			||||
  t | 
				
			||||
  "Suggest adding OverloadedStrings pragma to file when getting type mismatches with [Char]." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-check-cabal-config-on-load | 
				
			||||
  t | 
				
			||||
  "Check changes cabal config on loading Haskell files and | 
				
			||||
restart the GHCi process if changed.." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-prompt-restart-on-cabal-change | 
				
			||||
  t | 
				
			||||
  "Ask whether to restart the GHCi process when the Cabal file | 
				
			||||
has changed?" | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-auto-import-loaded-modules | 
				
			||||
  nil | 
				
			||||
  "Auto import the modules reported by GHC to have been loaded?" | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-reload-with-fbytecode | 
				
			||||
  nil | 
				
			||||
  "When using -fobject-code, auto reload with -fbyte-code (and | 
				
			||||
then restore the -fobject-code) so that all module info and | 
				
			||||
imports become available?" | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-use-presentation-mode | 
				
			||||
  nil | 
				
			||||
  "Use presentation mode to show things like type info instead of | 
				
			||||
  printing to the message area." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-process-suggest-restart | 
				
			||||
  t | 
				
			||||
  "Suggest restarting the process when it has died" | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-interactive-mode-scroll-to-bottom | 
				
			||||
  nil | 
				
			||||
  "Scroll to bottom in the REPL always." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-interactive-popup-errors | 
				
			||||
  t | 
				
			||||
  "Popup errors in a separate buffer." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-interactive-mode-collapse | 
				
			||||
  nil | 
				
			||||
  "Collapse printed results." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-interactive-types-for-show-ambiguous | 
				
			||||
  t | 
				
			||||
  "Show types when there's no Show instance or there's an | 
				
			||||
ambiguous class constraint." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
(defvar haskell-interactive-prompt "λ> " | 
				
			||||
  "The prompt to use.") | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-interactive-mode-eval-mode | 
				
			||||
  nil | 
				
			||||
  "Use the given mode's font-locking to render some text." | 
				
			||||
  :type '(choice function (const :tag "None" nil)) | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-interactive-mode-hide-multi-line-errors | 
				
			||||
  nil | 
				
			||||
  "Hide collapsible multi-line compile messages by default." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-interactive-mode-delete-superseded-errors | 
				
			||||
  t | 
				
			||||
  "Whether to delete compile messages superseded by recompile/reloads." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-interactive-mode-include-file-name | 
				
			||||
  t | 
				
			||||
  "Include the file name of the module being compiled when | 
				
			||||
printing compilation messages." | 
				
			||||
  :type 'boolean | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-import-mapping | 
				
			||||
  '() | 
				
			||||
  "Support a mapping from module to import lines. | 
				
			||||
 | 
				
			||||
E.g. '((\"Data.Map\" . \"import qualified Data.Map as M | 
				
			||||
import Data.Map (Map) | 
				
			||||
\")) | 
				
			||||
 | 
				
			||||
This will import | 
				
			||||
 | 
				
			||||
import qualified Data.Map as M | 
				
			||||
import Data.Map (Map) | 
				
			||||
 | 
				
			||||
when Data.Map is the candidate. | 
				
			||||
 | 
				
			||||
" | 
				
			||||
  :type '(repeat (cons (string :tag "Module name") | 
				
			||||
                       (string :tag "Import lines"))) | 
				
			||||
  :group 'haskell-interactive) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-language-extensions | 
				
			||||
  '() | 
				
			||||
  "Language extensions in use. Should be in format: -XFoo, | 
				
			||||
-XNoFoo etc. The idea is that various tools written with HSE (or | 
				
			||||
any haskell-mode code that needs to be aware of syntactical | 
				
			||||
properties; such as an indentation mode) that don't know what | 
				
			||||
extensions to use can use this variable. Examples: hlint, | 
				
			||||
hindent, structured-haskell-mode, tool-de-jour, etc. | 
				
			||||
 | 
				
			||||
You can set this per-project with a .dir-locals.el file, in the | 
				
			||||
same vein as `haskell-indent-spaces'." | 
				
			||||
  :group 'haskell | 
				
			||||
  :type '(repeat 'string)) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 
				
			||||
;; Accessor functions | 
				
			||||
 | 
				
			||||
(defun haskell-process-type () | 
				
			||||
  "Return `haskell-process-type', or a guess if that variable is 'auto." | 
				
			||||
  (if (eq 'auto haskell-process-type) | 
				
			||||
      (cond | 
				
			||||
       ;; User has explicitly initialized this project with cabal | 
				
			||||
       ((locate-dominating-file default-directory "cabal.sandbox.config") | 
				
			||||
        'cabal-repl) | 
				
			||||
       ((and (locate-dominating-file default-directory "stack.yaml") | 
				
			||||
             (executable-find "stack")) | 
				
			||||
        'stack-ghci) | 
				
			||||
       ((locate-dominating-file | 
				
			||||
         default-directory | 
				
			||||
         (lambda (d) | 
				
			||||
           (cl-find-if (lambda (f) (string-match-p ".\\.cabal\\'" f)) (directory-files d)))) | 
				
			||||
        'cabal-repl) | 
				
			||||
       (t 'ghci)) | 
				
			||||
    haskell-process-type)) | 
				
			||||
 | 
				
			||||
(provide 'haskell-customize) | 
				
			||||
@ -1,744 +0,0 @@
					 | 
				
			||||
;;; haskell-debug.el --- Debugging mode via GHCi -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright (c) 2014 Chris Done. All rights reserved. | 
				
			||||
 | 
				
			||||
;; 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/>. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'cl-lib) | 
				
			||||
(require 'haskell-session) | 
				
			||||
(require 'haskell-process) | 
				
			||||
(require 'haskell-interactive-mode) | 
				
			||||
(require 'haskell-font-lock) | 
				
			||||
 | 
				
			||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 
				
			||||
;; Configuration | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defgroup haskell-debug nil | 
				
			||||
  "Settings for debugging support." | 
				
			||||
  :link '(custom-manual "(haskell-mode)haskell-debug") | 
				
			||||
  :group 'haskell) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defface haskell-debug-warning-face | 
				
			||||
  '((t :inherit 'compilation-warning)) | 
				
			||||
  "Face for warnings." | 
				
			||||
  :group 'haskell-debug) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defface haskell-debug-trace-number-face | 
				
			||||
  '((t :weight bold :background "#f5f5f5")) | 
				
			||||
  "Face for numbers in backtrace." | 
				
			||||
  :group 'haskell-debug) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defface haskell-debug-newline-face | 
				
			||||
  '((t :weight bold :background "#f0f0f0")) | 
				
			||||
  "Face for newlines in trace steps." | 
				
			||||
  :group 'haskell-debug) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defface haskell-debug-keybinding-face | 
				
			||||
  '((t :inherit 'font-lock-type-face :weight bold)) | 
				
			||||
  "Face for keybindings." | 
				
			||||
  :group 'haskell-debug) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defface haskell-debug-heading-face | 
				
			||||
  '((t :inherit 'font-lock-keyword-face)) | 
				
			||||
  "Face for headings." | 
				
			||||
  :group 'haskell-debug) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defface haskell-debug-muted-face | 
				
			||||
  '((t :foreground "#999")) | 
				
			||||
  "Face for muteds." | 
				
			||||
  :group 'haskell-debug) | 
				
			||||
 | 
				
			||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 
				
			||||
;; Mode | 
				
			||||
 | 
				
			||||
(define-derived-mode haskell-debug-mode | 
				
			||||
  text-mode "Debug" | 
				
			||||
  "Major mode for debugging Haskell via GHCi.") | 
				
			||||
 | 
				
			||||
(define-key haskell-debug-mode-map (kbd "g") 'haskell-debug/refresh) | 
				
			||||
(define-key haskell-debug-mode-map (kbd "s") 'haskell-debug/step) | 
				
			||||
(define-key haskell-debug-mode-map (kbd "t") 'haskell-debug/trace) | 
				
			||||
(define-key haskell-debug-mode-map (kbd "d") 'haskell-debug/delete) | 
				
			||||
(define-key haskell-debug-mode-map (kbd "b") 'haskell-debug/break-on-function) | 
				
			||||
(define-key haskell-debug-mode-map (kbd "a") 'haskell-debug/abandon) | 
				
			||||
(define-key haskell-debug-mode-map (kbd "c") 'haskell-debug/continue) | 
				
			||||
(define-key haskell-debug-mode-map (kbd "p") 'haskell-debug/previous) | 
				
			||||
(define-key haskell-debug-mode-map (kbd "n") 'haskell-debug/next) | 
				
			||||
(define-key haskell-debug-mode-map (kbd "RET") 'haskell-debug/select) | 
				
			||||
 | 
				
			||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 
				
			||||
;; Globals | 
				
			||||
 | 
				
			||||
(defvar haskell-debug-history-cache nil | 
				
			||||
  "Cache of the tracing history.") | 
				
			||||
 | 
				
			||||
(defvar haskell-debug-bindings-cache nil | 
				
			||||
  "Cache of the current step's bindings.") | 
				
			||||
 | 
				
			||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 
				
			||||
;; Macros | 
				
			||||
 | 
				
			||||
(defmacro haskell-debug-with-breakpoints (&rest body) | 
				
			||||
  "Breakpoints need to exist to start stepping." | 
				
			||||
  `(if (haskell-debug-get-breakpoints) | 
				
			||||
       ,@body | 
				
			||||
     (error "No breakpoints to step into!"))) | 
				
			||||
 | 
				
			||||
(defmacro haskell-debug-with-modules (&rest body) | 
				
			||||
  "Modules need to exist to do debugging stuff." | 
				
			||||
  `(if (haskell-debug-get-modules) | 
				
			||||
       ,@body | 
				
			||||
     (error "No modules loaded!"))) | 
				
			||||
 | 
				
			||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 
				
			||||
;; Interactive functions | 
				
			||||
 | 
				
			||||
(defun haskell-debug/select () | 
				
			||||
  "Select whatever is at point." | 
				
			||||
  (interactive) | 
				
			||||
  (cond | 
				
			||||
   ((get-text-property (point) 'break) | 
				
			||||
    (let ((break (get-text-property (point) 'break))) | 
				
			||||
      (haskell-debug-highlight (plist-get break :path) | 
				
			||||
                               (plist-get break :span)))) | 
				
			||||
   ((get-text-property (point) 'module) | 
				
			||||
    (let ((break (get-text-property (point) 'module))) | 
				
			||||
      (haskell-debug-highlight (plist-get break :path)))))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug/abandon () | 
				
			||||
  "Abandon the current computation." | 
				
			||||
  (interactive) | 
				
			||||
  (haskell-debug-with-breakpoints | 
				
			||||
   (haskell-process-queue-sync-request (haskell-debug-process) ":abandon") | 
				
			||||
   (message "Computation abandoned.") | 
				
			||||
   (setq haskell-debug-history-cache nil) | 
				
			||||
   (setq haskell-debug-bindings-cache nil) | 
				
			||||
   (haskell-debug/refresh))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug/continue () | 
				
			||||
  "Continue the current computation." | 
				
			||||
  (interactive) | 
				
			||||
  (haskell-debug-with-breakpoints | 
				
			||||
   (haskell-process-queue-sync-request (haskell-debug-process) ":continue") | 
				
			||||
   (message "Computation continued.") | 
				
			||||
   (setq haskell-debug-history-cache nil) | 
				
			||||
   (setq haskell-debug-bindings-cache nil) | 
				
			||||
   (haskell-debug/refresh))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug/break-on-function () | 
				
			||||
  "Break on function IDENT." | 
				
			||||
  (interactive) | 
				
			||||
  (haskell-debug-with-modules | 
				
			||||
   (let ((ident (read-from-minibuffer "Function: " | 
				
			||||
                                      (haskell-ident-at-point)))) | 
				
			||||
     (haskell-process-queue-sync-request | 
				
			||||
      (haskell-debug-process) | 
				
			||||
      (concat ":break " | 
				
			||||
              ident)) | 
				
			||||
     (message "Breaking on function: %s" ident) | 
				
			||||
     (haskell-debug/refresh)))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug/start-step (expr) | 
				
			||||
  "Start stepping EXPR." | 
				
			||||
  (interactive (list (read-from-minibuffer "Expression to step through: "))) | 
				
			||||
  (haskell-debug/step expr)) | 
				
			||||
 | 
				
			||||
(defun haskell-debug/breakpoint-numbers () | 
				
			||||
  "List breakpoint numbers." | 
				
			||||
  (interactive) | 
				
			||||
  (let ((breakpoints (mapcar (lambda (breakpoint) | 
				
			||||
                               (number-to-string (plist-get breakpoint :number))) | 
				
			||||
                             (haskell-debug-get-breakpoints)))) | 
				
			||||
    (if (null breakpoints) | 
				
			||||
        (message "No breakpoints.") | 
				
			||||
      (message "Breakpoint(s): %s" | 
				
			||||
               (mapconcat #'identity | 
				
			||||
                          breakpoints | 
				
			||||
                          ", "))))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug/next () | 
				
			||||
  "Go to next step to inspect bindings." | 
				
			||||
  (interactive) | 
				
			||||
  (haskell-debug-with-breakpoints | 
				
			||||
   (haskell-debug-navigate "forward"))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug/previous () | 
				
			||||
  "Go to previous step to inspect the bindings." | 
				
			||||
  (interactive) | 
				
			||||
  (haskell-debug-with-breakpoints | 
				
			||||
   (haskell-debug-navigate "back"))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug/refresh () | 
				
			||||
  "Refresh the debugger buffer." | 
				
			||||
  (interactive) | 
				
			||||
  (with-current-buffer (haskell-debug-buffer-name (haskell-debug-session)) | 
				
			||||
    (cd (haskell-session-current-dir (haskell-debug-session))) | 
				
			||||
    (let ((inhibit-read-only t) | 
				
			||||
          (p (point))) | 
				
			||||
      (erase-buffer) | 
				
			||||
      (insert (propertize (concat "Debugging " | 
				
			||||
                                  (haskell-session-name (haskell-debug-session)) | 
				
			||||
                                  "\n\n") | 
				
			||||
                          'face `((:weight bold)))) | 
				
			||||
      (let ((modules (haskell-debug-get-modules)) | 
				
			||||
            (breakpoints (haskell-debug-get-breakpoints)) | 
				
			||||
            (context (haskell-debug-get-context)) | 
				
			||||
            (history (haskell-debug-get-history))) | 
				
			||||
        (unless modules | 
				
			||||
          (insert (propertize "You have to load a module to start debugging." | 
				
			||||
                              'face | 
				
			||||
                              'haskell-debug-warning-face) | 
				
			||||
                  "\n\n")) | 
				
			||||
        (haskell-debug-insert-bindings modules breakpoints context) | 
				
			||||
        (when modules | 
				
			||||
          (haskell-debug-insert-current-context context history) | 
				
			||||
          (haskell-debug-insert-breakpoints breakpoints)) | 
				
			||||
        (haskell-debug-insert-modules modules)) | 
				
			||||
      (insert "\n") | 
				
			||||
      (goto-char (min (point-max) p))))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug/delete () | 
				
			||||
  "Delete whatever's at the point." | 
				
			||||
  (interactive) | 
				
			||||
  (cond | 
				
			||||
   ((get-text-property (point) 'break) | 
				
			||||
    (let ((break (get-text-property (point) 'break))) | 
				
			||||
      (when (y-or-n-p (format "Delete breakpoint #%d?" | 
				
			||||
                              (plist-get break :number))) | 
				
			||||
        (haskell-process-queue-sync-request | 
				
			||||
         (haskell-debug-process) | 
				
			||||
         (format ":delete %d" | 
				
			||||
                 (plist-get break :number))) | 
				
			||||
        (haskell-debug/refresh)))))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug/trace () | 
				
			||||
  "Trace the expression." | 
				
			||||
  (interactive) | 
				
			||||
  (haskell-debug-with-modules | 
				
			||||
   (haskell-debug-with-breakpoints | 
				
			||||
    (let ((expr (read-from-minibuffer "Expression to trace: " | 
				
			||||
                                      (haskell-ident-at-point)))) | 
				
			||||
      (haskell-process-queue-sync-request | 
				
			||||
       (haskell-debug-process) | 
				
			||||
       (concat ":trace " expr)) | 
				
			||||
      (message "Tracing expression: %s" expr) | 
				
			||||
      (haskell-debug/refresh))))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug/step (&optional expr) | 
				
			||||
  "Step into the next function." | 
				
			||||
  (interactive) | 
				
			||||
  (haskell-debug-with-breakpoints | 
				
			||||
   (let* ((breakpoints (haskell-debug-get-breakpoints)) | 
				
			||||
          (context (haskell-debug-get-context)) | 
				
			||||
          (string | 
				
			||||
           (haskell-process-queue-sync-request | 
				
			||||
            (haskell-debug-process) | 
				
			||||
            (if expr | 
				
			||||
                (concat ":step " expr) | 
				
			||||
              ":step")))) | 
				
			||||
     (cond | 
				
			||||
      ((string= string "not stopped at a breakpoint\n") | 
				
			||||
       (if haskell-debug-bindings-cache | 
				
			||||
           (progn (setq haskell-debug-bindings-cache nil) | 
				
			||||
                  (haskell-debug/refresh)) | 
				
			||||
         (call-interactively 'haskell-debug/start-step))) | 
				
			||||
      (t (let ((maybe-stopped-at (haskell-debug-parse-stopped-at string))) | 
				
			||||
           (cond | 
				
			||||
            (maybe-stopped-at | 
				
			||||
             (setq haskell-debug-bindings-cache | 
				
			||||
                   maybe-stopped-at) | 
				
			||||
             (message "Computation paused.") | 
				
			||||
             (haskell-debug/refresh)) | 
				
			||||
            (t | 
				
			||||
             (if context | 
				
			||||
                 (message "Computation finished.") | 
				
			||||
               (when (y-or-n-p "Computation completed without breaking. Reload the module and retry?") | 
				
			||||
                 (message "Reloading and resetting breakpoints...") | 
				
			||||
                 (haskell-interactive-mode-reset-error (haskell-debug-session)) | 
				
			||||
                 (cl-loop for break in breakpoints | 
				
			||||
                          do (haskell-process-queue-sync-request | 
				
			||||
                              (haskell-debug-process) | 
				
			||||
                              (concat ":load " (plist-get break :path)))) | 
				
			||||
                 (cl-loop for break in breakpoints | 
				
			||||
                          do (haskell-debug-break break)) | 
				
			||||
                 (haskell-debug/step expr))))))))) | 
				
			||||
   (haskell-debug/refresh))) | 
				
			||||
 | 
				
			||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 
				
			||||
;; Internal functions | 
				
			||||
 | 
				
			||||
(defun haskell-debug-session () | 
				
			||||
  "Get the Haskell session." | 
				
			||||
  (or (haskell-session-maybe) | 
				
			||||
      (error "No Haskell session associated with this debug | 
				
			||||
      buffer. Please just close the buffer and start again."))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-process () | 
				
			||||
  "Get the Haskell session." | 
				
			||||
  (or (haskell-session-process (haskell-session-maybe)) | 
				
			||||
      (error "No Haskell session associated with this debug | 
				
			||||
      buffer. Please just close the buffer and start again."))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-buffer-name (session) | 
				
			||||
  "The debug buffer name for the current session." | 
				
			||||
  (format "*debug:%s*" | 
				
			||||
          (haskell-session-name session))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-get-breakpoints () | 
				
			||||
  "Get the list of breakpoints currently set." | 
				
			||||
  (let ((string (haskell-process-queue-sync-request | 
				
			||||
                 (haskell-debug-process) | 
				
			||||
                 ":show breaks"))) | 
				
			||||
    (if (string= string "No active breakpoints.\n") | 
				
			||||
        (list) | 
				
			||||
      (mapcar #'haskell-debug-parse-break-point | 
				
			||||
              (haskell-debug-split-string string))))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-get-modules () | 
				
			||||
  "Get the list of modules currently set." | 
				
			||||
  (let ((string (haskell-process-queue-sync-request | 
				
			||||
                 (haskell-debug-process) | 
				
			||||
                 ":show modules"))) | 
				
			||||
    (if (string= string "") | 
				
			||||
        (list) | 
				
			||||
      (mapcar #'haskell-debug-parse-module | 
				
			||||
              (haskell-debug-split-string string))))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-get-context () | 
				
			||||
  "Get the current context." | 
				
			||||
  (let ((string (haskell-process-queue-sync-request | 
				
			||||
                 (haskell-debug-process) | 
				
			||||
                 ":show context"))) | 
				
			||||
    (if (string= string "") | 
				
			||||
        nil | 
				
			||||
      (haskell-debug-parse-context string)))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-get-history () | 
				
			||||
  "Get the step history." | 
				
			||||
  (let ((string (haskell-process-queue-sync-request | 
				
			||||
                 (haskell-debug-process) | 
				
			||||
                 ":history"))) | 
				
			||||
    (if (or (string= string "") | 
				
			||||
            (string= string "Not stopped at a breakpoint\n")) | 
				
			||||
        nil | 
				
			||||
      (if (string= string "Empty history. Perhaps you forgot to use :trace?\n") | 
				
			||||
          nil | 
				
			||||
        (let ((entries (mapcar #'haskell-debug-parse-history-entry | 
				
			||||
                               (cl-remove-if (lambda (line) (or (string= "<end of history>" line) | 
				
			||||
                                                                (string= "..." line))) | 
				
			||||
                                             (haskell-debug-split-string string))))) | 
				
			||||
          (setq haskell-debug-history-cache | 
				
			||||
                entries) | 
				
			||||
          entries))))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-insert-bindings (modules breakpoints context) | 
				
			||||
  "Insert a list of bindings." | 
				
			||||
  (if breakpoints | 
				
			||||
      (progn (haskell-debug-insert-binding "t" "trace an expression") | 
				
			||||
             (haskell-debug-insert-binding "s" "step into an expression") | 
				
			||||
             (haskell-debug-insert-binding "b" "breakpoint" t)) | 
				
			||||
    (progn | 
				
			||||
      (when modules | 
				
			||||
        (haskell-debug-insert-binding "b" "breakpoint")) | 
				
			||||
      (when breakpoints | 
				
			||||
        (haskell-debug-insert-binding "s" "step into an expression" t)))) | 
				
			||||
  (when breakpoints | 
				
			||||
    (haskell-debug-insert-binding "d" "delete breakpoint")) | 
				
			||||
  (when context | 
				
			||||
    (haskell-debug-insert-binding "a" "abandon context") | 
				
			||||
    (haskell-debug-insert-binding "c" "continue" t)) | 
				
			||||
  (when context | 
				
			||||
    (haskell-debug-insert-binding "p" "previous step") | 
				
			||||
    (haskell-debug-insert-binding "n" "next step" t)) | 
				
			||||
  (haskell-debug-insert-binding "g" "refresh" t) | 
				
			||||
  (insert "\n")) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-insert-current-context (context history) | 
				
			||||
  "Insert the current context." | 
				
			||||
  (haskell-debug-insert-header "Context") | 
				
			||||
  (if context | 
				
			||||
      (haskell-debug-insert-context context history) | 
				
			||||
    (haskell-debug-insert-debug-finished)) | 
				
			||||
  (insert "\n")) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-insert-breakpoints (breakpoints) | 
				
			||||
  "insert the list of breakpoints." | 
				
			||||
  (haskell-debug-insert-header "Breakpoints") | 
				
			||||
  (if (null breakpoints) | 
				
			||||
      (haskell-debug-insert-muted "No active breakpoints.") | 
				
			||||
    (cl-loop for break in breakpoints | 
				
			||||
             do (insert (propertize (format "%d" | 
				
			||||
                                            (plist-get break :number)) | 
				
			||||
                                    'face `((:weight bold)) | 
				
			||||
                                    'break break) | 
				
			||||
                        (haskell-debug-muted " - ") | 
				
			||||
                        (propertize (plist-get break :module) | 
				
			||||
                                    'break break | 
				
			||||
                                    'break break) | 
				
			||||
                        (haskell-debug-muted | 
				
			||||
                         (format " (%d:%d)" | 
				
			||||
                                 (plist-get (plist-get break :span) :start-line) | 
				
			||||
                                 (plist-get (plist-get break :span) :start-col))) | 
				
			||||
                        "\n"))) | 
				
			||||
  (insert "\n")) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-insert-modules (modules) | 
				
			||||
  "Insert the list of modules." | 
				
			||||
  (haskell-debug-insert-header "Modules") | 
				
			||||
  (if (null modules) | 
				
			||||
      (haskell-debug-insert-muted "No loaded modules.") | 
				
			||||
    (progn (cl-loop for module in modules | 
				
			||||
                    do (insert (propertize (plist-get module :module) | 
				
			||||
                                           'module module | 
				
			||||
                                           'face `((:weight bold))) | 
				
			||||
                               (haskell-debug-muted " - ") | 
				
			||||
                               (propertize (file-name-nondirectory (plist-get module :path)) | 
				
			||||
                                           'module module)) | 
				
			||||
                    do (insert "\n"))))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-split-string (string) | 
				
			||||
  "Split GHCi's line-based output, stripping the trailing newline." | 
				
			||||
  (split-string string "\n" t)) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-parse-context (string) | 
				
			||||
  "Parse the context." | 
				
			||||
  (cond | 
				
			||||
   ((string-match "^--> \\(.+\\)\n  \\(.+\\)" string) | 
				
			||||
    (let ((name (match-string 1 string)) | 
				
			||||
          (stopped (haskell-debug-parse-stopped-at (match-string 2 string)))) | 
				
			||||
      (list :name name | 
				
			||||
            :path (plist-get stopped :path) | 
				
			||||
            :span (plist-get stopped :span)))))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-insert-binding (binding desc &optional end) | 
				
			||||
  "Insert a helpful keybinding." | 
				
			||||
  (insert (propertize binding 'face 'haskell-debug-keybinding-face) | 
				
			||||
          (haskell-debug-muted " - ") | 
				
			||||
          desc | 
				
			||||
          (if end | 
				
			||||
              "\n" | 
				
			||||
            (haskell-debug-muted ", ")))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-insert-header (title) | 
				
			||||
  "Insert a header title." | 
				
			||||
  (insert (propertize title | 
				
			||||
                      'face 'haskell-debug-heading-face) | 
				
			||||
          "\n\n")) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-insert-context (context history) | 
				
			||||
  "Insert the context and history." | 
				
			||||
  (when context | 
				
			||||
    (insert (propertize (plist-get context :name) 'face `((:weight bold))) | 
				
			||||
            (haskell-debug-muted " - ") | 
				
			||||
            (file-name-nondirectory (plist-get context :path)) | 
				
			||||
            (haskell-debug-muted " (stopped)") | 
				
			||||
            "\n")) | 
				
			||||
  (when haskell-debug-bindings-cache | 
				
			||||
    (insert "\n") | 
				
			||||
    (let ((bindings haskell-debug-bindings-cache)) | 
				
			||||
      (insert | 
				
			||||
       (haskell-debug-get-span-string | 
				
			||||
        (plist-get bindings :path) | 
				
			||||
        (plist-get bindings :span))) | 
				
			||||
      (insert "\n\n") | 
				
			||||
      (cl-loop for binding in (plist-get bindings :types) | 
				
			||||
               do (insert (haskell-fontify-as-mode binding 'haskell-mode) | 
				
			||||
                          "\n")))) | 
				
			||||
  (let ((history (or history | 
				
			||||
                     (list (haskell-debug-make-fake-history context))))) | 
				
			||||
    (when history | 
				
			||||
      (insert "\n") | 
				
			||||
      (haskell-debug-insert-history history)))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-insert-debug-finished () | 
				
			||||
  "Insert message that no debugging is happening, but if there is | 
				
			||||
some old history, then display that." | 
				
			||||
  (if haskell-debug-history-cache | 
				
			||||
      (progn (haskell-debug-insert-muted "Finished debugging.") | 
				
			||||
             (insert "\n") | 
				
			||||
             (haskell-debug-insert-history haskell-debug-history-cache)) | 
				
			||||
    (haskell-debug-insert-muted "Not debugging right now."))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-insert-muted (text) | 
				
			||||
  "Insert some muted text." | 
				
			||||
  (insert (haskell-debug-muted text) | 
				
			||||
          "\n")) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-muted (text) | 
				
			||||
  "Make some muted text." | 
				
			||||
  (propertize text 'face 'haskell-debug-muted-face)) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-parse-logged (string) | 
				
			||||
  "Parse the logged breakpoint." | 
				
			||||
  (cond | 
				
			||||
   ((string= "no more logged breakpoints\n" string) | 
				
			||||
    nil) | 
				
			||||
   ((string= "already at the beginning of the history\n" string) | 
				
			||||
    nil) | 
				
			||||
   (t | 
				
			||||
    (with-temp-buffer | 
				
			||||
      (insert string) | 
				
			||||
      (goto-char (point-min)) | 
				
			||||
      (list :path (progn (search-forward " at ") | 
				
			||||
                         (buffer-substring-no-properties | 
				
			||||
                          (point) | 
				
			||||
                          (1- (search-forward ":")))) | 
				
			||||
            :span (haskell-debug-parse-span | 
				
			||||
                   (buffer-substring-no-properties | 
				
			||||
                    (point) | 
				
			||||
                    (line-end-position))) | 
				
			||||
            :types (progn (forward-line) | 
				
			||||
                          (haskell-debug-split-string | 
				
			||||
                           (buffer-substring-no-properties | 
				
			||||
                            (point) | 
				
			||||
                            (point-max))))))))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-parse-stopped-at (string) | 
				
			||||
  "Parse the location stopped at from the given string. | 
				
			||||
 | 
				
			||||
For example: | 
				
			||||
 | 
				
			||||
Stopped at /home/foo/project/src/x.hs:6:25-36 | 
				
			||||
 | 
				
			||||
" | 
				
			||||
  (let ((index (string-match "Stopped at \\([^:]+\\):\\(.+\\)\n?" | 
				
			||||
                             string))) | 
				
			||||
    (when index | 
				
			||||
      (list :path (match-string 1 string) | 
				
			||||
            :span (haskell-debug-parse-span (match-string 2 string)) | 
				
			||||
            :types (cdr (haskell-debug-split-string (substring string index))))))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-get-span-string (path span) | 
				
			||||
  "Get the string from the PATH and the SPAN." | 
				
			||||
  (save-window-excursion | 
				
			||||
    (find-file path) | 
				
			||||
    (buffer-substring | 
				
			||||
     (save-excursion | 
				
			||||
       (goto-char (point-min)) | 
				
			||||
       (forward-line (1- (plist-get span :start-line))) | 
				
			||||
       (forward-char (1- (plist-get span :start-col))) | 
				
			||||
       (point)) | 
				
			||||
     (save-excursion | 
				
			||||
       (goto-char (point-min)) | 
				
			||||
       (forward-line (1- (plist-get span :end-line))) | 
				
			||||
       (forward-char (plist-get span :end-col)) | 
				
			||||
       (point))))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-make-fake-history (context) | 
				
			||||
  "Make a fake history item." | 
				
			||||
  (list :index -1 | 
				
			||||
        :path (plist-get context :path) | 
				
			||||
        :span (plist-get context :span))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-insert-history (history) | 
				
			||||
  "Insert tracing HISTORY." | 
				
			||||
  (let ((i (length history))) | 
				
			||||
    (cl-loop for span in history | 
				
			||||
             do (let ((string (haskell-debug-get-span-string | 
				
			||||
                               (plist-get span :path) | 
				
			||||
                               (plist-get span :span)))) | 
				
			||||
                  (insert (propertize (format "%4d" i) | 
				
			||||
                                      'face 'haskell-debug-trace-number-face) | 
				
			||||
                          " " | 
				
			||||
                          (haskell-debug-preview-span | 
				
			||||
                           (plist-get span :span) | 
				
			||||
                           string | 
				
			||||
                           t) | 
				
			||||
                          "\n") | 
				
			||||
                  (setq i (1- i)))))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-parse-span (string) | 
				
			||||
  "Parse a source span from a string. | 
				
			||||
 | 
				
			||||
Examples: | 
				
			||||
 | 
				
			||||
  (5,1)-(6,37) | 
				
			||||
  6:25-36 | 
				
			||||
  5:20 | 
				
			||||
 | 
				
			||||
People like to make other people's lives interesting by making | 
				
			||||
variances in source span notation." | 
				
			||||
  (cond | 
				
			||||
   ((string-match "\\([0-9]+\\):\\([0-9]+\\)-\\([0-9]+\\)" | 
				
			||||
                  string) | 
				
			||||
    (list :start-line (string-to-number (match-string 1 string)) | 
				
			||||
          :start-col (string-to-number (match-string 2 string)) | 
				
			||||
          :end-line (string-to-number (match-string 1 string)) | 
				
			||||
          :end-col (string-to-number (match-string 3 string)))) | 
				
			||||
   ((string-match "\\([0-9]+\\):\\([0-9]+\\)" | 
				
			||||
                  string) | 
				
			||||
    (list :start-line (string-to-number (match-string 1 string)) | 
				
			||||
          :start-col (string-to-number (match-string 2 string)) | 
				
			||||
          :end-line (string-to-number (match-string 1 string)) | 
				
			||||
          :end-col (string-to-number (match-string 2 string)))) | 
				
			||||
   ((string-match "(\\([0-9]+\\),\\([0-9]+\\))-(\\([0-9]+\\),\\([0-9]+\\))" | 
				
			||||
                  string) | 
				
			||||
    (list :start-line (string-to-number (match-string 1 string)) | 
				
			||||
          :start-col (string-to-number (match-string 2 string)) | 
				
			||||
          :end-line (string-to-number (match-string 3 string)) | 
				
			||||
          :end-col (string-to-number (match-string 4 string)))) | 
				
			||||
   (t (error "Unable to parse source span from string: %s" | 
				
			||||
             string)))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-preview-span (span string &optional collapsed) | 
				
			||||
  "Make a one-line preview of the given expression." | 
				
			||||
  (with-temp-buffer | 
				
			||||
    (haskell-mode) | 
				
			||||
    (insert string) | 
				
			||||
    (when (/= 0 (plist-get span :start-col)) | 
				
			||||
      (indent-rigidly (point-min) | 
				
			||||
                      (point-max) | 
				
			||||
                      1)) | 
				
			||||
    (if (fboundp 'font-lock-ensure) | 
				
			||||
        (font-lock-ensure) | 
				
			||||
      (with-no-warnings (font-lock-fontify-buffer))) | 
				
			||||
    (when (/= 0 (plist-get span :start-col)) | 
				
			||||
      (indent-rigidly (point-min) | 
				
			||||
                      (point-max) | 
				
			||||
                      -1)) | 
				
			||||
    (goto-char (point-min)) | 
				
			||||
    (if collapsed | 
				
			||||
        (replace-regexp-in-string | 
				
			||||
         "\n[ ]*" | 
				
			||||
         (propertize " " 'face 'haskell-debug-newline-face) | 
				
			||||
         (buffer-substring (point-min) | 
				
			||||
                           (point-max))) | 
				
			||||
      (buffer-string)))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-start (session) | 
				
			||||
  "Start the debug mode." | 
				
			||||
  (setq buffer-read-only t) | 
				
			||||
  (haskell-session-assign session) | 
				
			||||
  (haskell-debug/refresh)) | 
				
			||||
 | 
				
			||||
(defun haskell-debug () | 
				
			||||
  "Start the debugger for the current Haskell (GHCi) session." | 
				
			||||
  (interactive) | 
				
			||||
  (let ((session (haskell-debug-session))) | 
				
			||||
    (switch-to-buffer-other-window (haskell-debug-buffer-name session)) | 
				
			||||
    (unless (eq major-mode 'haskell-debug-mode) | 
				
			||||
      (haskell-debug-mode) | 
				
			||||
      (haskell-debug-start session)))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-break (break) | 
				
			||||
  "Set BREAK breakpoint in module at line/col." | 
				
			||||
  (haskell-process-queue-without-filters | 
				
			||||
   (haskell-debug-process) | 
				
			||||
   (format ":break %s %s %d" | 
				
			||||
           (plist-get break :module) | 
				
			||||
           (plist-get (plist-get break :span) :start-line) | 
				
			||||
           (plist-get (plist-get break :span) :start-col)))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-navigate (direction) | 
				
			||||
  "Navigate in DIRECTION \"back\" or \"forward\"." | 
				
			||||
  (let ((string (haskell-process-queue-sync-request | 
				
			||||
                 (haskell-debug-process) | 
				
			||||
                 (concat ":" direction)))) | 
				
			||||
    (let ((bindings (haskell-debug-parse-logged string))) | 
				
			||||
      (setq haskell-debug-bindings-cache | 
				
			||||
            bindings) | 
				
			||||
      (when (not bindings) | 
				
			||||
        (message "No more %s results!" direction))) | 
				
			||||
    (haskell-debug/refresh))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-session-debugging-p (session) | 
				
			||||
  "Does the session have a debugging buffer open?" | 
				
			||||
  (not (not (get-buffer (haskell-debug-buffer-name session))))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-highlight (path &optional span) | 
				
			||||
  "Highlight the file at span." | 
				
			||||
  (let ((p (make-overlay | 
				
			||||
            (line-beginning-position) | 
				
			||||
            (line-end-position)))) | 
				
			||||
    (overlay-put p 'face `((:background "#eee"))) | 
				
			||||
    (with-current-buffer | 
				
			||||
        (if span | 
				
			||||
            (save-window-excursion | 
				
			||||
              (find-file path) | 
				
			||||
              (current-buffer)) | 
				
			||||
          (find-file path) | 
				
			||||
          (current-buffer)) | 
				
			||||
      (let ((o (when span | 
				
			||||
                 (make-overlay | 
				
			||||
                  (save-excursion | 
				
			||||
                    (goto-char (point-min)) | 
				
			||||
                    (forward-line (1- (plist-get span :start-line))) | 
				
			||||
                    (forward-char (1- (plist-get span :start-col))) | 
				
			||||
                    (point)) | 
				
			||||
                  (save-excursion | 
				
			||||
                    (goto-char (point-min)) | 
				
			||||
                    (forward-line (1- (plist-get span :end-line))) | 
				
			||||
                    (forward-char (plist-get span :end-col)) | 
				
			||||
                    (point)))))) | 
				
			||||
        (when o | 
				
			||||
          (overlay-put o 'face `((:background "#eee")))) | 
				
			||||
        (sit-for 0.5) | 
				
			||||
        (when o | 
				
			||||
          (delete-overlay o)) | 
				
			||||
        (delete-overlay p))))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-parse-history-entry (string) | 
				
			||||
  "Parse a history entry." | 
				
			||||
  (if (string-match "^\\([-0-9]+\\)[ ]+:[ ]+\\([A-Za-z0-9_':]+\\)[ ]+(\\([^:]+\\):\\(.+?\\))$" | 
				
			||||
                    string) | 
				
			||||
      (list :index (string-to-number (match-string 1 string)) | 
				
			||||
            :name (match-string 2 string) | 
				
			||||
            :path (match-string 3 string) | 
				
			||||
            :span (haskell-debug-parse-span (match-string 4 string))) | 
				
			||||
    (error "Unable to parse history entry: %s" string))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-parse-module (string) | 
				
			||||
  "Parse a module and path. | 
				
			||||
 | 
				
			||||
For example: | 
				
			||||
 | 
				
			||||
X                ( /home/foo/X.hs, interpreted ) | 
				
			||||
 | 
				
			||||
" | 
				
			||||
  (if (string-match "^\\([^ ]+\\)[ ]+( \\([^ ]+?\\), [a-z]+ )$" | 
				
			||||
                    string) | 
				
			||||
      (list :module (match-string 1 string) | 
				
			||||
            :path (match-string 2 string)) | 
				
			||||
    (error "Unable to parse module from string: %s" | 
				
			||||
           string))) | 
				
			||||
 | 
				
			||||
(defun haskell-debug-parse-break-point (string) | 
				
			||||
  "Parse a breakpoint number, module and location from a string. | 
				
			||||
 | 
				
			||||
For example: | 
				
			||||
 | 
				
			||||
[13] Main /home/foo/src/x.hs:(5,1)-(6,37) | 
				
			||||
 | 
				
			||||
" | 
				
			||||
  (if (string-match "^\\[\\([0-9]+\\)\\] \\([^ ]+\\) \\([^:]+\\):\\(.+\\)$" | 
				
			||||
                    string) | 
				
			||||
      (list :number (string-to-number (match-string 1 string)) | 
				
			||||
            :module (match-string 2 string) | 
				
			||||
            :path (match-string 3 string) | 
				
			||||
            :span (haskell-debug-parse-span (match-string 4 string))) | 
				
			||||
    (error "Unable to parse breakpoint from string: %s" | 
				
			||||
           string))) | 
				
			||||
 | 
				
			||||
(provide 'haskell-debug) | 
				
			||||
 | 
				
			||||
;;; haskell-debug.el ends here | 
				
			||||
@ -1,619 +0,0 @@
					 | 
				
			||||
;;; 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 | 
				
			||||
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						@ -1,589 +0,0 @@
					 | 
				
			||||
;;; haskell-font-lock.el --- Font locking module for Haskell Mode -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright 2003, 2004, 2005, 2006, 2007, 2008  Free Software Foundation, Inc. | 
				
			||||
;; Copyright 1997-1998  Graeme E Moss, and Tommy Thorn | 
				
			||||
 | 
				
			||||
;; Author: 1997-1998 Graeme E Moss <gem@cs.york.ac.uk> | 
				
			||||
;;         1997-1998 Tommy Thorn <thorn@irisa.fr> | 
				
			||||
;;         2003      Dave Love <fx@gnu.org> | 
				
			||||
;; Keywords: faces files Haskell | 
				
			||||
 | 
				
			||||
;; 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/>. | 
				
			||||
 | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'cl-lib) | 
				
			||||
(require 'haskell-mode) | 
				
			||||
(require 'font-lock) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-font-lock-symbols nil | 
				
			||||
  "Display \\ and -> and such using symbols in fonts. | 
				
			||||
 | 
				
			||||
This may sound like a neat trick, but be extra careful: it changes the | 
				
			||||
alignment and can thus lead to nasty surprises w.r.t layout." | 
				
			||||
  :group 'haskell | 
				
			||||
  :type 'boolean) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-font-lock-symbols-alist | 
				
			||||
  '(("\\" . "λ") | 
				
			||||
    ("not" . "¬") | 
				
			||||
    ("->" . "→") | 
				
			||||
    ("<-" . "←") | 
				
			||||
    ("=>" . "⇒") | 
				
			||||
    ("()" . "∅") | 
				
			||||
    ("==" . "≡") | 
				
			||||
    ("/=" . "≢") | 
				
			||||
    (">=" . "≥") | 
				
			||||
    ("<=" . "≤") | 
				
			||||
    ("!!" . "‼") | 
				
			||||
    ("&&" . "∧") | 
				
			||||
    ("||" . "∨") | 
				
			||||
    ("sqrt" . "√") | 
				
			||||
    ("undefined" . "⊥") | 
				
			||||
    ("pi" . "π") | 
				
			||||
    ("~>" . "⇝") ;; Omega language | 
				
			||||
    ;; ("~>" "↝") ;; less desirable | 
				
			||||
    ("-<" . "↢") ;; Paterson's arrow syntax | 
				
			||||
    ;; ("-<" "⤙") ;; nicer but uncommon | 
				
			||||
    ("::" . "∷") | 
				
			||||
    ("." "∘" ; "○" | 
				
			||||
     ;; Need a predicate here to distinguish the . used by | 
				
			||||
     ;; forall <foo> . <bar>. | 
				
			||||
     haskell-font-lock-dot-is-not-composition) | 
				
			||||
    ("forall" . "∀")) | 
				
			||||
  "Alist mapping Haskell symbols to chars. | 
				
			||||
 | 
				
			||||
Each element has the form (STRING . COMPONENTS) or (STRING | 
				
			||||
COMPONENTS PREDICATE). | 
				
			||||
 | 
				
			||||
STRING is the Haskell symbol. | 
				
			||||
COMPONENTS is a representation specification suitable as an argument to | 
				
			||||
`compose-region'. | 
				
			||||
PREDICATE if present is a function of one argument (the start position | 
				
			||||
of the symbol) which should return non-nil if this mapping should | 
				
			||||
be disabled at that position." | 
				
			||||
  :type '(alist string string) | 
				
			||||
  :group 'haskell) | 
				
			||||
 | 
				
			||||
(defun haskell-font-lock-dot-is-not-composition (start) | 
				
			||||
  "Return non-nil if the \".\" at START is not a composition operator. | 
				
			||||
This is the case if the \".\" is part of a \"forall <tvar> . <type>\"." | 
				
			||||
  (save-excursion | 
				
			||||
    (goto-char start) | 
				
			||||
    (or (re-search-backward "\\<forall\\>[^.\"]*\\=" | 
				
			||||
                            (line-beginning-position) t) | 
				
			||||
        (not (or | 
				
			||||
              (string= " " (string (char-after start))) | 
				
			||||
              (string= " " (string (char-before start)))))))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defface haskell-keyword-face | 
				
			||||
  '((t :inherit font-lock-keyword-face)) | 
				
			||||
  "Face used to highlight Haskell keywords." | 
				
			||||
  :group 'haskell) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defface haskell-constructor-face | 
				
			||||
  '((t :inherit font-lock-type-face)) | 
				
			||||
  "Face used to highlight Haskell constructors." | 
				
			||||
  :group 'haskell) | 
				
			||||
 | 
				
			||||
;; This used to be `font-lock-variable-name-face' but it doesn't result in | 
				
			||||
;; a highlighting that's consistent with other modes (it's mostly used | 
				
			||||
;; for function defintions). | 
				
			||||
(defface haskell-definition-face | 
				
			||||
  '((t :inherit font-lock-function-name-face)) | 
				
			||||
  "Face used to highlight Haskell definitions." | 
				
			||||
  :group 'haskell) | 
				
			||||
 | 
				
			||||
;; This is probably just wrong, but it used to use | 
				
			||||
;; `font-lock-function-name-face' with a result that was not consistent with | 
				
			||||
;; other major modes, so I just exchanged with `haskell-definition-face'. | 
				
			||||
;;;###autoload | 
				
			||||
(defface haskell-operator-face | 
				
			||||
  '((t :inherit font-lock-variable-name-face)) | 
				
			||||
  "Face used to highlight Haskell operators." | 
				
			||||
  :group 'haskell) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defface haskell-pragma-face | 
				
			||||
  '((t :inherit font-lock-preprocessor-face)) | 
				
			||||
  "Face used to highlight Haskell pragmas." | 
				
			||||
  :group 'haskell) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defface haskell-literate-comment-face | 
				
			||||
  '((t :inherit font-lock-doc-face)) | 
				
			||||
  "Face with which to fontify literate comments. | 
				
			||||
Inherit from `default' to avoid fontification of them." | 
				
			||||
  :group 'haskell) | 
				
			||||
 | 
				
			||||
(defun haskell-font-lock-compose-symbol (alist) | 
				
			||||
  "Compose a sequence of ascii chars into a symbol. | 
				
			||||
Regexp match data 0 points to the chars." | 
				
			||||
  ;; Check that the chars should really be composed into a symbol. | 
				
			||||
  (let* ((start (match-beginning 0)) | 
				
			||||
         (end (match-end 0)) | 
				
			||||
         (syntaxes (cond | 
				
			||||
                    ((eq (char-syntax (char-after start)) ?w) '(?w)) | 
				
			||||
                    ((eq (char-syntax (char-after start)) ?.) '(?.)) | 
				
			||||
                    ;; Special case for the . used for qualified names. | 
				
			||||
                    ((and (eq (char-after start) ?\.) (= end (1+ start))) | 
				
			||||
                     '(?_ ?\\ ?w)) | 
				
			||||
                    (t '(?_ ?\\)))) | 
				
			||||
         sym-data) | 
				
			||||
    (if (or (memq (char-syntax (or (char-before start) ?\ )) syntaxes) | 
				
			||||
            (memq (char-syntax (or (char-after end) ?\ )) syntaxes) | 
				
			||||
            (or (elt (syntax-ppss) 3) (elt (syntax-ppss) 4)) | 
				
			||||
            (and (consp (setq sym-data (cdr (assoc (match-string 0) alist)))) | 
				
			||||
                 (let ((pred (cadr sym-data))) | 
				
			||||
                   (setq sym-data (car sym-data)) | 
				
			||||
                   (funcall pred start)))) | 
				
			||||
        ;; No composition for you.  Let's actually remove any composition | 
				
			||||
        ;; we may have added earlier and which is now incorrect. | 
				
			||||
        (remove-text-properties start end '(composition)) | 
				
			||||
      ;; That's a symbol alright, so add the composition. | 
				
			||||
      (compose-region start end sym-data))) | 
				
			||||
  ;; Return nil because we're not adding any face property. | 
				
			||||
  nil) | 
				
			||||
 | 
				
			||||
(defun haskell-font-lock-symbols-keywords () | 
				
			||||
  (when (and haskell-font-lock-symbols | 
				
			||||
	     haskell-font-lock-symbols-alist | 
				
			||||
	     (fboundp 'compose-region)) | 
				
			||||
    `((,(regexp-opt (mapcar 'car haskell-font-lock-symbols-alist) t) | 
				
			||||
       (0 (haskell-font-lock-compose-symbol ',haskell-font-lock-symbols-alist) | 
				
			||||
	  ;; In Emacs-21, if the `override' field is nil, the face | 
				
			||||
	  ;; expressions is only evaluated if the text has currently | 
				
			||||
	  ;; no face.  So force evaluation by using `keep'. | 
				
			||||
	  keep))))) | 
				
			||||
 | 
				
			||||
;; The font lock regular expressions. | 
				
			||||
(defun haskell-font-lock-keywords-create (literate) | 
				
			||||
  "Create fontification definitions for Haskell scripts. | 
				
			||||
Returns keywords suitable for `font-lock-keywords'." | 
				
			||||
  (let* (;; Bird-style literate scripts start a line of code with | 
				
			||||
         ;; "^>", otherwise a line of code starts with "^". | 
				
			||||
         (line-prefix (if (eq literate 'bird) "^> ?" "^")) | 
				
			||||
 | 
				
			||||
         (varid "\\b[[:lower:]_][[:alnum:]'_]*\\b") | 
				
			||||
         ;; We allow ' preceding conids because of DataKinds/PolyKinds | 
				
			||||
         (conid "\\b'?[[:upper:]][[:alnum:]'_]*\\b") | 
				
			||||
         (modid (concat "\\b" conid "\\(\\." conid "\\)*\\b")) | 
				
			||||
         (qvarid (concat modid "\\." varid)) | 
				
			||||
         (qconid (concat modid "\\." conid)) | 
				
			||||
         (sym "\\s.+") | 
				
			||||
 | 
				
			||||
         ;; Reserved identifiers | 
				
			||||
         (reservedid | 
				
			||||
          (concat "\\<" | 
				
			||||
                  ;; `as', `hiding', and `qualified' are part of the import | 
				
			||||
                  ;; spec syntax, but they are not reserved. | 
				
			||||
                  ;; `_' can go in here since it has temporary word syntax. | 
				
			||||
                  ;; (regexp-opt | 
				
			||||
                  ;;  '("case" "class" "data" "default" "deriving" "do" | 
				
			||||
                  ;;    "else" "if" "import" "in" "infix" "infixl" | 
				
			||||
                  ;;    "infixr" "instance" "let" "module" "newtype" "of" | 
				
			||||
                  ;;    "then" "type" "where" "_") t) | 
				
			||||
                  "\\(_\\|c\\(ase\\|lass\\)\\|d\\(ata\\|e\\(fault\\|riving\\)\\|o\\)\\|else\\|i\\(mport\\|n\\(fix[lr]?\\|stance\\)\\|[fn]\\)\\|let\\|module\\|mdo\\|newtype\\|of\\|rec\\|proc\\|t\\(hen\\|ype\\)\\|where\\)" | 
				
			||||
                  "\\>")) | 
				
			||||
 | 
				
			||||
         ;; Top-level declarations | 
				
			||||
         (topdecl-var | 
				
			||||
          (concat line-prefix "\\(" varid "\\(?:\\s-*,\\s-*" varid "\\)*" "\\)\\s-*" | 
				
			||||
                  ;; optionally allow for a single newline after identifier | 
				
			||||
                  ;; NOTE: not supported for bird-style .lhs files | 
				
			||||
                  (if (eq literate 'bird) nil "\\([\n]\\s-+\\)?") | 
				
			||||
                  ;; A toplevel declaration can be followed by a definition | 
				
			||||
                  ;; (=), a type (::) or (∷), a guard, or a pattern which can | 
				
			||||
                  ;; either be a variable, a constructor, a parenthesized | 
				
			||||
                  ;; thingy, or an integer or a string. | 
				
			||||
                  "\\(" varid "\\|" conid "\\|::\\|∷\\|=\\||\\|\\s(\\|[0-9\"']\\)")) | 
				
			||||
         (topdecl-var2 | 
				
			||||
          (concat line-prefix "\\(" varid "\\|" conid "\\)\\s-*`\\(" varid "\\)`")) | 
				
			||||
         (topdecl-bangpat | 
				
			||||
          (concat line-prefix "\\(" varid "\\)\\s-*!")) | 
				
			||||
         (topdecl-sym | 
				
			||||
          (concat line-prefix "\\(" varid "\\|" conid "\\)\\s-*\\(" sym "\\)")) | 
				
			||||
         (topdecl-sym2 (concat line-prefix "(\\(" sym "\\))")) | 
				
			||||
 | 
				
			||||
         keywords) | 
				
			||||
 | 
				
			||||
    (setq keywords | 
				
			||||
          `(;; NOTICE the ordering below is significant | 
				
			||||
            ;; | 
				
			||||
            ("^#.*$" 0 'font-lock-preprocessor-face t) | 
				
			||||
 | 
				
			||||
            ,@(haskell-font-lock-symbols-keywords) | 
				
			||||
 | 
				
			||||
            (,reservedid 1 'haskell-keyword-face) | 
				
			||||
 | 
				
			||||
            ;; Special case for `as', `hiding', `safe' and `qualified', which are | 
				
			||||
            ;; keywords in import statements but are not otherwise reserved. | 
				
			||||
            ("\\<import[ \t]+\\(?:\\(safe\\>\\)[ \t]*\\)?\\(?:\\(qualified\\>\\)[ \t]*\\)?\\(?:\"[^\"]*\"[\t ]*\\)?[^ \t\n()]+[ \t]*\\(?:\\(\\<as\\>\\)[ \t]*[^ \t\n()]+[ \t]*\\)?\\(\\<hiding\\>\\)?" | 
				
			||||
             (1 'haskell-keyword-face nil lax) | 
				
			||||
             (2 'haskell-keyword-face nil lax) | 
				
			||||
             (3 'haskell-keyword-face nil lax) | 
				
			||||
             (4 'haskell-keyword-face nil lax)) | 
				
			||||
 | 
				
			||||
            ;; Special case for `foreign import' | 
				
			||||
            ;; keywords in foreign import statements but are not otherwise reserved. | 
				
			||||
            ("\\<\\(foreign\\)[ \t]+\\(import\\)[ \t]+\\(?:\\(ccall\\|stdcall\\|cplusplus\\|jvm\\|dotnet\\)[ \t]+\\)?\\(?:\\(safe\\|unsafe\\|interruptible\\)[ \t]+\\)?" | 
				
			||||
             (1 'haskell-keyword-face nil lax) | 
				
			||||
             (2 'haskell-keyword-face nil lax) | 
				
			||||
             (3 'haskell-keyword-face nil lax) | 
				
			||||
             (4 'haskell-keyword-face nil lax)) | 
				
			||||
 | 
				
			||||
            ;; Special case for `foreign export' | 
				
			||||
            ;; keywords in foreign export statements but are not otherwise reserved. | 
				
			||||
            ("\\<\\(foreign\\)[ \t]+\\(export\\)[ \t]+\\(?:\\(ccall\\|stdcall\\|cplusplus\\|jvm\\|dotnet\\)[ \t]+\\)?" | 
				
			||||
             (1 'haskell-keyword-face nil lax) | 
				
			||||
             (2 'haskell-keyword-face nil lax) | 
				
			||||
             (3 'haskell-keyword-face nil lax)) | 
				
			||||
 | 
				
			||||
            ;; Toplevel Declarations. | 
				
			||||
            ;; Place them *before* generic id-and-op highlighting. | 
				
			||||
            (,topdecl-var  (1 'haskell-definition-face)) | 
				
			||||
            (,topdecl-var2 (2 'haskell-definition-face)) | 
				
			||||
            (,topdecl-bangpat  (1 'haskell-definition-face)) | 
				
			||||
            (,topdecl-sym  (2 (unless (member (match-string 2) '("\\" "=" "->" "→" "<-" "←" "::" "∷" "," ";" "`")) | 
				
			||||
                                'haskell-definition-face))) | 
				
			||||
            (,topdecl-sym2 (1 (unless (member (match-string 1) '("\\" "=" "->" "→" "<-" "←" "::" "∷" "," ";" "`")) | 
				
			||||
                                'haskell-definition-face))) | 
				
			||||
 | 
				
			||||
            ;; These four are debatable... | 
				
			||||
            ("(\\(,*\\|->\\))" 0 'haskell-constructor-face) | 
				
			||||
            ("\\[\\]" 0 'haskell-constructor-face) | 
				
			||||
 | 
				
			||||
            (,(concat "`" varid "`") 0 'haskell-operator-face) | 
				
			||||
            (,(concat "`" conid "`") 0 'haskell-operator-face) | 
				
			||||
            (,(concat "`" qvarid "`") 0 'haskell-operator-face) | 
				
			||||
            (,(concat "`" qconid "`") 0 'haskell-operator-face) | 
				
			||||
 | 
				
			||||
            (,qconid 0 'haskell-constructor-face) | 
				
			||||
 | 
				
			||||
            (,conid 0 'haskell-constructor-face) | 
				
			||||
 | 
				
			||||
            (,sym 0 (if (and (eq (char-after (match-beginning 0)) ?:) | 
				
			||||
                             (not (member (match-string 0) '("::" "∷")))) | 
				
			||||
                        'haskell-constructor-face | 
				
			||||
                      'haskell-operator-face)))) | 
				
			||||
    keywords)) | 
				
			||||
 | 
				
			||||
(defvar haskell-font-lock-latex-cache-pos nil | 
				
			||||
  "Position of cache point used by `haskell-font-lock-latex-cache-in-comment'. | 
				
			||||
Should be at the start of a line.") | 
				
			||||
(make-variable-buffer-local 'haskell-font-lock-latex-cache-pos) | 
				
			||||
 | 
				
			||||
(defvar haskell-font-lock-latex-cache-in-comment nil | 
				
			||||
  "If `haskell-font-lock-latex-cache-pos' is outside a | 
				
			||||
\\begin{code}..\\end{code} block (and therefore inside a comment), | 
				
			||||
this variable is set to t, otherwise nil.") | 
				
			||||
(make-variable-buffer-local 'haskell-font-lock-latex-cache-in-comment) | 
				
			||||
 | 
				
			||||
(defun haskell-font-lock-latex-comments (end) | 
				
			||||
  "Sets `match-data' according to the region of the buffer before end | 
				
			||||
that should be commented under LaTeX-style literate scripts." | 
				
			||||
  (let ((start (point))) | 
				
			||||
    (if (= start end) | 
				
			||||
        ;; We're at the end.  No more to fontify. | 
				
			||||
        nil | 
				
			||||
      (if (not (eq start haskell-font-lock-latex-cache-pos)) | 
				
			||||
          ;; If the start position is not cached, calculate the state | 
				
			||||
          ;; of the start. | 
				
			||||
          (progn | 
				
			||||
            (setq haskell-font-lock-latex-cache-pos start) | 
				
			||||
            ;; If the previous \begin{code} or \end{code} is a | 
				
			||||
            ;; \begin{code}, then start is not in a comment, otherwise | 
				
			||||
            ;; it is in a comment. | 
				
			||||
            (setq haskell-font-lock-latex-cache-in-comment | 
				
			||||
                  (if (and | 
				
			||||
                       (re-search-backward | 
				
			||||
                        "^\\(\\(\\\\begin{code}\\)\\|\\(\\\\end{code}\\)\\)$" | 
				
			||||
                        (point-min) t) | 
				
			||||
                       (match-end 2)) | 
				
			||||
                      nil t)) | 
				
			||||
            ;; Restore position. | 
				
			||||
            (goto-char start))) | 
				
			||||
      (if haskell-font-lock-latex-cache-in-comment | 
				
			||||
          (progn | 
				
			||||
            ;; If start is inside a comment, search for next \begin{code}. | 
				
			||||
            (re-search-forward "^\\\\begin{code}$" end 'move) | 
				
			||||
            ;; Mark start to end of \begin{code} (if present, till end | 
				
			||||
            ;; otherwise), as a comment. | 
				
			||||
            (set-match-data (list start (point))) | 
				
			||||
            ;; Return point, as a normal regexp would. | 
				
			||||
            (point)) | 
				
			||||
        ;; If start is inside a code block, search for next \end{code}. | 
				
			||||
        (if (re-search-forward "^\\\\end{code}$" end t) | 
				
			||||
            ;; If one found, mark it as a comment, otherwise finish. | 
				
			||||
            (point)))))) | 
				
			||||
 | 
				
			||||
(defconst haskell-basic-syntactic-keywords | 
				
			||||
  '(;; Character constants (since apostrophe can't have string syntax). | 
				
			||||
    ;; Beware: do not match something like 's-}' or '\n"+' since the first ' | 
				
			||||
    ;; might be inside a comment or a string. | 
				
			||||
    ;; This still gets fooled with "'"'"'"'"'"', but ... oh well. | 
				
			||||
    ("\\Sw\\('\\)\\([^\\'\n]\\|\\\\.[^\\'\n \"}]*\\)\\('\\)" (1 "\"") (3 "\"")) | 
				
			||||
    ;; Deal with instances of `--' which don't form a comment | 
				
			||||
    ("[!#$%&*+./:<=>?@^|~\\]*--[!#$%&*+./:<=>?@^|~\\-]*" (0 (cond ((or (nth 3 (syntax-ppss)) (numberp (nth 4 (syntax-ppss)))) | 
				
			||||
                              ;; There are no such instances inside | 
				
			||||
                              ;; nestable comments or strings | 
				
			||||
                              nil) | 
				
			||||
                             ((string-match "\\`-*\\'" (match-string 0)) | 
				
			||||
                              ;; Sequence of hyphens. Do nothing in | 
				
			||||
                              ;; case of things like `{---'. | 
				
			||||
                              nil) | 
				
			||||
                             ((string-match "\\`[^-]+--.*" (match-string 0)) | 
				
			||||
                              ;; Extra characters before comment starts | 
				
			||||
                              ".") | 
				
			||||
                             (t ".")))) ; other symbol sequence | 
				
			||||
 | 
				
			||||
    ;; Implement Haskell Report 'escape' and 'gap' rules. Backslash | 
				
			||||
    ;; inside of a string is escaping unless it is preceeded by | 
				
			||||
    ;; another escaping backslash. There can be whitespace between | 
				
			||||
    ;; those two. | 
				
			||||
    ;; | 
				
			||||
    ;; Backslashes outside of string never escape. | 
				
			||||
    ;; | 
				
			||||
    ;; Note that (> 0 (skip-syntax-backward ".")) this skips over *escaping* | 
				
			||||
    ;; backslashes only. | 
				
			||||
    ("\\\\" (0 (when (save-excursion (and (nth 3 (syntax-ppss)) | 
				
			||||
                                          (goto-char (match-beginning 0)) | 
				
			||||
                                          (skip-syntax-backward "->") | 
				
			||||
                                          (or (not (eq ?\\ (char-before))) | 
				
			||||
                                              (> 0 (skip-syntax-backward "."))))) | 
				
			||||
                  "\\"))) | 
				
			||||
 | 
				
			||||
    ;; QuasiQuotes syntax: [quoter| string |], quoter is unqualified | 
				
			||||
    ;; name, no spaces, string is arbitrary (including newlines), | 
				
			||||
    ;; finishes at the first occurence of |], no escaping is provided. | 
				
			||||
    ;; | 
				
			||||
    ;; The quoter cannot be "e", "t", "d", or "p", since those overlap | 
				
			||||
    ;; with Template Haskell quotations. | 
				
			||||
    ;; | 
				
			||||
    ;; QuasiQuotes opens only when outside of a string or a comment | 
				
			||||
    ;; and closes only when inside a quasiquote. | 
				
			||||
    ;; | 
				
			||||
    ;; (syntax-ppss) returns list with two interesting elements: | 
				
			||||
    ;; nth 3. non-nil if inside a string. (it is the character that will | 
				
			||||
    ;;        terminate the string, or t if the string should be terminated | 
				
			||||
    ;;        by a generic string delimiter.) | 
				
			||||
    ;; nth 4. nil if outside a comment, t if inside a non-nestable comment, | 
				
			||||
    ;;        else an integer (the current comment nesting). | 
				
			||||
    ;; | 
				
			||||
    ;; Note also that we need to do in in a single pass, hence a regex | 
				
			||||
    ;; that covers both the opening and the ending of a quasiquote. | 
				
			||||
 | 
				
			||||
    ("\\(\\[[[:alnum:]]+\\)?\\(|\\)\\(]\\)?" | 
				
			||||
     (2 (save-excursion | 
				
			||||
          (goto-char (match-beginning 0)) | 
				
			||||
          (if (eq ?\[ (char-after)) | 
				
			||||
              ;; opening case | 
				
			||||
              (unless (or (nth 3 (syntax-ppss)) | 
				
			||||
                          (nth 4 (syntax-ppss)) | 
				
			||||
                          (member (match-string 1) | 
				
			||||
                                  '("[e" "[t" "[d" "[p"))) | 
				
			||||
                "\"") | 
				
			||||
            ;; closing case | 
				
			||||
            (when (and (eq ?| (nth 3 (syntax-ppss))) | 
				
			||||
                       (equal "]" (match-string 3)) | 
				
			||||
                       ) | 
				
			||||
              "\""))))) | 
				
			||||
    )) | 
				
			||||
 | 
				
			||||
(defconst haskell-bird-syntactic-keywords | 
				
			||||
  (cons '("^[^\n>]"  (0 "<")) | 
				
			||||
        haskell-basic-syntactic-keywords)) | 
				
			||||
 | 
				
			||||
(defconst haskell-latex-syntactic-keywords | 
				
			||||
  (append | 
				
			||||
   '(("^\\\\begin{code}\\(\n\\)" 1 "!") | 
				
			||||
     ;; Note: buffer is widened during font-locking. | 
				
			||||
     ("\\`\\(.\\|\n\\)" (1 "!"))               ; start comment at buffer start | 
				
			||||
     ("^\\(\\\\\\)end{code}$" 1 "!")) | 
				
			||||
   haskell-basic-syntactic-keywords)) | 
				
			||||
 | 
				
			||||
(defun haskell-syntactic-face-function (state) | 
				
			||||
  "`font-lock-syntactic-face-function' for Haskell." | 
				
			||||
  (cond | 
				
			||||
   ((nth 3 state) 'font-lock-string-face) ; as normal | 
				
			||||
   ;; Else comment.  If it's from syntax table, use default face. | 
				
			||||
   ((or (eq 'syntax-table (nth 7 state)) | 
				
			||||
        (and (eq haskell-literate 'bird) | 
				
			||||
             (memq (char-before (nth 8 state)) '(nil ?\n)))) | 
				
			||||
    'haskell-literate-comment-face) | 
				
			||||
   ;; Detect pragmas. A pragma is enclosed in special comment | 
				
			||||
   ;; delimeters {-# .. #-}. | 
				
			||||
   ((save-excursion | 
				
			||||
      (goto-char (nth 8 state)) | 
				
			||||
      (and (looking-at "{-#") | 
				
			||||
           (forward-comment 1) | 
				
			||||
           (goto-char (- (point) 3)) | 
				
			||||
           (looking-at "#-}"))) | 
				
			||||
    'haskell-pragma-face) | 
				
			||||
   ;; Haddock comment start with either "-- [|^*$]" or "{- ?[|^*$]" | 
				
			||||
   ;; (note space optional for nested comments and mandatory for | 
				
			||||
   ;; double dash comments). | 
				
			||||
   ;; | 
				
			||||
   ;; Haddock comment will also continue on next line, provided: | 
				
			||||
   ;; - current line is a double dash haddock comment | 
				
			||||
   ;; - next line is also double dash comment | 
				
			||||
   ;; - there is only whitespace between | 
				
			||||
   ;; | 
				
			||||
   ;; We recognize double dash haddock comments by property | 
				
			||||
   ;; 'font-lock-doc-face attached to newline. In case of bounded | 
				
			||||
   ;; comments newline is outside of comment. | 
				
			||||
   ((save-excursion | 
				
			||||
      (goto-char (nth 8 state)) | 
				
			||||
      (or (looking-at "\\(?:{- ?\\|-- \\)[|^*$]") | 
				
			||||
	  (and (looking-at "--")              ; are we at double dash comment | 
				
			||||
	       (forward-line -1)              ; this is nil on first line | 
				
			||||
	       (eq (get-text-property (line-end-position) 'face) | 
				
			||||
		   'font-lock-doc-face)	      ; is a doc face | 
				
			||||
	       (forward-line) | 
				
			||||
	       (skip-syntax-forward "-")      ; see if there is only whitespace | 
				
			||||
	       (eq (point) (nth 8 state)))))  ; we are back in position | 
				
			||||
    'font-lock-doc-face) | 
				
			||||
   (t 'font-lock-comment-face))) | 
				
			||||
 | 
				
			||||
(defconst haskell-font-lock-keywords | 
				
			||||
  (haskell-font-lock-keywords-create nil) | 
				
			||||
  "Font lock definitions for non-literate Haskell.") | 
				
			||||
 | 
				
			||||
(defconst haskell-font-lock-bird-literate-keywords | 
				
			||||
  (haskell-font-lock-keywords-create 'bird) | 
				
			||||
  "Font lock definitions for Bird-style literate Haskell.") | 
				
			||||
 | 
				
			||||
(defconst haskell-font-lock-latex-literate-keywords | 
				
			||||
  (haskell-font-lock-keywords-create 'latex) | 
				
			||||
  "Font lock definitions for LaTeX-style literate Haskell.") | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-font-lock-choose-keywords () | 
				
			||||
  (let ((literate (if (boundp 'haskell-literate) haskell-literate))) | 
				
			||||
    (cl-case literate | 
				
			||||
      (bird haskell-font-lock-bird-literate-keywords) | 
				
			||||
      ((latex tex) haskell-font-lock-latex-literate-keywords) | 
				
			||||
      (t haskell-font-lock-keywords)))) | 
				
			||||
 | 
				
			||||
(defun haskell-font-lock-choose-syntactic-keywords () | 
				
			||||
  (let ((literate (if (boundp 'haskell-literate) haskell-literate))) | 
				
			||||
    (cl-case literate | 
				
			||||
      (bird haskell-bird-syntactic-keywords) | 
				
			||||
      ((latex tex) haskell-latex-syntactic-keywords) | 
				
			||||
      (t haskell-basic-syntactic-keywords)))) | 
				
			||||
 | 
				
			||||
(defun haskell-font-lock-defaults-create () | 
				
			||||
  "Locally set `font-lock-defaults' for Haskell." | 
				
			||||
  (set (make-local-variable 'font-lock-defaults) | 
				
			||||
       '(haskell-font-lock-choose-keywords | 
				
			||||
         nil nil ((?\' . "w") (?_  . "w")) nil | 
				
			||||
         (font-lock-syntactic-keywords | 
				
			||||
          . haskell-font-lock-choose-syntactic-keywords) | 
				
			||||
         (font-lock-syntactic-face-function | 
				
			||||
          . haskell-syntactic-face-function) | 
				
			||||
         ;; Get help from font-lock-syntactic-keywords. | 
				
			||||
         (parse-sexp-lookup-properties . t)))) | 
				
			||||
 | 
				
			||||
;; The main functions. | 
				
			||||
(defun turn-on-haskell-font-lock () | 
				
			||||
  "Turns on font locking in current buffer for Haskell 1.4 scripts. | 
				
			||||
 | 
				
			||||
Changes the current buffer's `font-lock-defaults', and adds the | 
				
			||||
following variables: | 
				
			||||
 | 
				
			||||
   `haskell-keyword-face'      for reserved keywords and syntax, | 
				
			||||
   `haskell-constructor-face'  for data- and type-constructors, class names, | 
				
			||||
                               and module names, | 
				
			||||
   `haskell-operator-face'     for symbolic and alphanumeric operators, | 
				
			||||
   `haskell-default-face'      for ordinary code. | 
				
			||||
 | 
				
			||||
The variables are initialised to the following font lock default faces: | 
				
			||||
 | 
				
			||||
   `haskell-keyword-face'      `font-lock-keyword-face' | 
				
			||||
   `haskell-constructor-face'  `font-lock-type-face' | 
				
			||||
   `haskell-operator-face'     `font-lock-function-name-face' | 
				
			||||
   `haskell-default-face'      <default face> | 
				
			||||
 | 
				
			||||
Two levels of fontification are defined: level one (the default) | 
				
			||||
and level two (more colour).  The former does not colour operators. | 
				
			||||
Use the variable `font-lock-maximum-decoration' to choose | 
				
			||||
non-default levels of fontification.  For example, adding this to | 
				
			||||
.emacs: | 
				
			||||
 | 
				
			||||
  (setq font-lock-maximum-decoration '((haskell-mode . 2) (t . 0))) | 
				
			||||
 | 
				
			||||
uses level two fontification for `haskell-mode' and default level for | 
				
			||||
all other modes.  See documentation on this variable for further | 
				
			||||
details. | 
				
			||||
 | 
				
			||||
To alter an attribute of a face, add a hook.  For example, to change | 
				
			||||
the foreground colour of comments to brown, add the following line to | 
				
			||||
.emacs: | 
				
			||||
 | 
				
			||||
  (add-hook 'haskell-font-lock-hook | 
				
			||||
      (lambda () | 
				
			||||
          (set-face-foreground 'haskell-comment-face \"brown\"))) | 
				
			||||
 | 
				
			||||
Note that the colours available vary from system to system.  To see | 
				
			||||
what colours are available on your system, call | 
				
			||||
`list-colors-display' from emacs. | 
				
			||||
 | 
				
			||||
To turn font locking on for all Haskell buffers, add this to .emacs: | 
				
			||||
 | 
				
			||||
  (add-hook 'haskell-mode-hook 'turn-on-haskell-font-lock) | 
				
			||||
 | 
				
			||||
To turn font locking on for the current buffer, call | 
				
			||||
`turn-on-haskell-font-lock'.  To turn font locking off in the current | 
				
			||||
buffer, call `turn-off-haskell-font-lock'. | 
				
			||||
 | 
				
			||||
Bird-style literate Haskell scripts are supported: If the value of | 
				
			||||
`haskell-literate-bird-style' (automatically set by the Haskell mode | 
				
			||||
of Moss&Thorn) is non-nil, a Bird-style literate script is assumed. | 
				
			||||
 | 
				
			||||
Invokes `haskell-font-lock-hook' if not nil." | 
				
			||||
  (haskell-font-lock-defaults-create) | 
				
			||||
  (run-hooks 'haskell-font-lock-hook) | 
				
			||||
  (turn-on-font-lock)) | 
				
			||||
 | 
				
			||||
(defun turn-off-haskell-font-lock () | 
				
			||||
  "Turns off font locking in current buffer." | 
				
			||||
  (font-lock-mode -1)) | 
				
			||||
 | 
				
			||||
(defun haskell-fontify-as-mode (text mode) | 
				
			||||
  "Fontify TEXT as MODE, returning the fontified text." | 
				
			||||
  (with-temp-buffer | 
				
			||||
    (funcall mode) | 
				
			||||
    (insert text) | 
				
			||||
    (if (fboundp 'font-lock-ensure) | 
				
			||||
        (font-lock-ensure) | 
				
			||||
      (with-no-warnings (font-lock-fontify-buffer))) | 
				
			||||
    (buffer-substring (point-min) (point-max)))) | 
				
			||||
 | 
				
			||||
;; Provide ourselves: | 
				
			||||
 | 
				
			||||
(provide 'haskell-font-lock) | 
				
			||||
 | 
				
			||||
;; Local Variables: | 
				
			||||
;; coding: utf-8-unix | 
				
			||||
;; tab-width: 8 | 
				
			||||
;; End: | 
				
			||||
 | 
				
			||||
;;; haskell-font-lock.el ends here | 
				
			||||
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						@ -1,223 +0,0 @@
					 | 
				
			||||
;;; haskell-lexeme.el --- haskell lexical tokens   -*- coding: utf-8; lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright (C) 2015 Gracjan Polak | 
				
			||||
 | 
				
			||||
;; 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: | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'rx) | 
				
			||||
 | 
				
			||||
(unless (category-docstring ?P) | 
				
			||||
  (define-category ?P "Haskell symbol constituent characters") | 
				
			||||
  (map-char-table | 
				
			||||
   #'(lambda (key val) | 
				
			||||
       (if (or | 
				
			||||
            (and (consp key) (> (car key) 128)) | 
				
			||||
            (and (numberp key) (> key 128))) | 
				
			||||
           (if (member val '(Pc Pd Po Sm Sc Sk So)) | 
				
			||||
	       (modify-category-entry key ?P)))) | 
				
			||||
   unicode-category-table) | 
				
			||||
 | 
				
			||||
  (dolist (key (string-to-list "!#$%&*+./<=>?@^|~\\-")) | 
				
			||||
    (modify-category-entry key ?P))) | 
				
			||||
 | 
				
			||||
(defconst haskell-lexeme-modid | 
				
			||||
  "[[:upper:]][[:alnum:]'_]*" | 
				
			||||
  "Regexp matching a valid Haskell module identifier. | 
				
			||||
 | 
				
			||||
Note that GHC accepts Unicode category UppercaseLetter as a first | 
				
			||||
character. Following letters are from Unicode categories | 
				
			||||
UppercaseLetter, LowercaseLetter, OtherLetter, TitlecaseLetter, | 
				
			||||
ModifierLetter, DecimalNumber, OtherNumber, backslash or | 
				
			||||
underscore. | 
				
			||||
 | 
				
			||||
Note that this differs from constructor identifier as the latter | 
				
			||||
one can have any number of hash character at the end to | 
				
			||||
accommodate MagicHash extension.") | 
				
			||||
 | 
				
			||||
(defconst haskell-lexeme-id | 
				
			||||
  "[[:alpha:]_][[:alnum:]'_]*#*" | 
				
			||||
  "Regexp matching a valid Haskell identifier. | 
				
			||||
 | 
				
			||||
GHC accepts a string starting with any alphabetic character or | 
				
			||||
underscore followed by any alphanumeric character or underscore | 
				
			||||
or apostrophe.") | 
				
			||||
 | 
				
			||||
(defconst haskell-lexeme-sym | 
				
			||||
  "\\(:?\\cP\\|:\\)+" | 
				
			||||
  "Regexp matching a valid Haskell variable or constructor symbol. | 
				
			||||
 | 
				
			||||
GHC accepts a string of chars from the set | 
				
			||||
[:!#$%&*+./<=>?@^|~\\-] or Unicode category Symbol for chars with | 
				
			||||
codes larger than 128 only.") | 
				
			||||
 | 
				
			||||
(defconst haskell-lexeme-modid-opt-prefix | 
				
			||||
  (concat "\\(?:" haskell-lexeme-modid "\\.\\)*") | 
				
			||||
  "Regexp matching a valid Haskell module prefix, potentially empty. | 
				
			||||
 | 
				
			||||
Module path prefix is separated by dots and finishes with a | 
				
			||||
dot. For path component syntax see `haskell-lexeme-modid'.") | 
				
			||||
 | 
				
			||||
(defconst haskell-lexeme-qid-or-qsym | 
				
			||||
  (rx-to-string `(: (regexp ,haskell-lexeme-modid-opt-prefix) | 
				
			||||
                    (group (| (regexp ,haskell-lexeme-id) (regexp ,haskell-lexeme-sym) | 
				
			||||
                              )))) | 
				
			||||
  "Regexp matching a valid qualified identifier or symbol. | 
				
			||||
 | 
				
			||||
Note that (match-string 1) returns the unqualified part.") | 
				
			||||
 | 
				
			||||
(defconst haskell-lexeme-qid | 
				
			||||
  (rx-to-string `(: (regexp "'*") | 
				
			||||
                    (regexp ,haskell-lexeme-modid-opt-prefix) | 
				
			||||
                    (group (regexp ,haskell-lexeme-id)))) | 
				
			||||
  "Regexp matching a valid qualified identifier. | 
				
			||||
 | 
				
			||||
Note that (match-string 1) returns the unqualified part.") | 
				
			||||
 | 
				
			||||
(defconst haskell-lexeme-qsym | 
				
			||||
  (rx-to-string `(: (regexp "'*") | 
				
			||||
                    (regexp ,haskell-lexeme-modid-opt-prefix) | 
				
			||||
                    (group (regexp ,haskell-lexeme-id)))) | 
				
			||||
  "Regexp matching a valid qualified symbol. | 
				
			||||
 | 
				
			||||
Note that (match-string 1) returns the unqualified part.") | 
				
			||||
 | 
				
			||||
(defconst haskell-lexeme-number | 
				
			||||
  (rx (| (: (regexp "[0-9]+\\.[0-9]+") (opt (regexp "[eE][-+]?[0-9]+"))) | 
				
			||||
         (regexp "[0-9]+[eE][-+]?[0-9]+") | 
				
			||||
         (regexp "0[xX][0-9a-fA-F]+") | 
				
			||||
         (regexp "0[oO][0-7]+") | 
				
			||||
         (regexp "[0-9]+"))) | 
				
			||||
  "Regexp matching a floating point, decimal, octal or hexadecimal number. | 
				
			||||
 | 
				
			||||
Note that negative sign char is not part of a number.") | 
				
			||||
 | 
				
			||||
(defconst haskell-lexeme-char-literal-inside | 
				
			||||
  (rx (| (regexp "[^\n'\\]") | 
				
			||||
         (: "\\" | 
				
			||||
            (| "a" "b" "f" "n" "r" "t" "v" "\\" "\"" "'" | 
				
			||||
               "NUL" "SOH" "STX" "ETX" "EOT" "ENQ" "ACK" | 
				
			||||
               "BEL" "BS" "HT" "LF" "VT" "FF" "CR" "SO" "SI" "DLE" | 
				
			||||
               "DC1" "DC2" "DC3" "DC4" "NAK" "SYN" "ETB" "CAN" | 
				
			||||
               "EM" "SUB" "ESC" "FS" "GS" "RS" "US" "SP" "DEL" | 
				
			||||
               (: "^" (regexp "[]A-Z@^_\\[]")))))) | 
				
			||||
  "Regexp matching an inside of a character literal.") | 
				
			||||
 | 
				
			||||
(defconst haskell-lexeme-char-literal | 
				
			||||
  (rx-to-string `(: "'" (regexp ,haskell-lexeme-char-literal-inside) "'")) | 
				
			||||
  "Regexp matching a character literal.") | 
				
			||||
 | 
				
			||||
(defconst haskell-lexeme-string-literal-inside | 
				
			||||
  (rx (* (| (regexp "[^\n\"\\]") | 
				
			||||
            (: "\\" | 
				
			||||
               (| "a" "b" "f" "n" "r" "t" "v" "\\" "\"" "'" "&" | 
				
			||||
                  "NUL" "SOH" "STX" "ETX" "EOT" "ENQ" "ACK" | 
				
			||||
                  "BEL" "BS" "HT" "LF" "VT" "FF" "CR" "SO" "SI" "DLE" | 
				
			||||
                  "DC1" "DC2" "DC3" "DC4" "NAK" "SYN" "ETB" "CAN" | 
				
			||||
                  "EM" "SUB" "ESC" "FS" "GS" "RS" "US" "SP" "DEL" | 
				
			||||
                  (: "^" (regexp "[]A-Z@^_\\[]")) | 
				
			||||
                  (regexp "[ \t\n\r\v\f]*\\\\")))))) | 
				
			||||
  "Regexp matching an inside of a string literal.") | 
				
			||||
 | 
				
			||||
(defconst haskell-lexeme-string-literal | 
				
			||||
  (rx-to-string `(: "\"" (regexp ,haskell-lexeme-string-literal-inside) "\"")) | 
				
			||||
  "Regexp matching a string literal.") | 
				
			||||
 | 
				
			||||
(defun haskell-lexeme-classify-by-first-char (char) | 
				
			||||
  "Classify token by CHAR. | 
				
			||||
 | 
				
			||||
CHAR is a chararacter that is assumed to be first character of a token." | 
				
			||||
  (let ((category (get-char-code-property char 'general-category))) | 
				
			||||
 | 
				
			||||
    (cond | 
				
			||||
     ((or (member char '(?! ?# ?$ ?% ?& ?* ?+ ?. ?/ ?< ?= ?> ?? ?@ ?^ ?| ?~ ?\\ ?-)) | 
				
			||||
          (and (> char 127) | 
				
			||||
               (member category '(Pc Pd Po Sm Sc Sk So)))) | 
				
			||||
      'varsym) | 
				
			||||
     ((equal char ?:) | 
				
			||||
      'consym) | 
				
			||||
     ((equal char ?\') | 
				
			||||
      'char) | 
				
			||||
     ((equal char ?\") | 
				
			||||
      'string) | 
				
			||||
     ((member category '(Lu Lt)) | 
				
			||||
      'conid) | 
				
			||||
     ((or (equal char ?_) | 
				
			||||
          (member category '(Ll Lo))) | 
				
			||||
      'varsym) | 
				
			||||
     ((and (>= char ?0) (<= char 9)) | 
				
			||||
      'number) | 
				
			||||
     ((member char '(?\] ?\[ ?\( ?\) ?\{ ?\} ?\` ?\, ?\;)) | 
				
			||||
      'special)))) | 
				
			||||
 | 
				
			||||
(defun haskell-lexeme-looking-at-token () | 
				
			||||
  "Like `looking-at' but understands Haskell lexemes. | 
				
			||||
 | 
				
			||||
Moves point forward over whitespace.  Returns a symbol describing | 
				
			||||
type of Haskell token recognized.  Use `match-string', | 
				
			||||
`match-beginning' and `match-end' with argument 0 to query match | 
				
			||||
result. | 
				
			||||
 | 
				
			||||
Possible results are: | 
				
			||||
- 'special: for chars [](){}`,; | 
				
			||||
- 'comment: for single line comments | 
				
			||||
- 'nested-comment: for multiline comments | 
				
			||||
- 'qsymid: for qualified identifiers or symbols | 
				
			||||
- 'string: for strings literals | 
				
			||||
- 'char: for char literals | 
				
			||||
- 'decimal: for decimal, float, hexadecimal and octal number literals | 
				
			||||
- 'template-haskell-quote: for a string of apostrophes for template haskell | 
				
			||||
 | 
				
			||||
Note that for qualified symbols (match-string 1) returns the | 
				
			||||
unqualified identifier or symbol.  Further qualification for | 
				
			||||
symbol or identifier can be done with: | 
				
			||||
 | 
				
			||||
   (haskell-lexeme-classify-by-first-char (char-after (match-beginning 1))) | 
				
			||||
 | 
				
			||||
See `haskell-lexeme-classify-by-first-char' for details." | 
				
			||||
  (skip-syntax-forward "->") | 
				
			||||
  (let | 
				
			||||
      ((case-fold-search nil) | 
				
			||||
       (point (point-marker))) | 
				
			||||
    (or | 
				
			||||
     (and (looking-at "{-") | 
				
			||||
          (progn | 
				
			||||
            (save-excursion | 
				
			||||
              (forward-comment 1) | 
				
			||||
              (set-match-data (list point (point-marker)))) | 
				
			||||
            'nested-comment)) | 
				
			||||
     (and (looking-at haskell-lexeme-char-literal) | 
				
			||||
          'char) | 
				
			||||
     (and (looking-at haskell-lexeme-string-literal) | 
				
			||||
          'string) | 
				
			||||
     (and (looking-at "[][(){}`,;]") | 
				
			||||
          'special) | 
				
			||||
     (and (looking-at haskell-lexeme-qid-or-qsym) | 
				
			||||
          (if (and (eq (- (match-end 0) (match-beginning 0)) 2) | 
				
			||||
                   (equal (match-string 0) "--")) | 
				
			||||
              (progn | 
				
			||||
                (set-match-data (list point (set-marker (make-marker) (line-end-position)))) | 
				
			||||
                'comment) | 
				
			||||
            'qsymid)) | 
				
			||||
     (and (looking-at haskell-lexeme-number) | 
				
			||||
          'number) | 
				
			||||
     (and (looking-at "'+") | 
				
			||||
          'template-haskell-quote)))) | 
				
			||||
 | 
				
			||||
(provide 'haskell-lexeme) | 
				
			||||
 | 
				
			||||
;;; haskell-lexeme.el ends here | 
				
			||||
@ -1,529 +0,0 @@
					 | 
				
			||||
;;; haskell-load.el --- Compiling and loading modules in the GHCi process -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright (c) 2014 Chris Done. All rights reserved. | 
				
			||||
 | 
				
			||||
;; 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/>. | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'cl-lib) | 
				
			||||
(require 'haskell-process) | 
				
			||||
(require 'haskell-interactive-mode) | 
				
			||||
(require 'haskell-modules) | 
				
			||||
(require 'haskell-commands) | 
				
			||||
(require 'haskell-session) | 
				
			||||
 | 
				
			||||
(defun haskell-process-look-config-changes (session) | 
				
			||||
  "Checks whether a cabal configuration file has | 
				
			||||
changed. Restarts the process if that is the case." | 
				
			||||
  (let ((current-checksum (haskell-session-get session 'cabal-checksum)) | 
				
			||||
        (new-checksum (haskell-cabal-compute-checksum | 
				
			||||
                       (haskell-session-get session 'cabal-dir)))) | 
				
			||||
    (when (not (string= current-checksum new-checksum)) | 
				
			||||
      (haskell-interactive-mode-echo session (format "Cabal file changed: %s" new-checksum)) | 
				
			||||
      (haskell-session-set-cabal-checksum session | 
				
			||||
                                          (haskell-session-get session 'cabal-dir)) | 
				
			||||
      (unless (and haskell-process-prompt-restart-on-cabal-change | 
				
			||||
                   (not (y-or-n-p "Cabal file changed; restart GHCi process? "))) | 
				
			||||
        (haskell-process-start (haskell-interactive-session)))))) | 
				
			||||
 | 
				
			||||
(defun haskell-process-live-build (process buffer echo-in-repl) | 
				
			||||
  "Show live updates for loading files." | 
				
			||||
  (cond ((haskell-process-consume | 
				
			||||
          process | 
				
			||||
          (concat "\\[[ ]*\\([0-9]+\\) of \\([0-9]+\\)\\]" | 
				
			||||
                  " Compiling \\([^ ]+\\)[ ]+" | 
				
			||||
                  "( \\([^ ]+\\), \\([^ ]+\\) )[^\r\n]*[\r\n]+")) | 
				
			||||
         (haskell-process-echo-load-message process buffer echo-in-repl nil) | 
				
			||||
         t) | 
				
			||||
        ((haskell-process-consume | 
				
			||||
          process | 
				
			||||
          (concat "\\[[ ]*\\([0-9]+\\) of \\([0-9]+\\)\\]" | 
				
			||||
                  " Compiling \\[TH\\] \\([^ ]+\\)[ ]+" | 
				
			||||
                  "( \\([^ ]+\\), \\([^ ]+\\) )[^\r\n]*[\r\n]+")) | 
				
			||||
         (haskell-process-echo-load-message process buffer echo-in-repl t) | 
				
			||||
         t) | 
				
			||||
        ((haskell-process-consume process "Loading package \\([^ ]+\\) ... linking ... done.\n") | 
				
			||||
         (haskell-mode-message-line | 
				
			||||
          (format "Loading: %s" | 
				
			||||
                  (match-string 1 buffer))) | 
				
			||||
         t) | 
				
			||||
        ((haskell-process-consume | 
				
			||||
          process | 
				
			||||
          "^Preprocessing executables for \\(.+?\\)\\.\\.\\.") | 
				
			||||
         (let ((msg (format "Preprocessing: %s" (match-string 1 buffer)))) | 
				
			||||
           (haskell-interactive-mode-echo | 
				
			||||
            (haskell-process-session process) | 
				
			||||
            msg) | 
				
			||||
           (haskell-mode-message-line msg))) | 
				
			||||
        ((haskell-process-consume process "Linking \\(.+?\\) \\.\\.\\.") | 
				
			||||
         (let ((msg (format "Linking: %s" (match-string 1 buffer)))) | 
				
			||||
           (haskell-interactive-mode-echo (haskell-process-session process) msg) | 
				
			||||
           (haskell-mode-message-line msg))) | 
				
			||||
        ((haskell-process-consume process "\nBuilding \\(.+?\\)\\.\\.\\.") | 
				
			||||
         (let ((msg (format "Building: %s" (match-string 1 buffer)))) | 
				
			||||
           (haskell-interactive-mode-echo | 
				
			||||
            (haskell-process-session process) | 
				
			||||
            msg) | 
				
			||||
           (haskell-mode-message-line msg))))) | 
				
			||||
 | 
				
			||||
(defun haskell-process-load-complete (session process buffer reload module-buffer &optional cont) | 
				
			||||
  "Handle the complete loading response. BUFFER is the string of | 
				
			||||
text being sent over the process pipe. MODULE-BUFFER is the | 
				
			||||
actual Emacs buffer of the module being loaded." | 
				
			||||
  (when (get-buffer (format "*%s:splices*" (haskell-session-name session))) | 
				
			||||
    (with-current-buffer (haskell-interactive-mode-splices-buffer session) | 
				
			||||
      (erase-buffer))) | 
				
			||||
  (let* ((ok (cond ((haskell-process-consume process "Ok, modules loaded: \\(.+\\)\\.$")       t) | 
				
			||||
		   ((haskell-process-consume process "Failed, modules loaded: \\(.+\\)\\.$") nil) | 
				
			||||
		   (t (error (message "Unexpected response from haskell process."))))) | 
				
			||||
	 (modules (haskell-process-extract-modules buffer)) | 
				
			||||
	 (cursor (haskell-process-response-cursor process)) | 
				
			||||
	 (warning-count 0)) | 
				
			||||
    (haskell-process-set-response-cursor process 0) | 
				
			||||
    (haskell-check-remove-overlays module-buffer) | 
				
			||||
    (while (haskell-process-errors-warnings module-buffer session process buffer) | 
				
			||||
      (setq warning-count (1+ warning-count))) | 
				
			||||
    (haskell-process-set-response-cursor process cursor) | 
				
			||||
    (if (and (not reload) | 
				
			||||
	     haskell-process-reload-with-fbytecode) | 
				
			||||
	(haskell-process-reload-with-fbytecode process module-buffer) | 
				
			||||
	(haskell-process-import-modules process (car modules))) | 
				
			||||
    (if ok | 
				
			||||
	(haskell-mode-message-line (if reload "Reloaded OK." "OK.")) | 
				
			||||
	(haskell-interactive-mode-compile-error session "Compilation failed.")) | 
				
			||||
    (when cont | 
				
			||||
      (condition-case e | 
				
			||||
	  (funcall cont ok) | 
				
			||||
	(error (message "%S" e)) | 
				
			||||
	(quit nil))))) | 
				
			||||
 | 
				
			||||
(defun haskell-process-suggest-imports (session file modules ident) | 
				
			||||
  "Given a list of MODULES, suggest adding them to the import section." | 
				
			||||
  (cl-assert session) | 
				
			||||
  (cl-assert file) | 
				
			||||
  (cl-assert ident) | 
				
			||||
  (let* ((process (haskell-session-process session)) | 
				
			||||
         (suggested-already (haskell-process-suggested-imports process)) | 
				
			||||
         (module (cond ((> (length modules) 1) | 
				
			||||
                        (when (y-or-n-p (format "Identifier `%s' not in scope, choose module to import?" | 
				
			||||
                                                ident)) | 
				
			||||
                          (haskell-complete-module-read "Module: " modules))) | 
				
			||||
                       ((= (length modules) 1) | 
				
			||||
                        (let ((module (car modules))) | 
				
			||||
                          (unless (member module suggested-already) | 
				
			||||
                            (haskell-process-set-suggested-imports process (cons module suggested-already)) | 
				
			||||
                            (when (y-or-n-p (format "Identifier `%s' not in scope, import `%s'?" | 
				
			||||
                                                    ident | 
				
			||||
                                                    module)) | 
				
			||||
                              module))))))) | 
				
			||||
    (when module | 
				
			||||
      (haskell-process-find-file session file) | 
				
			||||
      (haskell-add-import module)))) | 
				
			||||
 | 
				
			||||
(defun haskell-process-trigger-suggestions (session msg file line) | 
				
			||||
  "Trigger prompting to add any extension suggestions." | 
				
			||||
  (cond ((let ((case-fold-search nil)) | 
				
			||||
           (or (and (string-match " -X\\([A-Z][A-Za-z]+\\)" msg) | 
				
			||||
                    (not (string-match "\\([A-Z][A-Za-z]+\\) is deprecated" msg))) | 
				
			||||
               (string-match "Use \\([A-Z][A-Za-z]+\\) to permit this" msg) | 
				
			||||
               (string-match "Use \\([A-Z][A-Za-z]+\\) to allow" msg) | 
				
			||||
               (string-match "use \\([A-Z][A-Za-z]+\\)" msg) | 
				
			||||
               (string-match "You need \\([A-Z][A-Za-z]+\\)" msg))) | 
				
			||||
         (when haskell-process-suggest-language-pragmas | 
				
			||||
           (haskell-process-suggest-pragma session "LANGUAGE" (match-string 1 msg) file))) | 
				
			||||
        ((string-match " The \\(qualified \\)?import of[ ][‘`‛]\\([^ ]+\\)['’] is redundant" msg) | 
				
			||||
         (when haskell-process-suggest-remove-import-lines | 
				
			||||
           (haskell-process-suggest-remove-import session | 
				
			||||
                                                  file | 
				
			||||
                                                  (match-string 2 msg) | 
				
			||||
                                                  line))) | 
				
			||||
        ((string-match "Warning: orphan instance: " msg) | 
				
			||||
         (when haskell-process-suggest-no-warn-orphans | 
				
			||||
           (haskell-process-suggest-pragma session "OPTIONS" "-fno-warn-orphans" file))) | 
				
			||||
        ((or (string-match "against inferred type [‘`‛]\\[Char\\]['’]" msg) | 
				
			||||
             (string-match "with actual type [‘`‛]\\[Char\\]['’]" msg)) | 
				
			||||
         (when haskell-process-suggest-overloaded-strings | 
				
			||||
           (haskell-process-suggest-pragma session "LANGUAGE" "OverloadedStrings" file))) | 
				
			||||
        ((string-match "^Not in scope: .*[‘`‛]\\(.+\\)['’]$" msg) | 
				
			||||
         (let* ((match1 (match-string 1 msg)) | 
				
			||||
                (ident (if (string-match "^[A-Za-z0-9_'.]+\\.\\(.+\\)$" match1) | 
				
			||||
                           ;; Skip qualification. | 
				
			||||
                           (match-string 1 match1) | 
				
			||||
                         match1))) | 
				
			||||
           (when haskell-process-suggest-hoogle-imports | 
				
			||||
             (let ((modules (haskell-process-hoogle-ident ident))) | 
				
			||||
               (haskell-process-suggest-imports session file modules ident))) | 
				
			||||
           (when haskell-process-suggest-haskell-docs-imports | 
				
			||||
             (let ((modules (haskell-process-haskell-docs-ident ident))) | 
				
			||||
               (haskell-process-suggest-imports session file modules ident))) | 
				
			||||
           (when haskell-process-suggest-hayoo-imports | 
				
			||||
             (let ((modules (haskell-process-hayoo-ident ident))) | 
				
			||||
               (haskell-process-suggest-imports session file modules ident))))) | 
				
			||||
        ((string-match "^[ ]+It is a member of the hidden package [‘`‛]\\([^@\r\n]+\\).*['’].$" msg) | 
				
			||||
         (when haskell-process-suggest-add-package | 
				
			||||
           (haskell-process-suggest-add-package session msg))))) | 
				
			||||
 | 
				
			||||
(defun haskell-process-do-cabal (command) | 
				
			||||
  "Run a Cabal command." | 
				
			||||
  (let ((process (haskell-interactive-process))) | 
				
			||||
    (cond | 
				
			||||
     ((let ((child (haskell-process-process process))) | 
				
			||||
        (not (equal 'run (process-status child)))) | 
				
			||||
      (message "Process is not running, so running directly.") | 
				
			||||
      (shell-command (concat "cabal " command) | 
				
			||||
                     (get-buffer-create "*haskell-process-log*") | 
				
			||||
                     (get-buffer-create "*haskell-process-log*")) | 
				
			||||
      (switch-to-buffer-other-window (get-buffer "*haskell-process-log*"))) | 
				
			||||
     (t (haskell-process-queue-command | 
				
			||||
         process | 
				
			||||
         (make-haskell-command | 
				
			||||
          :state (list (haskell-interactive-session) process command 0) | 
				
			||||
 | 
				
			||||
          :go | 
				
			||||
          (lambda (state) | 
				
			||||
            (haskell-process-send-string | 
				
			||||
             (cadr state) | 
				
			||||
             (format haskell-process-do-cabal-format-string | 
				
			||||
                     (haskell-session-cabal-dir (car state)) | 
				
			||||
                     (format "%s %s" | 
				
			||||
                             (cl-ecase (haskell-process-type) | 
				
			||||
                               ('ghci haskell-process-path-cabal) | 
				
			||||
                               ('cabal-repl haskell-process-path-cabal) | 
				
			||||
                               ('cabal-ghci haskell-process-path-cabal) | 
				
			||||
                               ('stack-ghci haskell-process-path-stack)) | 
				
			||||
                             (cl-caddr state))))) | 
				
			||||
 | 
				
			||||
          :live | 
				
			||||
          (lambda (state buffer) | 
				
			||||
            (let ((cmd (replace-regexp-in-string "^\\([a-z]+\\).*" | 
				
			||||
                                                 "\\1" | 
				
			||||
                                                 (cl-caddr state)))) | 
				
			||||
              (cond ((or (string= cmd "build") | 
				
			||||
                         (string= cmd "install")) | 
				
			||||
                     (haskell-process-live-build (cadr state) buffer t)) | 
				
			||||
                    (t | 
				
			||||
                     (haskell-process-cabal-live state buffer))))) | 
				
			||||
 | 
				
			||||
          :complete | 
				
			||||
          (lambda (state response) | 
				
			||||
            (let* ((process (cadr state)) | 
				
			||||
                   (session (haskell-process-session process)) | 
				
			||||
                   (message-count 0) | 
				
			||||
                   (cursor (haskell-process-response-cursor process))) | 
				
			||||
	      ;; XXX: what the hell about the rampant code duplication? | 
				
			||||
              (haskell-process-set-response-cursor process 0) | 
				
			||||
              (while (haskell-process-errors-warnings nil session process response) | 
				
			||||
                (setq message-count (1+ message-count))) | 
				
			||||
              (haskell-process-set-response-cursor process cursor) | 
				
			||||
              (let ((msg (format "Complete: cabal %s (%s compiler messages)" | 
				
			||||
                                 (cl-caddr state) | 
				
			||||
                                 message-count))) | 
				
			||||
                (haskell-interactive-mode-echo session msg) | 
				
			||||
                (when (= message-count 0) | 
				
			||||
                  (haskell-interactive-mode-echo | 
				
			||||
                   session | 
				
			||||
                   "No compiler messages, dumping complete output:") | 
				
			||||
                  (haskell-interactive-mode-echo session response)) | 
				
			||||
                (haskell-mode-message-line msg) | 
				
			||||
                (when (and haskell-notify-p | 
				
			||||
                           (fboundp 'notifications-notify)) | 
				
			||||
                  (notifications-notify | 
				
			||||
                   :title (format "*%s*" (haskell-session-name (car state))) | 
				
			||||
                   :body msg | 
				
			||||
                   :app-name (cl-ecase (haskell-process-type) | 
				
			||||
                               ('ghci haskell-process-path-cabal) | 
				
			||||
                               ('cabal-repl haskell-process-path-cabal) | 
				
			||||
                               ('cabal-ghci haskell-process-path-cabal) | 
				
			||||
                               ('stack-ghci haskell-process-path-stack)) | 
				
			||||
                   :app-icon haskell-process-logo))))))))))) | 
				
			||||
 | 
				
			||||
(defun haskell-process-echo-load-message (process buffer echo-in-repl th) | 
				
			||||
  "Echo a load message." | 
				
			||||
  (let ((session (haskell-process-session process)) | 
				
			||||
        (module-name (match-string 3 buffer)) | 
				
			||||
        (file-name (match-string 4 buffer))) | 
				
			||||
    (haskell-interactive-show-load-message | 
				
			||||
     session | 
				
			||||
     'compiling | 
				
			||||
     module-name | 
				
			||||
     (haskell-session-strip-dir session file-name) | 
				
			||||
     echo-in-repl | 
				
			||||
     th))) | 
				
			||||
 | 
				
			||||
(defun haskell-process-extract-modules (buffer) | 
				
			||||
  "Extract the modules from the process buffer." | 
				
			||||
  (let* ((modules-string (match-string 1 buffer)) | 
				
			||||
         (modules (split-string modules-string ", "))) | 
				
			||||
    (cons modules modules-string))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defface haskell-error-face | 
				
			||||
  '((((supports :underline (:style wave))) | 
				
			||||
     :underline (:style wave :color "#dc322f")) | 
				
			||||
    (t | 
				
			||||
     :inherit error)) | 
				
			||||
  "Face used for marking error lines." | 
				
			||||
  :group 'haskell-mode) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defface haskell-warning-face | 
				
			||||
  '((((supports :underline (:style wave))) | 
				
			||||
     :underline (:style wave :color "#b58900")) | 
				
			||||
    (t | 
				
			||||
     :inherit warning)) | 
				
			||||
  "Face used for marking warning lines." | 
				
			||||
  :group 'haskell-mode) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defface haskell-hole-face | 
				
			||||
  '((((supports :underline (:style wave))) | 
				
			||||
     :underline (:style wave :color "#6c71c4")) | 
				
			||||
    (t | 
				
			||||
     :inherit warning)) | 
				
			||||
  "Face used for marking hole lines." | 
				
			||||
  :group 'haskell-mode) | 
				
			||||
 | 
				
			||||
(defvar haskell-check-error-fringe   (propertize "!" 'display '(left-fringe exclamation-mark))) | 
				
			||||
(defvar haskell-check-warning-fringe (propertize "?" 'display '(left-fringe question-mark))) | 
				
			||||
(defvar haskell-check-hole-fringe    (propertize "_" 'display '(left-fringe horizontal-bar))) | 
				
			||||
 | 
				
			||||
(defun haskell-check-overlay-p (ovl) | 
				
			||||
  (overlay-get ovl 'haskell-check)) | 
				
			||||
 | 
				
			||||
(defun haskell-check-filter-overlays (xs) | 
				
			||||
  (cl-remove-if-not 'haskell-check-overlay-p xs)) | 
				
			||||
 | 
				
			||||
(defun haskell-check-remove-overlays (buffer) | 
				
			||||
  (with-current-buffer buffer | 
				
			||||
    (remove-overlays (point-min) (point-max) 'haskell-check t))) | 
				
			||||
 | 
				
			||||
(defmacro with-overlay-properties (proplist ovl &rest body) | 
				
			||||
  "Evaluate BODY with names in PROPLIST bound to the values of | 
				
			||||
correspondingly-named overlay properties of OVL." | 
				
			||||
  (let ((ovlvar (cl-gensym "OVL-"))) | 
				
			||||
    `(let* ((,ovlvar ,ovl) | 
				
			||||
	    ,@(mapcar (lambda (p) `(,p (overlay-get ,ovlvar ',p))) proplist)) | 
				
			||||
       ,@body))) | 
				
			||||
 | 
				
			||||
(defun overlay-start> (o1 o2) | 
				
			||||
  (> (overlay-start o1) (overlay-start o2))) | 
				
			||||
(defun overlay-start< (o1 o2) | 
				
			||||
  (< (overlay-start o1) (overlay-start o2))) | 
				
			||||
 | 
				
			||||
(defun first-overlay-in-if (test beg end) | 
				
			||||
  (let ((ovls (cl-remove-if-not test (overlays-in beg end)))) | 
				
			||||
    (cl-first (sort (cl-copy-list ovls) 'overlay-start<)))) | 
				
			||||
 | 
				
			||||
(defun last-overlay-in-if (test beg end) | 
				
			||||
  (let ((ovls (cl-remove-if-not test (overlays-in beg end)))) | 
				
			||||
    (cl-first (sort (cl-copy-list ovls) 'overlay-start>)))) | 
				
			||||
 | 
				
			||||
(defun haskell-error-overlay-briefly (ovl) | 
				
			||||
  (with-overlay-properties (haskell-msg haskell-msg-type) ovl | 
				
			||||
    (cond ((not (eq haskell-msg-type 'warning)) | 
				
			||||
	   haskell-msg) | 
				
			||||
	  ((string-prefix-p "Warning:\n    " haskell-msg) | 
				
			||||
	   (cl-subseq haskell-msg 13)) | 
				
			||||
	  (t (error "Invariant failed: a warning message from GHC has unexpected form: %s." haskell-msg))))) | 
				
			||||
 | 
				
			||||
(defun haskell-goto-error-overlay (ovl) | 
				
			||||
  (cond (ovl | 
				
			||||
	 (goto-char (overlay-start ovl)) | 
				
			||||
	 (haskell-mode-message-line (haskell-error-overlay-briefly ovl))) | 
				
			||||
	(t | 
				
			||||
	 (message "No further notes from Haskell compiler.")))) | 
				
			||||
 | 
				
			||||
(defun haskell-goto-prev-error () | 
				
			||||
  (interactive) | 
				
			||||
  (haskell-goto-error-overlay | 
				
			||||
   (let ((ovl-at (cl-first (haskell-check-filter-overlays (overlays-at (point)))))) | 
				
			||||
     (or (last-overlay-in-if 'haskell-check-overlay-p | 
				
			||||
			     (point-min) (if ovl-at (overlay-start ovl-at) (point))) | 
				
			||||
	 ovl-at)))) | 
				
			||||
 | 
				
			||||
(defun haskell-goto-next-error () | 
				
			||||
  (interactive) | 
				
			||||
  (haskell-goto-error-overlay | 
				
			||||
   (let ((ovl-at (cl-first (haskell-check-filter-overlays (overlays-at (point)))))) | 
				
			||||
     (or (first-overlay-in-if 'haskell-check-overlay-p | 
				
			||||
			      (if ovl-at (overlay-end ovl-at) (point)) (point-max)) | 
				
			||||
	 ovl-at)))) | 
				
			||||
 | 
				
			||||
(defun haskell-check-paint-overlay (buffer error-from-this-file-p line msg file type hole coln) | 
				
			||||
  (with-current-buffer buffer | 
				
			||||
    (let (beg end) | 
				
			||||
      (goto-char (point-min)) | 
				
			||||
      ;; XXX: we can avoid excess buffer walking by relying on the maybe-fact that | 
				
			||||
      ;;      GHC sorts error messages by line number, maybe. | 
				
			||||
      (cond | 
				
			||||
	(error-from-this-file-p | 
				
			||||
	 (forward-line (1- line)) | 
				
			||||
	 (forward-char (1- coln)) | 
				
			||||
	 (setq beg (point)) | 
				
			||||
	 (if (eq type 'hole) | 
				
			||||
	     (forward-char (length hole)) | 
				
			||||
	     (skip-chars-forward "^[:space:]" (line-end-position))) | 
				
			||||
	 (setq end (point))) | 
				
			||||
	(t | 
				
			||||
	 (setq beg (point)) | 
				
			||||
	 (forward-line) | 
				
			||||
	 (setq end (point)))) | 
				
			||||
      (let ((ovl (make-overlay beg end))) | 
				
			||||
	(overlay-put ovl 'haskell-check t) | 
				
			||||
	(overlay-put ovl 'haskell-file file) | 
				
			||||
	(overlay-put ovl 'haskell-msg msg) | 
				
			||||
	(overlay-put ovl 'haskell-msg-type type) | 
				
			||||
	(overlay-put ovl 'help-echo msg) | 
				
			||||
	(overlay-put ovl 'haskell-hole hole) | 
				
			||||
	(cl-destructuring-bind (face fringe) (cl-case type | 
				
			||||
					       (warning (list 'haskell-warning-face haskell-check-warning-fringe)) | 
				
			||||
					       (hole    (list 'haskell-hole-face    haskell-check-hole-fringe)) | 
				
			||||
					       (error   (list 'haskell-error-face   haskell-check-error-fringe))) | 
				
			||||
	  (overlay-put ovl 'before-string fringe) | 
				
			||||
	  (overlay-put ovl 'face face)))))) | 
				
			||||
 | 
				
			||||
(defun haskell-process-errors-warnings (module-buffer session process buffer &optional return-only) | 
				
			||||
  "Trigger handling type errors or warnings.  Either prints the | 
				
			||||
messages in the interactive buffer or if CONT is specified, | 
				
			||||
passes the error onto that. | 
				
			||||
 | 
				
			||||
When MODULE-BUFFER is non-NIL, paint error overlays." | 
				
			||||
  (save-excursion | 
				
			||||
    (cond | 
				
			||||
      ((haskell-process-consume | 
				
			||||
	process | 
				
			||||
	"\\(Module imports form a cycle:[ \n]+module [^ ]+ ([^)]+)[[:unibyte:][:nonascii:]]+?\\)\nFailed") | 
				
			||||
       (let ((err (match-string 1 buffer))) | 
				
			||||
	 (if (string-match "module [`'‘‛]\\([^ ]+\\)['’`] (\\([^)]+\\))" err) | 
				
			||||
	     (let* ((default-directory (haskell-session-current-dir session)) | 
				
			||||
		    (module (match-string 1 err)) | 
				
			||||
		    (file (match-string 2 err)) | 
				
			||||
		    (relative-file-name (file-relative-name file))) | 
				
			||||
	       (unless return-only | 
				
			||||
		 (haskell-interactive-show-load-message | 
				
			||||
		  session | 
				
			||||
		  'import-cycle | 
				
			||||
		  module | 
				
			||||
		  relative-file-name | 
				
			||||
		  nil | 
				
			||||
		  nil) | 
				
			||||
		 (haskell-interactive-mode-compile-error | 
				
			||||
		  session | 
				
			||||
		  (format "%s:1:0: %s" | 
				
			||||
			  relative-file-name | 
				
			||||
			  err))) | 
				
			||||
	       (list :file file :line 1 :col 0 :msg err :type 'error)) | 
				
			||||
	     t))) | 
				
			||||
      ((haskell-process-consume | 
				
			||||
	process | 
				
			||||
	(concat "[\r\n]\\([A-Z]?:?[^ \r\n:][^:\n\r]+\\):\\([0-9()-:]+\\):" | 
				
			||||
		"[ \n\r]+\\([[:unibyte:][:nonascii:]]+?\\)\n[^ ]")) | 
				
			||||
       (haskell-process-set-response-cursor process | 
				
			||||
					    (- (haskell-process-response-cursor process) 1)) | 
				
			||||
       (let* ((buffer (haskell-process-response process)) | 
				
			||||
	      (file (match-string 1 buffer)) | 
				
			||||
	      (location-raw (match-string 2 buffer)) | 
				
			||||
	      (error-msg (match-string 3 buffer)) | 
				
			||||
	      (type (cond ((string-match "^Warning:" error-msg)  'warning) | 
				
			||||
			  ((string-match "^Splicing " error-msg) 'splice) | 
				
			||||
			  (t                                     'error))) | 
				
			||||
	      (critical (not (eq type 'warning))) | 
				
			||||
	      ;; XXX: extract hole information, pass down to `haskell-check-paint-overlay' | 
				
			||||
	      (final-msg (format "%s:%s: %s" | 
				
			||||
				 (haskell-session-strip-dir session file) | 
				
			||||
				 location-raw | 
				
			||||
				 error-msg)) | 
				
			||||
	      (location (haskell-process-parse-error (concat file ":" location-raw ": x"))) | 
				
			||||
	      (line (plist-get location :line)) | 
				
			||||
	      (col1 (plist-get location :col))) | 
				
			||||
	 (when module-buffer | 
				
			||||
	   (haskell-check-paint-overlay module-buffer (string= (file-truename (buffer-file-name module-buffer)) (file-truename file)) | 
				
			||||
					line error-msg file type nil col1)) | 
				
			||||
	 (if return-only | 
				
			||||
	     (list :file file :line line :col col1 :msg error-msg :type type) | 
				
			||||
	     (progn (funcall (cl-case type | 
				
			||||
			       (warning  'haskell-interactive-mode-compile-warning) | 
				
			||||
			       (splice   'haskell-interactive-mode-compile-splice) | 
				
			||||
			       (error    'haskell-interactive-mode-compile-error)) | 
				
			||||
			     session final-msg) | 
				
			||||
		    (when critical | 
				
			||||
		      (haskell-mode-message-line final-msg)) | 
				
			||||
		    (haskell-process-trigger-suggestions | 
				
			||||
		     session | 
				
			||||
		     error-msg | 
				
			||||
		     file | 
				
			||||
		     (plist-get (haskell-process-parse-error final-msg) :line)) | 
				
			||||
		    t))))))) | 
				
			||||
 | 
				
			||||
(defun haskell-interactive-show-load-message (session type module-name file-name echo th) | 
				
			||||
  "Show the '(Compiling|Loading) X' message." | 
				
			||||
  (let ((msg (concat | 
				
			||||
              (cl-ecase type | 
				
			||||
                ('compiling | 
				
			||||
                 (if haskell-interactive-mode-include-file-name | 
				
			||||
                     (format "Compiling: %s (%s)" module-name file-name) | 
				
			||||
                   (format "Compiling: %s" module-name))) | 
				
			||||
                ('loading (format "Loading: %s" module-name)) | 
				
			||||
                ('import-cycle (format "Module has an import cycle: %s" module-name))) | 
				
			||||
              (if th " [TH]" "")))) | 
				
			||||
    (haskell-mode-message-line msg) | 
				
			||||
    (when haskell-interactive-mode-delete-superseded-errors | 
				
			||||
      (haskell-interactive-mode-delete-compile-messages session file-name)) | 
				
			||||
    (when echo | 
				
			||||
      (haskell-interactive-mode-echo session msg)))) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-process-reload-devel-main () | 
				
			||||
  "Reload the module `DevelMain' and then run | 
				
			||||
`DevelMain.update'. This is for doing live update of the code of | 
				
			||||
servers or GUI applications. Put your development version of the | 
				
			||||
program in `DevelMain', and define `update' to auto-start the | 
				
			||||
program on a new thread, and use the `foreign-store' package to | 
				
			||||
access the running context across :load/:reloads in GHCi." | 
				
			||||
  (interactive) | 
				
			||||
  (with-current-buffer (or (get-buffer "DevelMain.hs") | 
				
			||||
                           (if (y-or-n-p "You need to open a buffer named DevelMain.hs. Find now?") | 
				
			||||
                               (ido-find-file) | 
				
			||||
                             (error "No DevelMain.hs buffer."))) | 
				
			||||
    (let ((session (haskell-interactive-session))) | 
				
			||||
      (let ((process (haskell-interactive-process))) | 
				
			||||
        (haskell-process-queue-command | 
				
			||||
         process | 
				
			||||
         (make-haskell-command | 
				
			||||
          :state (list :session session | 
				
			||||
                       :process process | 
				
			||||
                       :buffer (current-buffer)) | 
				
			||||
          :go (lambda (state) | 
				
			||||
                (haskell-process-send-string (plist-get state ':process) | 
				
			||||
                                             ":l DevelMain")) | 
				
			||||
          :live (lambda (state buffer) | 
				
			||||
                  (haskell-process-live-build (plist-get state ':process) | 
				
			||||
                                              buffer | 
				
			||||
                                              nil)) | 
				
			||||
          :complete (lambda (state response) | 
				
			||||
                      (haskell-process-load-complete | 
				
			||||
                       (plist-get state ':session) | 
				
			||||
                       (plist-get state ':process) | 
				
			||||
                       response | 
				
			||||
                       nil | 
				
			||||
                       (plist-get state ':buffer) | 
				
			||||
                       (lambda (ok) | 
				
			||||
                         (when ok | 
				
			||||
                           (haskell-process-queue-without-filters | 
				
			||||
                            (haskell-interactive-process) | 
				
			||||
                            "DevelMain.update") | 
				
			||||
                           (message "DevelMain updated."))))))))))) | 
				
			||||
 | 
				
			||||
(provide 'haskell-load) | 
				
			||||
@ -1,159 +0,0 @@
					 | 
				
			||||
;;; haskell-menu.el --- A Haskell sessions menu -*- lexical-binding: t -*- | 
				
			||||
 | 
				
			||||
;; Copyright (C) 2013  Chris Done | 
				
			||||
 | 
				
			||||
;; Author: Chris Done <chrisdone@gmail.com> | 
				
			||||
 | 
				
			||||
;; 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 GNU Emacs; see the file COPYING.  If not, write to | 
				
			||||
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 
				
			||||
;; Boston, MA 02110-1301, USA. | 
				
			||||
 | 
				
			||||
;;; Commentary: | 
				
			||||
 | 
				
			||||
;;; Todo: | 
				
			||||
 | 
				
			||||
;;; Code: | 
				
			||||
 | 
				
			||||
(require 'cl-lib) | 
				
			||||
(require 'haskell-compat) | 
				
			||||
(require 'haskell-session) | 
				
			||||
(require 'haskell-process) | 
				
			||||
(require 'haskell-interactive-mode) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defcustom haskell-menu-buffer-name "*haskell-menu*" | 
				
			||||
  "The name of the Haskell session menu buffer" | 
				
			||||
  :group 'haskell-interactive | 
				
			||||
  :type 'string) | 
				
			||||
 | 
				
			||||
;;;###autoload | 
				
			||||
(defun haskell-menu () | 
				
			||||
  "Launch the Haskell sessions menu." | 
				
			||||
  (interactive) | 
				
			||||
  (or (get-buffer haskell-menu-buffer-name) | 
				
			||||
      (with-current-buffer (get-buffer-create haskell-menu-buffer-name) | 
				
			||||
        (haskell-menu-mode))) | 
				
			||||
  (switch-to-buffer-other-window (get-buffer haskell-menu-buffer-name)) | 
				
			||||
  (haskell-menu-revert-function nil nil)) | 
				
			||||
 | 
				
			||||
(define-derived-mode haskell-menu-mode special-mode "Haskell Session Menu" | 
				
			||||
  "Major mode for managing Haskell sessions. | 
				
			||||
Each line describes one session. | 
				
			||||
Letters do not insert themselves; instead, they are commands." | 
				
			||||
  (setq buffer-read-only t) | 
				
			||||
  (set (make-local-variable 'revert-buffer-function) | 
				
			||||
       'haskell-menu-revert-function) | 
				
			||||
  (setq truncate-lines t) | 
				
			||||
  (haskell-menu-revert-function nil t)) | 
				
			||||
 | 
				
			||||
(suppress-keymap haskell-menu-mode-map t) | 
				
			||||
(define-key haskell-menu-mode-map (kbd "n") 'next-line) | 
				
			||||
(define-key haskell-menu-mode-map (kbd "p") 'previous-line) | 
				
			||||
(define-key haskell-menu-mode-map (kbd "RET") 'haskell-menu-mode-ret) | 
				
			||||
 | 
				
			||||
(defun haskell-menu-revert-function (_arg1 _arg2) | 
				
			||||
  "Function to refresh the display." | 
				
			||||
  (let ((buffer-read-only nil) | 
				
			||||
        (orig-line (line-number-at-pos)) | 
				
			||||
        (orig-col (current-column))) | 
				
			||||
    (or (eq buffer-undo-list t) | 
				
			||||
        (setq buffer-undo-list nil)) | 
				
			||||
    (erase-buffer) | 
				
			||||
    (haskell-menu-insert-menu) | 
				
			||||
    (goto-char (point-min)) | 
				
			||||
    (forward-line (1- orig-line)) | 
				
			||||
    (forward-char orig-col))) | 
				
			||||
 | 
				
			||||
(defun haskell-menu-insert-menu () | 
				
			||||
  "Insert the Haskell sessions menu to the current buffer." | 
				
			||||
  (if (null haskell-sessions) | 
				
			||||
      (insert "No Haskell sessions.") | 
				
			||||
    (haskell-menu-tabulate | 
				
			||||
     (list "Name" "PID" "Time" "RSS" "Cabal directory" "Working directory" "Command") | 
				
			||||
     (mapcar (lambda (session) | 
				
			||||
               (let ((process (haskell-process-process (haskell-session-process session)))) | 
				
			||||
                 (cond | 
				
			||||
                  (process | 
				
			||||
                   (let ((id (process-id process))) | 
				
			||||
                     (list (propertize (haskell-session-name session) 'face 'buffer-menu-buffer) | 
				
			||||
                           (if (process-live-p process) (number-to-string id) "-") | 
				
			||||
                           (if (process-live-p process) | 
				
			||||
                               (format-time-string "%H:%M:%S" | 
				
			||||
                                                   (encode-time (cl-caddr (assoc 'etime (process-attributes id))) | 
				
			||||
                                                                0 0 0 0 0)) | 
				
			||||
                             "-") | 
				
			||||
                           (if (process-live-p process) | 
				
			||||
                               (concat (number-to-string (/ (cdr (assoc 'rss (process-attributes id))) | 
				
			||||
                                                            1024)) | 
				
			||||
                                       "MB") | 
				
			||||
                             "-") | 
				
			||||
                           (haskell-session-cabal-dir session) | 
				
			||||
                           (haskell-session-current-dir session) | 
				
			||||
                           (mapconcat 'identity (process-command process) " ")))) | 
				
			||||
                  (t (list (propertize (haskell-session-name session) 'face 'buffer-menu-buffer) | 
				
			||||
                           "—" | 
				
			||||
                           "—" | 
				
			||||
                           "—" | 
				
			||||
                           (haskell-session-cabal-dir session) | 
				
			||||
                           (haskell-session-current-dir session)))))) | 
				
			||||
             haskell-sessions)))) | 
				
			||||
 | 
				
			||||
(defun haskell-menu-tabulate (headings rows) | 
				
			||||
  "Prints a list of lists as a formatted table to the current buffer." | 
				
			||||
  (let* ((columns (length headings)) | 
				
			||||
         (widths (make-list columns 0))) | 
				
			||||
    ;; Calculate column widths. This is kind of hideous. | 
				
			||||
    (dolist (row rows) | 
				
			||||
      (setq widths | 
				
			||||
            (let ((list (list))) | 
				
			||||
              (dotimes (i columns) | 
				
			||||
                (setq list (cons (max (nth i widths) | 
				
			||||
                                      (1+ (length (nth i row))) | 
				
			||||
                                      (1+ (length (nth i headings)))) | 
				
			||||
                                 list))) | 
				
			||||
              (reverse list)))) | 
				
			||||
    ;; Print headings. | 
				
			||||
    (let ((heading (propertize " " 'display '(space :align-to 0)))) | 
				
			||||
      (dotimes (i columns) | 
				
			||||
        (setq heading (concat heading | 
				
			||||
                              (format (concat "%-" (number-to-string (nth i widths)) "s") | 
				
			||||
                                      (nth i headings))))) | 
				
			||||
      (setq header-line-format heading)) | 
				
			||||
    ;; Print tabulated rows. | 
				
			||||
    (dolist (row rows) | 
				
			||||
      (dotimes (i columns) | 
				
			||||
        (insert (format (concat "%-" (number-to-string (nth i widths)) "s") | 
				
			||||
                        (nth i row)))) | 
				
			||||
      (insert "\n")))) | 
				
			||||
 | 
				
			||||
(defun haskell-menu-mode-ret () | 
				
			||||
  "Handle RET key." | 
				
			||||
  (interactive) | 
				
			||||
  (let* ((name (save-excursion | 
				
			||||
                 (goto-char (line-beginning-position)) | 
				
			||||
                 (buffer-substring-no-properties (point) | 
				
			||||
                                                 (progn (search-forward " ") | 
				
			||||
                                                        (forward-char -1) | 
				
			||||
                                                        (point))))) | 
				
			||||
         (session (car (cl-remove-if-not (lambda (session) | 
				
			||||
                                           (string= (haskell-session-name session) | 
				
			||||
                                                    name)) | 
				
			||||
                                         haskell-sessions)))) | 
				
			||||
    (switch-to-buffer (haskell-session-interactive-buffer session)))) | 
				
			||||
 | 
				
			||||
(provide 'haskell-menu) | 
				
			||||
 | 
				
			||||
;;; haskell-menu.el ends here | 
				
			||||
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						@ -1,5 +0,0 @@
					 | 
				
			||||
(define-package "haskell-mode" "20151022.340" "A Haskell editing mode" | 
				
			||||
  '((cl-lib "0.5"))) | 
				
			||||
;; Local Variables: | 
				
			||||
;; no-byte-compile: t | 
				
			||||
;; End: | 
				
			||||
									
										
											File diff suppressed because it is too large
											Load Diff