Skip to content

Commit 26414a2

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 26414a2

5 files changed

+104
-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

+6
Original file line numberDiff line numberDiff line change
@@ -1517,6 +1517,12 @@ 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 (mapconcat #'identity
1523+
(nbutlast (split-string response "\n")
1524+
(1+ (cl-count ?\n haskell-interactive-prompt)))
1525+
"\n")))
15201526
;; Propertize for eldoc
15211527
(save-match-data
15221528
(when (string-match " :: " response)

haskell-interactive-mode.el

+66-40
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,12 @@ do the
247247
:}"
248248
(if (not (string-match-p "\n" expr))
249249
expr
250-
(let ((len (length haskell-interactive-prompt))
250+
(let ((len (if (or haskell-interactive-use-interactive-prompt
251+
(not (string-match "\n.*\\'" haskell-interactive-prompt)))
252+
(length haskell-interactive-prompt)
253+
(- (match-end 0)
254+
(match-beginning 0)
255+
1)))
251256
(lines (split-string expr "\n")))
252257
(cl-loop for elt on (cdr lines) do
253258
(setcar elt (substring (car elt) len)))
@@ -295,21 +300,30 @@ do the
295300
(defun haskell-interactive-mode-prompt (&optional session)
296301
"Show a prompt at the end of the REPL buffer.
297302
If SESSION is non-nil, use the REPL buffer associated with
298-
SESSION, otherwise operate on the current buffer."
303+
SESSION, otherwise operate on the current buffer. The prompt
304+
inserted is specified by `haskell-interactive-prompt'.
305+
When `haskell-interactive-use-interactive-prompt' is non-nil,
306+
the prompt is inserted in this function. Otherwise it was already
307+
set in the `haskell-process-send-startup' and has already been
308+
inserted in the buffer by the process."
299309
(with-current-buffer (if session
300310
(haskell-session-interactive-buffer session)
301311
(current-buffer))
302312
(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)))
313+
(if haskell-interactive-use-interactive-prompt
314+
(let ((prompt (propertize haskell-interactive-prompt
315+
'font-lock-face 'haskell-interactive-face-prompt
316+
'prompt t
317+
'read-only t
318+
'rear-nonsticky t)))
319+
;; At the time of writing, front-stickying the first char gives an error
320+
;; Has unfortunate side-effect of being able to insert before the prompt
321+
(insert (substring prompt 0 1)
322+
(propertize (substring prompt 1)
323+
'front-sticky t)))
324+
(let ((inhibit-read-only t))
325+
(unless (= (point) (point-min))
326+
(put-text-property (1- (point)) (point) 'prompt t))))
313327
(let ((marker (setq-local haskell-interactive-mode-prompt-start (make-marker))))
314328
(set-marker marker (point)))
315329
(when haskell-interactive-mode-scroll-to-bottom
@@ -322,16 +336,13 @@ SESSION, otherwise operate on the current buffer."
322336
(let ((prop-text (propertize text
323337
'font-lock-face 'haskell-interactive-face-result
324338
'front-sticky t
325-
'prompt t
326339
'read-only t
327340
'rear-nonsticky t
328341
'result t)))
329342
(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))
343+
(setq prop-text (propertize prop-text
344+
'font-lock-face 'haskell-interactive-face-prompt2
345+
'prompt2 t)))
335346
(insert (ansi-color-apply prop-text))
336347
(haskell-interactive-mode-handle-h)
337348
(let ((marker (setq-local haskell-interactive-mode-result-end (make-marker))))
@@ -973,20 +984,34 @@ don't care when the thing completes as long as it's soonish."
973984
(setq haskell-interactive-mode-history-index 0)
974985
(haskell-interactive-mode-history-toggle -1))))
975986

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))
987+
(defun haskell-interactive-mode-prompt-previous (&optional arg)
988+
"Jump to the ARGth previous prompt."
989+
(interactive "p")
990+
(if (< arg 0)
991+
(haskell-interactive-mode-prompt-next (- arg))
992+
(end-of-line 1)
993+
(unless (or (get-text-property (1- (point)) 'prompt)
994+
(zerop arg))
995+
(cl-incf arg 0.5)) ; do it an extra time if not at a prompt
996+
(dotimes (_ (* 2 arg))
997+
(goto-char (or (previous-single-property-change (point) 'prompt)
998+
(point))))
999+
(when (get-text-property (point) 'prompt)
1000+
;; went too far (at first prompt)
1001+
(goto-char (next-single-property-change (point) 'prompt)))))
1002+
1003+
(defun haskell-interactive-mode-prompt-next (&optional arg)
1004+
"Jump to the ARGth next prompt."
1005+
(interactive "p")
1006+
(if (< arg 0)
1007+
(haskell-interactive-mode-prompt-previous (- arg))
1008+
(when (and (get-text-property (point) 'prompt)
1009+
(not (zerop arg)))
1010+
;; don't start on a prompt
1011+
(haskell-interactive-mode-prompt-previous 1))
1012+
(dotimes (_ (* 2 arg))
1013+
(goto-char (or (next-single-property-change (point) 'prompt)
1014+
(point-max))))))
9901015

9911016
(defun haskell-interactive-mode-clear ()
9921017
"Clear the screen and put any current input into the history."
@@ -1054,14 +1079,15 @@ If there is one, pop that up in a buffer, similar to `debug-on-error'."
10541079
(with-current-buffer (haskell-session-interactive-buffer session)
10551080
(save-excursion
10561081
(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))))))
1082+
(let ((inhibit-read-only t))
1083+
(insert (if mode
1084+
(haskell-fontify-as-mode
1085+
(concat message "\n")
1086+
mode)
1087+
(propertize (concat message "\n")
1088+
'front-sticky t
1089+
'read-only t
1090+
'rear-nonsticky t)))))))
10651091

10661092
(defun haskell-interactive-mode-splices-buffer (session)
10671093
"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)