about summary refs log tree commit diff
path: root/configs/shared/emacs/.emacs.d/elpa/haskell-mode-20180601.143/haskell-interactive-mode.el
diff options
context:
space:
mode:
Diffstat (limited to 'configs/shared/emacs/.emacs.d/elpa/haskell-mode-20180601.143/haskell-interactive-mode.el')
-rw-r--r--configs/shared/emacs/.emacs.d/elpa/haskell-mode-20180601.143/haskell-interactive-mode.el1129
1 files changed, 1129 insertions, 0 deletions
diff --git a/configs/shared/emacs/.emacs.d/elpa/haskell-mode-20180601.143/haskell-interactive-mode.el b/configs/shared/emacs/.emacs.d/elpa/haskell-mode-20180601.143/haskell-interactive-mode.el
new file mode 100644
index 000000000000..c218c6c3facb
--- /dev/null
+++ b/configs/shared/emacs/.emacs.d/elpa/haskell-mode-20180601.143/haskell-interactive-mode.el
@@ -0,0 +1,1129 @@
+;;; haskell-interactive-mode.el --- The interactive Haskell mode -*- lexical-binding: t -*-
+
+;; Copyright © 2011-2012  Chris Done
+;;             2016       Arthur Fayzrakhmanov
+
+;; 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 'haskell-mode)
+(require 'haskell-compile)
+(require 'haskell-process)
+(require 'haskell-session)
+(require 'haskell-font-lock)
+(require 'haskell-presentation-mode)
+(require 'haskell-utils)
+(require 'haskell-string)
+(require 'ansi-color)
+(require 'cl-lib)
+(require 'etags)
+
+(defvar-local haskell-interactive-mode-history-index 0)
+
+(defvar-local haskell-interactive-mode-history (list))
+
+(defvar-local haskell-interactive-mode-old-prompt-start nil
+  "Mark used for the old beginning of the prompt.")
+
+(defun haskell-interactive-prompt-regex ()
+  "Generate a regex for searching for any occurence of the prompt\
+at the beginning of the line.  This should prevent any
+interference with prompts that look like haskell expressions."
+  (concat "^" (regexp-quote haskell-interactive-prompt)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Globals used internally
+
+(declare-function haskell-interactive-kill "haskell")
+
+(defvar haskell-interactive-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map (kbd "RET") 'haskell-interactive-mode-return)
+    (define-key map (kbd "SPC") 'haskell-interactive-mode-space)
+    (define-key map (kbd "C-j") 'haskell-interactive-mode-newline-indent)
+    (define-key map (kbd "C-a") 'haskell-interactive-mode-beginning)
+    (define-key map (kbd "<home>") 'haskell-interactive-mode-beginning)
+    (define-key map (kbd "C-c C-k") 'haskell-interactive-mode-clear)
+    (define-key map (kbd "C-c C-c") 'haskell-process-interrupt)
+    (define-key map (kbd "C-c C-f") 'next-error-follow-minor-mode)
+    (define-key map (kbd "C-c C-z") 'haskell-interactive-switch-back)
+    (define-key map (kbd "M-p") 'haskell-interactive-mode-history-previous)
+    (define-key map (kbd "M-n") 'haskell-interactive-mode-history-next)
+    (define-key map (kbd "C-c C-p") 'haskell-interactive-mode-prompt-previous)
+    (define-key map (kbd "C-c C-n") 'haskell-interactive-mode-prompt-next)
+    (define-key map (kbd "C-<up>") 'haskell-interactive-mode-history-previous)
+    (define-key map (kbd "C-<down>") 'haskell-interactive-mode-history-next)
+    (define-key map (kbd "TAB") 'haskell-interactive-mode-tab)
+    (define-key map (kbd "<C-S-backspace>") 'haskell-interactive-mode-kill-whole-line)
+    map)
+  "Keymap used in `haskell-interactive-mode'.")
+
+(define-derived-mode haskell-interactive-mode fundamental-mode "Interactive-Haskell"
+  "Interactive mode for Haskell.
+
+Key bindings:
+\\{haskell-interactive-mode-map}"
+  :group 'haskell-interactive
+  :syntax-table haskell-mode-syntax-table
+
+  (setq haskell-interactive-mode-history (list))
+  (setq haskell-interactive-mode-history-index 0)
+
+  (setq next-error-function #'haskell-interactive-next-error-function)
+  (add-hook 'completion-at-point-functions
+            #'haskell-interactive-mode-completion-at-point-function nil t)
+  (add-hook 'kill-buffer-hook #'haskell-interactive-kill nil t)
+  (haskell-interactive-mode-prompt))
+
+(defvar haskell-interactive-mode-prompt-start
+  nil
+  "Mark used for the beginning of the prompt.")
+
+(defvar haskell-interactive-mode-result-end
+  nil
+  "Mark used to figure out where the end of the current result output is.
+Used to distinguish betwen user input.")
+
+(defvar-local haskell-interactive-previous-buffer nil
+  "Records the buffer to which `haskell-interactive-switch-back' should jump.
+This is set by `haskell-interactive-switch', and should otherwise
+be nil.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Hooks
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Mode
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Faces
+
+;;;###autoload
+(defface haskell-interactive-face-prompt
+  '((t :inherit font-lock-function-name-face))
+  "Face for the prompt."
+  :group 'haskell-interactive)
+
+;;;###autoload
+(defface haskell-interactive-face-prompt2
+  '((t :inherit font-lock-keyword-face))
+  "Face for the prompt2 in multi-line mode."
+  :group 'haskell-interactive)
+
+;;;###autoload
+(defface haskell-interactive-face-compile-error
+  '((t :inherit compilation-error))
+  "Face for compile errors."
+  :group 'haskell-interactive)
+
+;;;###autoload
+(defface haskell-interactive-face-compile-warning
+  '((t :inherit compilation-warning))
+  "Face for compiler warnings."
+  :group 'haskell-interactive)
+
+;;;###autoload
+(defface haskell-interactive-face-result
+  '((t :inherit font-lock-string-face))
+  "Face for the result."
+  :group 'haskell-interactive)
+
+;;;###autoload
+(defface haskell-interactive-face-garbage
+  '((t :inherit font-lock-string-face))
+  "Face for trailing garbage after a command has completed."
+  :group 'haskell-interactive)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Actions
+
+(defun haskell-interactive-mode-newline-indent ()
+  "Make newline and indent."
+  (interactive)
+  (newline)
+  (indent-to (length haskell-interactive-prompt))
+  (indent-relative))
+
+(defun haskell-interactive-mode-kill-whole-line ()
+  "Kill the whole REPL line."
+  (interactive)
+  (kill-region haskell-interactive-mode-prompt-start
+               (line-end-position)))
+
+(defun haskell-interactive-switch-back ()
+  "Switch back to the buffer from which this interactive buffer was reached."
+  (interactive)
+  (if haskell-interactive-previous-buffer
+      (switch-to-buffer-other-window haskell-interactive-previous-buffer)
+    (message "No previous buffer.")))
+
+(defun haskell-interactive-copy-to-prompt ()
+  "Copy the current line to the prompt, overwriting the current prompt."
+  (interactive)
+  (let ((l (buffer-substring-no-properties (line-beginning-position)
+                                           (line-end-position))))
+    ;; If it looks like the prompt is at the start of the line, chop
+    ;; it off.
+    (when (and (>= (length l) (length haskell-interactive-prompt))
+               (string= (substring l 0 (length haskell-interactive-prompt))
+                        haskell-interactive-prompt))
+      (setq l (substring l (length haskell-interactive-prompt))))
+
+    (haskell-interactive-mode-set-prompt l)))
+
+(defun haskell-interactive-mode-space (n)
+  "Handle the space key."
+  (interactive "p")
+  (if (and (bound-and-true-p god-local-mode)
+           (fboundp 'god-mode-self-insert))
+      (call-interactively 'god-mode-self-insert)
+    (if (haskell-interactive-at-compile-message)
+        (next-error-no-select 0)
+      (self-insert-command n))))
+
+(defun haskell-interactive-at-prompt (&optional end-line)
+  "If at prompt, return start position of user-input, otherwise return nil.
+If END-LINE is non-nil, then return non-nil when the end of line
+is at the prompt."
+  (if (>= (if end-line (line-end-position) (point))
+          haskell-interactive-mode-prompt-start)
+      haskell-interactive-mode-prompt-start
+    nil))
+
+(define-derived-mode haskell-error-mode
+  special-mode "Error"
+  "Major mode for viewing Haskell compile errors.")
+
+;; (define-key haskell-error-mode-map (kbd "q") 'quit-window)
+
+(defun haskell-interactive-mode-handle-h ()
+  "Handle ^H in output."
+  (let ((bound (point-min))
+        (inhibit-read-only t))
+    (save-excursion
+      (while (search-backward "\b" bound t 1)
+        (save-excursion
+          (forward-char)
+          (let ((end (point)))
+            (if (search-backward-regexp "[^\b]" bound t 1)
+                (forward-char)
+              (goto-char (point-min)))
+            (let ((start (point)))
+              (delete-region (max (- (point) (- end start))
+                                  (point-min))
+                             end))))))))
+
+(defun haskell-interactive-mode-multi-line (expr)
+  "If a multi-line expression EXPR has been entered, then reformat it to be:
+
+:{
+do the
+   multi-liner
+   expr
+:}"
+  (if (not (string-match-p "\n" expr))
+      expr
+    (let ((pre (format "^%s" (regexp-quote haskell-interactive-prompt)))
+          (lines (split-string expr "\n")))
+      (cl-loop for elt on (cdr lines) do
+               (setcar elt (replace-regexp-in-string pre "" (car elt))))
+      ;; Temporarily set prompt2 to be empty to avoid unwanted output
+      (concat ":set prompt2 \"\"\n"
+              ":{\n"
+              (mapconcat #'identity lines "\n")
+              "\n:}\n"
+              (format ":set prompt2 \"%s\"" haskell-interactive-prompt2)))))
+
+(defun haskell-interactive-mode-line-is-query (line)
+  "Is LINE actually a :t/:k/:i?"
+  (and (string-match "^:[itk] " line)
+       t))
+
+(defun haskell-interactive-mode-beginning ()
+  "Go to the start of the line."
+  (interactive)
+  (if (haskell-interactive-at-prompt)
+      (goto-char haskell-interactive-mode-prompt-start)
+    (move-beginning-of-line nil)))
+
+(defun haskell-interactive-mode-input-partial ()
+  "Get the interactive mode input up to point."
+  (let ((input-start (haskell-interactive-at-prompt)))
+    (unless input-start
+      (error "not at prompt"))
+    (buffer-substring-no-properties input-start (point))))
+
+(defun haskell-interactive-mode-input ()
+  "Get the interactive mode input."
+  (buffer-substring-no-properties
+   haskell-interactive-mode-prompt-start
+   (point-max)))
+
+(defun haskell-interactive-mode-prompt (&optional session)
+  "Show a prompt at the end of the REPL buffer.
+If SESSION is non-nil, use the REPL buffer associated with
+SESSION, otherwise operate on the current buffer."
+  (with-current-buffer (if session
+                           (haskell-session-interactive-buffer session)
+                         (current-buffer))
+    (save-excursion
+      (goto-char (point-max))
+      (let ((prompt (propertize haskell-interactive-prompt
+                                'font-lock-face 'haskell-interactive-face-prompt
+                                'prompt t
+                                'read-only haskell-interactive-prompt-read-only
+                                'rear-nonsticky t)))
+        ;; At the time of writing, front-stickying the first char gives an error
+        ;; Has unfortunate side-effect of being able to insert before the prompt
+        (insert (substring prompt 0 1)
+                (propertize (substring prompt 1)
+                            'front-sticky t)))
+      (let ((marker (setq-local haskell-interactive-mode-prompt-start (make-marker))))
+        (set-marker marker (point))))
+    (when (haskell-interactive-at-prompt t)
+      (haskell-interactive-mode-scroll-to-bottom))))
+
+(defun haskell-interactive-mode-eval-result (session text)
+  "Insert the result of an eval as plain text."
+  (with-current-buffer (haskell-session-interactive-buffer session)
+    (let ((at-end (eobp))
+          (prop-text (propertize text
+                                 'font-lock-face 'haskell-interactive-face-result
+                                 'front-sticky t
+                                 'prompt t
+                                 'read-only haskell-interactive-mode-read-only
+                                 'rear-nonsticky t
+                                 'result t)))
+      (save-excursion
+        (goto-char (point-max))
+        (when (string= text haskell-interactive-prompt2)
+          (setq prop-text
+                (propertize prop-text
+                            'font-lock-face 'haskell-interactive-face-prompt2
+                            'read-only haskell-interactive-prompt-read-only)))
+        (insert (ansi-color-apply prop-text))
+        (haskell-interactive-mode-handle-h)
+        (let ((marker (setq-local haskell-interactive-mode-result-end (make-marker))))
+          (set-marker marker (point))))
+      (when at-end
+        (haskell-interactive-mode-scroll-to-bottom)))))
+
+(defun haskell-interactive-mode-scroll-to-bottom ()
+  "Scroll to bottom."
+  (let ((w (get-buffer-window (current-buffer))))
+    (when w
+      (goto-char (point-max))
+      (set-window-point w (point)))))
+
+(defun haskell-interactive-mode-compile-error (session message)
+  "Echo an error."
+  (haskell-interactive-mode-compile-message
+   session message 'haskell-interactive-face-compile-error))
+
+(defun haskell-interactive-mode-compile-warning (session message)
+  "Warning message."
+  (haskell-interactive-mode-compile-message
+   session message 'haskell-interactive-face-compile-warning))
+
+(defun haskell-interactive-mode-compile-message (session message type)
+  "Echo a compiler warning."
+  (with-current-buffer (haskell-session-interactive-buffer session)
+    (setq next-error-last-buffer (current-buffer))
+    (save-excursion
+      (haskell-interactive-mode-goto-end-point)
+      (let ((lines (string-match "^\\(.*\\)\n\\([[:unibyte:][:nonascii:]]+\\)" message)))
+        (if lines
+            (progn
+              (insert (propertize (concat (match-string 1 message) " …\n")
+                                  'expandable t
+                                  'font-lock-face type
+                                  'front-sticky t
+                                  'read-only haskell-interactive-mode-read-only
+                                  'rear-nonsticky t))
+              (insert (propertize (concat (match-string 2 message) "\n")
+                                  'collapsible t
+                                  'font-lock-face type
+                                  'front-sticky t
+                                  'invisible haskell-interactive-mode-hide-multi-line-errors
+                                  'message-length (length (match-string 2 message))
+                                  'read-only haskell-interactive-mode-read-only
+                                  'rear-nonsticky t)))
+          (insert (propertize (concat message "\n")
+                              'font-lock-face type
+                              'front-sticky t
+                              'read-only haskell-interactive-mode-read-only
+                              'rear-nonsticky t)))))))
+
+(defun haskell-interactive-mode-insert (session message)
+  "Echo a read only piece of text before the prompt."
+  (with-current-buffer (haskell-session-interactive-buffer session)
+    (save-excursion
+      (haskell-interactive-mode-goto-end-point)
+      (insert (propertize message
+                          'front-sticky t
+                          'read-only t
+                          'rear-nonsticky t)))))
+
+(defun haskell-interactive-mode-goto-end-point ()
+  "Go to the 'end' of the buffer (before the prompt)."
+  (goto-char haskell-interactive-mode-prompt-start)
+  (goto-char (line-beginning-position)))
+
+(defun haskell-interactive-mode-history-add (input)
+  "Add INPUT to the history."
+  (setq haskell-interactive-mode-history
+        (cons ""
+              (cons input
+                    (cl-remove-if (lambda (i) (or (string= i input) (string= i "")))
+                                  haskell-interactive-mode-history))))
+  (setq haskell-interactive-mode-history-index
+        0))
+
+(defun haskell-interactive-mode-tab ()
+  "Do completion if at prompt or else try collapse/expand."
+  (interactive)
+  (cond
+   ((haskell-interactive-at-prompt)
+    (completion-at-point))
+   ((get-text-property (point) 'collapsible)
+    (let ((column (current-column)))
+      (search-backward-regexp "^[^ ]")
+      (haskell-interactive-mode-tab-expand)
+      (goto-char (+ column (line-beginning-position)))))
+   (t (haskell-interactive-mode-tab-expand))))
+
+(defun haskell-interactive-mode-tab-expand ()
+  "Expand the rest of the message."
+  (cond ((get-text-property (point) 'expandable)
+         (let* ((pos (1+ (line-end-position)))
+                (visibility (get-text-property pos 'invisible))
+                (length (1+ (get-text-property pos 'message-length))))
+           (let ((inhibit-read-only t))
+             (put-text-property pos
+                                (+ pos length)
+                                'invisible
+                                (not visibility)))))))
+
+(defconst haskell-interactive-mode-error-regexp
+  "^\\(\\(?:[A-Z]:\\)?[^ \r\n:][^\r\n:]*\\):\\([0-9()-:]+\\):?")
+
+(defun haskell-interactive-at-compile-message ()
+  "Am I on a compile message?"
+  (and (not (haskell-interactive-at-prompt))
+       (save-excursion
+         (goto-char (line-beginning-position))
+         (looking-at haskell-interactive-mode-error-regexp))))
+
+(defun haskell-interactive-mode-error-backward (&optional count)
+  "Go backward to the previous error."
+  (interactive)
+  (search-backward-regexp haskell-interactive-mode-error-regexp nil t count))
+
+(defun haskell-interactive-mode-error-forward (&optional count)
+  "Go forward to the next error, or return to the REPL."
+  (interactive)
+  (goto-char (line-end-position))
+  (if (search-forward-regexp haskell-interactive-mode-error-regexp nil t count)
+      (progn (goto-char (line-beginning-position))
+             t)
+    (progn (goto-char (point-max))
+           nil)))
+
+(defun haskell-interactive-mode-delete-compile-messages (session &optional file-name)
+  "Delete compile messages in REPL buffer.
+If FILE-NAME is non-nil, restrict to removing messages concerning
+FILE-NAME only."
+  (with-current-buffer (haskell-session-interactive-buffer session)
+    (save-excursion
+      (goto-char (point-min))
+      (when (search-forward-regexp "^Compilation failed.$" nil t 1)
+        (let ((inhibit-read-only t))
+          (delete-region (line-beginning-position)
+                         (1+ (line-end-position))))
+        (goto-char (point-min)))
+      (while (when (re-search-forward haskell-interactive-mode-error-regexp nil t)
+               (let ((msg-file-name (match-string-no-properties 1))
+                     (msg-startpos (line-beginning-position)))
+                 ;; skip over hanging continuation message lines
+                 (while (progn (forward-line) (looking-at "^[ ]+")))
+
+                 (when (or (not file-name) (string= file-name msg-file-name))
+                   (let ((inhibit-read-only t))
+                     (set-text-properties msg-startpos (point) nil))
+                   (delete-region msg-startpos (point))
+                   ))
+               t)))))
+
+;;;###autoload
+(defun haskell-interactive-mode-reset-error (session)
+  "Reset the error cursor position."
+  (interactive)
+  (with-current-buffer (haskell-session-interactive-buffer session)
+    (haskell-interactive-mode-goto-end-point)
+    (let ((mrk (point-marker)))
+      (haskell-session-set session 'next-error-locus nil)
+      (haskell-session-set session 'next-error-region (cons mrk (copy-marker mrk t))))
+    (goto-char (point-max))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Misc
+
+(declare-function haskell-interactive-switch "haskell")
+(declare-function haskell-session "haskell")
+
+(defun haskell-session-interactive-buffer (s)
+  "Get the session interactive buffer."
+  (let ((buffer (haskell-session-get s 'interactive-buffer)))
+    (if (and buffer (buffer-live-p buffer))
+        buffer
+      (let ((buffer-name (format "*%s*" (haskell-session-name s)))
+            (index 0))
+        (while (get-buffer buffer-name)
+          (setq buffer-name (format "*%s <%d>*" (haskell-session-name s) index))
+          (setq index (1+ index)))
+        (let ((buffer (get-buffer-create buffer-name)))
+          (haskell-session-set-interactive-buffer s buffer)
+          (with-current-buffer buffer
+            (haskell-interactive-mode)
+            (haskell-session-assign s))
+          (haskell-interactive-switch)
+          buffer)))))
+
+(defun haskell-interactive-buffer ()
+  "Get the interactive buffer of the session."
+  (haskell-session-interactive-buffer (haskell-session)))
+
+(defun haskell-process-cabal-live (state buffer)
+  "Do live updates for Cabal processes."
+  (haskell-interactive-mode-insert
+   (haskell-process-session (cadr state))
+   (replace-regexp-in-string
+    haskell-process-prompt-regex
+    ""
+    (substring buffer (cl-cadddr state))))
+  (setf (cl-cdddr state) (list (length buffer)))
+  nil)
+
+(defun haskell-process-parse-error (string)
+  "Parse the line number from the error string STRING."
+  (let ((span nil))
+    (cl-loop for regex
+             in haskell-compilation-error-regexp-alist
+             do (when (string-match (car regex) string)
+                  (setq span
+                        (list :file (match-string 1 string)
+                              :line (string-to-number (match-string 2 string))
+                              :col (string-to-number (match-string 4 string))
+                              :line2 (when (match-string 3 string)
+                                       (string-to-number (match-string 3 string)))
+                              :col2 (when (match-string 5 string)
+                                      (string-to-number (match-string 5 string)))))))
+    span))
+
+(defun haskell-process-suggest-add-package (session msg)
+  "Add the (matched) module to your cabal file.
+Cabal file is selected using SESSION's name, module matching is done in MSG."
+  (let* ((suggested-package (match-string 1 msg))
+         (package-name (replace-regexp-in-string "-[^-]+$" "" suggested-package))
+         (version (progn (string-match "\\([^-]+\\)$" suggested-package)
+                         (match-string 1 suggested-package)))
+         (cabal-file (concat (haskell-session-name session)
+                             ".cabal")))
+    (haskell-mode-toggle-interactive-prompt-state)
+    (unwind-protect
+        (when (y-or-n-p
+               (format "Add `%s' to %s?"
+                       package-name
+                       cabal-file))
+          (haskell-cabal-add-dependency package-name version nil t)
+          (when (y-or-n-p (format "Enable -package %s in the GHCi session?" package-name))
+            (haskell-process-queue-without-filters
+             (haskell-session-process session)
+             (format ":set -package %s" package-name))))
+      (haskell-mode-toggle-interactive-prompt-state t))))
+
+(defun haskell-process-suggest-remove-import (session file import line)
+  "Suggest removing or commenting out import statement.
+Asks user to handle redundant import statement using interactive
+SESSION in specified FILE to remove IMPORT on given LINE."
+  (let ((first t))
+    (haskell-mode-toggle-interactive-prompt-state)
+    (unwind-protect
+        (cl-case (read-event
+                  (propertize (format "%sThe import line `%s' is redundant. Remove? (y, n, c: comment out)  "
+                                      (if (not first)
+                                          "Please answer n, y or c: "
+                                        "")
+                                      import)
+                              'face
+                              'minibuffer-prompt))
+          (?y
+           (haskell-process-find-file session file)
+           (save-excursion
+             (goto-char (point-min))
+             (forward-line (1- line))
+             (goto-char (line-beginning-position))
+             (delete-region (line-beginning-position)
+                            (line-end-position))))
+          (?n
+           (message "Ignoring redundant import %s" import))
+          (?c
+           (haskell-process-find-file session file)
+           (save-excursion
+             (goto-char (point-min))
+             (forward-line (1- line))
+             (goto-char (line-beginning-position))
+             (insert "-- "))))
+      ;; unwind
+      (haskell-mode-toggle-interactive-prompt-state t))))
+
+(defun haskell-process-find-file (session file)
+  "Find the given file in the project."
+  (find-file (cond ((file-exists-p (concat (haskell-session-current-dir session) "/" file))
+                    (concat (haskell-session-current-dir session) "/" file))
+                   ((file-exists-p (concat (haskell-session-cabal-dir session) "/" file))
+                    (concat (haskell-session-cabal-dir session) "/" file))
+                   (t file))))
+
+(defun haskell-process-suggest-pragma (session pragma extension file)
+  "Suggest to add something to the top of the file.
+SESSION is used to search given file.  Adds PRAGMA and EXTENSION
+wrapped in compiler directive at the top of FILE."
+  (let ((string  (format "{-# %s %s #-}" pragma extension)))
+    (haskell-mode-toggle-interactive-prompt-state)
+    (unwind-protect
+        (when (y-or-n-p (format "Add %s to the top of the file? " string))
+          (haskell-process-find-file session file)
+          (save-excursion
+            (goto-char (point-min))
+            (insert (concat string "\n"))))
+      (haskell-mode-toggle-interactive-prompt-state t))))
+
+(defun haskell-interactive-mode-insert-error (response)
+  "Insert an error message."
+  (insert "\n"
+          (haskell-fontify-as-mode
+           response
+           'haskell-mode))
+  (haskell-interactive-mode-prompt))
+
+(defun haskell-interactive-popup-error (response)
+  "Popup an error."
+  (if haskell-interactive-popup-errors
+      (let ((buf (get-buffer-create "*HS-Error*")))
+        (pop-to-buffer buf nil t)
+        (with-current-buffer buf
+
+          (haskell-error-mode)
+          (let ((inhibit-read-only t))
+            (erase-buffer)
+            (insert (propertize response
+                                'font-lock-face
+                                'haskell-interactive-face-compile-error))
+            (goto-char (point-min))
+            (delete-blank-lines)
+            (insert (propertize "-- Hit `q' to close this window.\n\n"
+                                'font-lock-face 'font-lock-comment-face))
+            (save-excursion
+              (goto-char (point-max))
+              (insert (propertize "\n-- To disable popups, customize `haskell-interactive-popup-errors'.\n\n"
+                                  'font-lock-face 'font-lock-comment-face))))))
+    (haskell-interactive-mode-insert-error response)))
+
+(defun haskell-interactive-next-error-function (&optional n reset)
+  "See `next-error-function' for more information."
+
+  (let* ((session (haskell-interactive-session))
+         (next-error-region (haskell-session-get session 'next-error-region))
+         (next-error-locus (haskell-session-get session 'next-error-locus))
+         (reset-locus nil))
+
+    (when (and next-error-region (or reset (and (/= n 0) (not next-error-locus))))
+      (goto-char (car next-error-region))
+      (unless (looking-at haskell-interactive-mode-error-regexp)
+        (haskell-interactive-mode-error-forward))
+
+      (setq reset-locus t)
+      (unless (looking-at haskell-interactive-mode-error-regexp)
+        (error "no errors found")))
+
+    ;; move point if needed
+    (cond
+     (reset-locus nil)
+     ((> n 0) (unless (haskell-interactive-mode-error-forward n)
+                (error "no more errors")))
+
+     ((< n 0) (unless (haskell-interactive-mode-error-backward (- n))
+                (error "no more errors"))))
+
+    (let ((orig-line (buffer-substring-no-properties (line-beginning-position) (line-end-position))))
+
+      (when (string-match haskell-interactive-mode-error-regexp orig-line)
+        (let* ((msgmrk (set-marker (make-marker) (line-beginning-position)))
+               (location (haskell-process-parse-error orig-line))
+               (file (plist-get location :file))
+               (line (plist-get location :line))
+               (col1 (plist-get location :col))
+               (col2 (plist-get location :col2))
+
+               (cabal-relative-file (expand-file-name file (haskell-session-cabal-dir session)))
+               (src-relative-file (expand-file-name file (haskell-session-current-dir session)))
+
+               (real-file (cond ((file-exists-p cabal-relative-file) cabal-relative-file)
+                                ((file-exists-p src-relative-file) src-relative-file))))
+
+          (haskell-session-set session 'next-error-locus msgmrk)
+
+          (if real-file
+              (let ((m1 (make-marker))
+                    (m2 (make-marker)))
+                (with-current-buffer (find-file-noselect real-file)
+                  (save-excursion
+                    (goto-char (point-min))
+                    (forward-line (1- line))
+                    (set-marker m1 (+ col1 (point) -1))
+
+                    (when col2
+                      (set-marker m2 (- (point) col2)))))
+                ;; ...finally select&hilight error locus
+                (compilation-goto-locus msgmrk m1 (and (marker-position m2) m2)))
+            (error "don't know where to find %S" file)))))))
+
+(defun haskell-interactive-session ()
+  "Get the `haskell-session', throw an error if it's not available."
+  (or (haskell-session-maybe)
+      (haskell-session-assign
+       (or (haskell-session-from-buffer)
+           (haskell-session-choose)
+           (error "No session associated with this buffer. Try M-x haskell-session-change or report this as a bug.")))))
+
+(defun haskell-interactive-process ()
+  "Get the Haskell session."
+  (or (haskell-session-process (haskell-interactive-session))
+      (error "No Haskell session/process associated with this
+      buffer. Maybe run M-x haskell-process-restart?")))
+
+(defun haskell-interactive-mode-do-presentation (expr)
+  "Present the given expression EXPR.
+Requires the `present' package to be installed.
+Will automatically import it qualified as Present."
+  (let ((p (haskell-interactive-process)))
+    ;; If Present.code isn't available, we probably need to run the
+    ;; setup.
+    (unless (string-match "^Present" (haskell-process-queue-sync-request p ":t Present.encode"))
+      (haskell-interactive-mode-setup-presentation p))
+    ;; Happily, let statements don't affect the `it' binding in any
+    ;; way, so we can fake it, no pun intended.
+    (let ((error (haskell-process-queue-sync-request
+                  p (concat "let it = Present.asData (" expr ")"))))
+      (if (not (string= "" error))
+          (haskell-interactive-mode-eval-result (haskell-interactive-session) (concat error "\n"))
+        (let ((hash (haskell-interactive-mode-presentation-hash)))
+          (haskell-process-queue-sync-request
+           p (format "let %s = Present.asData (%s)" hash expr))
+          (let* ((presentation (haskell-interactive-mode-present-id
+                                hash
+                                (list 0))))
+            (insert "\n")
+            (haskell-interactive-mode-insert-presentation hash presentation)
+            (haskell-interactive-mode-eval-result (haskell-interactive-session) "\n"))))
+      (haskell-interactive-mode-prompt (haskell-interactive-session)))))
+
+(defun haskell-interactive-mode-present-id (hash id)
+  "Generate a presentation for the current expression at ID."
+  ;; See below for commentary of this statement.
+  (let ((p (haskell-interactive-process)))
+    (haskell-process-queue-without-filters
+     p "let _it = it")
+    (let* ((text (haskell-process-queue-sync-request
+                  p
+                  (format "Present.putStr (Present.encode (Present.fromJust (Present.present (Present.fromJust (Present.fromList [%s])) %s)))"
+                          (mapconcat 'identity (mapcar 'number-to-string id) ",")
+                          hash)))
+           (reply
+            (if (string-match "^*** " text)
+                '((rep nil))
+              (read text))))
+      ;; Not necessary, but nice to restore it to the expression that
+      ;; the user actually typed in.
+      (haskell-process-queue-without-filters
+       p "let it = _it")
+      reply)))
+
+(defun haskell-presentation-present-slot (btn)
+  "The callback to evaluate the slot and present it in place of the button BTN."
+  (let ((id (button-get btn 'presentation-id))
+        (hash (button-get btn 'hash))
+        (parent-rep (button-get btn 'parent-rep))
+        (continuation (button-get btn 'continuation)))
+    (let ((point (point)))
+      (button-put btn 'invisible t)
+      (delete-region (button-start btn) (button-end btn))
+      (haskell-interactive-mode-insert-presentation
+       hash
+       (haskell-interactive-mode-present-id hash id)
+       parent-rep
+       continuation)
+      (when (> (point) point)
+        (goto-char (1+ point))))))
+
+(defun haskell-interactive-mode-presentation-slot (hash slot parent-rep &optional continuation)
+  "Make a slot at point, pointing to ID."
+  (let ((type (car slot))
+        (id (cadr slot)))
+    (if (member (intern type) '(Integer Char Int Float Double))
+        (haskell-interactive-mode-insert-presentation
+         hash
+         (haskell-interactive-mode-present-id hash id)
+         parent-rep
+         continuation)
+      (haskell-interactive-mode-presentation-slot-button slot parent-rep continuation hash))))
+
+(defun haskell-interactive-mode-presentation-slot-button (slot parent-rep continuation hash)
+  (let ((start (point))
+        (type (car slot))
+        (id (cadr slot)))
+    (insert (propertize type 'font-lock-face '(:height 0.8 :underline t :inherit font-lock-comment-face)))
+    (let ((button (make-text-button start (point)
+                                    :type 'haskell-presentation-slot-button)))
+      (button-put button 'hide-on-click t)
+      (button-put button 'presentation-id id)
+      (button-put button 'parent-rep parent-rep)
+      (button-put button 'continuation continuation)
+      (button-put button 'hash hash))))
+
+(defun haskell-interactive-mode-insert-presentation (hash presentation &optional parent-rep continuation)
+  "Insert the presentation, hooking up buttons for each slot."
+  (let* ((rep (cadr (assoc 'rep presentation)))
+         (text (cadr (assoc 'text presentation)))
+         (slots (cadr (assoc 'slots presentation)))
+         (nullary (null slots)))
+    (cond
+     ((string= "integer" rep)
+      (insert (propertize text 'font-lock-face 'font-lock-constant)))
+     ((string= "floating" rep)
+      (insert (propertize text 'font-lock-face 'font-lock-constant)))
+     ((string= "char" rep)
+      (insert (propertize
+               (if (string= "string" parent-rep)
+                   (replace-regexp-in-string "^'\\(.+\\)'$" "\\1" text)
+                 text)
+               'font-lock-face 'font-lock-string-face)))
+     ((string= "tuple" rep)
+      (insert "(")
+      (let ((first t))
+        (cl-loop for slot in slots
+                 do (unless first (insert ","))
+                 do (haskell-interactive-mode-presentation-slot hash slot rep)
+                 do (setq first nil)))
+      (insert ")"))
+     ((string= "list" rep)
+      (if (null slots)
+          (if continuation
+              (progn (delete-char -1)
+                     (delete-indentation))
+            (insert "[]"))
+        (let ((i 0))
+          (unless continuation
+            (insert "["))
+          (let ((start-column (current-column)))
+            (cl-loop for slot in slots
+                     do (haskell-interactive-mode-presentation-slot
+                         hash
+                         slot
+                         rep
+                         (= i (1- (length slots))))
+                     do (when (not (= i (1- (length slots))))
+                          (insert "\n")
+                          (indent-to (1- start-column))
+                          (insert ","))
+                     do (setq i (1+ i))))
+          (unless continuation
+            (insert "]")))))
+     ((string= "string" rep)
+      (unless (string= "string" parent-rep)
+        (insert (propertize "\"" 'font-lock-face 'font-lock-string-face)))
+      (cl-loop for slot in slots
+               do (haskell-interactive-mode-presentation-slot hash slot rep))
+      (unless (string= "string" parent-rep)
+        (insert (propertize "\"" 'font-lock-face 'font-lock-string-face))))
+     ((string= "alg" rep)
+      (when (and parent-rep
+                 (not nullary)
+                 (not (string= "list" parent-rep)))
+        (insert "("))
+      (let ((start-column (current-column)))
+        (insert (propertize text 'font-lock-face 'font-lock-type-face))
+        (cl-loop for slot in slots
+                 do (insert "\n")
+                 do (indent-to (+ 2 start-column))
+                 do (haskell-interactive-mode-presentation-slot hash slot rep)))
+      (when (and parent-rep
+                 (not nullary)
+                 (not (string= "list" parent-rep)))
+        (insert ")")))
+     ((string= "record" rep)
+      (let ((start-column (current-column)))
+        (insert (propertize text 'font-lock-face 'font-lock-type-face)
+                " { ")
+        (cl-loop for field in slots
+                 do (insert "\n")
+                 do (indent-to (+ 2 start-column))
+                 do (let ((name (nth 0 field))
+                          (slot (nth 1 field)))
+                      (insert name " = ")
+                      (haskell-interactive-mode-presentation-slot hash slot rep)))
+        (insert "\n")
+        (indent-to start-column)
+        (insert "}")))
+     ((eq rep nil)
+      (insert (propertize "?" 'font-lock-face 'font-lock-warning)))
+     (t
+      (let ((err "Unable to present! This very likely means Emacs
+is out of sync with the `present' package. You should make sure
+they're both up to date, or report a bug."))
+        (insert err)
+        (error err))))))
+
+(defun haskell-interactive-mode-setup-presentation (p)
+  "Setup the GHCi REPL for using presentations.
+
+Using asynchronous queued commands as opposed to sync at this
+stage, as sync would freeze up the UI a bit, and we actually
+don't care when the thing completes as long as it's soonish."
+  ;; Import dependencies under Present.* namespace
+  (haskell-process-queue-without-filters p "import qualified Data.Maybe as Present")
+  (haskell-process-queue-without-filters p "import qualified Data.ByteString.Lazy as Present")
+  (haskell-process-queue-without-filters p "import qualified Data.AttoLisp as Present")
+  (haskell-process-queue-without-filters p "import qualified Present.ID as Present")
+  (haskell-process-queue-without-filters p "import qualified Present as Present")
+  ;; Make a dummy expression to avoid "Loading package" nonsense
+  (haskell-process-queue-without-filters
+   p "Present.present (Present.fromJust (Present.fromList [0])) ()"))
+
+(defvar haskell-interactive-mode-presentation-hash 0
+  "Counter for the hash.")
+
+(defun haskell-interactive-mode-presentation-hash ()
+  "Generate a presentation hash."
+  (format "_present_%s"
+          (setq haskell-interactive-mode-presentation-hash
+                (1+ haskell-interactive-mode-presentation-hash))))
+
+(define-button-type 'haskell-presentation-slot-button
+  'action 'haskell-presentation-present-slot
+  'follow-link t
+  'help-echo "Click to expand…")
+
+(defun haskell-interactive-mode-history-toggle (n)
+  "Toggle the history N items up or down."
+  (unless (null haskell-interactive-mode-history)
+    (setq haskell-interactive-mode-history-index
+          (mod (+ haskell-interactive-mode-history-index n)
+               (length haskell-interactive-mode-history)))
+    (unless (zerop haskell-interactive-mode-history-index)
+      (message "History item: %d" haskell-interactive-mode-history-index))
+    (haskell-interactive-mode-set-prompt
+     (nth haskell-interactive-mode-history-index
+          haskell-interactive-mode-history))))
+
+(defun haskell-interactive-mode-set-prompt (p)
+  "Set (and overwrite) the current prompt."
+  (with-current-buffer (haskell-session-interactive-buffer (haskell-interactive-session))
+    (goto-char haskell-interactive-mode-prompt-start)
+    (delete-region (point) (point-max))
+    (insert p)))
+
+(defun haskell-interactive-mode-history-previous (arg)
+  "Cycle backwards through input history."
+  (interactive "*p")
+  (when (haskell-interactive-at-prompt)
+    (if (not (zerop arg))
+        (haskell-interactive-mode-history-toggle arg)
+      (setq haskell-interactive-mode-history-index 0)
+      (haskell-interactive-mode-history-toggle 1))))
+
+(defun haskell-interactive-mode-history-next (arg)
+  "Cycle forward through input history."
+  (interactive "*p")
+  (when (haskell-interactive-at-prompt)
+    (if (not (zerop arg))
+        (haskell-interactive-mode-history-toggle (- arg))
+      (setq haskell-interactive-mode-history-index 0)
+      (haskell-interactive-mode-history-toggle -1))))
+
+(defun haskell-interactive-mode-prompt-previous ()
+  "Jump to the previous prompt."
+  (interactive)
+  (let ((prev-prompt-pos
+         (save-excursion
+           (beginning-of-line) ;; otherwise prompt at current line matches
+           (and (search-backward-regexp (haskell-interactive-prompt-regex) nil t)
+                (match-end 0)))))
+    (when prev-prompt-pos (goto-char prev-prompt-pos))))
+
+(defun haskell-interactive-mode-prompt-next ()
+  "Jump to the next prompt."
+  (interactive)
+  (search-forward-regexp (haskell-interactive-prompt-regex) nil t))
+
+(defun haskell-interactive-mode-clear ()
+  "Clear the screen and put any current input into the history."
+  (interactive)
+  (let ((session (haskell-interactive-session)))
+    (with-current-buffer (haskell-session-interactive-buffer session)
+      (let ((inhibit-read-only t))
+        (set-text-properties (point-min) (point-max) nil))
+      (delete-region (point-min) (point-max))
+      (remove-overlays)
+      (haskell-interactive-mode-prompt session)
+      (haskell-session-set session 'next-error-region nil)
+      (haskell-session-set session 'next-error-locus nil))
+    (with-current-buffer (get-buffer-create "*haskell-process-log*")
+      (let ((inhibit-read-only t))
+        (delete-region (point-min) (point-max)))
+      (remove-overlays))))
+
+(defun haskell-interactive-mode-completion-at-point-function ()
+  "Offer completions for partial expression between prompt and point.
+This completion function is used in interactive REPL buffer itself."
+  (when (haskell-interactive-at-prompt)
+    (let* ((process (haskell-interactive-process))
+           (inp (haskell-interactive-mode-input-partial))
+           (resp2 (haskell-process-get-repl-completions process inp))
+           (rlen (-  (length inp) (length (car resp2))))
+           (coll (append (if (string-prefix-p inp "import") '("import"))
+                         (if (string-prefix-p inp "let") '("let"))
+                         (cdr resp2))))
+      (list (- (point) rlen) (point) coll))))
+
+(defun haskell-interactive-mode-trigger-compile-error (state response)
+  "Look for an <interactive> compile error.
+If there is one, pop that up in a buffer, similar to `debug-on-error'."
+  (when (and haskell-interactive-types-for-show-ambiguous
+             (string-match "^\n<interactive>:[-0-9]+:[-0-9]+:" response)
+             (not (string-match "^\n<interactive>:[-0-9]+:[-0-9]+:[\n ]+[Ww]arning:" response)))
+    (let ((inhibit-read-only t))
+      (delete-region haskell-interactive-mode-prompt-start (point))
+      (set-marker haskell-interactive-mode-prompt-start
+                  haskell-interactive-mode-old-prompt-start)
+      (goto-char (point-max)))
+    (cond
+     ((and (not (haskell-interactive-mode-line-is-query (elt state 2)))
+           (or (string-match "No instance for (?Show[ \n]" response)
+               (string-match "Ambiguous type variable " response)))
+      (haskell-process-reset (haskell-interactive-process))
+      (let ((resp (haskell-process-queue-sync-request
+                   (haskell-interactive-process)
+                   (concat ":t "
+                           (buffer-substring-no-properties
+                            haskell-interactive-mode-prompt-start
+                            (point-max))))))
+        (cond
+         ((not (string-match "<interactive>:" resp))
+          (haskell-interactive-mode-insert-error resp))
+         (t (haskell-interactive-popup-error response)))))
+     (t (haskell-interactive-popup-error response)
+        t))
+    t))
+
+;;;###autoload
+(defun haskell-interactive-mode-echo (session message &optional mode)
+  "Echo a read only piece of text before the prompt."
+  (with-current-buffer (haskell-session-interactive-buffer session)
+    (save-excursion
+      (haskell-interactive-mode-goto-end-point)
+      (insert (if mode
+                  (haskell-fontify-as-mode
+                   (concat message "\n")
+                   mode)
+                (propertize (concat message "\n")
+                            'front-sticky t
+                            'read-only t
+                            'rear-nonsticky t))))))
+
+(defun haskell-interactive-mode-splices-buffer (session)
+  "Get the splices buffer for the current SESSION."
+  (get-buffer-create (haskell-interactive-mode-splices-buffer-name session)))
+
+(defun haskell-interactive-mode-splices-buffer-name (session)
+  (format "*%s:splices*" (haskell-session-name session)))
+
+(defun haskell-interactive-mode-compile-splice (session message)
+  "Echo a compiler splice."
+  (with-current-buffer (haskell-interactive-mode-splices-buffer session)
+    (unless (eq major-mode 'haskell-mode)
+      (haskell-mode))
+    (let* ((parts (split-string message "\n  ======>\n"))
+           (file-and-decl-lines (split-string (nth 0 parts) "\n"))
+           (file (nth 0 file-and-decl-lines))
+           (decl (mapconcat #'identity (cdr file-and-decl-lines) "\n"))
+           (output (nth 1 parts)))
+      (insert "-- " file "\n")
+      (let ((start (point)))
+        (insert decl "\n")
+        (indent-rigidly start (point) -4))
+      (insert "-- =>\n")
+      (let ((start (point)))
+        (insert output "\n")
+        (indent-rigidly start (point) -4)))))
+
+(defun haskell-interactive-mode-insert-garbage (session message)
+  "Echo a read only piece of text before the prompt."
+  (with-current-buffer (haskell-session-interactive-buffer session)
+    (save-excursion
+      (haskell-interactive-mode-goto-end-point)
+      (insert (propertize message
+                          'front-sticky t
+                          'font-lock-face 'haskell-interactive-face-garbage
+                          'read-only t
+                          'rear-nonsticky t)))))
+
+;;;###autoload
+(defun haskell-process-show-repl-response (line)
+  "Send LINE to the GHCi process and echo the result in some fashion.
+Result will be printed in the minibuffer or presented using
+function `haskell-presentation-present', depending on variable
+`haskell-process-use-presentation-mode'."
+  (let ((process (haskell-interactive-process)))
+    (haskell-process-queue-command
+     process
+     (make-haskell-command
+      :state (cons process line)
+      :go (lambda (state)
+            (haskell-process-send-string (car state) (cdr state)))
+      :complete (lambda (state response)
+                  (if haskell-process-use-presentation-mode
+                      (haskell-presentation-present
+                       (haskell-process-session (car state))
+                       response)
+                    (haskell-mode-message-line response)))))))
+
+(provide 'haskell-interactive-mode)
+
+;;; haskell-interactive-mode.el ends here