Skip to content

Commit 1edbe03

Browse files
committed
Support internal prompts
Some people do fancy stuff with the prompt (e.g. multiline, colouring). This offers some support for it. The haskell-interactive-mode-prompt-previous/next used the prompt regex to search for the prompt, but this doesn't work with variable prompts (i.e. containing module names). Now they use text property search.
1 parent cd820dc commit 1edbe03

5 files changed

+100
-47
lines changed

haskell-commands.el

+16-4
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,9 @@ You can create new session using function `haskell-session-make'."
104104
":set -v1"
105105
":set +c") ; :type-at in GHC 8+
106106
"\n"))
107-
(haskell-process-send-string process ":set prompt \"\\4\"")
108107
(haskell-process-send-string process (format ":set prompt2 \"%s\""
109-
haskell-interactive-prompt2)))
108+
haskell-interactive-prompt2))
109+
(haskell-process-send-string process ":set prompt \"\\4\""))
110110

111111
:live (lambda (process buffer)
112112
(when (haskell-process-consume
@@ -134,8 +134,20 @@ If I break, you can:
134134
1. Restart: M-x haskell-process-restart
135135
2. Configure logging: C-h v haskell-process-log (useful for debugging)
136136
3. General config: M-x customize-mode
137-
4. Hide these tips: C-h v haskell-process-show-debug-tips")))))))
138-
137+
4. Hide these tips: C-h v haskell-process-show-debug-tips")))
138+
(unless haskell-interactive-use-interactive-prompt
139+
(with-current-buffer (haskell-session-interactive-buffer
140+
(haskell-process-session process))
141+
(setq-local haskell-interactive-mode-prompt-start (point-max-marker)))
142+
;; Now it's safe to set the prompt
143+
;; Make sure to double escape any newlines
144+
(haskell-interactive-mode-run-expr
145+
(format ":set prompt \"%s\\4\""
146+
(replace-regexp-in-string "\n"
147+
"\\n"
148+
haskell-interactive-prompt
149+
nil
150+
t))))))))
139151
(defun haskell-commands-process ()
140152
"Get the Haskell session, throws an error if not available."
141153
(or (haskell-session-process (haskell-session-maybe))

haskell-customize.el

+11
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,17 @@ The default is `haskell-interactive-prompt' with the last > replaced with |."
324324
:type 'string
325325
:group 'haskell-interactive)
326326

