about summary refs log tree commit diff
path: root/configs/shared/emacs/.emacs.d/elpa/intero-20180703.18/intero.el
diff options
context:
space:
mode:
Diffstat (limited to 'configs/shared/emacs/.emacs.d/elpa/intero-20180703.18/intero.el')
-rw-r--r--configs/shared/emacs/.emacs.d/elpa/intero-20180703.18/intero.el3648
1 files changed, 3648 insertions, 0 deletions
diff --git a/configs/shared/emacs/.emacs.d/elpa/intero-20180703.18/intero.el b/configs/shared/emacs/.emacs.d/elpa/intero-20180703.18/intero.el
new file mode 100644
index 0000000000..0553b63073
--- /dev/null
+++ b/configs/shared/emacs/.emacs.d/elpa/intero-20180703.18/intero.el
@@ -0,0 +1,3648 @@
+;;; intero.el --- Complete development mode for Haskell
+
+;; Copyright (c) 2016 Chris Done
+;; Copyright (c) 2016 Steve Purcell
+;; Copyright (C) 2016 Артур Файзрахманов
+;; Copyright (c) 2015 Athur Fayzrakhmanov
+;; Copyright (C) 2015 Gracjan Polak
+;; Copyright (c) 2013 Herbert Valerio Riedel
+;; Copyright (c) 2007 Stefan Monnier
+
+;; Author: Chris Done <chrisdone@fpcomplete.com>
+;; Maintainer: Chris Done <chrisdone@fpcomplete.com>
+;; URL: https://github.com/commercialhaskell/intero
+;; Package-Version: 20180703.18
+;; Created: 3rd June 2016
+;; Version: 0.1.13
+;; Keywords: haskell, tools
+;; Package-Requires: ((flycheck "0.25") (company "0.8") (emacs "24.4") (haskell-mode "13.0"))
+
+;; 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:
+;;
+;; Mode that enables:
+;;
+;; * Flycheck type checking ✓
+;; * Company mode completion ✓
+;; * Go to definition ✓
+;; * Type of selection ✓
+;; * Info ✓
+;; * REPL ✓
+;; * Apply suggestions (extensions, imports, etc.) ✓
+;; * Find uses
+;; * Completion of stack targets ✓
+;; * List all types in all expressions
+;; * Import management
+;; * Dependency management
+
+;;; Code:
+
+(require 'flycheck)
+(require 'json)
+(require 'warnings)
+(require 'cl-lib)
+(require 'company)
+(require 'comint)
+(require 'widget)
+(require 'eldoc)
+(eval-when-compile
+  (require 'wid-edit))
+(require 'tramp)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+
+(defgroup intero nil
+  "Complete development mode for Haskell"
+  :group 'haskell)
+
+(defcustom intero-package-version
+  (cl-case system-type
+    ;; Until <https://github.com/haskell/network/issues/313> is fixed:
+    (windows-nt "0.1.32")
+    (cygwin "0.1.32")
+    (t "0.1.32"))
+  "Package version to auto-install.
+
+This version does not necessarily have to be the latest version
+of intero published on Hackage.  Sometimes there are changes to
+Intero which have no use for the Emacs mode.  It is only bumped
+when the Emacs mode actually requires newer features from the
+intero executable, otherwise we force our users to upgrade
+pointlessly."
+  :group 'intero
+  :type 'string)
+
+(defcustom intero-repl-no-load
+  t
+  "Pass --no-load when starting the repl.
+This causes it to skip loading the files from the selected target."
+  :group 'intero
+  :type 'boolean)
+
+(defcustom intero-repl-no-build
+  t
+  "Pass --no-build when starting the repl.
+This causes it to skip building the target."
+  :group 'intero
+  :type 'boolean)
+
+(defcustom intero-debug nil
+  "Show debug output."
+  :group 'intero
+  :type 'boolean)
+
+(defcustom intero-whitelist
+  nil
+  "Projects to whitelist.
+
+It should be a list of directories.
+
+To use this, use the following mode hook:
+  (add-hook 'haskell-mode-hook 'intero-mode-whitelist)
+or use `intero-global-mode' and add \"/\" to `intero-blacklist'."
+  :group 'intero
+  :type 'string)
+
+(defcustom intero-blacklist
+  nil
+  "Projects to blacklist.
+
+It should be a list of directories.
+
+To use this, use the following mode hook:
+  (add-hook 'haskell-mode-hook 'intero-mode-blacklist)
+or use `intero-global-mode'."
+  :group 'intero
+  :type 'string)
+
+(defcustom intero-stack-executable
+  "stack"
+  "Name or path to the Stack executable to use."
+  :group 'intero
+  :type 'string)
+
+(defcustom intero-pop-to-repl
+  t
+  "When non-nil, pop to REPL when code is sent to it."
+  :group 'intero
+  :type 'boolean)
+
+(defcustom intero-extra-ghc-options nil
+  "Extra GHC options to pass to intero executable.
+
+For example, this variable can be used to run intero with extra
+warnings and perform more checks via flycheck error reporting."
+  :group 'intero
+  :type '(repeat string))
+
+(defcustom intero-extra-ghci-options nil
+  "Extra options to pass to GHCi when running `intero-repl'.
+
+For example, this variable can be used to enable some ghci extensions
+by default."
+  :group 'intero
+  :type '(repeat string))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Modes
+
+(defvar intero-mode-map (make-sparse-keymap)
+  "Intero minor mode's map.")
+
+(defvar-local intero-lighter " Intero"
+  "Lighter for the intero minor mode.")
+
+;;;###autoload
+(define-minor-mode intero-mode
+  "Minor mode for Intero.
+
+\\{intero-mode-map}"
+  :lighter intero-lighter
+  :keymap intero-mode-map
+  (when (bound-and-true-p interactive-haskell-mode)
+    (when (fboundp 'interactive-haskell-mode)
+      (message "Disabling interactive-haskell-mode ...")
+      (interactive-haskell-mode -1)))
+  (if intero-mode
+      (progn
+        (intero-flycheck-enable)
+        (add-hook 'completion-at-point-functions 'intero-completion-at-point nil t)
+        (add-to-list (make-local-variable 'company-backends) 'intero-company)
+        (company-mode)
+        (setq-local company-minimum-prefix-length 1)
+        (unless eldoc-documentation-function
+          (setq-local eldoc-documentation-function #'ignore))
+        (add-function :before-until (local 'eldoc-documentation-function) #'intero-eldoc)
+        )
+    (progn
+      (remove-function (local 'eldoc-documentation-function) #'intero-eldoc)
+      (message "Intero mode disabled."))))
+
+;;;###autoload
+(defun intero-mode-whitelist ()
+  "Run intero-mode when the current project is in `intero-whitelist'."
+  (interactive)
+  (when (intero-directories-contain-file (buffer-file-name) intero-whitelist)
+    (intero-mode)))
+
+;;;###autoload
+(defun intero-mode-blacklist ()
+  "Run intero-mode unless the current project is in `intero-blacklist'."
+  (interactive)
+  (unless (intero-directories-contain-file (buffer-file-name) intero-blacklist)
+    (intero-mode)))
+
+(dolist (f '(intero-mode-whitelist intero-mode-blacklist))
+  (make-obsolete
+   f
+   "use `intero-global-mode', which honours `intero-whitelist' and `intero-blacklist'."
+   "2017-05-13"))
+
+
+(define-key intero-mode-map (kbd "C-c C-t") 'intero-type-at)
+(define-key intero-mode-map (kbd "M-?") 'intero-uses-at)
+(define-key intero-mode-map (kbd "C-c C-i") 'intero-info)
+(define-key intero-mode-map (kbd "M-.") 'intero-goto-definition)
+(define-key intero-mode-map (kbd "C-c C-l") 'intero-repl-load)
+(define-key intero-mode-map (kbd "C-c C-c") 'intero-repl-eval-region)
+(define-key intero-mode-map (kbd "C-c C-z") 'intero-repl)
+(define-key intero-mode-map (kbd "C-c C-r") 'intero-apply-suggestions)
+(define-key intero-mode-map (kbd "C-c C-e") 'intero-expand-splice-at-point)
+
+(defun intero-directories-contain-file (file dirs)
+  "Return non-nil if FILE is contained in at least one of DIRS."
+  (and (not (null file))
+       (cl-some (lambda (directory)
+                  (file-in-directory-p file directory))
+                dirs)))
+
+(defun intero-mode-maybe ()
+  "Enable `intero-mode' in all Haskell mode buffers.
+The buffer's filename (or working directory) is checked against
+`intero-whitelist' and `intero-blacklist'.  If both the whitelist
+and blacklist match, then the whitelist entry wins, and
+`intero-mode' is enabled."
+  (when (and (derived-mode-p 'haskell-mode)
+             (let* ((file (or (buffer-file-name) default-directory))
+                    (blacklisted (intero-directories-contain-file
+                                  file intero-blacklist))
+                    (whitelisted (intero-directories-contain-file
+                                  file intero-whitelist)))
+               (or whitelisted (not blacklisted))))
+    (intero-mode 1)))
+
+;;;###autoload
+(define-globalized-minor-mode intero-global-mode
+  intero-mode intero-mode-maybe
+  :require 'intero)
+
+(define-obsolete-function-alias 'global-intero-mode 'intero-global-mode)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Global variables/state
+
+(defvar intero-temp-file-buffer-mapping
+  (make-hash-table)
+  "A mapping from file names to buffers.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Buffer-local variables/state
+
+(defvar-local intero-callbacks (list)
+  "List of callbacks waiting for output.
+LIST is a FIFO.")
+
+(defvar-local intero-async-network-cmd nil
+  "Command to send to the async network process when we connect.")
+
+(defvar-local intero-async-network-connected nil
+  "Did we successfully connect to the intero service?")
+
+(defvar-local intero-async-network-state nil
+  "State to pass to the callback once we get a response.")
+
+(defvar-local intero-async-network-worker nil
+  "The worker we're associated with.")
+
+(defvar-local intero-async-network-callback nil
+  "Callback to call when the connection is closed.")
+
+(defvar-local intero-arguments (list)
+  "Arguments used to call the stack process.")
+
+(defvar-local intero-targets (list)
+  "Targets used for the stack process.")
+
+(defvar-local intero-repl-last-loaded nil
+  "Last loaded module in the REPL.")
+
+(defvar-local intero-repl-send-after-load nil
+  "Send a command after every load.")
+
+(defvar-local intero-start-time nil
+  "Start time of the stack process.")
+
+(defvar-local intero-source-buffer (list)
+  "Buffer from which Intero was first requested to start.")
+
+(defvar-local intero-project-root nil
+  "The project root of the current buffer.")
+
+(defvar-local intero-package-name nil
+  "The package name associated with the current buffer.")
+
+(defvar-local intero-deleting nil
+  "The process of the buffer is being deleted.")
+
+(defvar-local intero-give-up nil
+  "When non-nil, give up trying to start the backend.
+A true value indicates that the backend could not start, or could
+not be installed.  The user will have to manually run
+`intero-restart' or `intero-targets' to destroy the buffer and
+create a fresh one without this variable enabled.")
+
+(defvar-local intero-try-with-build nil
+  "Try starting intero without --no-build.
+This is slower, but will build required dependencies.")
+
+(defvar-local intero-starting nil
+  "When non-nil, indicates that the intero process starting up.")
+
+(defvar-local intero-service-port nil
+  "Port that the intero process is listening on for asynchronous commands.")
+
+(defvar-local intero-hoogle-port nil
+  "Port that hoogle server is listening on.")
+
+(defvar-local intero-suggestions nil
+  "Auto actions for the buffer.")
+
+(defvar-local intero-extensions nil
+  "Extensions supported by the compiler.")
+
+(defvar-local intero-ghc-version nil
+  "GHC version used by the project.")
+
+(defvar-local intero-buffer-host nil
+  "The hostname of the box hosting the intero process for the current buffer.")
+
+(defvar-local intero-stack-yaml nil
+  "The yaml file that intero should tell stack to use. When nil,
+  intero relies on stack's default, usually the 'stack.yaml' in
+  the project root.")
+
+(defun intero-inherit-local-variables (buffer)
+  "Make the current buffer inherit values of certain local variables from BUFFER."
+  (let ((variables '(intero-stack-executable
+                     intero-repl-no-build
+                     intero-repl-no-load
+                     intero-stack-yaml
+                     ;; TODO: shouldn’t more of the above be here?
+                     )))
+    (cl-loop for v in variables do
+             (set (make-local-variable v) (buffer-local-value v buffer)))))
+
+(defmacro intero-with-temp-buffer (&rest body)
+  "Run BODY in `with-temp-buffer', but inherit certain local variables from the current buffer first."
+  (declare (indent 0) (debug t))
+  `(let ((initial-buffer (current-buffer)))
+     (with-temp-buffer
+       (intero-inherit-local-variables initial-buffer)
+       ,@body)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Interactive commands
+
+(defun intero-add-package (package)
+  "Add a dependency on PACKAGE to the currently-running project backend."
+  (interactive "sPackage: ")
+  (intero-blocking-call 'backend (concat ":set -package " package))
+  (flycheck-buffer))
+
+(defun intero-toggle-debug ()
+  "Toggle debugging mode on/off."
+  (interactive)
+  (setq intero-debug (not intero-debug))
+  (message "Intero debugging is: %s" (if intero-debug "ON" "OFF")))
+
+(defun intero-list-buffers ()
+  "List hidden process buffers created by intero.
+
+You can use this to kill them or look inside."
+  (interactive)
+  (let ((buffers (cl-remove-if-not
+                  (lambda (buffer)
+                    (string-match-p " intero:" (buffer-name buffer)))
+                  (buffer-list))))
+    (if buffers
+        (display-buffer
+         (list-buffers-noselect
+          nil
+          buffers))
+      (error "There are no Intero process buffers"))))
+
+(defun intero-cd ()
+  "Change directory in the backend process."
+  (interactive)
+  (intero-async-call
+   'backend
+   (concat ":cd "
+           (read-directory-name "Change Intero directory: "))))
+
+(defun intero-fontify-expression (expression)
+  "Return a haskell-fontified version of EXPRESSION."
+  (intero-with-temp-buffer
+    (when (fboundp 'haskell-mode)
+      (let ((flycheck-checkers nil)
+            (haskell-mode-hook nil))
+        (haskell-mode)))
+    (insert expression)
+    (if (fboundp 'font-lock-ensure)
+        (font-lock-ensure)
+      (font-lock-fontify-buffer))
+    (buffer-string)))
+
+(defun intero-uses-at ()
+  "Highlight where the identifier at point is used."
+  (interactive)
+  (let* ((thing (intero-thing-at-point))
+         (uses (split-string (apply #'intero-get-uses-at thing)
+                             "\n"
+                             t)))
+    (unless (null uses)
+      (let ((highlighted nil))
+        (cl-loop
+         for use in uses
+         when (string-match
+               "\\(.*?\\):(\\([0-9]+\\),\\([0-9]+\\))-(\\([0-9]+\\),\\([0-9]+\\))$"
+               use)
+         do (let* ((returned-file (match-string 1 use))
+                   (loaded-file (intero-extend-path-by-buffer-host returned-file))
+                   (sline (string-to-number (match-string 2 use)))
+                   (scol (string-to-number (match-string 3 use)))
+                   (eline (string-to-number (match-string 4 use)))
+                   (ecol (string-to-number (match-string 5 use)))
+                   (start (save-excursion (goto-char (point-min))
+                                          (forward-line (1- sline))
+                                          (forward-char (1- scol))
+                                          (point))))
+              (when (intero-temp-file-p loaded-file)
+                (unless highlighted
+                  (intero-highlight-uses-mode))
+                (setq highlighted t)
+                (intero-highlight-uses-mode-highlight
+                 start
+                 (save-excursion (goto-char (point-min))
+                                 (forward-line (1- eline))
+                                 (forward-char (1- ecol))
+                                 (point))
+                 (= start (car thing))))))))))
+
+(defun intero-type-at (insert)
+  "Get the type of the thing or selection at point.
+
+With prefix argument INSERT, inserts the type above the current
+line as a type signature."
+  (interactive "P")
+  (let* ((thing (intero-thing-at-point))
+         (origin-buffer (current-buffer))
+         (origin (buffer-name))
+         (package (intero-package-name))
+         (ty (apply #'intero-get-type-at thing))
+         (string (buffer-substring (nth 0 thing) (nth 1 thing))))
+    (if insert
+        (save-excursion
+          (goto-char (line-beginning-position))
+          (insert (intero-fontify-expression ty) "\n"))
+      (with-current-buffer (intero-help-buffer)
+        (let ((buffer-read-only nil)
+              (help-string
+               (concat
+                (intero-fontify-expression string)
+                " in `"
+                (propertize origin 'origin-buffer origin-buffer)
+                "'"
+                " (" package ")"
+                "\n\n"
+                (intero-fontify-expression ty))))
+          (erase-buffer)
+          (intero-help-push-history origin-buffer help-string)
+          (intero-help-pagination)
+          (insert help-string)
+          (goto-char (point-min))))
+      (message
+       "%s" (intero-fontify-expression ty)))))
+
+(defun intero-info (ident)
+  "Get the info of the thing with IDENT at point."
+  (interactive (list (intero-ident-at-point)))
+  (let ((origin-buffer (current-buffer))
+        (package (intero-package-name))
+        (info (intero-get-info-of ident))
+        (origin (buffer-name)))
+    (with-current-buffer (pop-to-buffer (intero-help-buffer))
+      (let ((buffer-read-only nil)
+            (help-string
+             (concat
+              (intero-fontify-expression ident)
+              " in `"
+              (propertize origin 'origin-buffer origin-buffer)
+              "'"
+              " (" package ")"
+              "\n\n"
+              (intero-fontify-expression info))))
+        (erase-buffer)
+        (intero-help-push-history origin-buffer help-string)
+        (intero-help-pagination)
+        (insert help-string)
+        (goto-char (point-min))))))
+
+(defun intero-goto-definition ()
+  "Jump to the definition of the thing at point.
+Returns nil when unable to find definition."
+  (interactive)
+  (let ((result (apply #'intero-get-loc-at (intero-thing-at-point))))
+
+    (if (not (string-match "\\(.*?\\):(\\([0-9]+\\),\\([0-9]+\\))-(\\([0-9]+\\),\\([0-9]+\\))$"
+                       result))
+        (message "%s" result)
+      (if (fboundp 'xref-push-marker-stack) ;; Emacs 25
+          (xref-push-marker-stack)
+        (with-no-warnings
+          (ring-insert find-tag-marker-ring (point-marker))))
+      (let* ((returned-file (match-string 1 result))
+             (line (string-to-number (match-string 2 result)))
+             (col (string-to-number (match-string 3 result)))
+             (loaded-file (intero-extend-path-by-buffer-host returned-file)))
+        (if (intero-temp-file-p loaded-file)
+            (let ((original-buffer (intero-temp-file-origin-buffer loaded-file)))
+              (if original-buffer
+                  (switch-to-buffer original-buffer)
+                (error "Attempted to load temp file.  Try restarting Intero.
+If the problem persists, please report this as a bug!")))
+          (find-file
+           (expand-file-name
+            returned-file
+            (intero-extend-path-by-buffer-host (intero-project-root)))))
+        (pop-mark)
+        (goto-char (point-min))
+        (forward-line (1- line))
+        (forward-char (1- col))
+        t))))
+
+(defmacro intero-with-dump-splices (exp)
+  "Run EXP but with dump-splices enabled in the intero backend process."
+  `(when (intero-blocking-call 'backend ":set -ddump-splices")
+     (let ((result ,exp))
+       (progn
+         nil ; Disable dump-splices here in future
+         result))))
+
+(defun intero-expand-splice-at-point ()
+  "Show the expansion of the template haskell splice at point."
+  (interactive)
+  (unless (intero-gave-up 'backend)
+    (intero-with-dump-splices
+     (let* ((output (intero-blocking-call
+                     'backend
+                     (concat ":load " (intero-path-for-ghci (intero-temp-file-name)))))
+            (msgs (intero-parse-errors-warnings-splices nil (current-buffer) output))
+            (line (line-number-at-pos))
+            (column (if (save-excursion
+                          (forward-char 1)
+                          (looking-back "$(" 1))
+                        (+ 2 (current-column))
+                      (if (looking-at-p "$(")
+                          (+ 3 (current-column))
+                        (1+ (current-column)))))
+            (expansion-msg
+             (cl-loop for msg in msgs
+                      when (and (eq (flycheck-error-level msg) 'splice)
+                                (= (flycheck-error-line msg) line)
+                                (<= (flycheck-error-column msg) column))
+                      return (flycheck-error-message msg)))
+            (expansion
+             (when expansion-msg
+               (string-trim
+                (replace-regexp-in-string "^Splicing expression" "" expansion-msg)))))
+       (when expansion
+         (message "%s" (intero-fontify-expression expansion)))))))
+
+(defun intero-restart ()
+  "Simply restart the process with the same configuration as before."
+  (interactive)
+  (when (intero-buffer-p 'backend)
+    (let ((targets (buffer-local-value 'intero-targets
+                                       (intero-buffer 'backend)))
+          (stack-yaml (buffer-local-value 'intero-stack-yaml
+                                          (intero-buffer 'backend))))
+      (intero-destroy 'backend)
+      (intero-get-worker-create 'backend targets (current-buffer) stack-yaml)
+      (intero-repl-restart))))
+
+(defun intero-read-targets ()
+  "Read a list of stack targets."
+  (let ((old-targets
+         (buffer-local-value 'intero-targets (intero-buffer 'backend)))
+        (available-targets (intero-get-targets)))
+    (if available-targets
+        (intero-multiswitch
+         "Set the targets to use for stack ghci:"
+         (mapcar (lambda (target)
+                   (list :key target
+                         :title target
+                         :default (member target old-targets)))
+                 available-targets))
+      (split-string (read-from-minibuffer "Targets: " nil nil nil nil old-targets)
+                    " "
+                    t))))
+
+(defun intero-targets (targets save-dir-local)
+  "Set the TARGETS to use for stack ghci.
+When SAVE-DIR-LOCAL is non-nil, save TARGETS as the
+directory-local value for `intero-targets'."
+  (interactive (list (intero-read-targets)
+                     (y-or-n-p "Save selected target(s) in directory local variables for future sessions? ")))
+  (intero-destroy)
+  (intero-get-worker-create 'backend targets (current-buffer))
+  (intero-repl-restart)
+  (when save-dir-local
+    (save-window-excursion
+      (let ((default-directory (intero-project-root)))
+        (add-dir-local-variable 'haskell-mode 'intero-targets targets)
+        (save-buffer)))))
+
+(defun intero-stack-yaml (file save-dir-local)
+  "Change the yaml FILE that intero should tell stack to use.
+Intero will be restarted with the new configuration.  When
+SAVE-DIR-LOCAL is non-nil, save FILE as the directory-local value
+for `intero-stack-yaml'."
+  (interactive (list (read-file-name
+                      "Select YAML config: "
+                      (file-name-as-directory (intero-project-root)))
+                     (y-or-n-p "Save selected stack yaml config in directory local variable for future sessions? ")))
+  (let ((stack-yaml (expand-file-name file)))
+    (setq intero-stack-yaml stack-yaml)
+    (with-current-buffer (intero-buffer 'backend)
+      (setq intero-stack-yaml stack-yaml))
+    (intero-restart)
+    (intero-repl-restart)
+    (when save-dir-local
+      (save-window-excursion
+        (let ((default-directory (intero-project-root)))
+          (add-dir-local-variable 'haskell-mode 'intero-stack-yaml stack-yaml)
+          (save-buffer))))))
+
+(defun intero-destroy (&optional worker)
+  "Stop WORKER and kill its associated process buffer.
+If not provided, WORKER defaults to the current worker process."
+  (interactive)
+  (if worker
+      (intero-delete-worker worker)
+    (intero-delete-worker 'backend)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; DevelMain integration
+
+(defun intero-devel-reload ()
+  "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 Intero."
+  (interactive)
+  (unwind-protect
+      (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")))
+        (message "Reloading ...")
+        (intero-async-call
+         'backend
+         ":load DevelMain"
+         (current-buffer)
+         (lambda (buffer reply)
+           (if (string-match-p "^O[Kk], modules loaded" reply)
+               (intero-async-call
+                'backend
+                "DevelMain.update"
+                buffer
+                (lambda (_buffer reply)
+                  (message "DevelMain updated. Output was:\n%s"
+                           reply)))
+             (progn
+               (message "DevelMain FAILED. Switch to DevelMain.hs and compile that.")
+               (switch-to-buffer buffer)
+               (flycheck-buffer)
+               (flycheck-list-errors))))))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Flycheck integration
+
+(defun intero-flycheck-enable ()
+  "Enable intero's flycheck support in this buffer."
+  (flycheck-select-checker 'intero)
+  (setq intero-check-last-mod-time nil
+        intero-check-last-results nil)
+  (flycheck-mode))
+
+(defun intero-check (checker cont)
+  "Run a check with CHECKER and pass the status onto CONT."
+  (if (intero-gave-up 'backend)
+      (run-with-timer 0
+                      nil
+                      cont
+                      'interrupted)
+    (let* ((file-buffer (current-buffer))
+           (staging-file (intero-path-for-ghci (intero-staging-file-name)))
+           (temp-file (intero-path-for-ghci (intero-temp-file-name))))
+      ;; We queue up to :move the staging file to the target temp
+      ;; file, which also updates its modified time.
+      (intero-async-call
+       'backend
+       (format ":move %s %s" staging-file temp-file))
+      ;; We load up the target temp file, which has only been updated
+      ;; by the copy above.
+      (intero-async-call
+       'backend
+       (concat ":load " temp-file)
+       (list :cont cont
+             :file-buffer file-buffer
+             :checker checker)
+       (lambda (state string)
+         (with-current-buffer (plist-get state :file-buffer)
+           (let* ((compile-ok (string-match "O[Kk], modules loaded: \\(.*\\)\\.$" string))
+                  (modules (match-string 1 string))
+                  (msgs (intero-parse-errors-warnings-splices
+                         (plist-get state :checker)
+                         (current-buffer)
+                         string)))
+             (intero-collect-compiler-messages msgs)
+             (let ((results (cl-remove-if (lambda (msg)
+                                            (eq 'splice (flycheck-error-level msg)))
+                                          msgs)))
+               (setq intero-check-last-results results)
+               (funcall (plist-get state :cont) 'finished results))
+             (when compile-ok
+               (intero-async-call 'backend
+                                  (concat ":module + "
+                                          (replace-regexp-in-string "," "" modules))
+                                  nil
+                                  (lambda (_st _))))))))
+      ;; We sleep for at least one second to allow a buffer period
+      ;; between module updates. GHCi will consider a module Foo to be
+      ;; unchanged even if its filename has changed or timestmap has
+      ;; changed, if the timestamp is less than 1 second.
+      (intero-async-call
+       'backend
+       ":sleep 1"))))
+
+(flycheck-define-generic-checker 'intero
+  "A syntax and type checker for Haskell using an Intero worker
+process."
+  :start 'intero-check
+  :modes '(haskell-mode literate-haskell-mode)
+  :predicate (lambda () intero-mode))
+
+(add-to-list 'flycheck-checkers 'intero)
+
+(defun intero-parse-errors-warnings-splices (checker buffer string)
+  "Parse flycheck errors and warnings.
+CHECKER and BUFFER are added to each item parsed from STRING."
+  (intero-with-temp-buffer
+    (insert string)
+    (goto-char (point-min))
+    (let ((messages (list))
+          (temp-file (intero-temp-file-name buffer))
+          (found-error-as-warning nil))
+      (while (search-forward-regexp
+              (concat "[\r\n]\\([A-Z]?:?[^ \r\n:][^:\n\r]+\\):\\([0-9()-:]+\\):"
+                      "[ \n\r]+\\([[:unibyte:][:nonascii:]]+?\\)\n[^ ]")
+              nil t 1)
+        (let* ((local-file (intero-canonicalize-path (match-string 1)))
+               (file (intero-extend-path-by-buffer-host local-file buffer))
+               (location-raw (match-string 2))
+               (msg (replace-regexp-in-string
+                     "[\n\r ]*|$"
+                     ""
+                     (match-string 3))) ;; Replace gross bullet points.
+               (type (cond ((string-match "^Warning:" msg)
+                            (setq msg (replace-regexp-in-string "^Warning: *" "" msg))
+                            (if (string-match-p
+                                 (rx bol
+                                     "["
+                                     (or "-Wdeferred-type-errors"
+                                         "-Wdeferred-out-of-scope-variables"
+                                         "-Wtyped-holes")
+                                     "]")
+                                 msg)
+                                (progn (setq found-error-as-warning t)
+                                       'error)
+                              'warning))
+                           ((string-match-p "^Splicing " msg) 'splice)
+                           (t                                 'error)))
+               (location (intero-parse-error
+                          (concat local-file ":" location-raw ": x")))
+               (line (plist-get location :line))
+               (column (plist-get location :col)))
+          (setq messages
+                (cons (flycheck-error-new-at
+                       line column type
+                       msg
+                       :checker checker
+                       :buffer buffer
+                       :filename (if (intero-paths-for-same-file temp-file file)
+                                     (intero-buffer-file-name buffer)
+                                   file))
+                      messages)))
+        (forward-line -1))
+      (delete-dups
+       (if found-error-as-warning
+           (cl-remove-if (lambda (msg) (eq 'warning (flycheck-error-level msg))) messages)
+         messages)))))
+
+(defconst intero-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.")
+
+(defun intero-parse-error (string)
+  "Parse the line number from the error in STRING."
+  (save-match-data
+    (when (string-match (mapconcat #'car intero-error-regexp-alist "\\|")
+                        string)
+      (let ((string3 (match-string 3 string))
+            (string5 (match-string 5 string)))
+        (list :file (match-string 1 string)
+              :line (string-to-number (match-string 2 string))
+              :col (string-to-number (match-string 4 string))
+              :line2 (when string3
+                       (string-to-number string3))
+              :col2 (when string5
+                      (string-to-number string5)))))))
+
+(defun intero-call-in-buffer (buffer func &rest args)
+  "In BUFFER, call FUNC with ARGS."
+  (with-current-buffer buffer
+    (apply func args)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Traditional completion-at-point function
+
+(defun intero-completion-at-point ()
+  "A (blocking) function suitable for use in `completion-at-point-functions'."
+  (let ((prefix-info (intero-completions-grab-prefix)))
+    (when prefix-info
+      (cl-destructuring-bind
+          (beg end prefix _type) prefix-info
+        (let ((completions
+               (intero-completion-response-to-list
+                (intero-blocking-call
+                 'backend
+                 (format ":complete repl %S" prefix)))))
+          (when completions
+            (list beg end completions)))))))
+
+(defun intero-repl-completion-at-point ()
+  "A (blocking) function suitable for use in `completion-at-point-functions'.
+Should only be used in the repl"
+  (let* ((beg (save-excursion (intero-repl-beginning-of-line) (point)))
+         (end (point))
+         (str (buffer-substring-no-properties beg end))
+         (repl-buffer (current-buffer))
+         (proc (get-buffer-process (current-buffer))))
+    (with-temp-buffer
+      (comint-redirect-send-command-to-process
+       (format ":complete repl %S" str) ;; command
+       (current-buffer) ;; output buffer
+       proc ;; target process
+       nil  ;; echo
+       t)   ;; no-display
+      (while (not (with-current-buffer repl-buffer
+                    comint-redirect-completed))
+        (sleep-for 0.01))
+      (let* ((completions (intero-completion-response-to-list (buffer-string)))
+             (first-line (car completions)))
+        (when (string-match "[^ ]* [^ ]* " first-line) ;; "2 2 :load src/"
+          (setq first-line (replace-match "" nil nil first-line))
+          (list (+ beg (length first-line)) end (cdr completions)))))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Company integration (auto-completion)
+
+(defconst intero-pragmas
+  '("CONLIKE" "SCC" "DEPRECATED" "INCLUDE" "INCOHERENT" "INLINABLE" "INLINE"
+    "LANGUAGE" "LINE" "MINIMAL" "NOINLINE" "NOUNPACK" "OPTIONS" "OPTIONS_GHC"
+    "OVERLAPPABLE" "OVERLAPPING" "OVERLAPS" "RULES" "SOURCE" "SPECIALIZE"
+    "UNPACK" "WARNING")
+  "Pragmas that GHC supports.")
+
+(defun intero-company (command &optional arg &rest ignored)
+  "Company source for intero, with the standard COMMAND and ARG args.
+Other arguments are IGNORED."
+  (interactive (list 'interactive))
+  (cl-case command
+    (interactive (company-begin-backend 'intero-company))
+    (prefix
+     (unless (intero-gave-up 'backend)
+       (or (let ((hole (intero-grab-hole)))
+             (when hole
+               (save-excursion
+                 (goto-char (cdr hole))
+                 (buffer-substring (car hole) (cdr hole)))))
+           (let ((prefix-info (intero-completions-grab-prefix)))
+             (when prefix-info
+               (cl-destructuring-bind
+                   (beg end prefix _type) prefix-info
+                 prefix))))))
+    (candidates
+     (unless (intero-gave-up 'backend)
+       (let ((beg-end (intero-grab-hole)))
+         (if beg-end
+             (cons :async
+                   (-partial 'intero-async-fill-at
+                             (current-buffer)
+                             (car beg-end)))
+           (let ((prefix-info (intero-completions-grab-prefix)))
+             (when prefix-info
+               (cons :async
+                     (-partial 'intero-company-callback
+                               (current-buffer)
+                               prefix-info))))))))))
+
+(define-obsolete-function-alias 'company-intero 'intero-company)
+
+(defun intero-company-callback (source-buffer prefix-info cont)
+  "Generate completions for SOURCE-BUFFER based on PREFIX-INFO and call CONT on the results."
+  (cl-destructuring-bind
+      (beg end prefix type) prefix-info
+    (or (and (bound-and-true-p intero-mode)
+             (cl-case type
+               (haskell-completions-module-name-prefix
+                (intero-get-repl-completions source-buffer (concat "import " prefix) cont)
+                t)
+               (haskell-completions-identifier-prefix
+                (intero-get-completions source-buffer beg end cont)
+                t)
+               (haskell-completions-language-extension-prefix
+                (intero-get-repl-completions
+                 source-buffer
+                 (concat ":set -X" prefix)
+                 (-partial (lambda (cont results)
+                             (funcall cont
+                                      (mapcar (lambda (x)
+                                                (replace-regexp-in-string "^-X" "" x))
+                                              results)))
+                           cont))
+                t)
+               (haskell-completions-pragma-name-prefix
+                (funcall cont
+                         (cl-remove-if-not
+                          (lambda (candidate)
+                            (string-prefix-p prefix candidate))
+                          intero-pragmas))
+                t)))
+        (intero-get-repl-completions source-buffer prefix cont))))
+
+(defun intero-completions-grab-prefix (&optional minlen)
+  "Grab prefix at point for possible completion.
+If specified, MINLEN is the shortest completion which will be
+considered."
+  (when (intero-completions-can-grab-prefix)
+    (let ((prefix (cond
+                   ((intero-completions-grab-pragma-prefix))
+                   ((intero-completions-grab-identifier-prefix)))))
+      (cond ((and minlen prefix)
+             (when (>= (length (nth 2 prefix)) minlen)
+               prefix))
+            (prefix prefix)))))
+
+(defun intero-completions-can-grab-prefix ()
+  "Check if the case is appropriate for grabbing completion prefix."
+  (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 intero-completions-grab-identifier-prefix ()
+  "Grab identifier prefix."
+  (let ((pos-at-point (intero-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)
+        (when (<= p end)
+          (setq end p)
+          (setq value (buffer-substring-no-properties start end))
+          (when (string-match-p (rx bos upper) value)
+            (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)
+                    (setq type 'haskell-completions-identifier-prefix))))))
+          (when (nth 8 (syntax-ppss))
+            (setq type 'haskell-completions-general-prefix))
+          (when value (list start end value type)))))))
+
+(defun intero-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 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)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Hole filling
+
+(defun intero-async-fill-at (buffer beg cont)
+  "Make the blocking call to the process."
+  (with-current-buffer buffer
+    (intero-async-call
+     'backend
+     (format
+      ":fill %s %d %d"
+      (intero-path-for-ghci (intero-temp-file-name))
+      (save-excursion (goto-char beg)
+                      (line-number-at-pos))
+      (save-excursion (goto-char beg)
+                      (1+ (current-column))))
+     (list :buffer (current-buffer) :cont cont)
+     (lambda (state reply)
+       (if (or (string-match "^Couldn't guess" reply)
+               (string-match "^Unable to " reply)
+               (intero-parse-error reply))
+           (funcall (plist-get state :cont) (list))
+         (with-current-buffer (plist-get state :buffer)
+           (let ((candidates
+                  (split-string
+                   (replace-regexp-in-string
+                    "\n$" ""
+                    reply)
+                   "[\r\n]"
+                   t)))
+             (when candidates
+               (funcall (plist-get state :cont) candidates)))))))))
+
+(defun intero-grab-hole ()
+  "When user is at a hole _ or _foo, return the starting point of
+that hole."
+  (let ((beg-end (intero-ident-pos-at-point)))
+    (when beg-end
+      (let ((string (buffer-substring-no-properties (car beg-end) (cdr beg-end))))
+        (when (string-match-p "^_" string)
+          beg-end)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; ELDoc integration
+
+(defvar-local intero-eldoc-cache (make-hash-table :test 'equal)
+  "Cache for types of regions, used by `intero-eldoc'.
+This is not for saving on requests (we make a request even if
+something is in cache, overwriting the old entry), but rather for
+making types show immediately when we do have them cached.")
+
+(defun intero-eldoc-maybe-print (msg)
+  "Print MSG with eldoc if eldoc would display a message now.
+Like `eldoc-print-current-symbol-info', but just printing MSG
+instead of using `eldoc-documentation-function'."
+  (with-demoted-errors "eldoc error: %s"
+    (and (or (eldoc-display-message-p)
+             ;; Erase the last message if we won't display a new one.
+             (when eldoc-last-message
+               (eldoc-message nil)
+               nil))
+         (eldoc-message msg))))
+
+(defun intero-eldoc ()
+  "ELDoc backend for intero."
+  (let ((buffer (intero-buffer 'backend)))
+    (when (and buffer (process-live-p (get-buffer-process buffer)))
+      (apply #'intero-get-type-at-async
+             (lambda (beg end ty)
+               (let ((response-status (intero-haskell-utils-repl-response-error-status ty)))
+                 (if (eq 'no-error response-status)
+                     (let ((msg (intero-fontify-expression
+                                 (replace-regexp-in-string "[ \n]+" " " ty))))
+                       ;; Got an updated type-at-point, cache and print now:
+                       (puthash (list beg end)
+                                msg
+                                intero-eldoc-cache)
+                       (intero-eldoc-maybe-print msg))
+                   ;; But if we're seeing errors, invalidate cache-at-point:
+                   (remhash (list beg end) intero-eldoc-cache))))
+             (intero-thing-at-point))))
+  ;; If we have something cached at point, print that first:
+  (gethash (intero-thing-at-point) intero-eldoc-cache))
+
+(defun intero-haskell-utils-repl-response-error-status (response)
+  "Parse response REPL's RESPONSE for errors.
+Returns one of the following symbols:
+
++ unknown-command
++ option-missing
++ interactive-error
++ no-error
+
+*Warning*: this funciton covers only three kind of responses:
+
+* \"unknown command …\"
+  REPL missing requested command
+* \"<interactive>:3:5: …\"
+  interactive REPL error
+* \"Couldn't guess that module name. Does it exist?\"
+  (:type-at and maybe some other commands error)
+* *all other reposnses* are treated as success reposneses and
+  'no-error is returned."
+  (let ((first-line (car (split-string response "\n" t))))
+    (cond
+     ((null first-line) 'no-error)
+     ((string-match-p "^unknown command" first-line)
+      'unknown-command)
+     ((string-match-p
+       "^Couldn't guess that module name. Does it exist?"
+       first-line)
+      'option-missing)
+     ((string-match-p "^<interactive>:" first-line)
+      'interactive-error)
+     ((string-match-p "^<no location info>:" first-line)
+      'inspection-error)
+     (t 'no-error))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; REPL
+
+(defconst intero-prompt-regexp "^\4 ")
+
+(defvar-local intero-repl-previous-buffer nil
+  "Records the buffer to which `intero-repl-switch-back' should jump.
+This is set by `intero-repl-buffer', and should otherwise be nil.")
+
+(defun intero-repl-clear-buffer ()
+  "Clear the current REPL buffer."
+  (interactive)
+  (let ((comint-buffer-maximum-size 0))
+    (comint-truncate-buffer)))
+
+(defmacro intero-with-repl-buffer (prompt-options &rest body)
+  "Evaluate given forms with the REPL as the current buffer.
+The REPL will be started if necessary, and the REPL buffer will
+be activated after evaluation.  PROMPT-OPTIONS are passed to
+`intero-repl-buffer'.  BODY is the forms to be evaluated."
+  (declare (indent defun))
+  (let ((repl-buffer (cl-gensym)))
+    `(let ((,repl-buffer (intero-repl-buffer ,prompt-options t)))
+       (with-current-buffer ,repl-buffer
+         ,@body)
+       (when intero-pop-to-repl
+         (pop-to-buffer ,repl-buffer)))))
+
+(defun intero-repl-after-load ()
+  "Set the command to run after load."
+  (interactive)
+  (if (eq major-mode 'intero-repl-mode)
+      (setq intero-repl-send-after-load
+            (read-from-minibuffer
+             "Command to run: "
+             (or intero-repl-send-after-load
+                 (car (ring-elements comint-input-ring))
+                 "")))
+    (error "Run this in the REPL.")))
+
+(defun intero-repl-load (&optional prompt-options)
+  "Load the current file in the REPL.
+If PROMPT-OPTIONS is non-nil, prompt with an options list."
+  (interactive "P")
+  (save-buffer)
+  (let ((file (intero-path-for-ghci (intero-buffer-file-name))))
+    (intero-with-repl-buffer prompt-options
+      (comint-simple-send
+         (get-buffer-process (current-buffer))
+         ":set prompt \"\\n\"")
+        (if (or (not intero-repl-last-loaded)
+                (not (equal file intero-repl-last-loaded)))
+            (progn
+              (comint-simple-send
+               (get-buffer-process (current-buffer))
+               (concat ":load " file))
+              (setq intero-repl-last-loaded file))
+          (comint-simple-send
+           (get-buffer-process (current-buffer))
+           ":reload"))
+        (when intero-repl-send-after-load
+          (comint-simple-send
+           (get-buffer-process (current-buffer))
+           intero-repl-send-after-load))
+        (comint-simple-send (get-buffer-process (current-buffer))
+                            ":set prompt \"\\4 \""))))
+
+(defun intero-repl-eval-region (begin end &optional prompt-options)
+  "Evaluate the code in region from BEGIN to END in the REPL.
+If the region is unset, the current line will be used.
+PROMPT-OPTIONS are passed to `intero-repl-buffer' if supplied."
+  (interactive "r")
+  (unless (use-region-p)
+    (setq begin (line-beginning-position)
+          end (line-end-position)))
+  (let ((text (buffer-substring-no-properties begin end)))
+    (intero-with-repl-buffer prompt-options
+      (comint-simple-send
+       (get-buffer-process (current-buffer))
+       text))))
+
+(defun intero-repl (&optional prompt-options)
+  "Start up the REPL for this stack project.
+If PROMPT-OPTIONS is non-nil, prompt with an options list."
+  (interactive "P")
+  (switch-to-buffer-other-window (intero-repl-buffer prompt-options t)))
+
+(defun intero-repl-restart ()
+  "Restart the REPL."
+  (interactive)
+  (let* ((root (intero-project-root))
+         (package-name (intero-package-name))
+         (backend-buffer (intero-buffer 'backend))
+         (name (format "*intero:%s:%s:repl*"
+                       (file-name-nondirectory root)
+                       package-name)))
+    (when (get-buffer name)
+      (with-current-buffer (get-buffer name)
+        (goto-char (point-max))
+        (let ((process (get-buffer-process (current-buffer))))
+          (when process (kill-process process)))
+        (intero-repl-mode-start backend-buffer
+                                (buffer-local-value 'intero-targets backend-buffer)
+                                nil
+                                (buffer-local-value 'intero-stack-yaml backend-buffer))))))
+
+(defun intero-repl-buffer (prompt-options &optional store-previous)
+  "Start the REPL buffer.
+If PROMPT-OPTIONS is non-nil, prompt with an options list.  When
+STORE-PREVIOUS is non-nil, note the caller's buffer in
+`intero-repl-previous-buffer'."
+  (let* ((root (intero-project-root))
+         (package-name (intero-package-name))
+         (name (format "*intero:%s:%s:repl*"
+                       (file-name-nondirectory root)
+                       package-name))
+         (initial-buffer (current-buffer))
+         (backend-buffer (intero-buffer 'backend)))
+    (with-current-buffer
+        (or (get-buffer name)
+            (with-current-buffer
+                (get-buffer-create name)
+              ;; The new buffer doesn't know if the initial buffer was hosted
+              ;; remotely or not, so we need to extend by the host of the
+              ;; initial buffer to cd. We could also achieve this by setting the
+              ;; buffer's intero-buffer-host, but intero-repl-mode wipes this, so
+              ;; we defer setting that until after.
+              (cd (intero-extend-path-by-buffer-host root initial-buffer))
+              (intero-repl-mode) ; wipes buffer-local variables
+              (intero-inherit-local-variables initial-buffer)
+              (setq intero-buffer-host (intero-buffer-host initial-buffer))
+              (intero-repl-mode-start backend-buffer
+                                      (buffer-local-value 'intero-targets backend-buffer)
+                                      prompt-options
+                                      (buffer-local-value 'intero-stack-yaml backend-buffer))
+              (current-buffer)))
+      (progn
+        (when store-previous
+          (setq intero-repl-previous-buffer initial-buffer))
+        (current-buffer)))))
+
+(defvar intero-hyperlink-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map [mouse-1]  'intero-find-file-with-line-and-char)
+    (define-key map [C-return] 'intero-find-file-with-line-and-char)
+    map)
+  "Keymap for clicking on links in REPL.")
+
+(define-derived-mode intero-repl-mode comint-mode "Intero-REPL"
+  "Interactive prompt for Intero."
+  (when (and (not (eq major-mode 'fundamental-mode))
+             (eq this-command 'intero-repl-mode))
+    (error "You probably meant to run: M-x intero-repl"))
+  (setq-local comint-prompt-regexp intero-prompt-regexp)
+  (setq-local warning-suppress-types (cons '(undo discard-info) warning-suppress-types))
+  (setq-local comint-prompt-read-only t)
+  (add-hook 'completion-at-point-functions 'intero-repl-completion-at-point nil t)
+  (company-mode))
+
+(defun intero-repl-mode-start (backend-buffer targets prompt-options stack-yaml)
+  "Start the process for the repl in the current buffer.
+BACKEND-BUFFER is used for options.  TARGETS is the targets to
+load.  If PROMPT-OPTIONS is non-nil, prompt with an options list.
+STACK-YAML is the stack yaml config to use.  When nil, tries to
+use project-wide intero-stack-yaml when nil, otherwise uses
+stack's default)."
+  (setq intero-targets targets)
+  (setq intero-repl-last-loaded nil)
+  (when stack-yaml
+    (setq intero-stack-yaml stack-yaml))
+  (when prompt-options
+    (intero-repl-options backend-buffer))
+  (let ((stack-yaml (if stack-yaml
+                        stack-yaml
+                      (buffer-local-value 'intero-stack-yaml backend-buffer)))
+        (arguments (intero-make-options-list
+                    "ghci"
+                    (or targets
+                        (let ((package-name (buffer-local-value 'intero-package-name
+                                                                backend-buffer)))
+                          (unless (equal "" package-name)
+                            (list package-name))))
+                    (buffer-local-value 'intero-repl-no-build backend-buffer)
+                    (buffer-local-value 'intero-repl-no-load backend-buffer)
+                    nil
+                    stack-yaml)))
+    (insert (propertize
+             (format "Starting:\n  %s ghci %s\n" intero-stack-executable
+                     (combine-and-quote-strings arguments))
+             'face 'font-lock-comment-face))
+    (let* ((script-buffer
+            (with-current-buffer (find-file-noselect (intero-make-temp-file "intero-script"))
+              (insert ":set prompt \"\"
+:set -fbyte-code
+:set -fdefer-type-errors
+:set -fdiagnostics-color=never
+:set prompt \"\\4 \"
+")
+              (basic-save-buffer)
+              (current-buffer)))
+           (script
+            (with-current-buffer script-buffer
+              (intero-localize-path (intero-buffer-file-name)))))
+      (let ((process
+             (get-buffer-process
+              (apply #'make-comint-in-buffer "intero" (current-buffer) intero-stack-executable nil "ghci"
+                     (append arguments
+                             (list "--verbosity" "silent")
+                             (list "--ghci-options"
+                                   (concat "-ghci-script=" script))
+                             (cl-mapcan (lambda (x) (list "--ghci-options" x)) intero-extra-ghci-options))))))
+        (when (process-live-p process)
+          (set-process-query-on-exit-flag process nil)
+          (message "Started Intero process for REPL.")
+          (kill-buffer script-buffer))))))
+
+(defun intero-repl-options (backend-buffer)
+  "Open an option menu to set options used when starting the REPL.
+Default options come from user customization and any temporary
+changes in the BACKEND-BUFFER."
+  (interactive)
+  (let* ((old-options
+          (list
+           (list :key "load-all"
+                 :title "Load all modules"
+                 :default (not (buffer-local-value 'intero-repl-no-load backend-buffer)))
+           (list :key "build-first"
+                 :title "Build project first"
+                 :default (not (buffer-local-value 'intero-repl-no-build backend-buffer)))))
+         (new-options (intero-multiswitch "Start REPL with options:" old-options)))
+    (with-current-buffer backend-buffer
+      (setq-local intero-repl-no-load (not (member "load-all" new-options)))
+      (setq-local intero-repl-no-build (not (member "build-first" new-options))))))
+
+(font-lock-add-keywords
+ 'intero-repl-mode
+ '(("\\(\4\\)"
+    (0 (prog1 ()
+         (compose-region (match-beginning 1)
+                         (match-end 1)
+                         ?λ))))))
+
+(define-key intero-repl-mode-map [remap move-beginning-of-line] 'intero-repl-beginning-of-line)
+(define-key intero-repl-mode-map [remap delete-backward-char] 'intero-repl-delete-backward-char)
+(define-key intero-repl-mode-map (kbd "C-c C-k") 'intero-repl-clear-buffer)
+(define-key intero-repl-mode-map (kbd "C-c C-z") 'intero-repl-switch-back)
+
+(defun intero-repl-delete-backward-char ()
+  "Delete backwards, excluding the prompt."
+  (interactive)
+  (unless (looking-back intero-prompt-regexp (line-beginning-position))
+    (call-interactively 'delete-backward-char)))
+
+(defun intero-repl-beginning-of-line ()
+  "Go to the beginning of the line, excluding the prompt."
+  (interactive)
+  (if (search-backward-regexp intero-prompt-regexp (line-beginning-position) t 1)
+      (goto-char (+ 2 (line-beginning-position)))
+    (call-interactively 'move-beginning-of-line)))
+
+(defun intero-repl-switch-back ()
+  "Switch back to the buffer from which this REPL buffer was reached."
+  (interactive)
+  (if intero-repl-previous-buffer
+      (switch-to-buffer-other-window intero-repl-previous-buffer)
+    (message "No previous buffer.")))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Buffer operations
+
+(defun intero-thing-at-point ()
+  "Return (list START END) of something at the point."
+  (if (region-active-p)
+      (list (region-beginning)
+            (region-end))
+    (let ((pos (intero-ident-pos-at-point)))
+      (if pos
+          (list (car pos) (cdr pos))
+        (list (point) (point))))))
+
+(defun intero-ident-at-point ()
+  "Return the identifier under point, or nil if none found.
+May return a qualified name."
+  (let ((reg (intero-ident-pos-at-point)))
+    (when reg
+      (buffer-substring-no-properties (car reg) (cdr reg)))))
+
+(defun intero-ident-pos-at-point ()
+  "Return the span of the identifier near point going backward.
+Returns nil if no identifier found or point is inside string or
+comment.  May return a qualified name."
+  (when (not (nth 8 (syntax-ppss)))
+    ;; Do not handle comments and strings
+    (let (start end)
+      ;; Initial point position is non-deterministic, it may occur anywhere
+      ;; inside identifier span, so the approach is:
+      ;; - first try go left and find left boundary
+      ;; - then try go right and find right boundary
+      ;;
+      ;; In both cases assume the longest path, e.g. when going left take into
+      ;; account than point may occur at the end of identifier, when going right
+      ;; take into account that point may occur at the beginning of identifier.
+      ;;
+      ;; We should handle `.` character very careful because it is heavily
+      ;; overloaded.  Examples of possible cases:
+      ;; Control.Monad.>>=  -- delimiter
+      ;; Control.Monad.when -- delimiter
+      ;; Data.Aeson..:      -- delimiter and operator symbol
+      ;; concat.map         -- composition function
+      ;; .?                 -- operator symbol
+      (save-excursion
+        ;; First, skip whitespace if we're on it, moving point to last
+        ;; identifier char.  That way, if we're at "map ", we'll see the word
+        ;; "map".
+        (when (and (looking-at-p (rx eol))
+                   (not (bolp)))
+          (backward-char))
+        (when (and (not (eobp))
+                   (eq (char-syntax (char-after)) ? ))
+          (skip-chars-backward " \t")
+          (backward-char))
+        ;; Now let's try to go left.
+        (save-excursion
+          (if (not (intero-mode--looking-at-varsym))
+              ;; Looking at non-operator char, this is quite simple
+              (progn
+                (skip-syntax-backward "w_")
+                ;; Remember position
+                (setq start (point)))
+            ;; Looking at operator char.
+            (while (and (not (bobp))
+                        (intero-mode--looking-at-varsym))
+              ;; skip all operator chars backward
+              (setq start (point))
+              (backward-char))
+            ;; Extra check for case when reached beginning of the buffer.
+            (when (intero-mode--looking-at-varsym)
+              (setq start (point))))
+          ;; Slurp qualification part if present.  If identifier is qualified in
+          ;; case of non-operator point will stop before `.` dot, but in case of
+          ;; operator it will stand at `.` delimiting dot.  So if we're looking
+          ;; at `.` let's step one char forward and try to get qualification
+          ;; part.
+          (goto-char start)
+          (when (looking-at-p (rx "."))
+            (forward-char))
+          (let ((pos (intero-mode--skip-qualification-backward)))
+            (when pos
+              (setq start pos))))
+        ;; Finally, let's try to go right.
+        (save-excursion
+          ;; Try to slurp qualification part first.
+          (skip-syntax-forward "w_")
+          (setq end (point))
+          (while (and (looking-at-p (rx "." upper))
+                      (not (zerop (progn (forward-char)
+                                         (skip-syntax-forward "w_")))))
+            (setq end (point)))
+          ;; If point was at non-operator we already done, otherwise we need an
+          ;; extra check.
+          (while (intero-mode--looking-at-varsym)
+            (forward-char)
+            (setq end (point))))
+        (when (not (= start end))
+          (cons start end))))))
+
+(defun intero-mode--looking-at-varsym ()
+  "Return t when point stands at operator symbol."
+  (when (not (eobp))
+    (let ((lex (intero-lexeme-classify-by-first-char (char-after))))
+      (or (eq lex 'varsym)
+          (eq lex 'consym)))))
+
+(defun intero-mode--skip-qualification-backward ()
+  "Skip qualified part of identifier backward.
+Expects point stands *after* delimiting dot.
+Returns beginning position of qualified part or nil if no qualified part found."
+  (when (not (and (bobp)
+                  (looking-at-p (rx bol))))
+    (let ((case-fold-search nil)
+          pos)
+      (while (and (eq (char-before) ?.)
+                  (progn (backward-char)
+                         (not (zerop (skip-syntax-backward "w'"))))
+                  (skip-syntax-forward "'")
+                  (looking-at-p "[[:upper:]]"))
+        (setq pos (point)))
+      pos)))
+
+(defun intero-lexeme-classify-by-first-char (char)
+  "Classify token by CHAR.
+CHAR is a chararacter that is assumed to be the 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)))
+      'varid)
+     ((and (>= char ?0) (<= char ?9))
+      'number)
+     ((member char '(?\] ?\[ ?\( ?\) ?\{ ?\} ?\` ?\, ?\;))
+      'special))))
+
+(defun intero-buffer-file-name (&optional buffer)
+  "Call function `buffer-file-name' for BUFFER and clean its result.
+The path returned is canonicalized and stripped of any text properties."
+  (let ((name (buffer-file-name buffer)))
+    (when name
+      (intero-canonicalize-path (substring-no-properties name)))))
+
+(defun intero-paths-for-same-file (path-1 path-2)
+  "Compare PATH-1 and PATH-2 to see if they represent the same file."
+  (let ((simplify-path #'(lambda (path)
+                           (if (tramp-tramp-file-p path)
+                               (let* ((dissection (tramp-dissect-file-name path))
+                                      (host (tramp-file-name-host dissection))
+                                      (localname (tramp-file-name-localname dissection)))
+                                 (concat host ":" localname))
+                             (expand-file-name path)))))
+    (string= (funcall simplify-path path-1) (funcall simplify-path path-2))))
+
+(defun intero-buffer-host (&optional buffer)
+  "Get the hostname of the box hosting the file behind the BUFFER."
+  (with-current-buffer (or buffer (current-buffer))
+    (let ((file (intero-buffer-file-name)))
+      (if intero-buffer-host
+          intero-buffer-host
+        (setq intero-buffer-host
+              (when file
+                (if (tramp-tramp-file-p file)
+                    (tramp-file-name-host (tramp-dissect-file-name file))
+                  "")))))))
+
+(defun intero-extend-path-by-buffer-host (path &optional buffer)
+  "Take a PATH, and extend it by the host of the provided BUFFER (default to current buffer).  Return PATH unchanged if the file is local, or the BUFFER has no host."
+  (with-current-buffer (or buffer (current-buffer))
+    (if (or (eq nil (intero-buffer-host)) (eq "" (intero-buffer-host)))
+        path
+      (expand-file-name
+       (concat "/"
+               (intero-buffer-host)
+               ":"
+               path)))))
+
+(defvar-local intero-temp-file-name nil
+  "The name of a temporary file to which the current buffer's content is copied.")
+
+(defun intero-temp-file-p (path)
+  "Is PATH a temp file?"
+  (string= (file-name-directory path)
+           (file-name-directory (intero-temp-file-dir))))
+
+(defun intero-temp-file-origin-buffer (temp-file)
+  "Get the original buffer that TEMP-FILE was created for."
+  (or
+   (gethash (intero-canonicalize-path temp-file)
+            intero-temp-file-buffer-mapping)
+   (cl-loop
+    for buffer in (buffer-list)
+    when (string= (intero-canonicalize-path temp-file)
+                  (buffer-local-value 'intero-temp-file-name buffer))
+    return buffer)))
+
+(defun intero-unmangle-file-path (file)
+  "If FILE is an intero temp file, return the original source path, otherwise FILE."
+  (or (when (intero-temp-file-p file)
+        (let ((origin-buffer (intero-temp-file-origin-buffer file)))
+          (when origin-buffer
+            (buffer-file-name origin-buffer))))
+      file))
+
+(defun intero-make-temp-file (prefix &optional dir-flag suffix)
+  "Like `make-temp-file', but using a different temp directory.
+PREFIX, DIR-FLAG and SUFFIX are all passed to `make-temp-file'
+unmodified.  A different directory is applied so that if docker
+is used with stack, the commands run inside docker can find the
+path."
+  (let ((temporary-file-directory
+         (intero-temp-file-dir)))
+    (make-directory temporary-file-directory t)
+    (make-temp-file prefix dir-flag suffix)))
+
+(defun intero-temp-file-dir ()
+  "Get the temporary file directory for the current intero project."
+  (let* ((intero-absolute-project-root
+          (intero-extend-path-by-buffer-host (intero-project-root)))
+         (temporary-file-directory
+          (expand-file-name ".stack-work/intero/"
+                            intero-absolute-project-root)))
+    temporary-file-directory))
+
+(defun intero-temp-file-name (&optional buffer)
+  "Return the name of a temp file pertaining to BUFFER."
+  (with-current-buffer (or buffer (current-buffer))
+    (or intero-temp-file-name
+        (progn (setq intero-temp-file-name
+                     (intero-canonicalize-path
+                      (intero-make-temp-file
+                       "intero" nil
+                       (concat "-TEMP." (if (buffer-file-name)
+                                            (file-name-extension (buffer-file-name))
+                                          "hs")))))
+               (puthash intero-temp-file-name
+                        (current-buffer)
+                        intero-temp-file-buffer-mapping)
+               intero-temp-file-name))))
+
+(defun intero-staging-file-name (&optional buffer)
+  "Return the name of a temp file containing an up-to-date copy of BUFFER's contents."
+  (with-current-buffer (or buffer (current-buffer))
+    (let* ((contents (buffer-string))
+           (fname (intero-canonicalize-path
+                   (intero-make-temp-file
+                    "intero" nil
+                    (concat "-STAGING." (if (buffer-file-name)
+                                            (file-name-extension (buffer-file-name))
+                                          "hs"))))))
+      (with-temp-file fname
+        (insert contents))
+      fname)))
+
+(defun intero-quote-path-for-ghci (path)
+  "Quote PATH as necessary so that it can be passed to a GHCi :command."
+  (concat "\"" (replace-regexp-in-string "\\([\\\"]\\)" "\\\\\\1" path nil nil) "\""))
+
+(defun intero-path-for-ghci (path)
+  "Turn a possibly-remote PATH into one that can be passed to a GHCi :command."
+  (intero-quote-path-for-ghci (intero-localize-path path)))
+
+(defun intero-localize-path (path)
+  "Turn a possibly-remote PATH to a purely local one.
+This is used to create paths which a remote intero process can load."
+  (if (tramp-tramp-file-p path)
+      (tramp-file-name-localname (tramp-dissect-file-name path))
+    path))
+
+(defun intero-canonicalize-path (path)
+  "Return a standardized version of PATH.
+Path names are standardised and drive names are
+capitalized (relevant on Windows)."
+  (intero-capitalize-drive-letter (convert-standard-filename path)))
+
+(defun intero-capitalize-drive-letter (path)
+  "Ensures the drive letter is capitalized in PATH.
+This applies to paths of the form
+x:\\foo\\bar (i.e., Windows)."
+  (save-match-data
+    (let ((drive-path (split-string path ":\\\\")))
+      (if (or (null (car drive-path)) (null (cdr drive-path)))
+          path
+        (concat (upcase (car drive-path)) ":\\" (cadr drive-path))))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Query/commands
+
+(defun intero-get-all-types ()
+  "Get all types in all expressions in all modules."
+  (intero-blocking-network-call 'backend ":all-types"))
+
+(defun intero-get-type-at (beg end)
+  "Get the type at the given region denoted by BEG and END."
+  (let ((result (intero-get-type-at-helper beg end)))
+    (if (string-match (regexp-quote "Couldn't guess that module name. Does it exist?")
+                      result)
+        (progn (flycheck-buffer)
+               (message "No type information yet, compiling module ...")
+               (intero-get-type-at-helper-process beg end))
+      result)))
+
+(defun intero-get-type-at-helper (beg end)
+  (replace-regexp-in-string
+   "\n$" ""
+   (intero-blocking-network-call
+    'backend
+    (intero-format-get-type-at beg end))))
+
+(defun intero-get-type-at-helper-process (beg end)
+  (replace-regexp-in-string
+   "\n$" ""
+   (intero-blocking-call
+    'backend
+    (intero-format-get-type-at beg end))))
+
+(defun intero-get-type-at-async (cont beg end)
+  "Call CONT with type of the region denoted by BEG and END.
+CONT is called within the current buffer, with BEG, END and the
+type as arguments."
+  (intero-async-network-call
+   'backend
+   (intero-format-get-type-at beg end)
+   (list :cont cont
+         :source-buffer (current-buffer)
+         :beg beg
+         :end end)
+   (lambda (state reply)
+     (with-current-buffer (plist-get state :source-buffer)
+       (funcall (plist-get state :cont)
+                (plist-get state :beg)
+                (plist-get state :end)
+                (replace-regexp-in-string "\n$" "" reply))))))
+
+(defun intero-format-get-type-at (beg end)
+  "Compose a request for getting types in region from BEG to END."
+  (format ":type-at %s %d %d %d %d %S"
+          (intero-path-for-ghci (intero-temp-file-name))
+          (save-excursion (goto-char beg)
+                          (line-number-at-pos))
+          (save-excursion (goto-char beg)
+                          (1+ (current-column)))
+          (save-excursion (goto-char end)
+                          (line-number-at-pos))
+          (save-excursion (goto-char end)
+                          (1+ (current-column)))
+          (buffer-substring-no-properties beg end)))
+
+(defun intero-get-info-of (thing)
+  "Get info for THING."
+  (let ((optimistic-result
+         (replace-regexp-in-string
+          "\n$" ""
+          (intero-blocking-call
+           'backend
+           (format ":info %s" thing)))))
+    (if (string-match-p "^<interactive>" optimistic-result)
+        ;; Load the module Interpreted so that we get information,
+        ;; then restore bytecode.
+        (progn (intero-async-call
+                'backend
+                ":set -fbyte-code")
+               (set-buffer-modified-p t)
+               (save-buffer)
+               (unless (member 'save flycheck-check-syntax-automatically)
+                 (intero-async-call
+                  'backend
+                  (concat ":load " (intero-path-for-ghci (intero-temp-file-name)))))
+               (intero-async-call
+                'backend
+                ":set -fobject-code")
+               (replace-regexp-in-string
+                "\n$" ""
+                (intero-blocking-call
+                 'backend
+                 (format ":info %s" thing))))
+      optimistic-result)))
+
+(defconst intero-unloaded-module-string "Couldn't guess that module name. Does it exist?")
+
+(defun intero-get-loc-at (beg end)
+  "Get the location of the identifier denoted by BEG and END."
+  (let ((result (intero-get-loc-at-helper beg end)))
+    (if (string-match (regexp-quote intero-unloaded-module-string)
+                      result)
+        (progn (flycheck-buffer)
+               (message "No location information yet, compiling module ...")
+               (intero-get-loc-at-helper-process beg end))
+      result)))
+
+(defun intero-get-loc-at-helper (beg end)
+  "Make the blocking call to the process."
+  (replace-regexp-in-string
+   "\n$" ""
+   (intero-blocking-network-call
+    'backend
+    (format ":loc-at %s %d %d %d %d %S"
+            (intero-path-for-ghci (intero-temp-file-name))
+            (save-excursion (goto-char beg)
+                            (line-number-at-pos))
+            (save-excursion (goto-char beg)
+                            (1+ (current-column)))
+            (save-excursion (goto-char end)
+                            (line-number-at-pos))
+            (save-excursion (goto-char end)
+                            (1+ (current-column)))
+            (buffer-substring-no-properties beg end)))))
+
+(defun intero-get-loc-at-helper-process (beg end)
+  "Make the blocking call to the process."
+  (replace-regexp-in-string
+   "\n$" ""
+   (intero-blocking-call
+    'backend
+    (format ":loc-at %s %d %d %d %d %S"
+            (intero-path-for-ghci (intero-temp-file-name))
+            (save-excursion (goto-char beg)
+                            (line-number-at-pos))
+            (save-excursion (goto-char beg)
+                            (1+ (current-column)))
+            (save-excursion (goto-char end)
+                            (line-number-at-pos))
+            (save-excursion (goto-char end)
+                            (1+ (current-column)))
+            (buffer-substring-no-properties beg end)))))
+
+(defun intero-get-uses-at (beg end)
+  "Return usage list for identifier denoted by BEG and END."
+  (let ((result (intero-get-uses-at-helper beg end)))
+    (if (string-match (regexp-quote intero-unloaded-module-string)
+                      result)
+        (progn (flycheck-buffer)
+               (message "No use information yet, compiling module ...")
+               (intero-get-uses-at-helper-process beg end))
+      result)))
+
+(defun intero-get-uses-at-helper (beg end)
+  "Return usage list for identifier denoted by BEG and END."
+  (replace-regexp-in-string
+   "\n$" ""
+   (intero-blocking-network-call
+    'backend
+    (format ":uses %s %d %d %d %d %S"
+            (intero-path-for-ghci (intero-temp-file-name))
+            (save-excursion (goto-char beg)
+                            (line-number-at-pos))
+            (save-excursion (goto-char beg)
+                            (1+ (current-column)))
+            (save-excursion (goto-char end)
+                            (line-number-at-pos))
+            (save-excursion (goto-char end)
+                            (1+ (current-column)))
+            (buffer-substring-no-properties beg end)))))
+
+(defun intero-get-uses-at-helper-process (beg end)
+  "Return usage list for identifier denoted by BEG and END."
+  (replace-regexp-in-string
+   "\n$" ""
+   (intero-blocking-call
+    'backend
+    (format ":uses %s %d %d %d %d %S"
+            (intero-path-for-ghci (intero-temp-file-name))
+            (save-excursion (goto-char beg)
+                            (line-number-at-pos))
+            (save-excursion (goto-char beg)
+                            (1+ (current-column)))
+            (save-excursion (goto-char end)
+                            (line-number-at-pos))
+            (save-excursion (goto-char end)
+                            (1+ (current-column)))
+            (buffer-substring-no-properties beg end)))))
+
+(defun intero-get-completions (source-buffer beg end cont)
+  "Get completions and send to SOURCE-BUFFER.
+Prefix is marked by positions BEG and END.  Completions are
+passed to CONT in SOURCE-BUFFER."
+  (intero-async-network-call
+   'backend
+   (format ":complete-at %s %d %d %d %d %S"
+           (intero-path-for-ghci (intero-temp-file-name))
+           (save-excursion (goto-char beg)
+                           (line-number-at-pos))
+           (save-excursion (goto-char beg)
+                           (1+ (current-column)))
+           (save-excursion (goto-char end)
+                           (line-number-at-pos))
+           (save-excursion (goto-char end)
+                           (1+ (current-column)))
+           (buffer-substring-no-properties beg end))
+   (list :cont cont :source-buffer source-buffer)
+   (lambda (state reply)
+     (with-current-buffer
+         (plist-get state :source-buffer)
+       (funcall
+        (plist-get state :cont)
+        (intero-completion-response-to-list reply))))))
+
+(defun intero-completion-response-to-list (reply)
+  "Convert the REPLY from a backend completion to a list."
+  (if (string-match-p "^*** Exception" reply)
+      (list)
+    (mapcar
+     (lambda (x)
+       (replace-regexp-in-string "\\\"" "" x))
+     (split-string reply "\n" t))))
+
+(defun intero-get-repl-completions (source-buffer prefix cont)
+  "Get REPL completions and send to SOURCE-BUFFER.
+Completions for PREFIX are passed to CONT in SOURCE-BUFFER."
+  (intero-async-call
+   'backend
+   (format ":complete repl %S" prefix)
+   (list :cont cont :source-buffer source-buffer)
+   (lambda (state reply)
+     (with-current-buffer
+         (plist-get state :source-buffer)
+       (funcall
+        (plist-get state :cont)
+        (mapcar
+         (lambda (x)
+           (replace-regexp-in-string "\\\"" "" x))
+         (cdr (split-string reply "\n" t))))))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Process communication
+
+(defun intero-call-process (program &optional infile destination display &rest args)
+  "Synchronously call PROGRAM.
+INFILE, DESTINATION, DISPLAY and ARGS are as for
+'call-process'/'process-file'.  Provides TRAMP compatibility for
+'call-process'; when the 'default-directory' is on a remote
+machine, PROGRAM is launched on that machine."
+  (let ((process-args (append (list program infile destination display) args)))
+    (apply 'process-file process-args)))
+
+(defun intero-call-stack (&optional infile destination display stack-yaml &rest args)
+  "Synchronously call stack using the same arguments as `intero-call-process'.
+INFILE, DESTINATION, DISPLAY and ARGS are as for
+`call-process'/`process-file'.  STACK-YAML specifies which stack
+yaml config to use, or stack's default when nil."
+  (let ((stack-yaml-args (when stack-yaml
+                           (list "--stack-yaml" stack-yaml))))
+    (apply #'intero-call-process intero-stack-executable
+           infile destination display
+           (append stack-yaml-args args))))
+
+(defun intero-delete-worker (worker)
+  "Delete the given WORKER."
+  (when (intero-buffer-p worker)
+    (with-current-buffer (intero-get-buffer-create worker)
+      (when (get-buffer-process (current-buffer))
+        (setq intero-deleting t)
+        (kill-process (get-buffer-process (current-buffer)))
+        (delete-process (get-buffer-process (current-buffer))))
+      (kill-buffer (current-buffer)))))
+
+(defun intero-blocking-call (worker cmd)
+  "Send WORKER the command string CMD and block pending its result."
+  (let ((result (list nil)))
+    (intero-async-call
+     worker
+     cmd
+     result
+     (lambda (result reply)
+       (setf (car result) reply)))
+    (let ((buffer (intero-buffer worker)))
+      (while (not (null (buffer-local-value 'intero-callbacks buffer)))
+        (sleep-for 0.0001)))
+    (car result)))
+
+(defun intero-blocking-network-call (worker cmd)
+  "Send WORKER the command string CMD via the network and block pending its result."
+  (let ((result (list nil)))
+    (intero-async-network-call
+     worker
+     cmd
+     result
+     (lambda (result reply)
+       (setf (car result) reply)))
+    (while (eq (car result) nil)
+      (sleep-for 0.0001))
+    (car result)))
+
+(defun intero-async-network-call (worker cmd &optional state callback)
+  "Send WORKER the command string CMD, via a network connection.
+The result, along with the given STATE, is passed to CALLBACK
+as (CALLBACK STATE REPLY)."
+  (if (file-remote-p default-directory)
+      (intero-async-call worker cmd state callback)
+      (let ((buffer (intero-buffer worker)))
+        (if (and buffer (process-live-p (get-buffer-process buffer)))
+            (with-current-buffer buffer
+              (if intero-service-port
+                  (let* ((buffer (generate-new-buffer (format " intero-network:%S" worker)))
+                         (process
+                          (make-network-process
+                           :name (format "%S" worker)
+                           :buffer buffer
+                           :host 'local
+                           :service intero-service-port
+                           :family 'ipv4
+                           :nowait t
+                           :noquery t
+                           :sentinel 'intero-network-call-sentinel)))
+                    (with-current-buffer buffer
+                      (setq intero-async-network-cmd cmd)
+                      (setq intero-async-network-state state)
+                      (setq intero-async-network-worker worker)
+                      (setq intero-async-network-callback callback)))
+                (progn (when intero-debug (message "No `intero-service-port', falling back ..."))
+                       (intero-async-call worker cmd state callback))))
+          (error "Intero process is not running: run M-x intero-restart to start it")))))
+
+(defun intero-network-call-sentinel (process event)
+  (pcase event
+    ;; This event sometimes gets sent when (delete-process) is called, but
+    ;; inconsistently. We can't rely on it for killing buffers, but we need to
+    ;; handle the possibility.
+    ("deleted\n")
+
+    ("open\n"
+     (with-current-buffer (process-buffer process)
+       (when intero-debug (message "Connected to service, sending %S" intero-async-network-cmd))
+       (setq intero-async-network-connected t)
+       (if intero-async-network-cmd
+           (process-send-string process (concat intero-async-network-cmd "\n"))
+         (delete-process process)
+         (kill-buffer (process-buffer process)))))
+    (_
+     (with-current-buffer (process-buffer process)
+       (if intero-async-network-connected
+           (when intero-async-network-callback
+             (when intero-debug (message "Calling callback with %S" (buffer-string)))
+             (funcall intero-async-network-callback
+                      intero-async-network-state
+                      (buffer-string)))
+         ;; We didn't successfully connect, so let's fallback to the
+         ;; process pipe.
+         (when intero-async-network-callback
+           (when intero-debug (message "Failed to connect, falling back ... "))
+           (setq intero-async-network-callback nil)
+           (intero-async-call
+            intero-async-network-worker
+            intero-async-network-cmd
+            intero-async-network-state
+            intero-async-network-callback))))
+     (delete-process process)
+     (kill-buffer (process-buffer process)))))
+
+(defun intero-async-call (worker cmd &optional state callback)
+  "Send WORKER the command string CMD.
+The result, along with the given STATE, is passed to CALLBACK
+as (CALLBACK STATE REPLY)."
+  (let ((buffer (intero-buffer worker)))
+    (if (and buffer (process-live-p (get-buffer-process buffer)))
+        (progn (with-current-buffer buffer
+                 (setq intero-callbacks
+                       (append intero-callbacks
+                               (list (list state
+                                           (or callback #'ignore)
+                                           cmd)))))
+               (when intero-debug
+                 (message "[Intero] -> %s" cmd))
+               (comint-simple-send (intero-process worker) cmd))
+      (error "Intero process is not running: run M-x intero-restart to start it"))))
+
+(defun intero-buffer (worker)
+  "Get the WORKER buffer for the current directory."
+  (let ((buffer (intero-get-buffer-create worker))
+        (targets (buffer-local-value 'intero-targets (current-buffer))))
+    (if (get-buffer-process buffer)
+        buffer
+      (intero-get-worker-create worker targets (current-buffer)
+                                (buffer-local-value
+                                 'intero-stack-yaml (current-buffer))))))
+
+(defun intero-process (worker)
+  "Get the WORKER process for the current directory."
+  (get-buffer-process (intero-buffer worker)))
+
+(defun intero-get-worker-create (worker &optional targets source-buffer stack-yaml)
+  "Start the given WORKER.
+If provided, use the specified TARGETS, SOURCE-BUFFER and STACK-YAML."
+  (let* ((buffer (intero-get-buffer-create worker)))
+    (if (get-buffer-process buffer)
+        buffer
+      (let ((install-status (intero-installed-p)))
+        (if (eq install-status 'installed)
+            (intero-start-process-in-buffer buffer targets source-buffer stack-yaml)
+          (intero-auto-install buffer install-status targets source-buffer stack-yaml))))))
+
+(defun intero-auto-install (buffer install-status &optional targets source-buffer stack-yaml)
+  "Automatically install Intero appropriately for BUFFER.
+INSTALL-STATUS indicates the current installation status.
+If supplied, use the given TARGETS, SOURCE-BUFFER and STACK-YAML."
+  (if (buffer-local-value 'intero-give-up buffer)
+      buffer
+    (let ((source-buffer (or source-buffer (current-buffer))))
+      (switch-to-buffer buffer)
+      (erase-buffer)
+      (insert (cl-case install-status
+                (not-installed "Intero is not installed in the Stack environment.")
+                (wrong-version "The wrong version of Intero is installed for this Emacs package.")))
+      (if (intero-version>= (intero-stack-version) '(1 6 1))
+          (intero-copy-compiler-tool-auto-install source-buffer targets buffer)
+        (intero-old-auto-install source-buffer targets buffer stack-yaml)))))
+
+(defun intero-copy-compiler-tool-auto-install (source-buffer targets buffer)
+  "Automatically install Intero appropriately for BUFFER.
+Use the given TARGETS, SOURCE-BUFFER and STACK-YAML."
+  (let ((ghc-version (intero-ghc-version-raw)))
+    (insert
+     (format "
+
+Installing intero-%s for GHC %s ...
+
+" intero-package-version ghc-version))
+    (redisplay)
+    (cl-case
+        (let ((default-directory (make-temp-file "intero" t)))
+          (intero-call-stack
+           nil (current-buffer) t nil "build"
+           "--copy-compiler-tool"
+           (concat "intero-" intero-package-version)
+           "--flag" "haskeline:-terminfo"
+           "--resolver" (concat "ghc-" ghc-version)
+           "ghc-paths-0.1.0.9" "mtl-2.2.2" "network-2.7.0.0" "random-1.1" "syb-0.7"))
+      (0
+       (message "Installed successfully! Starting Intero in a moment ...")
+       (bury-buffer buffer)
+       (switch-to-buffer source-buffer)
+       (intero-start-process-in-buffer buffer targets source-buffer))
+      (1
+       (with-current-buffer buffer (setq-local intero-give-up t))
+       (insert (propertize "Could not install Intero!
+
+We don't know why it failed. Please read the above output and try
+installing manually. If that doesn't work, report this as a
+problem.
+
+WHAT TO DO NEXT
+
+If you don't want to Intero to try installing itself again for
+this project, just keep this buffer around in your Emacs.
+
+If you'd like to try again next time you try use an Intero
+feature, kill this buffer.
+"
+                           'face 'compilation-error))
+       nil))))
+
+(defun intero-old-auto-install (source-buffer targets buffer stack-yaml)
+  "Automatically install Intero appropriately for BUFFER.
+Use the given TARGETS, SOURCE-BUFFER and STACK-YAML."
+  (insert
+   "
+
+Installing intero-%s automatically ...
+
+" intero-package-version)
+  (redisplay)
+  (cl-case (intero-call-stack
+            nil (current-buffer) t stack-yaml
+            "build"
+            (with-current-buffer buffer
+              (let* ((cabal-file (intero-cabal-find-file))
+                     (package-name (intero-package-name cabal-file)))
+                ;; For local development. Most users'll
+                ;; never hit this behaviour.
+                (if (string= package-name "intero")
+                    "intero"
+                  (concat "intero-" intero-package-version))))
+            "ghc-paths" "syb"
+            "--flag" "haskeline:-terminfo")
+    (0
+     (message "Installed successfully! Starting Intero in a moment ...")
+     (bury-buffer buffer)
+     (switch-to-buffer source-buffer)
+     (intero-start-process-in-buffer buffer targets source-buffer))
+    (1
+     (with-current-buffer buffer (setq-local intero-give-up t))
+     (insert (propertize "Could not install Intero!
+
+We don't know why it failed. Please read the above output and try
+installing manually. If that doesn't work, report this as a
+problem.
+
+WHAT TO DO NEXT
+
+If you don't want to Intero to try installing itself again for
+this project, just keep this buffer around in your Emacs.
+
+If you'd like to try again next time you try use an Intero
+feature, kill this buffer.
+"
+                         'face 'compilation-error))
+     nil)))
+
+(defun intero-start-process-in-buffer (buffer &optional targets source-buffer stack-yaml)
+  "Start an Intero worker in BUFFER.
+Uses the specified TARGETS if supplied.
+Automatically performs initial actions in SOURCE-BUFFER, if specified.
+Uses the default stack config file, or STACK-YAML file if given."
+  (if (buffer-local-value 'intero-give-up buffer)
+      buffer
+    (let* ((options
+            (intero-make-options-list
+             (intero-executable-path stack-yaml)
+             (or targets
+                 (let ((package-name (buffer-local-value 'intero-package-name buffer)))
+                   (unless (equal "" package-name)
+                     (list package-name))))
+             (not (buffer-local-value 'intero-try-with-build buffer))
+             t ;; pass --no-load to stack
+             t ;; pass -ignore-dot-ghci to intero
+             stack-yaml ;; let stack choose a default when nil
+             ))
+           (arguments (cons "ghci" options))
+           (process (with-current-buffer buffer
+                      (when intero-debug
+                        (message "Intero arguments: %s" (combine-and-quote-strings arguments)))
+                      (message "Booting up intero ...")
+                      (apply #'start-file-process "stack" buffer intero-stack-executable
+                             arguments))))
+      (set-process-query-on-exit-flag process nil)
+      (process-send-string process ":set -fobject-code\n")
+      (process-send-string process ":set -fdefer-type-errors\n")
+      (process-send-string process ":set -fdiagnostics-color=never\n")
+      (process-send-string process ":set prompt \"\\4\"\n")
+      (with-current-buffer buffer
+        (erase-buffer)
+        (when stack-yaml
+          (setq intero-stack-yaml stack-yaml))
+        (setq intero-targets targets)
+        (setq intero-start-time (current-time))
+        (setq intero-source-buffer source-buffer)
+        (setq intero-arguments arguments)
+        (setq intero-starting t)
+        (setq intero-callbacks
+              (list (list (cons source-buffer
+                                buffer)
+                          (lambda (buffers msg)
+                            (let ((source-buffer (car buffers))
+                                  (process-buffer (cdr buffers)))
+                              (with-current-buffer process-buffer
+                                (when (string-match "^Intero-Service-Port: \\([0-9]+\\)\n" msg)
+                                  (setq intero-service-port (string-to-number (match-string 1 msg))))
+                                (setq-local intero-starting nil))
+                              (when source-buffer
+                                (with-current-buffer source-buffer
+                                  (when flycheck-mode
+                                    (run-with-timer 0 nil
+                                                    'intero-call-in-buffer
+                                                    (current-buffer)
+                                                    'intero-flycheck-buffer)))))
+                            (message "Booted up intero!"))))))
+      (set-process-filter
+       process
+       (lambda (process string)
+         (when intero-debug
+           (message "[Intero] <- %s" string))
+         (when (buffer-live-p (process-buffer process))
+           (with-current-buffer (process-buffer process)
+             (goto-char (point-max))
+             (insert string)
+             (when (and intero-try-with-build
+                        intero-starting)
+               (let ((last-line (buffer-substring-no-properties
+                                 (line-beginning-position)
+                                 (line-end-position))))
+                 (if (string-match-p "^Progress" last-line)
+                     (message "Booting up intero (building dependencies: %s)"
+                              (downcase
+                               (or (car (split-string (replace-regexp-in-string
+                                                       "\u0008+" "\n"
+                                                       last-line)
+                                                      "\n" t))
+                                   "...")))
+                   (message "Booting up intero ..."))))
+             (intero-read-buffer)))))
+      (set-process-sentinel process 'intero-sentinel)
+      buffer)))
+
+(defun intero-flycheck-buffer ()
+  "Run flycheck in the buffer.
+Restarts flycheck in case there was a problem and flycheck is stuck."
+  (flycheck-mode -1)
+  (flycheck-mode)
+  (flycheck-buffer))
+
+(defun intero-make-options-list (with-ghc targets no-build no-load ignore-dot-ghci stack-yaml)
+  "Make the stack ghci options list.
+TARGETS are the build targets.  When non-nil, NO-BUILD and
+NO-LOAD enable the correspondingly-named stack options.  When
+IGNORE-DOT-GHCI is non-nil, it enables the corresponding GHCI
+option.  STACK-YAML is the stack config file to use (or stack's
+default when nil)."
+  (append (when stack-yaml
+            (list "--stack-yaml" stack-yaml))
+          (list "--with-ghc"
+                with-ghc
+                "--docker-run-args=--interactive=true --tty=false"
+                )
+          (when no-build
+            (list "--no-build"))
+          (when no-load
+            (list "--no-load"))
+          (when ignore-dot-ghci
+            (list "--ghci-options" "-ignore-dot-ghci"))
+          (cl-mapcan (lambda (x) (list "--ghci-options" x)) intero-extra-ghc-options)
+          targets))
+
+(defun intero-sentinel (process change)
+  "Handle when PROCESS reports a CHANGE.
+This is a standard process sentinel function."
+  (when (buffer-live-p (process-buffer process))
+    (when (and (not (process-live-p process)))
+      (let ((buffer (process-buffer process)))
+        (if (with-current-buffer buffer intero-deleting)
+            (message "Intero process deleted.")
+          (if (and (intero-unsatisfied-package-p buffer)
+                   (not (buffer-local-value 'intero-try-with-build buffer)))
+              (progn (with-current-buffer buffer (setq-local intero-try-with-build t))
+                     (intero-start-process-in-buffer
+                      buffer
+                      (buffer-local-value 'intero-targets buffer)
+                      (buffer-local-value 'intero-source-buffer buffer)))
+            (progn (with-current-buffer buffer (setq-local intero-give-up t))
+                   (intero-show-process-problem process change))))))))
+
+(defun intero-unsatisfied-package-p (buffer)
+  "Return non-nil if BUFFER contain GHCi's unsatisfied package complaint."
+  (with-current-buffer buffer
+    (save-excursion
+      (goto-char (point-min))
+      (search-forward-regexp "cannot satisfy -package" nil t 1))))
+
+(defun intero-executable-path (stack-yaml)
+  "The path for the intero executable."
+  (intero-with-temp-buffer
+    (cl-case (save-excursion
+               (intero-call-stack
+                nil (current-buffer) t intero-stack-yaml "path" "--compiler-tools-bin"))
+      (0 (replace-regexp-in-string "[\r\n]+$" "/intero" (buffer-string)))
+      (1 "intero"))))
+
+(defun intero-installed-p ()
+  "Return non-nil if intero (of the right version) is installed in the stack environment."
+  (redisplay)
+  (intero-with-temp-buffer
+    (if (= 0 (intero-call-stack
+              nil t nil intero-stack-yaml
+              "exec"
+              "--verbosity" "silent"
+              "--"
+              (intero-executable-path intero-stack-yaml)
+              "--version"))
+        (progn
+          (goto-char (point-min))
+          ;; This skipping comes due to https://github.com/commercialhaskell/intero/pull/216/files
+          (when (looking-at "Intero ")
+            (goto-char (match-end 0)))
+          ;;
+          (if (string= (buffer-substring (point) (line-end-position))
+                       intero-package-version)
+              'installed
+            'wrong-version))
+      'not-installed)))
+
+(defun intero-show-process-problem (process change)
+  "Report to the user that PROCESS reported CHANGE, causing it to end."
+  (message "Problem with Intero!")
+  (switch-to-buffer (process-buffer process))
+  (goto-char (point-max))
+  (insert "\n---\n\n")
+  (insert
+   (propertize
+    (concat
+     "This is the buffer where Emacs talks to intero. It's normally hidden,
+but a problem occcured.
+
+TROUBLESHOOTING
+
+It may be obvious if there is some text above this message
+indicating a problem.
+
+If you do not wish to use Intero for some projects, see
+https://github.com/commercialhaskell/intero#whitelistingblacklisting-projects
+
+The process ended. Here is the reason that Emacs gives us:
+
+"
+     "  " change
+     "\n"
+     "For troubleshooting purposes, here are the arguments used to launch intero:
+
+"
+     (format "  %s %s"
+             intero-stack-executable
+             (combine-and-quote-strings intero-arguments))
+
+     "
+
+It's worth checking that the correct stack executable is being
+found on your path, or has been set via
+`intero-stack-executable'.  The executable being used now is:
+
+  "
+     (executable-find intero-stack-executable)
+     "
+
+WHAT TO DO NEXT
+
+If you fixed the problem, just kill this buffer, Intero will make
+a fresh one and attempt to start the process automatically as
+soon as you start editing code again.
+
+If you are unable to fix the problem, just leave this buffer
+around in Emacs and Intero will not attempt to start the process
+anymore.
+
+You can always run M-x intero-restart to make it try again.
+
+")
+    'face 'compilation-error)))
+
+(defun intero-read-buffer ()
+  "In the process buffer, we read what's in it."
+  (let ((repeat t))
+    (while repeat
+      (setq repeat nil)
+      (goto-char (point-min))
+      (when (search-forward "\4" (point-max) t 1)
+        (let* ((next-callback (pop intero-callbacks))
+               (state (nth 0 next-callback))
+               (func (nth 1 next-callback)))
+          (let ((string (intero-strip-carriage-returns (buffer-substring (point-min) (1- (point))))))
+            (if next-callback
+                (progn (intero-with-temp-buffer
+                         (funcall func state string))
+                       (setq repeat t))
+              (when intero-debug
+                (intero--warn "Received output but no callback in `intero-callbacks': %S"
+                              string)))))
+        (delete-region (point-min) (point))))))
+
+(defun intero-strip-carriage-returns (string)
+  "Strip the \\r from Windows \\r\\n line endings in STRING."
+  (replace-regexp-in-string "\r" "" string))
+
+(defun intero-get-buffer-create (worker)
+  "Get or create the stack buffer for WORKER.
+Uses the directory of the current buffer for context."
+  (let* ((root (intero-extend-path-by-buffer-host (intero-project-root)))
+         (cabal-file (intero-cabal-find-file))
+         (package-name (if cabal-file
+                           (intero-package-name cabal-file)
+                         ""))
+         (initial-buffer (current-buffer))
+         (buffer-name (intero-buffer-name worker))
+         (default-directory (if cabal-file
+                                (file-name-directory cabal-file)
+                              root)))
+    (with-current-buffer
+        (get-buffer-create buffer-name)
+      (intero-inherit-local-variables initial-buffer)
+      (setq intero-package-name package-name)
+      (cd default-directory)
+      (current-buffer))))
+
+(defun intero-gave-up (worker)
+  "Return non-nil if starting WORKER or installing intero failed."
+  (and (intero-buffer-p worker)
+       (let ((buffer (get-buffer (intero-buffer-name worker))))
+         (buffer-local-value 'intero-give-up buffer))))
+
+(defun intero-buffer-p (worker)
+  "Return non-nil if a buffer exists for WORKER."
+  (get-buffer (intero-buffer-name worker)))
+
+(defun intero-buffer-name (worker)
+  "For a given WORKER, create a buffer name."
+  (let* ((root (intero-project-root))
+         (package-name (intero-package-name)))
+    (concat " intero:"
+            (format "%s" worker)
+            ":"
+            package-name
+            " "
+            root)))
+
+(defun intero-project-root ()
+  "Get the current stack config directory.
+This is the directory where the file specified in
+`intero-stack-yaml' is located, or if nil then the directory
+where stack.yaml is placed for this project, or the global one if
+no such project-specific config exists."
+  (if intero-project-root
+      intero-project-root
+    (let ((stack-yaml intero-stack-yaml))
+      (setq intero-project-root
+            (intero-with-temp-buffer
+              (cl-case (save-excursion
+                         (intero-call-stack nil (current-buffer) nil stack-yaml
+                                            "path"
+                                            "--project-root"
+                                            "--verbosity" "silent"))
+                (0 (buffer-substring (line-beginning-position) (line-end-position)))
+                (t (intero--warn "Couldn't get the Stack project root.
+
+This can be caused by a syntax error in your stack.yaml file. Check that out.
+
+If you do not wish to use Intero for some projects, see
+https://github.com/commercialhaskell/intero#whitelistingblacklisting-projects
+
+Otherwise, please report this as a bug!
+
+For debugging purposes, try running the following in your terminal:
+
+%s path --project-root" intero-stack-executable)
+                   nil)))))))
+
+(defun intero-ghc-version ()
+  "Get the GHC version used by the project, calls only once per backend."
+  (with-current-buffer (intero-buffer 'backend)
+    (or intero-ghc-version
+        (setq intero-ghc-version
+              (intero-ghc-version-raw)))))
+
+(defun intero-ghc-version-raw ()
+  "Get the GHC version used by the project."
+  (intero-with-temp-buffer
+    (cl-case (save-excursion
+               (intero-call-stack
+                nil (current-buffer) t intero-stack-yaml
+                "ghc" "--" "--numeric-version"))
+      (0
+       (buffer-substring (line-beginning-position) (line-end-position)))
+      (1 nil))))
+
+(defun intero-version>= (new0 old0)
+  "Is the version NEW >= OLD?"
+  (or (and (null new0) (null old0))
+      (let ((new (or new0 (list 0)))
+            (old (or old0 (list 0))))
+        (or (> (car new)
+               (car old))
+            (and (= (car new)
+                    (car old))
+                 (intero-version>= (cdr new)
+                                   (cdr old)))))))
+
+(defun intero-stack-version ()
+  "Get the version components of stack."
+  (let* ((str (intero-stack-version-raw))
+         (parts (mapcar #'string-to-number (split-string str "\\."))))
+    parts))
+
+(defun intero-stack-version-raw ()
+  "Get the Stack version in PATH."
+  (intero-with-temp-buffer
+    (cl-case (save-excursion
+               (intero-call-stack
+                nil (current-buffer) t intero-stack-yaml "--numeric-version"))
+      (0
+       (buffer-substring (line-beginning-position) (line-end-position)))
+      (1 nil))))
+
+(defun intero-get-targets ()
+  "Get all available targets."
+  (with-current-buffer (intero-buffer 'backend)
+    (intero-with-temp-buffer
+      (cl-case (intero-call-stack nil (current-buffer) t
+                                  intero-stack-yaml
+                                  "ide" "targets")
+        (0
+         (cl-remove-if-not
+          (lambda (line)
+            (string-match-p "^[A-Za-z0-9-:_]+$" line))
+          (split-string (buffer-string) "[\r\n]" t)))
+        (1 nil)))))
+
+(defun intero-package-name (&optional cabal-file)
+  "Get the current package name from a nearby .cabal file.
+If there is none, return an empty string.  If specified, use
+CABAL-FILE rather than trying to locate one."
+  (or intero-package-name
+      (setq intero-package-name
+            (let ((cabal-file (or cabal-file
+                                  (intero-cabal-find-file))))
+              (if cabal-file
+                  (replace-regexp-in-string
+                   ".cabal$" ""
+                   (file-name-nondirectory cabal-file))
+                "")))))
+
+(defun intero-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 `intero-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 (intero-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 intero-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 (lambda (path)
+                          (or (file-directory-p path)
+                              (not (file-exists-p path))))
+                        (directory-files dir t ".\\.cabal\\'" t))))
+    (cond
+     ((= (length cabal-files) 1) (car cabal-files)) ;; exactly one candidate found
+     (allow-multiple cabal-files) ;; pass-thru multiple candidates
+     (t nil))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Multiselection
+
+(defvar intero-multiswitch-keymap
+  (let ((map (copy-keymap widget-keymap)))
+    (define-key map (kbd "C-c C-c") 'exit-recursive-edit)
+    (define-key map (kbd "C-c C-k") 'abort-recursive-edit)
+    (define-key map (kbd "C-g")     'abort-recursive-edit)
+    map))
+
+(defun intero-multiswitch (title options)
+  "Displaying TITLE, read multiple flags from a list of OPTIONS.
+Each option is a plist of (:key :default :title) wherein:
+
+  :key should be something comparable with EQUAL
+  :title should be a string
+  :default (boolean) specifies the default checkedness"
+  (let ((available-width (window-total-width)))
+    (save-window-excursion
+      (intero-with-temp-buffer
+        (rename-buffer (generate-new-buffer-name "multiswitch"))
+        (widget-insert (concat title "\n\n"))
+        (widget-insert (propertize "Select options with RET, hit " 'face 'font-lock-comment-face))
+        (widget-create 'push-button :notify
+                       (lambda (&rest ignore)
+                         (exit-recursive-edit))
+                       "C-c C-c")
+        (widget-insert (propertize " to apply these choices, or hit " 'face 'font-lock-comment-face))
+        (widget-create 'push-button :notify
+                       (lambda (&rest ignore)
+                         (abort-recursive-edit))
+                       "C-c C-k")
+        (widget-insert (propertize " to cancel.\n\n" 'face 'font-lock-comment-face))
+        (let* ((me (current-buffer))
+               (choices (mapcar (lambda (option)
+                                  (append option (list :value (plist-get option :default))))
+                                options)))
+          (cl-loop for option in choices
+                   do (widget-create
+                       'toggle
+                       :notify (lambda (widget &rest ignore)
+                                 (setq choices
+                                       (mapcar (lambda (choice)
+                                                 (if (equal (plist-get choice :key)
+                                                            (plist-get (cdr widget) :key))
+                                                     (plist-put choice :value (plist-get (cdr widget) :value))
+                                                   choice))
+                                               choices)))
+                       :on (concat "[x] " (plist-get option :title))
+                       :off (concat "[ ] " (plist-get option :title))
+                       :value (plist-get option :default)
+                       :key (plist-get option :key)))
+          (let ((lines (line-number-at-pos)))
+            (select-window (split-window-below))
+            (switch-to-buffer me)
+            (goto-char (point-min)))
+          (use-local-map intero-multiswitch-keymap)
+          (widget-setup)
+          (recursive-edit)
+          (kill-buffer me)
+          (mapcar (lambda (choice)
+                    (plist-get choice :key))
+                  (cl-remove-if-not (lambda (choice)
+                                      (plist-get choice :value))
+                                    choices)))))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Hoogle
+
+(defun intero-hoogle-blocking-query (query)
+  "Make a request of QUERY using the local hoogle server.
+If running, otherwise returns nil.
+
+It is the responsibility of the caller to make sure the server is
+running; the user might not want to start the server
+automatically."
+  (let ((buffer (intero-hoogle-get-buffer)))
+    (when buffer
+      (let ((url (intero-hoogle-url buffer query)))
+        (with-current-buffer (url-retrieve-synchronously url t)
+          (search-forward "\n\n" nil t 1)
+          (json-read-from-string
+           (buffer-substring (line-beginning-position)
+                             (line-end-position))))))))
+
+(defun intero-hoogle-url (buffer query)
+  "Via hoogle server BUFFER make the HTTP URL for QUERY."
+  (format "http://127.0.0.1:%d/?hoogle=%s&mode=json"
+          (buffer-local-value 'intero-hoogle-port buffer)
+          (url-encode-url query)))
+
+(defun intero-hoogle-get-worker-create ()
+  "Get or create the hoogle worker."
+  (let* ((buffer (intero-hoogle-get-buffer-create)))
+    (if (get-buffer-process buffer)
+        buffer
+      (intero-start-hoogle-process-in-buffer buffer))))
+
+(defun intero-start-hoogle-process-in-buffer (buffer)
+  "Start the process in BUFFER, returning BUFFER."
+  (let* ((port (intero-free-port))
+         (process (with-current-buffer buffer
+                    (message "Booting up hoogle ...")
+                    (setq intero-hoogle-port port)
+                    (start-process "hoogle"
+                                   buffer
+                                   intero-stack-executable
+                                   "hoogle"
+                                   "server"
+                                   "--no-setup"
+                                   "--"
+                                   "--local"
+                                   "--port"
+                                   (number-to-string port)))))
+    (set-process-query-on-exit-flag process nil)
+    (set-process-sentinel process 'intero-hoogle-sentinel)
+    buffer))
+
+(defun intero-free-port ()
+  "Get the next free port to use."
+  (let ((proc (make-network-process
+               :name "port-check"
+               :family 'ipv4
+               :host "127.0.0.1"
+               :service t
+               :server t)))
+    (delete-process proc)
+    (process-contact proc :service)))
+
+(defun intero-hoogle-sentinel (process change)
+  "For the hoogle PROCESS there is a CHANGE to handle."
+  (message "Hoogle sentinel: %S %S" process change))
+
+(defun intero-hoogle-get-buffer-create ()
+  "Get or create the Hoogle buffer for the current stack project."
+  (let* ((root (intero-project-root))
+         (buffer-name (intero-hoogle-buffer-name root))
+         (buf (get-buffer buffer-name))
+         (initial-buffer (current-buffer))
+         (default-directory root))
+    (if buf
+        buf
+      (with-current-buffer (get-buffer-create buffer-name)
+        (intero-inherit-local-variables initial-buffer)
+        (cd default-directory)
+        (current-buffer)))))
+
+(defun intero-hoogle-get-buffer ()
+  "Get the Hoogle buffer for the current stack project."
+  (let* ((root (intero-project-root))
+         (buffer-name (intero-hoogle-buffer-name root)))
+    (get-buffer buffer-name)))
+
+(defun intero-hoogle-buffer-name (root)
+  "For a given worker, create a buffer name using ROOT."
+  (concat "*Hoogle:" root "*"))
+
+(defun intero-hoogle-ready-p ()
+  "Is hoogle ready to be started?"
+  (intero-with-temp-buffer
+    (cl-case (intero-call-stack nil (current-buffer) t intero-stack-yaml
+                                "hoogle" "--no-setup" "--verbosity" "silent")
+      (0 t))))
+
+(defun intero-hoogle-supported-p ()
+  "Is the stack hoogle command supported?"
+  (intero-with-temp-buffer
+    (cl-case (intero-call-stack nil (current-buffer) t
+                                intero-stack-yaml
+                                "hoogle" "--help")
+      (0 t))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Collecting information from compiler messages
+
+(defun intero-collect-compiler-messages (msgs)
+  "Collect information from compiler MSGS.
+
+This may update in-place the MSGS objects to hint that
+suggestions are available."
+  (setq intero-suggestions nil)
+  (let ((extension-regex (concat " " (regexp-opt (intero-extensions) t) "\\>"))
+        (quoted-symbol-regex "[‘`‛]\\([^ ]+\\)['’]"))
+    (cl-loop
+     for msg in msgs
+     do (let ((text (flycheck-error-message msg))
+              (note nil))
+          ;; Messages of this format:
+          ;;
+          ;;     • Constructor ‘Assert’ does not have the required strict field(s): assertName,
+          ;; assertDoc, assertExpression,
+          ;; assertSection
+          (let ((start 0))
+            (while (or
+                    (string-match "does not have the required strict field.*?:[\n\t\r ]" text start)
+                    (string-match "Fields of .*? not initialised:[\n\t\r ]" text start))
+              (let* ((match-end (match-end 0))
+                     (fields
+                      (let ((reached-end nil))
+                        (mapcar
+                         (lambda (field)
+                           (with-temp-buffer
+                             (insert field)
+                             (goto-char (point-min))
+                             (intero-ident-at-point)))
+                         (cl-remove-if
+                          (lambda (field)
+                            (or reached-end
+                                (when (string-match "[\r\n]" field)
+                                  (setq reached-end t)
+                                  nil)))
+                          (split-string
+                           (substring text match-end)
+                           "[\n\t\r ]*,[\n\t\r ]*" t))))))
+                (setq note t)
+                (add-to-list
+                 'intero-suggestions
+                 (list :type 'add-missing-fields
+                       :fields fields
+                       :line (flycheck-error-line msg)
+                       :column (flycheck-error-column msg)))
+                (setq start (min (length text) (1+ match-end))))))
+
+          ;; Messages of this format:
+          ;;
+          ;; Can't make a derived instance of ‘Functor X’:
+          ;;       You need DeriveFunctor to derive an instance for this class
+          ;;       Try GeneralizedNewtypeDeriving for GHC's newtype-deriving extension
+          ;;       In the newtype declaration for ‘X’
+          (let ((start 0))
+            (while (let ((case-fold-search nil))
+                     (string-match extension-regex text start))
+              (setq note t)
+              (add-to-list 'intero-suggestions
+                           (list :type 'add-extension
+                                 :extension (match-string 1 text)))
+              (setq start (min (length text) (1+ (match-end 0))))))
+          ;; Messages of this format:
+          ;;
+          ;; Could not find module ‘Language.Haskell.TH’
+          ;;     It is a member of the hidden package ‘template-haskell’.
+          ;;     Use -v to see a list of the files searched for....
+          (let ((start 0))
+            (while (string-match "It is a member of the hidden package [‘`‛]\\([^ ]+\\)['’]" text start)
+              (setq note t)
+              (add-to-list 'intero-suggestions
+                           (list :type 'add-package
+                                 :package (match-string 1 text)))
+              (setq start (min (length text) (1+ (match-end 0))))))
+          ;; Messages of this format:
+          ;; Expected type: String
+          ;; Actual type: Data.Text.Internal.Builder.Builder
+          (let ((start 0))
+            (while (or (string-match
+                        "Expected type: String" text start)
+                       (string-match
+                        "Actual type: String" text start)
+                       (string-match
+                        "Actual type: \\[Char\\]" text start)
+                       (string-match
+                        "Expected type: \\[Char\\]" text start))
+              (setq note t)
+              (add-to-list 'intero-suggestions
+                           (list :type 'add-extension
+                                 :extension "OverloadedStrings"))
+              (setq start (min (length text) (1+ (match-end 0))))))
+          ;; Messages of this format:
+          ;;
+          ;; Defaulting the following constraint(s) to type ‘Integer’
+          ;;   (Num a0) arising from the literal ‘1’
+          ;; In the expression: 2
+          ;; In an equation for ‘x'’: x' = 2
+          (let ((start 0))
+            (while (string-match
+                    " Defaulting the following constraint" text start)
+              (setq note t)
+              (add-to-list 'intero-suggestions
+                           (list :type 'add-ghc-option
+                                 :option "-fno-warn-type-defaults"))
+              (setq start (min (length text) (1+ (match-end 0))))))
+          ;; Messages of this format:
+          ;;
+          ;;     This binding for ‘x’ shadows the existing binding
+          (let ((start 0))
+            (while (string-match
+                    " This binding for ‘\\(.*\\)’ shadows the existing binding" text start)
+              (setq note t)
+              (add-to-list 'intero-suggestions
+                           (list :type 'add-ghc-option
+                                 :option "-fno-warn-name-shadowing"))
+              (setq start (min (length text) (1+ (match-end 0))))))
+          ;; Messages of this format:
+          ;; Perhaps you want to add ‘foo’ to the import list
+          ;; in the import of ‘Blah’
+          ;; (/path/to/thing:19
+          (when (string-match "Perhaps you want to add [‘`‛]\\([^ ]+\\)['’][\n ]+to[\n ]+the[\n ]+import[\n ]+list[\n ]+in[\n ]+the[\n ]+import[\n ]+of[\n ]+[‘`‛]\\([^ ]+\\)['’][\n ]+(\\([^ ]+\\):(?\\([0-9]+\\)[:,]"
+                              text)
+            (let ((ident (match-string 1 text))
+                  (module (match-string 2 text))
+                  (file (match-string 3 text))
+                  (line (string-to-number (match-string 4 text))))
+              (setq note t)
+              (add-to-list 'intero-suggestions
+                           (list :type 'add-to-import
+                                 :module module
+                                 :ident ident
+                                 :line line))))
+          ;; Messages of this format:
+          ;;
+          ;; The import of ‘Control.Monad’ is redundant
+          ;;   except perhaps to import instances from ‘Control.Monad’
+          ;; To import instances alone, use: import Control.Monad()... (intero)
+          (when (string-match
+                 " The \\(qualified \\)?import of[ ][‘`‛]\\([^ ]+\\)['’] is redundant"
+                 text)
+            (setq note t)
+            (add-to-list 'intero-suggestions
+                         (list :type 'remove-import
+                               :module (match-string 2 text)
+                               :line (flycheck-error-line msg))))
+          ;; Messages of this format:
+          ;;
+          ;; Not in scope: ‘putStrn’
+          ;; Perhaps you meant one of these:
+          ;;   ‘putStr’ (imported from Prelude),
+          ;;   ‘putStrLn’ (imported from Prelude)
+          ;;
+          ;; Or this format:
+          ;;
+          ;; error:
+          ;;    • Variable not in scope: lopSetup :: [Statement Exp']
+          ;;    • Perhaps you meant ‘loopSetup’ (line 437)
+          (when (string-match
+                 "[Nn]ot in scope: \\(data constructor \\|type constructor or class \\)?[‘`‛]?\\([^'’ ]+\\).*\n.*Perhaps you meant"
+                 text)
+            (let ((typo (match-string 2 text))
+                  (start (min (length text) (1+ (match-end 0)))))
+              (while (string-match quoted-symbol-regex text start)
+                (setq note t)
+                (add-to-list 'intero-suggestions
+                             (list :type 'fix-typo
+                                   :typo typo
+                                   :replacement (match-string 1 text)
+                                   :column (flycheck-error-column msg)
+                                   :line (flycheck-error-line msg)))
+                (setq start (min (length text) (1+ (match-end 0)))))))
+          ;; Messages of this format:
+          ;;
+          ;;     Top-level binding with no type signature: main :: IO ()
+          (when (string-match
+                 "Top-level binding with no type signature:"
+                 text)
+            (let ((start (min (length text) (match-end 0))))
+              (setq note t)
+              (add-to-list 'intero-suggestions
+                           (list :type 'add-signature
+                                 :signature (mapconcat #'identity (split-string (substring text start)) " ")
+                                 :line (flycheck-error-line msg)))))
+          ;; Messages of this format:
+          (when (string-match "The import of [‘`‛]\\(.+?\\)[’`'][\n ]+from[\n ]+module[\n ]+[‘`‛]\\(.+?\\)[’`'][\n ]+is[\n ]+redundant" text)
+            (let ((module (match-string 2 text))
+                  (idents (split-string (match-string 1 text) "," t "[ \n]+")))
+              (setq note t)
+              (add-to-list 'intero-suggestions
+                           (list :type 'redundant-import-item
+                                 :idents idents
+                                 :line (flycheck-error-line msg)
+                                 :module module))))
+          ;; Messages of this format:
+          ;;
+          ;;     Redundant constraints: (Arith var, Bitwise var)
+          ;; Or
+          ;;     Redundant constraint: Arith var
+          ;; Or
+          ;;     Redundant constraints: (Arith var,
+          ;;                             Bitwise var,
+          ;;                             Functor var,
+          ;;                             Applicative var,
+          ;;                             Monad var)
+          (when (string-match "Redundant constraints?: " text)
+            (let* ((redundant-start (match-end 0))
+                   (parts (intero-with-temp-buffer
+                            (insert (substring text redundant-start))
+                            (goto-char (point-min))
+                            ;; A lone unparenthesized constraint might
+                            ;; be multiple sexps.
+                            (while (not (eq (point) (point-at-eol)))
+                              (forward-sexp))
+                            (let ((redundant-end (point)))
+                              (search-forward-regexp ".*\n.*In the ")
+                              (cons (buffer-substring (point-min) redundant-end)
+                                    (buffer-substring (match-end 0) (point-max)))))))
+              (setq note t)
+              (add-to-list
+               'intero-suggestions
+               (let ((rest (cdr parts))
+                     (redundant (let ((raw (car parts)))
+                                  (if (eq (string-to-char raw) ?\()
+                                      (substring raw 1 (1- (length raw)))
+                                    raw))))
+                 (list :type 'redundant-constraint
+                       :redundancies (mapcar #'string-trim
+                                             (intero-parse-comma-list redundant))
+                       :signature (mapconcat #'identity (split-string rest) " ")
+                       :line (flycheck-error-line msg))))))
+          ;; Add a note if we found a suggestion to make
+          (when note
+            (setf (flycheck-error-message msg)
+                  (concat text
+                          "\n\n"
+                          (propertize "(Hit `C-c C-r' in the Haskell buffer to apply suggestions)"
+                                      'face 'font-lock-warning-face)))))))
+  (setq intero-lighter
+        (if (null intero-suggestions)
+            " Intero"
+          (format " Intero:%d" (length intero-suggestions)))))
+
+(defun intero-extensions ()
+  "Get extensions for the current project's GHC."
+  (with-current-buffer (intero-buffer 'backend)
+    (or intero-extensions
+        (setq intero-extensions
+              (cl-remove-if-not
+               (lambda (str) (let ((case-fold-search nil))
+                               (string-match "^[A-Z][A-Za-z0-9]+$" str)))
+               (split-string
+                (shell-command-to-string
+                 (concat intero-stack-executable
+                         (if intero-stack-yaml
+                             (concat "--stack-yaml " intero-stack-yaml)
+                           "")
+                         " exec --verbosity silent -- ghc --supported-extensions"))))))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Auto actions
+
+(defun intero-parse-comma-list (text)
+  "Parse a list of comma-separated expressions in TEXT."
+  (cl-loop for tok in (split-string text "[[:space:]\n]*,[[:space:]\n]*")
+           with acc = nil
+           append (let* ((clist (string-to-list tok))
+                         (num-open (-count (lambda (c) (or (eq c ?\() (eq c ?\[)))
+                                           clist))
+                         (num-close (-count (lambda (c) (or (eq c ?\)) (eq c ?\])))
+                                            clist)))
+                    (cond
+                     ((> num-open num-close) (progn (add-to-list 'acc tok) nil))
+                     ((> num-close num-open) (let ((tmp (reverse (cons tok acc))))
+                                               (setq acc nil)
+                                               (list (string-join tmp ", "))))
+                     (t (list tok))))))
+
+(defun intero-apply-suggestions ()
+  "Prompt and apply the suggestions."
+  (interactive)
+  (if (null intero-suggestions)
+      (message "No suggestions to apply")
+    (let ((to-apply
+           (intero-multiswitch
+            (format "There are %d suggestions to apply:" (length intero-suggestions))
+            (cl-remove-if-not
+             #'identity
+             (mapcar
+              (lambda (suggestion)
+                (cl-case (plist-get suggestion :type)
+                  (add-to-import
+                   (list :key suggestion
+                         :title (format "Add ‘%s’ to import of ‘%s’"
+                                        (plist-get suggestion :ident)
+                                        (plist-get suggestion :module))
+                         :default t))
+                  (add-missing-fields
+                   (list :key suggestion
+                         :default t
+                         :title
+                         (format "Add missing fields to record: %s"
+                                 (mapconcat (lambda (ident)
+                                              (concat "‘" ident "’"))
+                                            (plist-get suggestion :fields)
+                                            ", "))))
+                  (redundant-import-item
+                   (list :key suggestion
+                         :title
+                         (format "Remove redundant imports %s from import of ‘%s’"
+                                 (mapconcat (lambda (ident)
+                                              (concat "‘" ident "’"))
+                                            (plist-get suggestion :idents) ", ")
+                                 (plist-get suggestion :module))
+                         :default t))
+                  (add-extension
+                   (list :key suggestion
+                         :title (concat "Add {-# LANGUAGE "
+                                        (plist-get suggestion :extension)
+                                        " #-}")
+                         :default (not (string= "OverloadedStrings" (plist-get suggestion :extension)))))
+                  (add-ghc-option
+                   (list :key suggestion
+                         :title (concat "Add {-# OPTIONS_GHC "
+                                        (plist-get suggestion :option)
+                                        " #-}")
+                         :default (not
+                                   (string=
+                                    (plist-get suggestion :option)
+                                    "-fno-warn-name-shadowing"))))
+                  (add-package
+                   (list :key suggestion
+                         :title (concat "Enable package: " (plist-get suggestion :package))
+                         :default t))
+                  (remove-import
+                   (list :key suggestion
+                         :title (concat "Remove: import "
+                                        (plist-get suggestion :module))
+                         :default t))
+                  (fix-typo
+                   (list :key suggestion
+                         :title (concat "Replace ‘"
+                                        (plist-get suggestion :typo)
+                                        "’ with ‘"
+                                        (plist-get suggestion :replacement)
+                                        "’")
+                         :default (null (cdr intero-suggestions))))
+                  (add-signature
+                   (list :key suggestion
+                         :title (concat "Add signature: "
+                                        (plist-get suggestion :signature))
+                         :default t))
+                  (redundant-constraint
+                   (list :key suggestion
+                         :title (concat
+                                 "Remove redundant constraints: "
+                                 (string-join (plist-get suggestion :redundancies)
+                                              ", ")
+                                 "\n    from the "
+                                 (plist-get suggestion :signature))
+                         :default nil))))
+              intero-suggestions)))))
+      (if (null to-apply)
+          (message "No changes selected to apply.")
+        (let ((sorted (sort to-apply
+                            (lambda (lt gt)
+                              (let ((lt-line   (or (plist-get lt :line)   0))
+                                    (lt-column (or (plist-get lt :column) 0))
+                                    (gt-line   (or (plist-get gt :line)   0))
+                                    (gt-column (or (plist-get gt :column) 0)))
+                                (or (> lt-line gt-line)
+                                    (and (= lt-line gt-line)
+                                         (> lt-column gt-column))))))))
+          ;; # Changes unrelated to the buffer
+          (cl-loop
+           for suggestion in sorted
+           do (cl-case (plist-get suggestion :type)
+                (add-package
+                 (intero-add-package (plist-get suggestion :package)))))
+          ;; # Changes that do not increase/decrease line numbers
+          ;;
+          ;; Update in-place suggestions
+          (cl-loop
+           for suggestion in sorted
+           do (cl-case (plist-get suggestion :type)
+                (add-to-import
+                 (save-excursion
+                   (goto-char (point-min))
+                   (forward-line (1- (plist-get suggestion :line)))
+                   (when (and (search-forward (plist-get suggestion :module) nil t 1)
+                              (search-forward "(" nil t 1))
+                     (insert (if (string-match-p "^[_a-zA-Z]" (plist-get suggestion :ident))
+                                 (plist-get suggestion :ident)
+                               (concat "(" (plist-get suggestion :ident) ")")))
+                     (unless (looking-at-p "[:space:]*)")
+                       (insert ", ")))))
+                (redundant-import-item
+                 (save-excursion
+                   (goto-char (point-min))
+                   (forward-line (1- (plist-get suggestion :line)))
+                   (let* ((case-fold-search nil)
+                          (start (search-forward "(" nil t 1))
+                          (end (or (save-excursion
+                                     (when (search-forward-regexp "\n[^ \t]" nil t 1)
+                                       (1- (point))))
+                                   (line-end-position)))
+                          (regex
+                           (concat
+                            "\\("
+                            (mapconcat
+                             (lambda (ident)
+                               (if (string-match-p "^[_a-zA-Z]" ident)
+                                   (concat "\\<" (regexp-quote ident) "\\> ?" "\\("(regexp-quote "(..)") "\\)?")
+                                 (concat "(" (regexp-quote ident) ")")))
+                             (plist-get suggestion :idents)
+                             "\\|")
+                            "\\)"))
+                          (string (buffer-substring start end)))
+                     (delete-region start end)
+                     (insert
+                      (replace-regexp-in-string
+                       ",[\n ]*)" ")"
+                       (replace-regexp-in-string
+                        "^[\n ,]*" ""
+                        (replace-regexp-in-string
+                         "[\n ,]*,[\n ,]*" ", "
+                         (replace-regexp-in-string
+                          ",[\n ]*)" ")"
+                          (replace-regexp-in-string
+                           regex ""
+                           string)))))
+                      (make-string (1- (length (split-string string "\n" t))) 10)))))
+                (fix-typo
+                 (save-excursion
+                   (goto-char (point-min))
+                   (forward-line (1- (plist-get suggestion :line)))
+                   (move-to-column (- (plist-get suggestion :column) 1))
+                   (delete-char (length (plist-get suggestion :typo)))
+                   (insert (plist-get suggestion :replacement))))
+                (add-missing-fields
+                 (save-excursion
+                   (goto-char (point-min))
+                   (forward-line (1- (plist-get suggestion :line)))
+                   (move-to-column (- (plist-get suggestion :column) 1))
+                   (search-forward "{")
+                   (unless (looking-at "}")
+                     (save-excursion (insert ", ")))
+                   (insert (mapconcat (lambda (field) (concat field " = _"))
+                                      (plist-get suggestion :fields)
+                                      ", "))))))
+          ;; # Changes that do increase/decrease line numbers
+          ;;
+          ;; Remove redundant constraints
+          (cl-loop
+           for suggestion in sorted
+           do (cl-case (plist-get suggestion :type)
+                (redundant-constraint
+                 (save-excursion
+                   (goto-char (point-min))
+                   (forward-line (1- (plist-get suggestion :line)))
+                   (search-forward-regexp "[[:alnum:][:space:]\n]*=>")
+                   (backward-sexp 2)
+                   (let ((start (1+ (point))))
+                     (forward-sexp)
+                     (let* ((end (1- (point)))
+                            (constraints (intero-parse-comma-list
+                                          (buffer-substring start end)))
+                            (nonredundant
+                             (cl-loop for r in (plist-get suggestion :redundancies)
+                                      with nonredundant = constraints
+                                      do (setq nonredundant (delete r nonredundant))
+                                      finally return nonredundant)))
+                       (goto-char start)
+                       (delete-char (- end start))
+                       (insert (string-join nonredundant ", "))))))))
+
+          ;; Add a type signature to a top-level binding.
+          (cl-loop
+           for suggestion in sorted
+           do (cl-case (plist-get suggestion :type)
+                (add-signature
+                 (save-excursion
+                   (goto-char (point-min))
+                   (forward-line (1- (plist-get suggestion :line)))
+                   (insert (plist-get suggestion :signature))
+                   (insert "\n")))))
+
+          ;; Remove import lines from the file. May remove more than one
+          ;; line per import.
+          (cl-loop
+           for suggestion in sorted
+           do (cl-case (plist-get suggestion :type)
+                (remove-import
+                 (save-excursion
+                   (goto-char (point-min))
+                   (forward-line (1- (plist-get suggestion :line)))
+                   (delete-region (line-beginning-position)
+                                  (or (when (search-forward-regexp "\n[^ \t]" nil t 1)
+                                        (1- (point)))
+                                      (line-end-position)))))))
+          ;; Add extensions to the top of the file
+          (cl-loop
+           for suggestion in sorted
+           do (cl-case (plist-get suggestion :type)
+                (add-extension
+                 (save-excursion
+                   (goto-char (point-min))
+                   (intero-skip-shebangs)
+                   (insert "{-# LANGUAGE "
+                           (plist-get suggestion :extension)
+                           " #-}\n")))
+                (add-ghc-option
+                 (save-excursion
+                   (goto-char (point-min))
+                   (intero-skip-shebangs)
+                   (insert "{-# OPTIONS_GHC "
+                           (plist-get suggestion :option)
+                           " #-}\n"))))))))))
+
+(defun intero-skip-shebangs ()
+  "Skip #! and -- shebangs used in Haskell scripts."
+  (when (looking-at-p "#!") (forward-line 1))
+  (when (looking-at-p "-- stack ") (forward-line 1)))
+
+(defun intero--warn (message &rest args)
+  "Display a warning message made from (format MESSAGE ARGS...).
+Equivalent to 'warn', but label the warning as coming from intero."
+  (display-warning 'intero (apply 'format message args) :warning))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Intero help buffer
+
+(defun intero-help-buffer ()
+  "Get the help buffer."
+  (with-current-buffer (get-buffer-create "*Intero-Help*")
+    (unless (eq major-mode 'intero-help-mode) (intero-help-mode))
+    (current-buffer)))
+
+(defvar-local intero-help-entries nil
+  "History for help entries.")
+
+(defun intero-help-pagination ()
+  "Insert pagination for the current help buffer."
+  (let ((buffer-read-only nil))
+    (when (> (length intero-help-entries) 1)
+      (insert-text-button
+       "[back]"
+       'buffer (current-buffer)
+       'action (lambda (&rest ignore)
+                 (let ((first (pop intero-help-entries)))
+                   (setcdr (last intero-help-entries) (cons first nil))
+                   (intero-help-refresh)))
+       'keymap (let ((map (make-sparse-keymap)))
+                 (define-key map [mouse-1] 'push-button)
+                 map))
+      (insert " ")
+      (insert-text-button
+       "[forward]"
+       'buffer (current-buffer)
+       'keymap (let ((map (make-sparse-keymap)))
+                 (define-key map [mouse-1] 'push-button)
+                 map)
+       'action (lambda (&rest ignore)
+                 (setq intero-help-entries
+                       (intero-bring-to-front intero-help-entries))
+                 (intero-help-refresh)))
+      (insert " ")
+      (insert-text-button
+       "[forget]"
+       'buffer (current-buffer)
+       'keymap (let ((map (make-sparse-keymap)))
+                 (define-key map [mouse-1] 'push-button)
+                 map)
+       'action (lambda (&rest ignore)
+                 (pop intero-help-entries)
+                 (intero-help-refresh)))
+      (insert "\n\n"))))
+
+(defun intero-help-refresh ()
+  "Refresh the help buffer with the current thing in the history."
+  (interactive)
+  (let ((buffer-read-only nil))
+    (erase-buffer)
+    (if (car intero-help-entries)
+        (progn
+          (intero-help-pagination)
+          (insert (cdr (car intero-help-entries)))
+          (goto-char (point-min)))
+      (insert "No help entries."))))
+
+(defun intero-bring-to-front (xs)
+  "Bring the last element of XS to the front."
+  (cons (car (last xs)) (butlast xs)))
+
+(defun intero-help-push-history (buffer item)
+  "Add (BUFFER . ITEM) to the history of help entries."
+  (push (cons buffer item) intero-help-entries))
+
+(defun intero-help-info (ident)
+  "Get the info of the thing with IDENT at point."
+  (interactive (list (intero-ident-at-point)))
+  (with-current-buffer (car (car intero-help-entries))
+    (intero-info ident)))
+
+(define-derived-mode intero-help-mode help-mode "Intero-Help"
+  "Help mode for intero."
+  (setq buffer-read-only t)
+  (setq intero-help-entries nil))
+
+(define-key intero-help-mode-map (kbd "g") 'intero-help-refresh)
+(define-key intero-help-mode-map (kbd "C-c C-i") 'intero-help-info)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Intero highlight uses mode
+
+(defvar intero-highlight-uses-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map (kbd "n") 'intero-highlight-uses-mode-next)
+    (define-key map (kbd "TAB") 'intero-highlight-uses-mode-next)
+    (define-key map (kbd "p") 'intero-highlight-uses-mode-prev)
+    (define-key map (kbd "S-TAB") 'intero-highlight-uses-mode-prev)
+    (define-key map (kbd "<backtab>") 'intero-highlight-uses-mode-prev)
+    (define-key map (kbd "RET") 'intero-highlight-uses-mode-stop-here)
+    (define-key map (kbd "r") 'intero-highlight-uses-mode-replace)
+    (define-key map (kbd "q") 'intero-highlight-uses-mode)
+    map)
+  "Keymap for using `intero-highlight-uses-mode'.")
+
+(defvar-local intero-highlight-uses-mode-point nil)
+(defvar-local intero-highlight-uses-buffer-old-mode nil)
+
+;;;###autoload
+(define-minor-mode intero-highlight-uses-mode
+  "Minor mode for highlighting and jumping between uses."
+  :lighter " Uses"
+  :keymap intero-highlight-uses-mode-map
+  (if intero-highlight-uses-mode
+      (progn (setq intero-highlight-uses-buffer-old-mode buffer-read-only)
+             (setq buffer-read-only t)
+             (setq intero-highlight-uses-mode-point (point)))
+    (progn (setq buffer-read-only intero-highlight-uses-buffer-old-mode)
+           (when intero-highlight-uses-mode-point
+             (goto-char intero-highlight-uses-mode-point))))
+  (remove-overlays (point-min) (point-max) 'intero-highlight-uses-mode-highlight t))
+
+(defun intero-highlight-uses-mode-replace ()
+  "Replace all highlighted instances in the buffer with something else."
+  (interactive)
+  (save-excursion
+    (goto-char (point-min))
+    (let ((o (intero-highlight-uses-mode-next)))
+      (when o
+        (let ((replacement
+               (read-from-minibuffer
+                (format "Replace uses %s with: "
+                        (buffer-substring
+                         (overlay-start o)
+                         (overlay-end o))))))
+          (let ((inhibit-read-only t))
+            (while o
+              (goto-char (overlay-start o))
+              (delete-region (overlay-start o)
+                             (overlay-end o))
+              (insert replacement)
+              (setq o (intero-highlight-uses-mode-next))))))))
+  (intero-highlight-uses-mode -1))
+
+(defun intero-highlight-uses-mode-stop-here ()
+  "Stop at this point."
+  (interactive)
+  (setq intero-highlight-uses-mode-point (point))
+  (intero-highlight-uses-mode -1))
+
+(defun intero-highlight-uses-mode-next ()
+  "Jump to next result."
+  (interactive)
+  (let ((os (sort (cl-remove-if (lambda (o)
+                                  (or (<= (overlay-start o) (point))
+                                      (not (overlay-get o 'intero-highlight-uses-mode-highlight))))
+                                (overlays-in (point) (point-max)))
+                  (lambda (a b)
+                    (< (overlay-start a)
+                       (overlay-start b))))))
+    (when os
+      (mapc
+       (lambda (o)
+         (when (overlay-get o 'intero-highlight-uses-mode-highlight)
+           (overlay-put o 'face 'lazy-highlight)))
+       (overlays-in (line-beginning-position) (line-end-position)))
+      (goto-char (overlay-start (car os)))
+      (overlay-put (car os) 'face 'isearch)
+      (car os))))
+
+(defun intero-highlight-uses-mode-prev ()
+  "Jump to previous result."
+  (interactive)
+  (let ((os (sort (cl-remove-if (lambda (o)
+                                  (or (>= (overlay-end o) (point))
+                                      (not (overlay-get o 'intero-highlight-uses-mode-highlight))))
+                                (overlays-in (point-min) (point)))
+                  (lambda (a b)
+                    (> (overlay-start a)
+                       (overlay-start b))))))
+    (when os
+      (mapc
+       (lambda (o)
+         (when (overlay-get o 'intero-highlight-uses-mode-highlight)
+           (overlay-put o 'face 'lazy-highlight)))
+       (overlays-in (line-beginning-position) (line-end-position)))
+      (goto-char (overlay-start (car os)))
+      (overlay-put (car os) 'face 'isearch)
+      (car os))))
+
+(defun intero-highlight-uses-mode-highlight (start end current)
+  "Make a highlight overlay at the span from START to END.
+If CURRENT, highlight the span uniquely."
+  (let ((o (make-overlay start end)))
+    (overlay-put o 'priority 999)
+    (overlay-put o 'face
+                 (if current
+                     'isearch
+                   'lazy-highlight))
+    (overlay-put o 'intero-highlight-uses-mode-highlight t)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(provide 'intero)
+
+;;; intero.el ends here