branch: elpa/inf-clojure
commit fe2a6f5794cce0779054bb4cb11b576ca437bbfe
Author: Bozhidar Batsov <[email protected]>
Commit: Bozhidar Batsov <[email protected]>
Fix preoutput filter to handle chunked comint output correctly
comint delivers process output in chunks (typically ~1024 bytes),
calling preoutput filter functions once per chunk. The old filter
prepended a newline to every chunk, causing spurious newlines in
long output (#153), and relied on this-command/last-command for
each chunk, which is fragile for later chunks (#136).
Use two buffer-local flags to track state across chunks:
- inf-clojure--filtering-output: set on the first chunk of a
response, cleared when a prompt is detected
- inf-clojure--output-pending-newline: ensures the newline is
prepended only once per response
Fixes #136
Fixes #153
---
inf-clojure.el | 39 +++++++++++++++++++++++++++++++++------
test/inf-clojure-tests.el | 29 +++++++++++++++++++++++++++++
2 files changed, 62 insertions(+), 6 deletions(-)
diff --git a/inf-clojure.el b/inf-clojure.el
index e2529b66c4..dc843b8ca5 100644
--- a/inf-clojure.el
+++ b/inf-clojure.el
@@ -686,14 +686,41 @@ If `comint-use-prompt-regexp' is nil (the default),
\\[comint-insert-input] on
"Remove subprompts from STRING."
(replace-regexp-in-string inf-clojure-subprompt "" string))
+(defvar-local inf-clojure--filtering-output nil
+ "Non-nil while filtering output from a programmatic inf-clojure command.
+Set on the first output chunk of an inf-clojure command and cleared
+when a prompt is detected, so that multi-chunk responses are filtered
+consistently without relying on `this-command'/`last-command' for
+every chunk.")
+
+(defvar-local inf-clojure--output-pending-newline nil
+ "Non-nil if the next output chunk should be preceded by a newline.
+Used to prepend a single newline before the first chunk of output
+from a programmatic command, separating it from the input.")
+
(defun inf-clojure-preoutput-filter (str)
- "Preprocess the output STR from interactive commands."
+ "Preprocess the output STR from interactive commands.
+For output triggered by inf-clojure commands (as opposed to direct
+user input), this removes subprompts and prepends a single newline
+before the first chunk. Output is tracked across chunks using
+`inf-clojure--filtering-output' so that later chunks are handled
+correctly even if `this-command'/`last-command' has changed."
(inf-clojure--log-string str "<-RES----")
- (cond
- ((string-prefix-p "inf-clojure-" (symbol-name (or this-command
last-command)))
- ;; Remove subprompts and prepend a newline to the output string
- (inf-clojure-chomp (concat "\n" (inf-clojure-remove-subprompts str))))
- (t str)))
+ ;; Detect the start of output from a programmatic inf-clojure command.
+ (when (and (not inf-clojure--filtering-output)
+ (let ((cmd (or this-command last-command)))
+ (and cmd (string-prefix-p "inf-clojure-" (symbol-name cmd)))))
+ (setq inf-clojure--filtering-output t)
+ (setq inf-clojure--output-pending-newline t))
+ (if (not inf-clojure--filtering-output)
+ str
+ ;; Stop filtering once a prompt appears (end of response).
+ (when (string-match inf-clojure-prompt str)
+ (setq inf-clojure--filtering-output nil))
+ (let ((prefix (when inf-clojure--output-pending-newline
+ (setq inf-clojure--output-pending-newline nil)
+ "\n")))
+ (concat prefix (inf-clojure-remove-subprompts str)))))
(defun inf-clojure-clear-repl-buffer ()
"Clear the REPL buffer."
diff --git a/test/inf-clojure-tests.el b/test/inf-clojure-tests.el
index 8fcf77a817..5ace111001 100644
--- a/test/inf-clojure-tests.el
+++ b/test/inf-clojure-tests.el
@@ -240,6 +240,35 @@ is a string\")
(it "preserves internal newlines"
(expect (inf-clojure-chomp "hello\nworld\n") :to-equal "hello\nworld")))
+(describe "inf-clojure-preoutput-filter"
+ (it "passes through output when not filtering"
+ (let ((inf-clojure--filtering-output nil)
+ (last-command 'self-insert-command))
+ (expect (inf-clojure-preoutput-filter "hello") :to-equal "hello")))
+ (it "prepends a newline only to the first chunk of inf-clojure command
output"
+ (let ((inf-clojure--filtering-output nil)
+ (inf-clojure--output-pending-newline nil)
+ (this-command 'inf-clojure-eval-last-sexp)
+ (inf-clojure-prompt "^[^=> \n]+=> *"))
+ ;; First chunk: should get newline prefix
+ (expect (inf-clojure-preoutput-filter "result")
+ :to-equal "\nresult")
+ ;; Second chunk: no newline prefix
+ (expect (inf-clojure-preoutput-filter " more")
+ :to-equal " more")))
+ (it "stops filtering when a prompt is detected"
+ (let ((inf-clojure--filtering-output t)
+ (inf-clojure--output-pending-newline nil)
+ (inf-clojure-prompt "^[^=> \n]+=> *"))
+ (inf-clojure-preoutput-filter "\nuser=> ")
+ (expect inf-clojure--filtering-output :to-be nil)))
+ (it "removes subprompts from filtered output"
+ (let ((inf-clojure--filtering-output t)
+ (inf-clojure--output-pending-newline nil)
+ (inf-clojure-subprompt " *#_=> *"))
+ (expect (inf-clojure-preoutput-filter "foo #_=> bar")
+ :to-equal "foobar"))))
+
(describe "inf-clojure-remove-subprompts"
(it "removes subprompts from a string"
(expect (inf-clojure-remove-subprompts "foo #_=> bar")