Skip to content

Commit 715d26e

Browse files
committed
Merge pull request #1341 from fice-t/interactive
prompt2 support and multi-line
2 parents 1df0a01 + 74fa98b commit 715d26e

File tree

5 files changed

+93
-51
lines changed

5 files changed

+93
-51
lines changed

doc/haskell-mode.texi

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1754,6 +1754,42 @@ To evaluate expressions, simply type one out and hit `RET`.
17541754
123
17551755
@end example
17561756

1757+
@subsection Evaluating multiline expressions
1758+
1759+
GHCi features two ways to evaluate multiline expressions. You can use
1760+
@code{:set +m} to
1761+
enable @uref{https://www.haskell.org/ghc/docs/latest/html/users_guide/ghci.html#multiline-input,
1762+
multiline input} for all expressions, or you can wrap your expression in
1763+
@code{:@{} and @code{:@}} (they have to be on their own lines).
1764+
1765+
The prompt will change to indicate that you're inputting a multiline
1766+
expression:
1767+
1768+
@example
1769+
λ> :@{
1770+
λ| let a = 10
1771+
λ| b = 20
1772+
λ| c = 30
1773+
λ| :@}
1774+
@end example
1775+
1776+
You can also simulate multiline mode by having your input contain
1777+
newline characters. You can input a literal newline character with
1778+
@kbd{C-q C-j}, or you can use:
1779+
1780+
@example
1781+
M-x haskell-interactive-mode-newline-indent
1782+
@end example
1783+
1784+
which is bound to @kbd{C-j}. This command indents after the newline. You
1785+
can simulate the above example like so:
1786+
1787+
@example
1788+
λ> let a = 10
1789+
b = 20
1790+
c = 30
1791+
@end example
1792+
17571793
@subsection Type of expressions
17581794

17591795
You can use normal @code{:type} which is part of GHCi to get the type of

haskell-commands.el

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,9 @@ You can create new session using function `haskell-session-make'."
9595
;; whole produces only one prompt marker as a response.
9696
(haskell-process-send-string process "Prelude.putStrLn \"\"")
9797
(haskell-process-send-string process ":set -v1")
98-
(haskell-process-send-string process ":set prompt \"\\4\""))
98+
(haskell-process-send-string process ":set prompt \"\\4\"")
99+
(haskell-process-send-string process (format ":set prompt2 \"%s\""
100+
haskell-interactive-prompt2)))
99101

100102
:live (lambda (process buffer)
101103
(when (haskell-process-consume

haskell-customize.el

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,8 +310,19 @@ ambiguous class constraint."
310310
:type 'boolean
311311
:group 'haskell-interactive)
312312

313-
(defvar haskell-interactive-prompt "λ> "
314-
"The prompt to use.")
313+
(defcustom haskell-interactive-prompt "λ> "
314+
"The prompt to use."
315+
:type 'string
316+
:group 'haskell-interactive)
317+
318+
(defcustom haskell-interactive-prompt2 (replace-regexp-in-string
319+
"> $"
320+
"| "
321+
haskell-interactive-prompt)
322+
"The multi-line prompt to use.
323+
The default is `haskell-interactive-prompt' with the last > replaced with |."
324+
:type 'string
325+
:group 'haskell-interactive)
315326

316327
(defcustom haskell-interactive-mode-eval-mode
317328
nil

haskell-interactive-mode.el

Lines changed: 40 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,12 @@ be nil.")
130130
"Face for the prompt."
131131
:group 'haskell-interactive)
132132

133+
;;;###autoload
134+
(defface haskell-interactive-face-prompt2
135+
'((t :inherit font-lock-keyword-face))
136+
"Face for the prompt2 in multi-line mode."
137+
:group 'haskell-interactive)
138+
133139
;;;###autoload
134140
(defface haskell-interactive-face-compile-error
135141
'((t :inherit compilation-error))
@@ -161,7 +167,8 @@ be nil.")
161167
"Make newline and indent."
162168
(interactive)
163169
(newline)
164-
(indent-according-to-mode))
170+
(indent-to (length haskell-interactive-prompt))
171+
(indent-relative))
165172

166173
(defun haskell-interactive-mode-kill-whole-line ()
167174
"Kill the whole REPL line."
@@ -230,22 +237,6 @@ be nil.")
230237
(point-min))
231238
end))))))))
232239

