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")

Reply via email to