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