Skip to content

Commit 7e536cc

Browse files
committed
Track prompt visibility explicitly in the REPL buffer
Marker positions alone can't disambiguate "live prompt at point-max" from "just-sent input committed at point-max" -- both end up with prompt-marker == input-start-marker == point-max. The previous heuristic fell through the cracks: output emitted in the no-input branch landed *after* a freshly inserted prompt instead of above it. Add a buffer-local port-repl-prompt-active-p flag set in port-repl--insert-prompt and cleared in port-repl-send-input. The emit-output path now branches on it: when non-nil, do the delete-prompt / insert-above / re-insert-prompt dance (which also preserves any typed-but-unsent input); when nil, just append at point-max and advance the markers.
1 parent 12c0f60 commit 7e536cc

2 files changed

Lines changed: 44 additions & 33 deletions

File tree

lisp/port-repl.el

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
(defvar-local port-repl-prompt-marker nil
3333
"Marker just past the most recently inserted prompt.")
3434

35+
(defvar-local port-repl-prompt-active-p nil
36+
"Non-nil when a live prompt is currently displayed at the end of the buffer.
37+
When nil, output is appended at point-max; when non-nil, it's
38+
inserted above the prompt (preserving any typed-but-unsent input).")
39+
3540
(defvar-local port-repl-history nil
3641
"Ring of previously sent inputs (most recent first).")
3742

@@ -130,7 +135,8 @@
130135
field port-repl-prompt))
131136
(set-marker port-repl-prompt-marker (point)))
132137
(set-marker port-repl-input-start-marker (point))
133-
(set-marker-insertion-type port-repl-input-start-marker nil)))
138+
(set-marker-insertion-type port-repl-input-start-marker nil)
139+
(setq port-repl-prompt-active-p t)))
134140

135141
(defun port-repl-handle-message (msg)
136142
"Render a single prepl MSG into the current REPL buffer.
@@ -156,42 +162,44 @@ MSG is an alist as produced by `port-client--parse-messages'."
156162
(format "%s\n" val)))
157163

158164
(defun port-repl--insert-output (text+face)
159-
"Insert TEXT+FACE (a (TEXT . FACE) pair) into the buffer above the prompt."
165+
"Insert TEXT+FACE (a (TEXT . FACE) pair) into the REPL buffer.
166+
If a prompt is currently displayed (`port-repl-prompt-active-p'),
167+
insert above it -- preserving any typed-but-unsent input -- so the
168+
prompt and the user's typing stay at the bottom. Otherwise (e.g.
169+
between sending a form and receiving its first response message)
170+
just append at point-max."
160171
(let* ((text (car text+face))
161172
(face (cdr text+face))
162-
(inhibit-read-only t)
163-
(insert-pos (marker-position port-repl-prompt-marker))
164-
(input-active (< insert-pos (point-max)))
165-
(saved-input (when input-active
166-
(buffer-substring-no-properties
167-
port-repl-input-start-marker (point-max)))))
168-
(when input-active
169-
(delete-region port-repl-input-start-marker (point-max))
170-
(delete-region (- insert-pos (port-repl--prompt-length)) insert-pos))
171-
(goto-char (if input-active
172-
(- insert-pos (port-repl--prompt-length))
173-
insert-pos))
174-
(let ((start (point)))
175-
(insert text)
176-
(add-text-properties start (point)
177-
`(read-only t
178-
rear-nonsticky (read-only)
179-
front-sticky (read-only)
180-
face ,face)))
173+
(inhibit-read-only t))
181174
(cond
182-
(input-active
183-
(port-repl--insert-prompt)
184-
(insert saved-input))
175+
(port-repl-prompt-active-p
176+
(let* ((insert-pos (marker-position port-repl-prompt-marker))
177+
(saved-input (buffer-substring-no-properties
178+
port-repl-input-start-marker (point-max))))
179+
(delete-region port-repl-input-start-marker (point-max))
180+
(delete-region (- insert-pos (port-repl--prompt-length)) insert-pos)
181+
(goto-char (- insert-pos (port-repl--prompt-length)))
182+
(port-repl--insert-output-text text face)
183+
(port-repl--insert-prompt)
184+
(insert saved-input)))
185185
(t
186-
;; No prompt currently on screen: keep the markers at the new
187-
;; point so subsequent messages of the same response append
188-
;; after this output instead of treating it as input that
189-
;; needs preserving.
186+
(goto-char (point-max))
187+
(port-repl--insert-output-text text face)
190188
(set-marker port-repl-prompt-marker (point))
191189
(set-marker port-repl-input-start-marker (point))))
192190
(set-window-point (get-buffer-window (current-buffer) 'visible)
193191
(point-max))))
194192

193+
(defun port-repl--insert-output-text (text face)
194+
"Insert TEXT at point with FACE and read-only properties."
195+
(let ((start (point)))
196+
(insert text)
197+
(add-text-properties start (point)
198+
`(read-only t
199+
rear-nonsticky (read-only)
200+
front-sticky (read-only)
201+
face ,face))))
202+
195203
(defun port-repl--prompt-length ()
196204
"Return the character length of the current prompt string."
197205
(length (format "%s=> "
@@ -257,11 +265,13 @@ MSG is an alist as produced by `port-client--parse-messages'."
257265
(add-text-properties port-repl-input-start-marker (point)
258266
'(read-only t
259267
rear-nonsticky (read-only)))
260-
;; Commit the just-sent text: advance the prompt and input-start
261-
;; markers past it so response messages append after the form
262-
;; rather than getting inserted "above the prompt".
268+
;; Commit the just-sent text: advance the markers past it and
269+
;; mark the prompt as no longer "live", so response messages
270+
;; append after the form rather than getting inserted "above
271+
;; the prompt".
263272
(set-marker port-repl-prompt-marker (point))
264-
(set-marker port-repl-input-start-marker (point)))
273+
(set-marker port-repl-input-start-marker (point))
274+
(setq port-repl-prompt-active-p nil))
265275
(push input port-repl-history)
266276
(setq port-repl-history-index -1)
267277
(port-client-send port--connection input))

test/port-repl-tests.el

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ not above the prompt."
4545
(add-text-properties port-repl-input-start-marker (point)
4646
'(read-only t rear-nonsticky (read-only)))
4747
(set-marker port-repl-prompt-marker (point))
48-
(set-marker port-repl-input-start-marker (point)))
48+
(set-marker port-repl-input-start-marker (point))
49+
(setq port-repl-prompt-active-p nil))
4950
;; The prepl response.
5051
(port-repl-handle-message
5152
'((:tag . :out) (:val . "hi\n")))

0 commit comments

Comments
 (0)