branch: elpa/aidermacs
commit e2c81ef3196249a7e68fde48d4e780b565765324
Author: Mingde (Matthew) Zeng <[email protected]>
Commit: Mingde (Matthew) Zeng <[email protected]>
Fix vterm advice #15
Signed-off-by: Mingde (Matthew) Zeng <[email protected]>
---
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 ()