branch: elpa/aidermacs commit fdce0f5b17c087356ad1233548543064a0a348d4 Author: Mingde (Matthew) Zeng <matthew...@posteo.net> Commit: Mingde (Matthew) Zeng <matthew...@posteo.net>
Simplify ediff algorithm --- aidermacs-backend-comint.el | 8 +- aidermacs-backend-vterm.el | 8 +- aidermacs.el | 192 ++++++++++++++++++++++---------------------- 3 files changed, 102 insertions(+), 106 deletions(-) diff --git a/aidermacs-backend-comint.el b/aidermacs-backend-comint.el index 3e38f6cba1..d34ba64b8b 100644 --- a/aidermacs-backend-comint.el +++ b/aidermacs-backend-comint.el @@ -26,7 +26,7 @@ ;; Forward declarations (declare-function aidermacs--prepare-for-code-edit "aidermacs") -(declare-function aidermacs--cleanup-all-temp-files "aidermacs") +(declare-function aidermacs--cleanup-temp-buffers "aidermacs") (declare-function aidermacs--show-ediff-for-edited-files "aidermacs") (declare-function aidermacs--detect-edited-files "aidermacs") (declare-function aidermacs--process-message-if-multi-line "aidermacs" (str)) @@ -116,7 +116,7 @@ that was matched at the start of the current syntax block.") (let ((edited-files (aidermacs--detect-edited-files))) (if edited-files (aidermacs--show-ediff-for-edited-files edited-files) - (aidermacs--cleanup-all-temp-files))) + (aidermacs--cleanup-temp-buffers))) (setq aidermacs--comint-output-temp "")))) (defun aidermacs-reset-font-lock-state () @@ -337,11 +337,11 @@ The output is collected and passed to the current callback." (buffer-string)))))) (defun aidermacs--cleanup-temp-files-on-interrupt-comint (orig-fun &rest args) - "Run `aidermacs--cleanup-all-temp-files' after interrupting a comint subjob. + "Run `aidermacs--cleanup-temp-buffers' after interrupting a comint subjob. ORIG-FUN is the original function being advised. ARGS are its arguments." (apply orig-fun args) (when (aidermacs--is-aidermacs-buffer-p) - (aidermacs--cleanup-all-temp-files))) + (aidermacs--cleanup-temp-buffers))) (provide 'aidermacs-backend-comint) diff --git a/aidermacs-backend-vterm.el b/aidermacs-backend-vterm.el index f7a56f71d9..39ff7f7858 100644 --- a/aidermacs-backend-vterm.el +++ b/aidermacs-backend-vterm.el @@ -39,7 +39,7 @@ (declare-function vterm-insert "vterm") (declare-function aidermacs--prepare-for-code-edit "aidermacs") -(declare-function aidermacs--cleanup-all-temp-files "aidermacs") +(declare-function aidermacs--cleanup-temp-buffers "aidermacs") (declare-function aidermacs--show-ediff-for-edited-files "aidermacs") (declare-function aidermacs--detect-edited-files "aidermacs") (declare-function aidermacs--store-output "aidermacs") @@ -108,7 +108,7 @@ If the finish sequence is detected, store the output via ;; Check if any files were edited and show ediff if needed (if edited-files (aidermacs--show-ediff-for-edited-files edited-files) - (aidermacs--cleanup-all-temp-files)) + (aidermacs--cleanup-temp-buffers)) ;; Restore the original process filter now that we've finished processing ;; this command's output. This returns vterm to its normal behavior. (set-process-filter proc orig-filter) @@ -235,7 +235,7 @@ BUFFER is the target buffer to send to. COMMAND is the text to send." (advice-remove 'vterm-send-return #'aidermacs--vterm-capture-keyboard-input)) (defun aidermacs--cleanup-temp-files-on-interrupt-vterm (&rest _args) - "Run `aidermacs--cleanup-all-temp-files' after interrupting a vterm subjob. + "Run `aidermacs--cleanup-temp-buffers' after interrupting a vterm subjob. _ARGS are the arguments." (when (and (aidermacs--is-aidermacs-buffer-p) (equal (this-command-keys) "\C-c\C-c")) @@ -243,7 +243,7 @@ _ARGS are the arguments." (setq-local aidermacs--vterm-processing-command nil) ;; Cancel any active timer (aidermacs--maybe-cancel-active-timer) - (aidermacs--cleanup-all-temp-files))) + (aidermacs--cleanup-temp-buffers))) (provide 'aidermacs-backend-vterm) ;;; aidermacs-backend-vterm.el ends here diff --git a/aidermacs.el b/aidermacs.el index 63043f8e43..3e654bb663 100644 --- a/aidermacs.el +++ b/aidermacs.el @@ -102,11 +102,10 @@ This is the file name without path." (defvar-local aidermacs-read-string-history nil "History list for aidermacs read string inputs.") -(defvar-local aidermacs--pre-edit-files nil - "Alist of (filename . temp-filename) pairs storing file state before Aider edits.") - -(defvar-local aidermacs--pre-edit-buffers nil - "Alist of (filename . temp-buffer) pairs for active ediff sessions.") +(defvar-local aidermacs--pre-edit-file-buffers nil + "Alist of (filename . temp-buffer) pairs storing file state before Aider edits. +These buffers contain the original content of files that might be modified by Aider. +Instead of writing to temporary files, we keep the original content in memory buffers.") ;;;###autoload @@ -301,108 +300,103 @@ This is useful for working in monorepos where you want to limit aider's scope." (aidermacs-run))) (defun aidermacs--capture-file-state (filename) - "Store the current state of FILENAME in a temporary file." + "Store the current state of FILENAME in a temporary buffer. +Creates a read-only buffer with the file's content, appropriate major mode, +and syntax highlighting to match the original file." (when (and filename (file-exists-p filename)) - (let ((temp-file (make-temp-file - (concat "aidermacs-" - (file-name-nondirectory filename) "-")))) - (condition-case err - (progn - (copy-file filename temp-file t) - (cons filename temp-file)) - (error - (message "Error capturing file state for %s: %s" - filename (error-message-string err)) - (when (file-exists-p temp-file) - (delete-file temp-file)) - nil))))) - -(defun aidermacs--cleanup-all-temp-files () - "Clean up all temporary files created for ediff sessions. -This is called when all ediff sessions are complete." + (condition-case err + (let ((temp-buffer (generate-new-buffer + (format " *aidermacs-pre-edit:%s*" + (file-name-nondirectory filename))))) + (with-current-buffer temp-buffer + (insert-file-contents filename) + (set-buffer-modified-p nil) + ;; Use same major mode as the original file + (let ((buffer-file-name filename)) + (set-auto-mode) + ;; Ensure syntax highlighting is applied + (font-lock-ensure)) + ;; Make buffer read-only + (setq buffer-read-only t)) + (cons filename temp-buffer)) + (error + (message "Error capturing file state for %s: %s" + filename (error-message-string err)) + nil)))) + +(defun aidermacs--cleanup-temp-buffers () + "Clean up all temporary buffers created for ediff sessions. +This is called when all ediff sessions are complete. +Kills all pre-edit buffers that were created to store original file content." (interactive) (with-current-buffer (get-buffer (aidermacs-get-buffer-name)) - (dolist (file-pair aidermacs--pre-edit-files) - (let ((temp-file (cdr file-pair))) - (when (and temp-file (stringp temp-file) (file-exists-p temp-file)) - (message "Deleting %s" temp-file) - (delete-file temp-file)))) + ;; Clean up buffers in the tracking list + (dolist (file-pair aidermacs--pre-edit-file-buffers) + (let ((temp-buffer (cdr file-pair))) + (when (and temp-buffer (buffer-live-p temp-buffer)) + (kill-buffer temp-buffer)))) + ;; Also clean up any stray pre-edit buffers that might have been missed + (dolist (buf (buffer-list)) + (when (and (string-match " \\*aidermacs-pre-edit:" (buffer-name buf)) + (buffer-live-p buf)) + (kill-buffer buf))) ;; Clear the list after cleanup - (setq aidermacs--pre-edit-files nil) - (setq aidermacs--pre-edit-buffers nil))) + (setq aidermacs--pre-edit-file-buffers nil))) (defun aidermacs--prepare-for-code-edit () - "Prepare for Aider code edits by capturing current file states." + "Prepare for code edits by capturing current file states in memory buffers. +Creates temporary buffers containing the original content of all tracked files." (let ((files aidermacs--tracked-files)) (when files - (setq aidermacs--pre-edit-files + (setq aidermacs--pre-edit-file-buffers (cl-remove-duplicates (mapcar (lambda (file) (let* ((clean-file (replace-regexp-in-string " (read-only)$" "" file)) (full-path (expand-file-name clean-file (aidermacs-project-root)))) ;; Only capture state if we don't already have it - (or (assoc full-path aidermacs--pre-edit-files) + (or (assoc full-path aidermacs--pre-edit-file-buffers) (aidermacs--capture-file-state full-path)))) files) :test (lambda (a b) (equal (car a) (car b))))) ;; Remove nil entries from the list (where capture failed or was skipped) - (setq aidermacs--pre-edit-files (delq nil aidermacs--pre-edit-files)) + (setq aidermacs--pre-edit-file-buffers (delq nil aidermacs--pre-edit-file-buffers)) ;; Run again if it's nil - (unless aidermacs--pre-edit-files + (unless aidermacs--pre-edit-file-buffers (aidermacs--prepare-for-code-edit))))) (defun aidermacs--ediff-quit-handler () "Handle ediff session cleanup and process next files in queue. -This function is called when an ediff session is quit and performs two tasks: -1. Cleans up resources (buffers and temp files) for the current ediff session -2. Processes the next file in the ediff queue if any remain" - ;; Clean up any pre-edit buffers - (dolist (buf (buffer-list)) - (when (string-match "\\*aidermacs-pre-edit:\\(.*\\)\\*" (buffer-name buf)) - (when (buffer-live-p buf) - (kill-buffer buf)))) - (aidermacs--process-next-ediff-file)) +This function is called when an ediff session is quit and processes +the next file in the ediff queue if any remain." + (when (and (boundp 'ediff-buffer-A) + (buffer-live-p ediff-buffer-A) + (string-match " \\*aidermacs-pre-edit:" + (buffer-name ediff-buffer-A))) + (aidermacs--process-next-ediff-file))) (defun aidermacs--setup-ediff-cleanup-hooks () "Set up hooks to ensure proper cleanup of temporary buffers after ediff." (add-hook 'ediff-quit-hook #'aidermacs--ediff-quit-handler)) (defun aidermacs--detect-edited-files () - "Parse current output to find files edited by Aider." - (let ((edited-files nil) - (output aidermacs--current-output)) - (with-temp-buffer - (insert output) - (goto-char (point-min)) - (while (re-search-forward "Applied edit to \\(.+\\)" nil t) - (push (match-string 1) edited-files))) + "Detect files edited by Aider by comparing buffer contents. +Returns a list of files that have been modified compared to their +pre-edit state stored in temporary buffers." + (let ((edited-files nil)) + (dolist (file-pair aidermacs--pre-edit-file-buffers) + (let* ((filename (car file-pair)) + (pre-edit-buffer (cdr file-pair)) + (current-content (with-temp-buffer + (when (file-exists-p filename) + (insert-file-contents filename)) + (buffer-string))) + (original-content (with-current-buffer pre-edit-buffer + (buffer-string)))) + (unless (equal original-content current-content) + (push filename edited-files)))) + ;; Return the list of edited files (nreverse edited-files))) -(defun aidermacs--create-pre-edit-buffer (filename temp-file) - "Create a buffer for FILENAME using content from TEMP-FILE for ediff." - (condition-case err - (let ((buffer (generate-new-buffer (format "*aidermacs-pre-edit:%s*" - (file-name-nondirectory filename))))) - (with-current-buffer buffer - (condition-case err2 - (progn - (insert-file-contents temp-file) - (set-buffer-modified-p nil) - ;; Use same major mode as the original file would have - (let ((buffer-file-name filename)) - (set-auto-mode)) - ;; Ensure syntax highlighting is applied - (font-lock-ensure) - ;; Make sure buffer is read-only - (setq buffer-read-only t)) - (error - (message "Error setting up pre-edit buffer: %s" (error-message-string err2)) - (kill-buffer buffer) - nil))) - buffer) - (error - (message "Failed to create pre-edit buffer: %s" (error-message-string err)) - nil))) (defvar-local aidermacs--ediff-queue nil "Buffer-local queue of files waiting to be processed by ediff.") @@ -413,34 +407,24 @@ This function is called when an ediff session is quit and performs two tasks: (if aidermacs--ediff-queue (let ((file (pop aidermacs--ediff-queue))) (aidermacs--show-ediff-for-file file)) - (aidermacs--cleanup-all-temp-files)))) + (aidermacs--cleanup-temp-buffers)))) (defun aidermacs--show-ediff-for-file (file) - "Show ediff for FILE." + "Uses the pre-edit buffer stored in memory to compare with the current file state." (let* ((full-path (expand-file-name file (aidermacs-project-root))) - (pre-edit-pair (assoc full-path aidermacs--pre-edit-files)) - (temp-file (and pre-edit-pair (cdr pre-edit-pair)))) - (if (and temp-file - (stringp temp-file) - (file-exists-p temp-file)) + (pre-edit-pair (assoc full-path aidermacs--pre-edit-file-buffers)) + (pre-edit-buffer (and pre-edit-pair (cdr pre-edit-pair)))) + (if (and pre-edit-buffer (buffer-live-p pre-edit-buffer)) (progn - ;; Create buffer from temp file only when needed - (let* ((pre-edit-buffer (aidermacs--create-pre-edit-buffer full-path temp-file)) - (current-buffer (or (get-file-buffer full-path) - (find-file-noselect full-path)))) + (let ((current-buffer (or (get-file-buffer full-path) + (find-file-noselect full-path)))) (with-current-buffer current-buffer (revert-buffer t t t)) (delete-other-windows (get-buffer-window (switch-to-buffer current-buffer))) - ;; Store buffer for cleanup - (unless (boundp 'aidermacs--pre-edit-buffers) - (setq-local aidermacs--pre-edit-buffers nil)) - (push (cons full-path pre-edit-buffer) aidermacs--pre-edit-buffers) - ;; Debug info - (message "Comparing %s with %s" temp-file full-path) ;; Start ediff session (ediff-buffers pre-edit-buffer current-buffer))) - ;; If no pre-edit temp file found, continue with next file - (message "No pre-edit file found for %s in %s out of %s, skipping" file pre-edit-pair aidermacs--pre-edit-files) + ;; If no pre-edit buffer found, continue with next file + (message "No pre-edit buffer found for %s, skipping" file) (aidermacs--process-next-ediff-file)))) (defun aidermacs--show-ediff-for-edited-files (edited-files) @@ -478,7 +462,7 @@ If CALLBACK is non-nil it will be called after the command finishes." (setq-local aidermacs--current-output nil) (setq-local aidermacs--last-command processed-command) ;; Always prepare for potential edits - (aidermacs--cleanup-all-temp-files) + (aidermacs--cleanup-temp-buffers) (aidermacs--prepare-for-code-edit)) (aidermacs--send-command-backend buffer processed-command redirect callback) @@ -520,6 +504,7 @@ If the current buffer is already the aidermacs buffer, do nothing." (defun aidermacs-exit () "Send the command \"/exit\" to the aidermacs buffer." (interactive) + (aidermacs--cleanup-temp-buffers) (aidermacs--send-command "/exit" t)) @@ -1186,7 +1171,18 @@ configuring, troubleshooting, etc." (setq-local aidermacs--current-mode 'help)) (message "Switched to help mode - aider will answer questions about using aider")) -;; Initialize the cleanup mechanisms +;; Add a hook to clean up temp buffers when an aidermacs buffer is killed +(defun aidermacs--cleanup-on-buffer-kill () + "Clean up temporary buffers when an aidermacs buffer is killed." + (when (string-match "^\\*aidermacs:" (buffer-name)) + (aidermacs--cleanup-temp-buffers))) + +(defun aidermacs--setup-cleanup-hooks () + "Set up hooks to ensure proper cleanup of temporary buffers." + (add-hook 'kill-buffer-hook #'aidermacs--cleanup-on-buffer-kill)) + +(aidermacs--setup-ediff-cleanup-hooks) +(aidermacs-setup-minor-mode) (aidermacs--setup-ediff-cleanup-hooks) (provide 'aidermacs)