327+
(defcustom haskell-interactive-use-interactive-prompt t
328+
"Non-nil means that haskell-interactive uses its prompt at the
329+
Emacs side rather than setting it in GHCi directly.
330+
331+
This is only useful to disable when you want a prompt containing
332+
your modules (as GHCi does by default), or if you apply extra
333+
properties (colours, etc.) to your prompt through GHCi."
334+
:type 'boolean
335+
:group 'haskell-interactive)
336+
337+
327338
(defcustom haskell-interactive-mode-eval-mode
328339
nil
329340
"Use the given mode's font-locking to render some text."

haskell-doc.el

+5
Original file line numberDiff line numberDiff line change
@@ -1517,6 +1517,11 @@ If SYNC is non-nil, make the call synchronously instead."
15171517
(setq response nil)
15181518
;; Remove a newline at the end
15191519
(setq response (replace-regexp-in-string "\n\\'" "" response))
1520+
(unless haskell-interactive-use-interactive-prompt
1521+
;; Remove the extra prompt (may span multiple lines)
1522+
(setq response (string-join (nbutlast (split-string response "\n")
1523+
(1+ (cl-count ?\n haskell-interactive-prompt)))
1524+
"\n")))
15201525
;; Propertize for eldoc
15211526
(save-match-data
15221527
(when (string-match " :: " response)

haskell-interactive-mode.el

+63-40
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,9 @@ do the
247247
:}"
248248
(if (not (string-match-p "\n" expr))
249249
expr
250-
(let ((len (length haskell-interactive-prompt))
250+
(let ((len (if haskell-interactive-use-interactive-prompt
251+
(length haskell-interactive-prompt)
252+
(length (last (split-string haskell-interactive-prompt "\n")))))
251253
(lines (split-string expr "\n")))
252254
(cl-loop for elt on (cdr lines) do
253255
(setcar elt (substring (car elt) len)))
@@ -295,21 +297,30 @@ do the
295297
(defun haskell-interactive-mode-prompt (&optional session)
296298
"Show a prompt at the end of the REPL buffer.
297299
If SESSION is non-nil, use the REPL buffer associated with
298-
SESSION, otherwise operate on the current buffer."
300+
SESSION, otherwise operate on the current buffer. The prompt
301+
inserted is specified by `haskell-interactive-prompt'.
302+
When `haskell-interactive-use-interactive-prompt' is non-nil,
303+
the prompt is inserted in this function. Otherwise it was already
304+
set in the `haskell-process-send-startup' and has already been
305+
inserted in the buffer by the process."
299306
(with-current-buffer (if session
300307
(haskell-session-interactive-buffer session)
301308
(current-buffer))
302309
(goto-char (point-max))
303-
(let ((prompt (propertize haskell-interactive-prompt
304-
'font-lock-face 'haskell-interactive-face-prompt
305-
'prompt t
306-
'read-only t
307-
'rear-nonsticky t)))
308-
;; At the time of writing, front-stickying the first char gives an error
309-
;; Has unfortunate side-effect of being able to insert before the prompt
310-
(insert (substring prompt 0 1)
311-
(propertize (substring prompt 1)
312-
'front-sticky t)))
310+
(if haskell-interactive-use-interactive-prompt
311+
(let ((prompt (propertize haskell-interactive-prompt
312+
'font-lock-face 'haskell-interactive-face-prompt
313+
'prompt t
314+
'read-only t
315+
'rear-nonsticky t)))
316+
;; At the time of writing, front-stickying the first char gives an error
317+
;; Has unfortunate side-effect of being able to insert before the prompt
318+
(insert (substring prompt 0 1)
319+
(propertize (substring prompt 1)
320+
'front-sticky t)))
321+
(let ((inhibit-read-only t))
322+
(unless (= (point) (point-min))
323+
(put-text-property (1- (point)) (point) 'prompt t))))
313324
(let ((marker (setq-local haskell-interactive-mode-prompt-start (make-marker))))
314325
(set-marker marker (point)))
315326
(when haskell-interactive-mode-scroll-to-bottom
@@ -322,16 +333,13 @@ SESSION, otherwise operate on the current buffer."
322333
(let ((prop-text (propertize text
323334
'font-lock-face 'haskell-interactive-face-result
324335
'front-sticky t
325-
'prompt t
326336
'read-only t
327337
'rear-nonsticky t
328338
'result t)))
329339
(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))
340+
(setq prop-text (propertize prop-text
341+
'font-lock-face 'haskell-interactive-face-prompt2
342+
'prompt2 t)))
335343
(insert (ansi-color-apply prop-text))
336344
(haskell-interactive-mode-handle-h)
337345
(let ((marker (setq-local haskell-interactive-mode-result-end (make-marker))))
@@ -973,20 +981,34 @@ don't care when the thing completes as long as it's soonish."
973981
(setq haskell-interactive-mode-history-index 0)
974982
(haskell-interactive-mode-history-toggle -1))))
975983

976-
(defun haskell-interactive-mode-prompt-previous ()
977-
"Jump to the previous prompt."
978-
(interactive)
979-
(let ((prev-prompt-pos
980-
(save-excursion
981-
(beginning-of-line) ;; otherwise prompt at current line matches
982-
(and (search-backward-regexp (haskell-interactive-prompt-regex) nil t)
983-
(match-end 0)))))
984-
(when prev-prompt-pos (goto-char prev-prompt-pos))))
985-
986-
(defun haskell-interactive-mode-prompt-next ()
987-
"Jump to the next prompt."
988-
(interactive)
989-
(search-forward-regexp (haskell-interactive-prompt-regex) nil t))
984+
(defun haskell-interactive-mode-prompt-previous (&optional arg)
985+
"Jump to the ARGth previous prompt."
986+
(interactive "p")
987+
(if (< arg 0)
988+
(haskell-interactive-mode-prompt-next (- arg))
989+
(end-of-line 1)
990+
(unless (or (get-text-property (1- (point)) 'prompt)
991+
(zerop arg))
992+
(cl-incf arg 0.5)) ; do it an extra time if not at a prompt
993+
(dotimes (_ (* 2 arg))
994+
(goto-char (or (previous-single-property-change (point) 'prompt)
995+
(point))))
996+
(when (get-text-property (point) 'prompt)
997+
;; went too far (at first prompt)
998+
(goto-char (next-single-property-change (point) 'prompt)))))
999+
1000+
(defun haskell-interactive-mode-prompt-next (&optional arg)
1001+
"Jump to the ARGth next prompt."
1002+
(interactive "p")
1003+
(if (< arg 0)
1004+
(haskell-interactive-mode-prompt-previous (- arg))
1005+
(when (and (get-text-property (point) 'prompt)
1006+
(not (zerop arg)))
1007+
;; don't start on a prompt
1008+
(haskell-interactive-mode-prompt-previous 1))
1009+
(dotimes (_ (* 2 arg))
1010+
(goto-char (or (next-single-property-change (point) 'prompt)
1011+
(point-max))))))
9901012

9911013
(defun haskell-interactive-mode-clear ()
9921014
"Clear the screen and put any current input into the history."
@@ -1054,14 +1076,15 @@ If there is one, pop that up in a buffer, similar to `debug-on-error'."
10541076
(with-current-buffer (haskell-session-interactive-buffer session)
10551077
(save-excursion
10561078
(haskell-interactive-mode-goto-end-point)
1057-
(insert (if mode
1058-
(haskell-fontify-as-mode
1059-
(concat message "\n")
1060-
mode)
1061-
(propertize (concat message "\n")
1062-
'front-sticky t
1063-
'read-only t
1064-
'rear-nonsticky t))))))
1079+
(let ((inhibit-read-only t))
1080+
(insert (if mode
1081+
(haskell-fontify-as-mode
1082+
(concat message "\n")
1083+
mode)
1084+
(propertize (concat message "\n")
1085+
'front-sticky t
1086+
'read-only t
1087+
'rear-nonsticky t)))))))
10651088

10661089
(defun haskell-interactive-mode-splices-buffer (session)
10671090
"Get the splices buffer for the current SESSION."

haskell-repl.el

+5-3
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
(defun haskell-interactive-handle-expr ()
2424
"Handle an inputted expression at the REPL."
2525
(let ((expr (haskell-interactive-mode-input)))
26-
(if (string= "" (replace-regexp-in-string " " "" expr))
26+
(if (and (string= "" (replace-regexp-in-string " " "" expr))
27+
haskell-interactive-use-interactive-prompt)
2728
;; Just make a new prompt on space-only input
2829
(progn
2930
(goto-char (point-max))
@@ -116,8 +117,9 @@
116117
(delete-region (1+ haskell-interactive-mode-prompt-start) (point))
117118
(goto-char (point-max))
118119
(let ((start (point)))
119-
(insert (haskell-fontify-as-mode text
120-
haskell-interactive-mode-eval-mode))
120+
(insert (ansi-color-apply (haskell-fontify-as-mode
121+
text
122+
haskell-interactive-mode-eval-mode)))
121123
(when haskell-interactive-mode-collapse
122124
(haskell-collapse start (point)))))))
123125

0 commit comments

Comments
 (0)