branch: elpa/aidermacs
commit e2c81ef3196249a7e68fde48d4e780b565765324
Author: Mingde (Matthew) Zeng <matthew...@posteo.net>
Commit: Mingde (Matthew) Zeng <matthew...@posteo.net>

    Fix vterm advice #15
    
    Signed-off-by: Mingde (Matthew) Zeng <matthew...@posteo.net>
---
 README.org                  |  2 +-
 aidermacs-backend-comint.el |  3 --
 aidermacs-backend-vterm.el  | 97 ++++++++++++++++++++++++---------------------
 aidermacs-backends.el       | 24 +++++++++--
 aidermacs.el                | 60 +++++++++++++++-------------
 5 files changed, 104 insertions(+), 82 deletions(-)

diff --git a/README.org b/README.org
index e6bf8f31a8..6fa5ddfd1f 100644
--- a/README.org
+++ b/README.org
@@ -48,7 +48,7 @@ With =aidermacs=, you get:
 4. Enhanced File Management from Emacs
    - List files currently in chat with =M-x aidermacs-list-added-files=
    - Drop specific files from chat with =M-x aidermacs-drop-file=
-   - View complete chat history with =M-x aidermacs-show-output-history=
+   - View output history with =M-x aidermacs-show-output-history=
    - and more
 
 5. Community-Driven Development
