branch: elpa/aidermacs
commit 3253a965662a645ba6f56dc04b8c859af405f65c
Author: Mingde (Matthew) Zeng <[email protected]>
Commit: Mingde (Matthew) Zeng <[email protected]>
Further refactor
Signed-off-by: Mingde (Matthew) Zeng <[email protected]>
---
aidermacs-backend-comint.el | 15 ++++-------
aidermacs-backend-vterm.el | 52 +++++++++++++++++++-------------------
aidermacs-backends.el | 17 +++----------
aidermacs-models.el | 61 ++++++++++++++++++++-------------------------
aidermacs.el | 52 +++++++++++++++++++++-----------------
5 files changed, 90 insertions(+), 107 deletions(-)
diff --git a/aidermacs-backend-comint.el b/aidermacs-backend-comint.el
index 1bdb9818da..ff7b2dfd04 100644
--- a/aidermacs-backend-comint.el
+++ b/aidermacs-backend-comint.el
@@ -39,8 +39,7 @@
("cpp" . "c++"))
"Map external language names to Emacs names."
:type '(alist :key-type (string :tag "Language Name/Alias")
- :value-type (string :tag "Mode Name (without -mode)"))
- :group 'aidermacs)
+ :value-type (string :tag "Mode Name (without -mode)")))
(defconst aidermacs-search-marker "<<<<<<< SEARCH")
(defconst aidermacs-diff-marker "=======")
@@ -52,24 +51,20 @@
(defcustom aidermacs-comint-multiline-newline-key "S-<return>"
"Key binding for `comint-accumulate' in Aidermacs buffers.
This allows for multi-line input without sending the command."
- :type 'string
- :group 'aidermacs)
+ :type 'string)
(defface aidermacs-command-separator
'((((type graphic)) :strike-through t :extend t)
(((type tty)) :inherit font-lock-comment-face :underline t :extend t))
- "Face for command separator in aidermacs."
- :group 'aidermacs)
+ "Face for command separator in aidermacs.")
(defface aidermacs-command-text
'((t :inherit bold))
- "Face for commands sent to aidermacs buffer."
- :group 'aidermacs)
+ "Face for commands sent to aidermacs buffer.")
(defface aidermacs-search-replace-block
'((t :inherit 'diff-refine-added :bold t))
- "Face for search/replace block content."
- :group 'aidermacs)
+ "Face for search/replace block content.")
(defvar aidermacs-font-lock-keywords
'(("^\x2500+\n?" 0 '(face aidermacs-command-separator) t)
diff --git a/aidermacs-backend-vterm.el b/aidermacs-backend-vterm.el
index 1ba32ba250..400450a864 100644
--- a/aidermacs-backend-vterm.el
+++ b/aidermacs-backend-vterm.el
@@ -63,8 +63,7 @@
(defcustom aidermacs-vterm-multiline-newline-key "S-<return>"
"Key binding to enter a newline without sending in vterm."
- :type 'string
- :group 'aidermacs)
+ :type 'string)
(defvar aidermacs-prompt-regexp)
@@ -88,7 +87,7 @@ If the finish sequence is detected, store the output via
;; Only check if we have a new prompt or haven't checked this
position yet
(last-check (or aidermacs--vterm-last-check-point start-point))
(should-check (> prompt-point last-check))
- ;; Simplified pattern that just looks for a shell prompt
+ ;; Pattern that looks for a shell prompt
(expected aidermacs-prompt-regexp))
;; Update the last check point
(setq aidermacs--vterm-last-check-point prompt-point)
@@ -125,31 +124,32 @@ This sets a temporary process filter that checks for the
finish sequence
after each output chunk, reducing the need for timers."
(when (and (aidermacs--is-aidermacs-buffer-p)
(not (string-empty-p aidermacs--last-command)))
- (let* ((start-point (condition-case nil
- (vterm--get-prompt-point)
- (error (point-min))))
- (proc (get-buffer-process (current-buffer)))
- (orig-filter (process-filter proc)))
- ;; Store the command for tracking in the correct buffer
- (with-current-buffer (process-buffer proc)
- ;; Initialize tracking variables
- (setq aidermacs--vterm-last-check-point nil)
- ;; Cancel any existing timer first
- (aidermacs--maybe-cancel-active-timer)
- ;; Create a new timer immediately to start checking for command
completion
- (setq aidermacs--vterm-active-timer
- (run-with-timer
- aidermacs-vterm-check-interval
- aidermacs-vterm-check-interval
- (lambda () (aidermacs--vterm-check-finish-sequence-repeated
proc orig-filter start-point))))))))
+ (let* ((start-point (condition-case nil
+ (vterm--get-prompt-point)
+ (error (point-min))))
+ (proc (get-buffer-process (current-buffer)))
+ (orig-filter (process-filter proc)))
+ ;; Store the command for tracking in the correct buffer
+ (with-current-buffer (process-buffer proc)
+ ;; Initialize tracking variables
+ (setq aidermacs--vterm-last-check-point nil)
+ ;; Cancel any existing timer first
+ (aidermacs--maybe-cancel-active-timer)
+ ;; Create a new timer immediately to start checking for command
completion
+ (setq aidermacs--vterm-active-timer
+ (run-with-timer
+ aidermacs-vterm-check-interval
+ aidermacs-vterm-check-interval
+ (lambda () (aidermacs--vterm-check-finish-sequence-repeated
proc orig-filter start-point))))))))
(defun aidermacs--maybe-cancel-active-timer (&optional buffer)
"Cancel the active timer if it exists.
-Use BUFFER if provided, otherwise retrieve it from `aidermacs-get-buffer-name'"
- (with-current-buffer (get-buffer (or buffer (aidermacs-get-buffer-name)))
- (when (timerp aidermacs--vterm-active-timer)
- (cancel-timer aidermacs--vterm-active-timer)
- (setq aidermacs--vterm-active-timer nil))))
+Use BUFFER if provided, otherwise retrieve it from
`aidermacs-get-buffer-name'."
+ (when-let ((buf (get-buffer (or buffer (aidermacs-get-buffer-name)))))
+ (with-current-buffer buf
+ (when (timerp aidermacs--vterm-active-timer)
+ (cancel-timer aidermacs--vterm-active-timer)
+ (setq aidermacs--vterm-active-timer nil)))))
(defun aidermacs-run-vterm (program args buffer-name)
"Create a vterm-based buffer and run aidermacs program.
@@ -243,7 +243,7 @@ _ARGS are the arguments."
(define-key map (kbd "C-c C-c") #'aidermacs-vterm-send-C-c)
(when (featurep 'evil)
(evil-define-minor-mode-key map 'insert
- (kbd "RET") (function aidermacs-vterm-send-return)))
+ (kbd "RET") (function
aidermacs-vterm-send-return)))
map)
"Keymap used when `aidermacs-vterm-mode' is enabled.")
diff --git a/aidermacs-backends.el b/aidermacs-backends.el
index 878700625e..f6d3c64cf7 100644
--- a/aidermacs-backends.el
+++ b/aidermacs-backends.el
@@ -32,28 +32,17 @@
(declare-function aidermacs--prepare-for-code-edit "aidermacs" ())
(declare-function aidermacs--get-files-in-session "aidermacs" (callback))
-(defgroup aidermacs-backends nil
- "Backend customization for aidermacs."
- :group 'aidermacs)
-
(defcustom aidermacs-backend 'comint
"Backend to use for the aidermacs process.
Options are `comint' (the default) or `vterm'. When set to `vterm',
aidermacs launches a fully functional vterm buffer instead
of using a comint process."
:type '(choice (const :tag "Comint" comint)
- (const :tag "VTerm" vterm))
- :group 'aidermacs-backends)
-
-;; Core output management functionality
-(defgroup aidermacs-output nil
- "Output handling for aidermacs."
- :group 'aidermacs)
+ (const :tag "VTerm" vterm)))
(defcustom aidermacs-output-limit 10
"Maximum number of output entries to keep in history."
- :type 'integer
- :group 'aidermacs-output)
+ :type 'integer)
(defvar-local aidermacs--output-history nil
"List to store aidermacs output history.
@@ -94,7 +83,7 @@ This is used to avoid having to run /ls repeatedly.")
Remove any files that don't exist."
(let* ((project-root (aidermacs-project-root))
(is-remote (file-remote-p project-root))
- (valid-files nil))
+ (valid-files nil))
(dolist (file aidermacs--tracked-files)
(let* ((is-readonly (string-match-p " (read-only)$" file))
(actual-file (if is-readonly
diff --git a/aidermacs-models.el b/aidermacs-models.el
index d3af67230c..0fdcdafa9e 100644
--- a/aidermacs-models.el
+++ b/aidermacs-models.el
@@ -32,30 +32,22 @@
(declare-function aidermacs-buffer-name "aidermacs" ())
(declare-function aidermacs-exit "aidermacs" ())
-(defgroup aidermacs-models nil
- "Model selection customization for aidermacs."
- :group 'aidermacs)
-
(defcustom aidermacs-default-model "sonnet"
"Default AI model to use for aidermacs sessions when not in Architect mode."
- :type 'string
- :group 'aidermacs-models)
+ :type 'string)
(defcustom aidermacs-architect-model "sonnet"
"Default AI model to use for architectural reasoning in aidermacs sessions."
- :type 'string
- :group 'aidermacs-models)
+ :type 'string)
(defcustom aidermacs-editor-model aidermacs-default-model
"Default AI model to use for code editing in aidermacs sessions.
Defaults to `aidermacs-default-model` if not explicitly set."
- :type 'string
- :group 'aidermacs-models)
+ :type 'string)
(defcustom aidermacs-use-architect-mode nil
"If non-nil, use separate Architect/Editor mode."
- :type 'boolean
- :group 'aidermacs-models)
+ :type 'boolean)
(defcustom aidermacs-popular-models
'("sonnet"
@@ -66,8 +58,7 @@ Defaults to `aidermacs-default-model` if not explicitly set."
"List of available AI models for selection.
Each model should be in the format expected by the aidermacs CLI.
Also based on aidermacs LLM benchmark:
https://aidermacs.chat/docs/leaderboards/"
- :type '(repeat string)
- :group 'aidermacs-models)
+ :type '(repeat string))
(defvar aidermacs--cached-models aidermacs-popular-models
"Cache of available AI models.")
@@ -79,28 +70,31 @@ Returns a list of model names with appropriate prefixes
based on the
API provider."
(let* ((url-parsed (url-generic-parse-url url))
(hostname (url-host url-parsed))
- (prefix (cond ((string= hostname "api.openai.com") "openai")
- ((string= hostname "openrouter.ai") "openrouter")
- ((string= hostname "api.deepseek.com") "deepseek")
- ((string= hostname "api.anthropic.com") "anthropic")
- ((string= hostname "generativelanguage.googleapis.com")
"gemini")
- (t (error "Unknown API host: %s" hostname))))
- (token (cond ((string= hostname "api.openai.com") (getenv
"OPENAI_API_KEY"))
- ((string= hostname "openrouter.ai") (getenv
"OPENROUTER_API_KEY"))
- ((string= hostname "api.deepseek.com") (getenv
"DEEPSEEK_API_KEY"))
- ((string= hostname "api.anthropic.com") (getenv
"ANTHROPIC_API_KEY"))
- ((string= hostname "generativelanguage.googleapis.com")
(getenv "GEMINI_API_KEY"))
- (t (error "Unknown API host: %s" hostname)))))
+ (prefix (pcase hostname
+ ("api.openai.com" "openai")
+ ("openrouter.ai" "openrouter")
+ ("api.deepseek.com" "deepseek")
+ ("api.anthropic.com" "anthropic")
+ ("generativelanguage.googleapis.com" "gemini")
+ (_ (error "Unknown API host: %s" hostname))))
+ (token (pcase hostname
+ ("api.openai.com" (getenv "OPENAI_API_KEY"))
+ ("openrouter.ai" (getenv "OPENROUTER_API_KEY"))
+ ("api.deepseek.com" (getenv "DEEPSEEK_API_KEY"))
+ ("api.anthropic.com" (getenv "ANTHROPIC_API_KEY"))
+ ("generativelanguage.googleapis.com" (getenv
"GEMINI_API_KEY"))
+ (_ (error "Unknown API host: %s" hostname)))))
(with-local-quit
(with-current-buffer
(let ((url-request-extra-headers
- (cond ((string= hostname "api.anthropic.com")
- `(("x-api-key" . ,token)
- ("anthropic-version" . "2023-06-01")))
- ((string= hostname "generativelanguage.googleapis.com")
- nil) ; No auth headers for Gemini, key is in URL
- (t
- `(("Authorization" . ,(concat "Bearer " token)))))))
+ (pcase hostname
+ ("api.anthropic.com"
+ `(("x-api-key" . ,token)
+ ("anthropic-version" . "2023-06-01")))
+ ("generativelanguage.googleapis.com"
+ nil) ; No auth headers for Gemini, key is in URL
+ (_
+ `(("Authorization" . ,(concat "Bearer " token)))))))
(url-retrieve-synchronously
(if (string= hostname "generativelanguage.googleapis.com")
(concat url "/models?key=" token)
@@ -121,7 +115,6 @@ API provider."
(alist-get 'name model))))))
models))))))
-
(defun aidermacs--select-model ()
"Provide model selection with completion.
This is a private function used internally."
diff --git a/aidermacs.el b/aidermacs.el
index 2f7f5180f1..91a6d31980 100644
--- a/aidermacs.el
+++ b/aidermacs.el
@@ -10,17 +10,16 @@
;;; Commentary:
-;; Aidermacs integrates with Aider for AI-assisted code modification
-;; in Emacs. Provides interface to interact with Aider through Emacs
-;; buffer with commands for file management, code generation, and
-;; question answering.
-;;
-;; Features:
-;; - AI-assisted code modification
-;; - Multiple backends (Comint and VTerm)
-;; - File management commands
-;; - Transient menus for quick access
-;; - Minor mode for prompt files
+;; Aidermacs integrates with Aider (https://aider.chat/) for AI-assisted code
+;; modification in Emacs. Aider lets you pair program with LLMs to edit code
+;; in your local git repository. It works with both new projects and existing
+;; code bases, supporting Claude, DeepSeek, ChatGPT, and can connect to almost
+;; any LLM including local models. Think of it as having a helpful coding
+;; partner that can understand your code, suggest improvements, fix bugs, and
+;; even write new code for you. Whether you're working on a new feature,
+;; debugging, or just need help understanding some code, Aidermacs provides an
+;; intuitive way to collaborate with AI while staying in your familiar Emacs
+;; environment.
;; Originally forked from Kang Tu <[email protected]>'s Aider.el.
@@ -201,7 +200,7 @@ If USE-EXISTING is non-nil, use an existing buffer instead
of creating new."
(file-exists-p (car dir-info))))
buffer-dirs)
(lambda (a b)
- ;; Sort by path length (deeper paths first)
+ ;; Sort by length of filenames (deeper filenames first)
(> (length (car a)) (length (car b)))))))
(display-root (cond
;; Use current directory for new subtree session
@@ -218,6 +217,11 @@ If USE-EXISTING is non-nil, use an existing buffer instead
of creating new."
"Run aidermacs process using the selected backend.
This function sets up the appropriate arguments and launches the process."
(interactive)
+ ;; Set up necessary hooks when aidermacs is actually run
+ (aidermacs--setup-ediff-cleanup-hooks)
+ (aidermacs--setup-cleanup-hooks)
+ (aidermacs-setup-minor-mode)
+
(let* ((buffer-name (aidermacs-get-buffer-name))
;; Process extra args: split each string on whitespace.
(flat-extra-args
@@ -351,8 +355,10 @@ the next file in the ediff queue if any remain."
(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))
+ "Set up hooks to ensure proper cleanup of temporary buffers after ediff.
+Only adds the hook if it's not already present."
+ (unless (member #'aidermacs--ediff-quit-handler ediff-quit-hook)
+ (add-hook 'ediff-quit-hook #'aidermacs--ediff-quit-handler)))
(defun aidermacs--detect-edited-files ()
"Parse current output to find files edited by Aider.
@@ -973,7 +979,7 @@ snippets, or other content to the session."
(insert ";; Just edit and save - changes will be available to aider\n\n")
(write-file filename))
(let ((command (aidermacs--prepare-file-paths-for-command "/read" (list
filename))))
- (aidermacs--send-command command))
+ (aidermacs--send-command command t t))
(find-file-other-window filename)
(message "Created and added scratchpad to session: %s" filename)))
@@ -1046,7 +1052,7 @@ Otherwise, send the line under cursor."
(with-restriction start end
(save-excursion
(goto-char (point-min))
- (while (search-forward "^[[:space:]]*\\(.+\\)[[:space:]]*$" nil t)
+ (while (search-forward "^[[:space:]]*\\([^[:space:]].*\\)[[:space:]]*$"
nil t)
(aidermacs--send-command (match-string 1))))))
(defun aidermacs-send-block-or-region ()
@@ -1130,6 +1136,7 @@ These are exact filename matches (including the dot
prefix)."
"Set up automatic enabling of `aidermacs-minor-mode' for specific files.
This adds a hook to automatically enable the minor mode for files
matching patterns in `aidermacs-auto-mode-files'.
+Only adds the hook if it's not already present.
The minor mode provides convenient keybindings for working with
prompt files and other Aider-related files:
@@ -1138,7 +1145,8 @@ prompt files and other Aider-related files:
\\[aidermacs-send-block-or-region] - Send block/region as whole
\\[aidermacs-switch-to-buffer] - Switch to Aidermacs buffer"
(interactive)
- (add-hook 'find-file-hook #'aidermacs--maybe-enable-minor-mode))
+ (unless (member #'aidermacs--maybe-enable-minor-mode find-file-hook)
+ (add-hook 'find-file-hook #'aidermacs--maybe-enable-minor-mode)))
;;;###autoload
(defun aidermacs-switch-to-code-mode ()
@@ -1191,12 +1199,10 @@ configuring, troubleshooting, etc."
(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)
+ "Set up hooks to ensure proper cleanup of temporary buffers.
+Only adds the hook if it's not already present."
+ (unless (member #'aidermacs--cleanup-on-buffer-kill kill-buffer-hook)
+ (add-hook 'kill-buffer-hook #'aidermacs--cleanup-on-buffer-kill)))
(provide 'aidermacs)
;;; aidermacs.el ends here