branch: elpa/aidermacs
commit 02acb8ac63cd730085c315340dcd51db4c547e35
Author: Mingde (Matthew) Zeng <[email protected]>
Commit: Mingde (Matthew) Zeng <[email protected]>

    Refactor aidermacs--parse-output-for-files and 
aidermacs--detect-edited-files
    
    Signed-off-by: Mingde (Matthew) Zeng <[email protected]>
---
 aidermacs-backends.el | 178 ++++++++++++++++++++++++++------------------------
 aidermacs.el          | 168 ++++++++++++++++++++++++-----------------------
 2 files changed, 177 insertions(+), 169 deletions(-)

diff --git a/aidermacs-backends.el b/aidermacs-backends.el
index f6d3c64cf7..2556c6e256 100644
--- a/aidermacs-backends.el
+++ b/aidermacs-backends.el
@@ -78,97 +78,103 @@ Returns a list of (timestamp . output-text) pairs, most 
recent first."
   "List of files that have been mentioned in the aidermacs output.
 This is used to avoid having to run /ls repeatedly.")
 
-(defun aidermacs--verify-tracked-files ()
-  "Verify files in `aidermacs--tracked-files` exist.
-Remove any files that don't exist."
-  (let* ((project-root (aidermacs-project-root))
-         (is-remote (file-remote-p project-root))
-         (valid-files nil))
-    (dolist (file aidermacs--tracked-files)
-      (let* ((is-readonly (string-match-p " (read-only)$" file))
-             (actual-file (if is-readonly
-                              (substring file 0 (- (length file) 12))
-                            file))
-             (full-path (expand-file-name actual-file project-root)))
-        (when (or (file-exists-p full-path) is-remote)
-          (push file valid-files))))
-    (setq aidermacs--tracked-files valid-files)))
-
 (defun aidermacs--parse-output-for-files (output)
   "Parse OUTPUT for files and add them to `aidermacs--tracked-files'."
   (when output
-    (let ((lines (split-string output "\n"))
-          (last-line "")
-          (in-udiff nil)
-          (current-udiff-file nil))
-      (dolist (line lines)
-        (cond
-         ;; Applied edit to <filename>
-         ((string-match "Applied edit to \\(\\./\\)?\\(.+\\)" line)
-          (when-let ((file (match-string 2 line)))
-            (add-to-list 'aidermacs--tracked-files file)))
-
-         ;; Added <filename> to the chat.
-         ((string-match "Added \\(\\./\\)?\\(.+\\) to the chat" line)
-          (when-let ((file (match-string 2 line)))
-            (add-to-list 'aidermacs--tracked-files file)))
-
-         ;; Removed <filename> from the chat (with or without ./ prefix)
-         ((string-match "Removed \\(\\./\\)?\\(.+\\) from the chat" line)
-          (when-let ((file (match-string 2 line)))
-            (setq aidermacs--tracked-files (delete file 
aidermacs--tracked-files))))
-
-         ;; Added <filename> to read-only files.
-         ((string-match "Added \\(\\./\\)?\\(.+\\) to read-only files" line)
-          (when-let ((file (match-string 2 line)))
-            (add-to-list 'aidermacs--tracked-files (concat file " 
(read-only)"))))
-
-         ;; Moved <file> from editable to read-only files in the chat
-         ((string-match "Moved \\(\\./\\)?\\(.+\\) from editable to read-only 
files in the chat" line)
-          (when-let ((file (match-string 2 line)))
-            (let ((editable-file (replace-regexp-in-string " (read-only)$" "" 
file)))
-              (setq aidermacs--tracked-files (delete editable-file 
aidermacs--tracked-files))
-              (add-to-list 'aidermacs--tracked-files (concat file " 
(read-only)")))))
-
-         ;; Moved <file> from read-only to editable files in the chat
-         ((string-match "Moved \\(\\./\\)?\\(.+\\) from read-only to editable 
files in the chat" line)
-          (when-let ((file (match-string 2 line)))
-            (let ((read-only-file (concat file " (read-only)")))
-              (setq aidermacs--tracked-files (delete read-only-file 
aidermacs--tracked-files))
-              (add-to-list 'aidermacs--tracked-files file))))
-
-         ;; <file>\nAdd file to the chat?
-         ((string-match "Add file to the chat?" line)
-          (add-to-list 'aidermacs--tracked-files last-line)
-          (aidermacs--prepare-for-code-edit))
-
-         ;; <file> is already in the chat as an editable file
-         ((string-match "\\(\\./\\)?\\(.+\\) is already in the chat as an 
editable file" line)
-          (when-let ((file (match-string 2 line)))
-            (add-to-list 'aidermacs--tracked-files file)))
-
-         ;; Handle udiff format
-         ;; Detect start of udiff with "--- filename"
-         ((string-match "^--- \\(\\./\\)?\\(.+\\)" line)
-          (setq in-udiff t
-                current-udiff-file (match-string 2 line)))
-
-         ;; Confirm udiff file with "+++ filename" line
-         ((and in-udiff
-               current-udiff-file
-               (string-match "^\\+\\+\\+ \\(\\./\\)?\\(.+\\)" line))
-          (let ((plus-file (match-string 2 line)))
-            ;; Only add if the filenames match (ignoring ./ prefix)
-            (when (string= (file-name-nondirectory current-udiff-file)
-                           (file-name-nondirectory plus-file))
-              (add-to-list 'aidermacs--tracked-files current-udiff-file)
-              (setq in-udiff nil
-                    current-udiff-file nil)))))
-
-        (setq last-line line))
+    (let ((tracked-files aidermacs--tracked-files))
+      (with-temp-buffer
+        (insert output)
+        (goto-char (point-min))
+
+        ;; Applied edit to <filename>
+        (while (search-forward "Applied edit to" nil t)
+          (beginning-of-line)
+          (when-let ((file (and (looking-at ".*Applied edit to 
\\(\\./\\)?\\(.+\\)")
+                                (match-string-no-properties 2))))
+            (add-to-list 'tracked-files file))
+          (forward-line 1))
+
+        ;; Combined file tracking logic
+        (goto-char (point-min))
+        (while (re-search-forward
+                "\\(Added\\|Removed\\|Moved\\) \\(\\./\\)?\\([^ ]+\\) 
\\(to\\|from\\) \\(the chat\\|editable\\|read-only\\) files?"
+                nil t)
+          (let* ((action (match-string 1))
+                 (file (match-string 3))
+                 (state (match-string 5)))
+            (cond
+             ;; Added files
+             ((string= action "Added")
+              (add-to-list 'tracked-files
+                           (if (string= state "read-only")
+                               (concat file " (read-only)")
+                             file)))
+
+             ;; Removed files
+             ((string= action "Removed")
+              (setq tracked-files (delete file tracked-files)))
+
+             ;; Moved files
+             ((string= action "Moved")
+              (let* ((from-state (if (string= state "editable") "read-only" 
"editable"))
+                     (old-file (if (string= from-state "read-only")
+                                   (concat file " (read-only)")
+                                 file))
+                     (new-file (if (string= state "read-only")
+                                   (concat file " (read-only)")
+                                 file)))
+                (setq tracked-files (delete old-file tracked-files))
+                (add-to-list 'tracked-files new-file))))))
+
+        ;; <file> is already in the chat as an editable file
+        (goto-char (point-min))
+        (while (search-forward " is already in the chat as an editable file" 
nil t)
+          (beginning-of-line)
+          (when-let ((file (and (looking-at "\\(\\./\\)?\\(.+\\) is already in 
the chat as an editable file")
+                                (match-string-no-properties 2))))
+            (add-to-list 'tracked-files file))
+          (forward-line 1))
+
+        ;; Add file to the chat?
+        (goto-char (point-min))
+        (while (search-forward "Add file to the chat?" nil t)
+          (save-excursion
+            (forward-line -1)
+            (let ((potential-file (string-trim (buffer-substring 
(line-beginning-position) (line-end-position)))))
+              (when (not (string-empty-p potential-file))
+                (add-to-list 'tracked-files potential-file)
+                (aidermacs--prepare-for-code-edit))))
+          (forward-line 1))
+
+        ;; Handle udiff format
+        (goto-char (point-min))
+        (while (search-forward "--- " nil t)
+          (message "processing %s " tracked-files)
+          (let* ((line-end (line-end-position))
+                 (current-udiff-file (when (looking-at "\\(\\./\\)?\\(.+\\)")
+                                       (match-string-no-properties 2))))
+            (when current-udiff-file
+              (forward-line 1)
+              (when (looking-at "\\+\\+\\+ \\(\\./\\)?\\(.+\\)")
+                (let ((plus-file (match-string-no-properties 2)))
+                  (when (string= (file-name-nondirectory current-udiff-file)
+                                 (file-name-nondirectory plus-file))
+                    (add-to-list 'tracked-files current-udiff-file))))))))
 
       ;; Verify all tracked files exist
-      (aidermacs--verify-tracked-files))))
+      (let* ((project-root (aidermacs-project-root))
+             (is-remote (file-remote-p project-root))
+             (valid-files nil))
+        (dolist (file tracked-files)
+          (let* ((is-readonly (string-match-p " (read-only)$" file))
+                 (actual-file (if is-readonly
+                                  (substring file 0 (- (length file) 12))
+                                file))
+                 (full-path (expand-file-name actual-file project-root)))
+            (when (or (file-exists-p full-path) is-remote)
+              (push file valid-files))))
+        (setq tracked-files valid-files))
+      (setq aidermacs--tracked-files tracked-files))))
 
 (defun aidermacs-reset-tracked-files ()
   "Reset the list of tracked files and force a refresh."
diff --git a/aidermacs.el b/aidermacs.el
index a0cf7b802b..df1c2726fc 100644
--- a/aidermacs.el
+++ b/aidermacs.el
@@ -82,7 +82,7 @@ When nil, disable auto-commits requiring manual git commits."
   "Get the project root using project.el, VC, or fallback to file directory.
 This function tries multiple methods to determine the project root."
   (or (when-let ((proj (project-current)))
-          (project-root proj))
+        (project-root proj))
       (vc-git-root default-directory)
       (when buffer-file-name
         (file-name-directory buffer-file-name))
@@ -175,42 +175,42 @@ This is used when you want to target an existing session."
 If USE-EXISTING is non-nil, use an existing buffer instead of creating new."
   (if use-existing
       (aidermacs-select-buffer-name)
-  (let* ((root (aidermacs-project-root))
-         (current-dir (file-truename default-directory))
-         ;; Get all existing aidermacs buffers
-         (aidermacs-buffers
-          (match-buffers #'aidermacs--is-aidermacs-buffer-p))
-         ;; Extract directory paths and subtree status from buffer names
-         (buffer-dirs
-          (mapcar
-           (lambda (buf)
-             (when (string-match "^\\*aidermacs:\\(.*?\\)\\*$"
-                                 (buffer-name buf))
-               (cons (match-string 1 (buffer-name buf))
-                     (match-string 2 (buffer-name buf)))))
-           aidermacs-buffers))
-         ;; Find closest parent directory that has an aidermacs session
-         (closest-parent
-          (caar
-           (sort
-            (cl-remove-if-not
-             (lambda (dir-info)
-               (and (car dir-info)
-                    (string-prefix-p (car dir-info) current-dir)
-                    (file-exists-p (car dir-info))))
-             buffer-dirs)
-            (lambda (a b)
-              ;; Sort by length of filenames (deeper filenames first)
-              (> (length (car a)) (length (car b)))))))
-         (display-root (cond
-                        ;; Use current directory for new subtree session
-                        (aidermacs-subtree-only current-dir)
-                        ;; Use closest parent if it exists
-                        (closest-parent closest-parent)
-                        ;; Fall back to project root for new non-subtree 
session
-                        (t root))))
-    (format "*aidermacs:%s*"
-            (file-truename display-root)))))
+    (let* ((root (aidermacs-project-root))
+           (current-dir (file-truename default-directory))
+           ;; Get all existing aidermacs buffers
+           (aidermacs-buffers
+            (match-buffers #'aidermacs--is-aidermacs-buffer-p))
+           ;; Extract directory paths and subtree status from buffer names
+           (buffer-dirs
+            (mapcar
+             (lambda (buf)
+               (when (string-match "^\\*aidermacs:\\(.*?\\)\\*$"
+                                   (buffer-name buf))
+                 (cons (match-string 1 (buffer-name buf))
+                       (match-string 2 (buffer-name buf)))))
+             aidermacs-buffers))
+           ;; Find closest parent directory that has an aidermacs session
+           (closest-parent
+            (caar
+             (sort
+              (cl-remove-if-not
+               (lambda (dir-info)
+                 (and (car dir-info)
+                      (string-prefix-p (car dir-info) current-dir)
+                      (file-exists-p (car dir-info))))
+               buffer-dirs)
+              (lambda (a b)
+                ;; Sort by length of filenames (deeper filenames first)
+                (> (length (car a)) (length (car b)))))))
+           (display-root (cond
+                          ;; Use current directory for new subtree session
+                          (aidermacs-subtree-only current-dir)
+                          ;; Use closest parent if it exists
+                          (closest-parent closest-parent)
+                          ;; Fall back to project root for new non-subtree 
session
+                          (t root))))
+      (format "*aidermacs:%s*"
+              (file-truename display-root)))))
 
 ;;;###autoload
 (defun aidermacs-run ()
@@ -262,7 +262,7 @@ This function sets up the appropriate arguments and 
launches the process."
                '("--subtree-only")))))
          (final-args (append backend-args flat-extra-args)))
     (if (and (get-buffer buffer-name)
-            (process-live-p (get-buffer-process buffer-name)))
+                (process-live-p (get-buffer-process buffer-name)))
         (aidermacs-switch-to-buffer buffer-name)
       (aidermacs-run-backend aidermacs-program final-args buffer-name)
       (with-current-buffer buffer-name
@@ -363,53 +363,54 @@ Only adds the hook if it's not already present."
 (defun aidermacs--detect-edited-files ()
   "Parse current output to find files edited by Aider.
 Returns a list of files that have been modified according to the output."
-  (let ((edited-files nil)
-        (output aidermacs--current-output))
+  (let ((project-root (aidermacs-project-root))
+        (output aidermacs--current-output)
+        (edited-files)
+        (unique-files)
+        (valid-files))
     (when output
-      (let ((lines (split-string output "\n"))
-            (last-line "")
-            (in-udiff nil)
-            (current-udiff-file nil))
-        (dolist (line lines)
-          (cond
-           ;; Case 1: Look for "Applied edit to <filename>" pattern
-           ((string-match "Applied edit to \\(\\./\\)?\\(.+\\)" line)
-            (when-let ((file (match-string 2 line)))
-              (push file edited-files)))
-
-           ;; Case 2: Look for a filename followed by triple backticks on next 
line
-           ((string-match "^```" line)
-            (let ((potential-file (string-trim last-line)))
-              (when (not (string-empty-p potential-file))
+      (with-temp-buffer
+        (insert output)
+        (goto-char (point-min))
+
+        ;; Case 1: Find "Applied edit to" lines
+        (while (search-forward "Applied edit to" nil t)
+          (beginning-of-line)
+          (when-let ((file (and (looking-at ".*Applied edit to 
\\(\\./\\)?\\([^[:space:]]+\\)")
+                                (match-string-no-properties 2))))
+            (push file edited-files))
+          (forward-line 1))
+
+        ;; Case 2: Find triple backtick blocks with filenames
+        (goto-char (point-min))
+        (while (search-forward "```" nil t)
+          (save-excursion
+            (forward-line -1)
+            (let ((potential-file (string-trim (buffer-substring 
(line-beginning-position) (line-end-position)))))
+              (when (and (not (string-empty-p potential-file))
+                         (not (string-match-p "\\`[[:space:]]*\\'" 
potential-file))
+                         (not (string-match-p "^```" potential-file)))
                 (push potential-file edited-files))))
-
-           ;; Case 3: Handle udiff format
-           ;; Detect start of udiff with "--- filename"
-           ((string-match "^--- \\(\\./\\)?\\(.+\\)" line)
-            (setq in-udiff t
-                  current-udiff-file (match-string 2 line)))
-
-           ;; Confirm udiff file with "+++ filename" line
-           ((and in-udiff
-                 current-udiff-file
-                 (string-match "^\\+\\+\\+ \\(\\./\\)?\\(.+\\)" line))
-            (let ((plus-file (match-string 2 line)))
-              ;; Only add if the filenames match (ignoring ./ prefix)
-              (when (string= (file-name-nondirectory current-udiff-file)
-                             (file-name-nondirectory plus-file))
-                (push current-udiff-file edited-files)
-                (setq in-udiff nil
-                      current-udiff-file nil)))))
-
-          (setq last-line line))))
-
-    ;; Filter the list to only include valid files
-    (let* ((project-root (aidermacs-project-root))
-           (unique-files (delete-dups edited-files))
-           (valid-files (cl-remove-if-not
+          (forward-line 1))
+
+        ;; Case 3: Handle udiff format
+        (goto-char (point-min))
+        (while (search-forward "--- " nil t)
+          (let* ((line-end (line-end-position))
+                 (current-udiff-file (buffer-substring (point) line-end)))
+            (forward-line 1)
+            (when (looking-at "\\+\\+\\+ ")
+              (let ((plus-file (buffer-substring (+ (point) 4) 
(line-end-position))))
+                (when (string= (file-name-nondirectory current-udiff-file)
+                               (file-name-nondirectory plus-file))
+                  (push current-udiff-file edited-files)))))))
+
+      ;; Filter the list to only include valid files
+      (setq unique-files (delete-dups edited-files))
+      (setq valid-files (cl-remove-if-not
                          (lambda (file)
                            (file-exists-p (expand-file-name file 
project-root)))
-                         unique-files)))
+                         unique-files))
       (nreverse valid-files))))
 
 (defvar-local aidermacs--ediff-queue nil
@@ -438,7 +439,7 @@ Returns a list of files that have been modified according 
to the output."
     (if (and pre-edit-buffer (buffer-live-p pre-edit-buffer))
         (progn
           (let ((current-buffer (or (get-file-buffer full-path)
-                                   (find-file-noselect 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)))
@@ -639,6 +640,7 @@ Sends the \"/ls\" command and returns the list of files via 
callback."
   (aidermacs--get-files-in-session
    (lambda (files)
      (message "%S" files)
+     (setq aidermacs--tracked-files files)
      files)))
 
 (defun aidermacs-drop-file ()
@@ -765,7 +767,7 @@ GUIDE is displayed in the prompt but not included in the 
final command."
                           (when region-text
                             (format " on code block:\n```\n%s\n```\n" 
region-text))))
          (prompt (concat command " " prompt-prefix context
-                        (when guide (format "(%s)" guide)) ": "))
+                         (when guide (format "(%s)" guide)) ": "))
          (user-command (aidermacs-plain-read-string prompt)))
     (concat command (unless (string-empty-p user-command)
                       (concat " " prompt-prefix context ": " user-command)))))

Reply via email to