diff --git a/aidermacs-backend-comint.el b/aidermacs-backend-comint.el
index d170ddcabc..f9b5e34a60 100644
--- a/aidermacs-backend-comint.el
+++ b/aidermacs-backend-comint.el
@@ -257,9 +257,6 @@ This allows for multi-line input without sending the 
command."
   (with-current-buffer buffer
     (let ((process (get-buffer-process buffer))
           (inhibit-read-only t))
-      ;; Store command before sending
-      (setq aidermacs--last-command command
-            aidermacs--current-output nil)
       (goto-char (process-mark process))
       (aidermacs-reset-font-lock-state)
       (insert (propertize command
diff --git a/aidermacs-backend-vterm.el b/aidermacs-backend-vterm.el
index d4f2c56039..ce96cda889 100644
--- a/aidermacs-backend-vterm.el
+++ b/aidermacs-backend-vterm.el
@@ -11,54 +11,62 @@
 (defvar vterm-shell)
 (defvar vterm-buffer-name)
 
-(defun aidermacs--vterm-output-advice (orig-fun &rest args)
-  "Capture output before and after executing `vterm-send-return'.
-This advice records the current prompt position as START-POINT,
-calls ORIG-FUN (with ARGS) and then waits until the expected finish
-sequence appears.  The expected finish sequence is defined as the
-substring from the newline before `(vterm--get-prompt-point)` to the
-newline after `(vterm--get-prompt-point)`, matching a regex of `\\n>.*`
-or `\\ndiff>.*`.
-
-Once that is detected, the output from START-POINT up to the beginning
-of the finish sequence is captured and stored via `aidermacs--store-output`.
+(defun aidermacs--vterm-check-finish-sequence-repeated (proc orig-filter 
start-point expected)
+  "Check for the finish sequence repeatedly in PROC’s buffer.
+Force a vterm render and redisplay. If the finish sequence is detected,
+store the output via `aidermacs--store-output`, restore ORIG-FILTER, and 
return t."
+  (when (buffer-live-p (process-buffer proc))
+    (with-current-buffer (process-buffer proc)
+      ;; Force vterm to update its display.
+      (when (fboundp 'vterm--render)
+        (vterm--render))
+      (force-window-update (selected-window))
+      (redisplay t)
+      (let* ((prompt-point (vterm--get-prompt-point))
+             (seq-start (or (save-excursion
+                              (goto-char prompt-point)
+                              (search-backward "\n" nil t))
+                            (point-min)))
+             (seq-end (or (save-excursion
+                            (goto-char prompt-point)
+                            (search-forward "\n" nil t))
+                          (point-max)))
+             (finish-seq (buffer-substring-no-properties seq-start seq-end)))
+        (when (and (string-match-p expected finish-seq)
+                   (< start-point prompt-point))
+          (let ((output (buffer-substring-no-properties start-point 
seq-start)))
+            (aidermacs--store-output (string-trim output)))
+          (set-process-filter proc orig-filter)
+          t)))))
 
-This is a covoluted HACK of capturing aider output until someone comes up with 
a better idea."
-  (when (and (bound-and-true-p aidermacs-minor-mode)
-             (eq major-mode 'vterm-mode))
-    (let* ((start-point (vterm--get-prompt-point))
-           (proc (get-buffer-process (current-buffer)))
-           (expected "\n[^[:space:]]*>[[:space:]].*\n"))
-      ;; Save the original process filter.
-      (let ((orig-filter (process-filter proc)))
-        ;; Set up our temporary filter.
+(defun aidermacs--vterm-output-advice (orig-fun &rest args)
+  "Advice for vterm output: capture output until the finish sequence appears.
+This sets a temporary process filter and installs a repeating timer
+to force vterm to update until the expected finish sequence is detected."
+  (if (and (bound-and-true-p aidermacs-minor-mode)
+           (eq major-mode 'vterm-mode))
+      (let* ((start-point (vterm--get-prompt-point))
+             (proc (get-buffer-process (current-buffer)))
+             (expected "\n[^[:space:]]*>[[:space:]].*\n")
+             (orig-filter (process-filter proc))
+             (timer nil))
+        ;; Set our temporary process filter.
         (set-process-filter
          proc
          (lambda (proc string)
-           ;; Call the original filter first.
+           ;; Call the original filter.
            (funcall orig-filter proc string)
-           ;; Then check for our finish sequence.
-           (let ((buffer (process-buffer proc)))
-             (with-current-buffer buffer
-               (let* ((prompt-point (vterm--get-prompt-point))
-                      (seq-start (or (save-excursion
-                                       (goto-char prompt-point)
-                                       (search-backward "\n" nil t))
-                                     (point-min)))
-                      (seq-end (or (save-excursion
-                                     (goto-char prompt-point)
-                                     (search-forward "\n" nil t))
-                                   (point-max)))
-                      (finish-seq (buffer-substring-no-properties seq-start 
seq-end)))
-                 (when (and (string-match-p expected finish-seq)
-                            (< start-point prompt-point))
-                   ;; Capture the output from the original start-point up to
-                   ;; the beginning of the finish sequence.
-                   (let ((output (buffer-substring-no-properties start-point 
seq-start)))
-                     (aidermacs--store-output (string-trim output))
-                     ;; Restore the original filter.
-                     (set-process-filter proc orig-filter)))))))))))
-    (apply orig-fun args))
+           ;; If we haven't yet started our repeating timer, do so.
+           (unless timer
+             (setq timer (run-with-timer
+                          0.05 0.05
+                          (lambda ()
+                            (when 
(aidermacs--vterm-check-finish-sequence-repeated
+                                   proc orig-filter start-point expected)
+                              (cancel-timer timer)
+                              (setq timer nil))))))))
+        (apply orig-fun args))
+    (apply orig-fun args)))
 
 (defun aidermacs-run-aidermacs-vterm (program args buffer-name)
   "Create a vterm-based buffer and run aidermacs PROGRAM with ARGS in 
BUFFER-NAME.
@@ -82,9 +90,6 @@ and BUFFER-NAME is the name of the vterm buffer."
 (defun aidermacs--send-command-vterm (buffer command)
   "Send COMMAND to the aidermacs vterm BUFFER."
   (with-current-buffer buffer
-    ;; Store command before sending
-    (setq aidermacs--last-command command
-          aidermacs--current-output nil)
     (vterm-send-string command)
     (vterm-send-return)))
 
diff --git a/aidermacs-backends.el b/aidermacs-backends.el
index 3914c07a78..b583b55902 100644
--- a/aidermacs-backends.el
+++ b/aidermacs-backends.el
@@ -55,13 +55,25 @@ Returns a list of (timestamp . output-text) pairs, most 
recent first."
   (interactive)
   (setq aidermacs--output-history nil))
 
+(defvar aidermacs--current-callback nil
+  "Store the callback function for the current command.")
+
+(defvar aidermacs--in-callback nil
+  "Flag to prevent recursive callbacks.")
+
 (defun aidermacs--store-output (output)
-  "Store OUTPUT string in the history with timestamp."
+  "Store OUTPUT string in the history with timestamp.
+If there's a callback function, call it with the output."
   (setq aidermacs--current-output (substring-no-properties output))
   (push (cons (current-time) (substring-no-properties output)) 
aidermacs--output-history)
   (when (> (length aidermacs--output-history) aidermacs-output-limit)
     (setq aidermacs--output-history
-          (seq-take aidermacs--output-history aidermacs-output-limit))))
+          (seq-take aidermacs--output-history aidermacs-output-limit)))
+  (unless aidermacs--in-callback
+    (when aidermacs--current-callback
+      (let ((aidermacs--in-callback t))
+        (funcall aidermacs--current-callback output)
+        (setq aidermacs--current-callback nil)))))
 
 ;; Backend dispatcher functions
 (defun aidermacs-run-aidermacs-backend (program args buffer-name)
@@ -74,8 +86,12 @@ and BUFFER-NAME is the name for the aidermacs buffer."
    (t
     (aidermacs-run-aidermacs-comint program args buffer-name))))
 
-(defun aidermacs--send-command-backend (buffer command)
-  "Send COMMAND to BUFFER using the appropriate backend."
+(defun aidermacs--send-command-backend (buffer command &optional callback)
+  "Send COMMAND to BUFFER using the appropriate backend.
+CALLBACK if provided will be called with the command output when available."
+  (setq aidermacs--last-command command
+        aidermacs--current-output nil
+        aidermacs--current-callback callback)
   (cond
    ((eq aidermacs-backend 'vterm)
     (aidermacs--send-command-vterm buffer command))
diff --git a/aidermacs.el b/aidermacs.el
index d9af242e4d..68bc2fdcde 100644
--- a/aidermacs.el
+++ b/aidermacs.el
@@ -206,15 +206,17 @@ With the universal argument, prompt to edit 
aidermacs-args before running."
         (aidermacs-switch-to-buffer)
       (aidermacs-run-aidermacs-backend aidermacs-program current-args 
buffer-name))))
 
-(defun aidermacs--send-command (command &optional switch-to-buffer)
-  "Send COMMAND to the corresponding aidermacs process after performing 
necessary checks.
-Dispatches to the appropriate backend."
+(defun aidermacs--send-command (command &optional switch-to-buffer callback)
+  "Send COMMAND to the corresponding aidermacs process.
+If SWITCH-TO-BUFFER is non-nil, switch to the aidermacs buffer.
+If CALLBACK is provided, it will be called with the command output when 
available."
   (if-let ((aidermacs-buffer (get-buffer (aidermacs-buffer-name))))
       (let ((processed-command (aidermacs--process-message-if-multi-line 
command)))
-        (aidermacs--send-command-backend aidermacs-buffer processed-command)
-        (when switch-to-buffer
+        (when (and switch-to-buffer aidermacs-buffer)
           (aidermacs-switch-to-buffer))
-        (sleep-for 0.2))
+        (aidermacs--send-command-backend aidermacs-buffer processed-command 
callback)
+        (when (and switch-to-buffer (not (string= (buffer-name) 
(aidermacs-buffer-name))))
+          (aidermacs-switch-to-buffer)))
     (message "Buffer %s does not exist. Please start aidermacs with 'M-x 
aidermacs-run-aidermacs'." aidermacs-buffer-name)))
 
 
@@ -228,8 +230,7 @@ If the current buffer is already the aidermacs buffer, do 
nothing."
   (interactive)
   (let ((buffer (get-buffer (aidermacs-buffer-name))))
     (cond
-     ((string= (buffer-name) (aidermacs-buffer-name))
-      (message "Already in aidermacs buffer"))
+     ((string= (buffer-name) (aidermacs-buffer-name)) t)
      ((and buffer (get-buffer-window buffer))
       (select-window (get-buffer-window buffer)))  ;; Switch to existing window
      (buffer
@@ -332,7 +333,6 @@ wrap it in {aidermacs\nstr\naidermacs}. Otherwise return 
STR unchanged."
 
 (defun aidermacs--parse-ls-output (output)
   "Parse the /ls command OUTPUT to extract files in chat.
-
 After the \"Files in chat:\" header, each subsequent line that begins with
 whitespace is processed. The first non-whitespace token is taken as the file 
name.
 Relative paths are resolved using the repository root (if available) or
@@ -351,11 +351,12 @@ Returns a deduplicated list of such file names."
         (let ((base (or (vc-git-root default-directory)
                         default-directory))
               files)
-          ;; Process lines that start with whitespace.
+          ;; Process each line that begins with whitespace.
           (while (and (not (eobp))
                       (string-match-p "^[[:space:]]" (thing-at-point 'line t)))
             (let* ((line (string-trim (thing-at-point 'line t)))
-                   (file (car (split-string line))))
+                   (parts (split-string line))
+                   (file (if parts (car parts) "")))
               (unless (string-empty-p file)
                 (let ((file-abs (if (file-name-absolute-p file)
                                     file
@@ -367,28 +368,31 @@ Returns a deduplicated list of such file names."
 
 ;;;###autoload
 (defun aidermacs-list-added-files ()
-  "List all files currently added to the chat session."
+  "List all files currently added to the chat session.
+Sends the \"/ls\" command and returns the list of files via callback."
   (interactive)
-  (aidermacs--send-command "/ls" t)
-  ;; Wait briefly for output to be processed
-  (sleep-for 0.5)
-  (if-let ((files (aidermacs--parse-ls-output aidermacs--current-output)))
-      (progn
-        (message "%s" (prin1-to-string files))
-        files)
-    (error "No files currently added to chat or unable to parse output")))
+  (aidermacs--send-command
+   "/ls" t
+   (lambda (output)
+     (let ((files (aidermacs--parse-ls-output output)))
+       (message "%s" (prin1-to-string files))
+       files))))
 
 ;;;###autoload
 (defun aidermacs-drop-file ()
   "Drop a file from the chat session by selecting from currently added files."
   (interactive)
-  (aidermacs--send-command "/ls" t)
-  ;; Wait briefly for output to be processed
-  (sleep-for 0.5)
-  (if-let* ((files (aidermacs-list-added-files))
-            (file (completing-read "Select file to drop: " files nil t)))
-      (aidermacs--send-command (format "/drop %s" file) t)
-    (error "No files available to drop")))
+  (aidermacs--send-command
+   "/ls" t
+   (lambda (output)
+     (condition-case nil
+         (if-let* ((files (aidermacs--parse-ls-output output))
+                   (file (completing-read "Select file to drop: " files nil 
t)))
+             (progn
+               (aidermacs--send-command (format "/drop %s" file)))
+           (message "No files available to drop"))
+       (quit (message "Drop file cancelled"))))))
+
 
 ;;;###autoload
 (defun aidermacs-show-output-history ()
@@ -408,7 +412,7 @@ Returns a deduplicated list of such file names."
       (goto-char (point-min))
       (setq buffer-read-only t)
       (local-set-key (kbd "q") 'kill-this-buffer)
-    (switch-to-buffer-other-frame buf))))
+      (switch-to-buffer-other-frame buf))))
 
 ;;;###autoload
 (defun aidermacs-get-last-output ()

Reply via email to