233-
(defun haskell-interactive-mode-cleanup-response (expr response)
234-
"Ignore the mess that GHCi outputs on multi-line input."
235-
(if (not (string-match "\n" expr))
236-
response
237-
(let ((i 0)
238-
(out "")
239-
(lines (length (split-string expr "\n"))))
240-
(cl-loop for part in (split-string response "| ")
241-
do (setq out
242-
(concat out
243-
(if (> i lines)
244-
(concat (if (or (= i 0) (= i (1+ lines))) "" "| ") part)
245-
"")))
246-
do (setq i (1+ i)))
247-
out)))
248-
249240
(defun haskell-interactive-mode-multi-line (expr)
250241
"If a multi-line expression EXPR has been entered, then reformat it to be:
251242
@@ -254,21 +245,18 @@ do the
254245
multi-liner
255246
expr
256247
:}"
257-
(if (not (string-match "\n" expr))
248+
(if (not (string-match-p "\n" expr))
258249
expr
259-
(let* ((i 0)
260-
(lines (split-string expr "\n"))
261-
(len (length lines)))
262-
(mapconcat 'identity
263-
(cl-loop for line in lines
264-
collect (cond ((= i 0)
265-
(concat ":{" "\n" line))
266-
((= i (1- len))
267-
(concat line "\n" ":}"))
268-
(t
269-
line))
270-
do (setq i (1+ i)))
271-
"\n"))))
250+
(let ((len (length haskell-interactive-prompt))
251+
(lines (split-string expr "\n")))
252+
(cl-loop for elt on (cdr lines) do
253+
(setcar elt (substring (car elt) len)))
254+
;; Temporarily set prompt2 to be empty to avoid unwanted output
255+
(concat ":set prompt2 \"\"\n"
256+
":{\n"
257+
(mapconcat #'identity lines "\n")
258+
"\n:}\n"
259+
(format ":set prompt2 \"%s\"" haskell-interactive-prompt2)))))
272260

273261
(defun haskell-interactive-trim (line)
274262
"Trim indentation off of LINE in the REPL."
@@ -331,21 +319,27 @@ SESSION, otherwise operate on the current buffer."
331319
"Insert the result of an eval as plain text."
332320
(with-current-buffer (haskell-session-interactive-buffer session)
333321
(goto-char (point-max))
334-
(insert (ansi-color-apply
335-
(propertize text
336-
'font-lock-face 'haskell-interactive-face-result
337-
'front-sticky t
338-
'prompt t
339-
'read-only t
340-
'rear-nonsticky t
341-
'result t)))
342-
(haskell-interactive-mode-handle-h)
343-
(let ((marker (setq-local haskell-interactive-mode-result-end (make-marker))))
344-
(set-marker marker
345-
(point)
346-
(current-buffer)))
347-
(when haskell-interactive-mode-scroll-to-bottom
348-
(haskell-interactive-mode-scroll-to-bottom))))
322+
(let ((prop-text (propertize text
323+
'font-lock-face 'haskell-interactive-face-result
324+
'front-sticky t
325+
'prompt t
326+
'read-only t
327+
'rear-nonsticky t
328+
'result t)))
329+
(when (string= text haskell-interactive-prompt2)
330+
(put-text-property 0
331+
(length haskell-interactive-prompt2)
332+
'font-lock-face
333+
'haskell-interactive-face-prompt2
334+
prop-text))
335+
(insert (ansi-color-apply prop-text))
336+
(haskell-interactive-mode-handle-h)
337+
(let ((marker (setq-local haskell-interactive-mode-result-end (make-marker))))
338+
(set-marker marker
339+
(point)
340+
(current-buffer)))
341+
(when haskell-interactive-mode-scroll-to-bottom
342+
(haskell-interactive-mode-scroll-to-bottom)))))
349343

350344
(defun haskell-interactive-mode-scroll-to-bottom ()
351345
"Scroll to bottom."

haskell-repl.el

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,7 @@
101101
"Print the result of evaluating the expression."
102102
(let ((response
103103
(with-temp-buffer
104-
(insert (haskell-interactive-mode-cleanup-response
105-
(cl-caddr state) response))
104+
(insert response)
106105
(haskell-interactive-mode-handle-h)
107106
(buffer-string))))
108107
(when haskell-interactive-mode-eval-mode

0 commit comments

Comments
 (0)