Skip to content

prompt2 support and multi-line #1341

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 31, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions doc/haskell-mode.texi
Original file line number Diff line number Diff line change
Expand Up @@ -1754,6 +1754,42 @@ To evaluate expressions, simply type one out and hit `RET`.
123
@end example

@subsection Evaluating multiline expressions

GHCi features two ways to evaluate multiline expressions. You can use
@code{:set +m} to
enable @uref{https://www.haskell.org/ghc/docs/latest/html/users_guide/ghci.html#multiline-input,
multiline input} for all expressions, or you can wrap your expression in
@code{:@{} and @code{:@}} (they have to be on their own lines).

The prompt will change to indicate that you're inputting a multiline
expression:

@example
λ> :@{
λ| let a = 10
λ| b = 20
λ| c = 30
λ| :@}
@end example

You can also simulate multiline mode by having your input contain
newline characters. You can input a literal newline character with
@kbd{C-q C-j}, or you can use:

@example
M-x haskell-interactive-mode-newline-indent
@end example

which is bound to @kbd{C-j}. This command indents after the newline. You
can simulate the above example like so:

@example
λ> let a = 10
b = 20
c = 30
@end example

@subsection Type of expressions

You can use normal @code{:type} which is part of GHCi to get the type of
Expand Down
4 changes: 3 additions & 1 deletion haskell-commands.el
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ You can create new session using function `haskell-session-make'."
;; whole produces only one prompt marker as a response.
(haskell-process-send-string process "Prelude.putStrLn \"\"")
(haskell-process-send-string process ":set -v1")
(haskell-process-send-string process ":set prompt \"\\4\""))
(haskell-process-send-string process ":set prompt \"\\4\"")
(haskell-process-send-string process (format ":set prompt2 \"%s\""
haskell-interactive-prompt2)))

:live (lambda (process buffer)
(when (haskell-process-consume
Expand Down
15 changes: 13 additions & 2 deletions haskell-customize.el
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,19 @@ ambiguous class constraint."
:type 'boolean
:group 'haskell-interactive)

(defvar haskell-interactive-prompt "λ> "
"The prompt to use.")
(defcustom haskell-interactive-prompt "λ> "
"The prompt to use."
:type 'string
:group 'haskell-interactive)

(defcustom haskell-interactive-prompt2 (replace-regexp-in-string
"> $"
"| "
haskell-interactive-prompt)
"The multi-line prompt to use.
The default is `haskell-interactive-prompt' with the last > replaced with |."
:type 'string
:group 'haskell-interactive)

(defcustom haskell-interactive-mode-eval-mode
nil
Expand Down
86 changes: 40 additions & 46 deletions haskell-interactive-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@ be nil.")
"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))
Expand Down Expand Up @@ -161,7 +167,8 @@ be nil.")
"Make newline and indent."
(interactive)
(newline)
(indent-according-to-mode))
(indent-to (length haskell-interactive-prompt))
(indent-relative))

(defun haskell-interactive-mode-kill-whole-line ()
"Kill the whole REPL line."
Expand Down Expand Up @@ -230,22 +237,6 @@ be nil.")
(point-min))
end))))))))

(defun haskell-interactive-mode-cleanup-response (expr response)
"Ignore the mess that GHCi outputs on multi-line input."
(if (not (string-match "\n" expr))
response
(let ((i 0)
(out "")
(lines (length (split-string expr "\n"))))
(cl-loop for part in (split-string response "| ")
do (setq out
(concat out
(if (> i lines)
(concat (if (or (= i 0) (= i (1+ lines))) "" "| ") part)
"")))
do (setq i (1+ i)))
out)))

(defun haskell-interactive-mode-multi-line (expr)
"If a multi-line expression EXPR has been entered, then reformat it to be:

Expand All @@ -254,21 +245,18 @@ do the
multi-liner
expr
:}"
(if (not (string-match "\n" expr))
(if (not (string-match-p "\n" expr))
expr
(let* ((i 0)
(lines (split-string expr "\n"))
(len (length lines)))
(mapconcat 'identity
(cl-loop for line in lines
collect (cond ((= i 0)
(concat ":{" "\n" line))
((= i (1- len))
(concat line "\n" ":}"))
(t
line))
do (setq i (1+ i)))
"\n"))))
(let ((len (length haskell-interactive-prompt))
(lines (split-string expr "\n")))
(cl-loop for elt on (cdr lines) do
(setcar elt (substring (car elt) len)))
;; 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-trim (line)
"Trim indentation off of LINE in the REPL."
Expand Down Expand Up @@ -331,21 +319,27 @@ SESSION, otherwise operate on the current buffer."
"Insert the result of an eval as plain text."
(with-current-buffer (haskell-session-interactive-buffer session)
(goto-char (point-max))
(insert (ansi-color-apply
(propertize text
'font-lock-face 'haskell-interactive-face-result
'front-sticky t
'prompt t
'read-only t
'rear-nonsticky t
'result t)))
(haskell-interactive-mode-handle-h)
(let ((marker (setq-local haskell-interactive-mode-result-end (make-marker))))
(set-marker marker
(point)
(current-buffer)))
(when haskell-interactive-mode-scroll-to-bottom
(haskell-interactive-mode-scroll-to-bottom))))
(let ((prop-text (propertize text
'font-lock-face 'haskell-interactive-face-result
'front-sticky t
'prompt t
'read-only t
'rear-nonsticky t
'result t)))
(when (string= text haskell-interactive-prompt2)
(put-text-property 0
(length haskell-interactive-prompt2)
'font-lock-face
'haskell-interactive-face-prompt2
prop-text))
(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)
(current-buffer)))
(when haskell-interactive-mode-scroll-to-bottom
(haskell-interactive-mode-scroll-to-bottom)))))

(defun haskell-interactive-mode-scroll-to-bottom ()
"Scroll to bottom."
Expand Down
3 changes: 1 addition & 2 deletions haskell-repl.el
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,7 @@
"Print the result of evaluating the expression."
(let ((response
(with-temp-buffer
(insert (haskell-interactive-mode-cleanup-response
(cl-caddr state) response))
(insert response)
(haskell-interactive-mode-handle-h)
(buffer-string))))
(when haskell-interactive-mode-eval-mode
Expand Down