branch: elpa/aidermacs commit 048b3847f49293bb961fc14c6cc0c2ef7883f909 Author: Mingde (Matthew) Zeng <matthew...@posteo.net> Commit: Mingde (Matthew) Zeng <matthew...@posteo.net>
Fully embrace aidermacs Signed-off-by: Mingde (Matthew) Zeng <matthew...@posteo.net> --- README.org | 40 +- aider-backend-comint.el | 50 --- aider-backend-vterm.el | 32 -- aider-backends.el | 49 --- aider-doom.el | 72 ---- aider-models.el | 39 -- aidermacs-backend-comint.el | 50 +++ aidermacs-backend-vterm.el | 32 ++ aidermacs-backends.el | 49 +++ aidermacs-doom.el | 72 ++++ aider-helm.el => aidermacs-helm.el | 31 +- aidermacs-models.el | 39 ++ aider-etc.el => aidermacs-utils.el | 6 +- aider.el => aidermacs.el | 785 +++++++++++++++++++------------------ scratch.aider | 36 +- test_aider.el | 168 -------- tests/test dir/a.kt | 1 - tests/test-aider-alias.el | 24 -- 18 files changed, 690 insertions(+), 885 deletions(-) diff --git a/README.org b/README.org index c334cee2d1..4c8644f0eb 100644 --- a/README.org +++ b/README.org @@ -59,43 +59,37 @@ With =aidermacs=, you get: - [[https://github.com/magit/transient][Transient]] and [[https://github.com/magit/magit][Magit]] ** Recommended -With Straight +*** With Straight #+BEGIN_SRC emacs-lisp -(use-package aider +(use-package aidermacs :straight (:host github :repo "MatthewZMD/aidermacs" :files ("*.el")) :config - (setq aider-args '("--model" "anthropic/claude-3-5-sonnet-20241022")) + (setq aidermacs-args '("--model" "anthropic/claude-3-5-sonnet-20241022")) (setenv "ANTHROPIC_API_KEY" anthropic-api-key) - (global-set-key (kbd "C-c a") 'aider-transient-menu)) + (global-set-key (kbd "C-c a") 'aidermacs-transient-menu)) #+END_SRC - With package-vc-install +*** With use-package #+BEGIN_SRC emacs-lisp -(package-vc-install '(aider :url "https://github.com/MatthewZMD/aidermacs")) +(use-package aidermacs + :config + (setq aidermacs-args '("--model" "o3-mini"))) #+END_SRC - For Doom Emacs +*** For Doom Emacs In =packages.el=: #+BEGIN_SRC emacs-lisp -(package! aider :recipe (:host github :repo "MatthewZMD/aidermacs" :files ("aider.el" "aider-doom.el"))) -#+END_SRC - -In =config.el=: -#+BEGIN_SRC emacs-lisp -(use-package aider - :config - (setq aider-args '("--model" "o3-mini"))) +(package! aidermacs :recipe (:host github :repo "MatthewZMD/aidermacs" :files ("*.el"))) #+END_SRC - * Configurability ** Terminal Backend Selection -Choose your preferred terminal backend by setting =aider-backend=: +Choose your preferred terminal backend by setting =aidermacs-backend=: #+BEGIN_SRC emacs-lisp ;; Use vterm backend (default is comint) -(setq aider-backend 'vterm) +(setq aidermacs-backend 'vterm) #+END_SRC Available backends: @@ -107,7 +101,7 @@ When using the comint backend, you can customize the key binding for multiline i #+BEGIN_SRC emacs-lisp ;; Change multiline input key (default is S-<return>) -(setq aider-comint-multiline-newline-key "C-<return>") +(setq aidermacs-comint-multiline-newline-key "C-<return>") #+END_SRC This key allows you to enter multiple lines without sending the command to Aider. Press =RET= normally to send the command. @@ -116,7 +110,7 @@ This key allows you to enter multiple lines without sending the command to Aider * Features ** Session Management -- Create repository-specific Aider sessions +- Create repository-specific Aidermacs sessions - Switch between multiple AI sessions - Toggle between window and frame views @@ -135,10 +129,10 @@ Access repository-specific prompt files with =C-c a p= for organizing and reusin * Development Workflow -1. Start an Aider session (=aider-run-aider=) +1. Start an Aidermacs session (=aidermacs-run-aider=) 2. Add relevant files to the session 3. Implement or modify code using: - - =aider-implement-todo= for new code - - =aider-function-or-region-refactor= for existing code + - =aidermacs-implement-todo= for new code + - =aidermacs-function-or-region-refactor= for existing code 4. Generate and run tests 5. Refine the implementation through additional prompts or manual edits diff --git a/aider-backend-comint.el b/aider-backend-comint.el deleted file mode 100644 index 24d1091acf..0000000000 --- a/aider-backend-comint.el +++ /dev/null @@ -1,50 +0,0 @@ -;;; aider-backend-comint.el --- Comint backend for aider.el -*- lexical-binding: t; -*- - -;;; Commentary: -;; Comint backend implementation for aider.el - -;;; Code: - -(require 'comint) - -(defcustom aider-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 'aider) - -(defun aider-run-aider-comint (program args buffer-name) - "Create a comint-based buffer and run aider PROGRAM with ARGS in BUFFER-NAME." - (let ((comint-terminfo-terminal "dumb")) - (unless (comint-check-proc buffer-name) - (apply 'make-comint-in-buffer "aider" buffer-name program nil args) - (with-current-buffer buffer-name - (comint-mode) - (setq-local comint-input-sender 'aider-input-sender) - (setq aider--font-lock-buffer - (get-buffer-create (concat " *aider-fontify" buffer-name))) - (add-hook 'kill-buffer-hook #'aider-kill-buffer nil t) - (add-hook 'comint-output-filter-functions #'aider-fontify-blocks 100 t) - (let ((local-map (make-sparse-keymap))) - (set-keymap-parent local-map comint-mode-map) - (define-key local-map (kbd aider-comint-multiline-newline-key) #'comint-accumulate) - (use-local-map local-map)) - (font-lock-add-keywords nil aider-font-lock-keywords t))))) - -(defun aider--send-command-comint (buffer command &optional switch-to-buffer) - "Send COMMAND to the aider comint BUFFER. -If SWITCH-TO-BUFFER is non-nil, switch to the buffer after sending." - (with-current-buffer buffer - (let ((process (get-buffer-process buffer)) - (inhibit-read-only t)) - (goto-char (process-mark process)) - (insert (propertize command - 'face 'aider-command-text - 'font-lock-face 'aider-command-text - 'rear-nonsticky t)) - (set-marker (process-mark process) (point)) - (comint-send-string process (concat command "\n"))))) - -(provide 'aider-backend-comint) - -;;; aider-backend-comint.el ends here diff --git a/aider-backend-vterm.el b/aider-backend-vterm.el deleted file mode 100644 index acc8aeedc0..0000000000 --- a/aider-backend-vterm.el +++ /dev/null @@ -1,32 +0,0 @@ -;;; aider-backend-vterm.el --- VTerm backend for aider.el -*- lexical-binding: t; -*- - -;;; Commentary: -;; VTerm backend implementation for aider.el - -;;; Code: - -(require 'vterm nil t) - -(defun aider-run-aider-vterm (program args buffer-name) - "Create a vterm-based buffer and run aider PROGRAM with ARGS in BUFFER-NAME." - (unless (require 'vterm nil t) - (error "vterm package is not available")) - (unless (get-buffer buffer-name) - (let ((cmd (concat program " " (mapconcat 'identity args " ")))) - (setq vterm-shell cmd) - (setq vterm-buffer-name buffer-name) - (vterm) - (with-current-buffer buffer-name - (setq-local aider-backend 'vterm) - (aider-minor-mode 1))))) - -(defun aider--send-command-vterm (buffer command &optional switch-to-buffer) - "Send COMMAND to the aider vterm BUFFER. -If SWITCH-TO-BUFFER is non-nil, switch to the buffer after sending." - (with-current-buffer buffer - (vterm-send-string command) - (vterm-send-return))) - -(provide 'aider-backend-vterm) - -;;; aider-backend-vterm.el ends here diff --git a/aider-backends.el b/aider-backends.el deleted file mode 100644 index 1911a42ee3..0000000000 --- a/aider-backends.el +++ /dev/null @@ -1,49 +0,0 @@ -;;; aider-backends.el --- Backend implementations for aider.el -*- lexical-binding: t; -*- - -;;; Commentary: -;; Backend dispatcher for aider.el - -;;; Code: - -(defgroup aider-backends nil - "Backend customization for aider." - :group 'aider) - -(defcustom aider-backend 'comint - "Backend to use for the aider process. -Options are 'comint (the default) or 'vterm. When set to 'vterm, aider will -launch a fully functional vterm buffer (with bracketed paste support) instead -of using a comint process." - :type '(choice (const :tag "Comint" comint) - (const :tag "VTerm" vterm)) - :group 'aider-backends) - - -(require 'aider-backend-comint) -(when (and (eq aider-backend 'vterm) (not (featurep 'vterm))) - (require 'aider-backend-vterm)) - - -;; Backend dispatcher functions -(defun aider-run-aider-backend (program args buffer-name) - "Run aider using the selected backend. -PROGRAM is the aider executable path, ARGS are command line arguments, -and BUFFER-NAME is the name for the aider buffer." - (cond - ((eq aider-backend 'vterm) - (aider-run-aider-vterm program args buffer-name)) - (t - (aider-run-aider-comint program args buffer-name)))) - -(defun aider--send-command-backend (buffer command &optional switch-to-buffer) - "Send COMMAND to BUFFER using the appropriate backend. -If SWITCH-TO-BUFFER is non-nil, switch to the buffer after sending." - (cond - ((eq aider-backend 'vterm) - (aider--send-command-vterm buffer command switch-to-buffer)) - (t - (aider--send-command-comint buffer command switch-to-buffer)))) - -(provide 'aider-backends) - -;;; aider-backends.el ends here diff --git a/aider-doom.el b/aider-doom.el deleted file mode 100644 index 09403a1580..0000000000 --- a/aider-doom.el +++ /dev/null @@ -1,72 +0,0 @@ -;;; aider-doom.el --- Description -*- lexical-binding: t; no-byte-compile: t -*- -;; -;; This file is not part of GNU Emacs. -;; -;;; Commentary: -;; -;; Doom integration for aidermacs -;; -;;; Code: - -(defun aider-doom-setup-keys () - "Setup aidermacs keybindings if the current buffer is in a git repository." - (when (and (featurep 'doom-keybinds) - (vc-backend (or (buffer-file-name) default-directory))) - (map! :leader - (:prefix ("A" . "aidermacs") - (:prefix ("a" . "Add") - :desc "Current file" "c" #'aider-add-current-file - :desc "File read-only" "f" #'aider-current-file-read-only - :desc "Files in window" "w" #'aider-add-files-in-current-window - :desc "Add Same Type Files under dir" "d" #'aider-add-same-type-files-under-dir - :desc "Batch direct marked files" "b" #'aider-batch-add-dired-marked-files - ) - - (:prefix ("b" . "Buffer") - :desc "Switch to aidermacs" "b" #'aider-switch-to-buffer - :desc "Clear aidermacs" "c" #'aider-clear - ) - - (:prefix ("s" . "Send") - :desc "Line at cursor" "l" #'aider-send-line-under-cursor - :desc "Paragraph at cursor, line by line" "p" #'aider-send-region-by-line - :desc "Region as block" "r" #'aider-send-region - ) - - (:prefix ("c" . "Code") - :desc "Architecture" "d" #'aider-architect-discussion - :desc "Change" "c" #'aider-code-change - :desc "Refactor Function or Region" "r" #'aider-function-or-region-refactor - :desc "Implement Requirement in-place" "i" #'aider-implement-todo - :desc "Undo change" "u" #'aider-undo-last-change - :desc "Show last commit" "g" #'aider-magit-show-last-commit - ) - - (:prefix ("d" . "Discuss") - :desc "Ask question" "a" #'aider-ask-question - :desc "Explain Function or Region" "r" #'aider-function-or-region-explain - :desc "Exception debugging" "e" #'aider-debug-exception - ) - - (:prefix ("t" . "Test") - :desc "Write Unit Test" "w" #'aider-write-unit-test - :desc "Fix Failed Test" "f" #'aider-fix-failing-test-under-cursor - ) - - (:prefix ("z" . "Other") - :desc "General command" "c" #'aider-general-command - :desc "Help" "h" #'aider-help - ) - - :desc "Open aidermacs" "o" #'aider-run-aider - :desc "Reset aidermacs" "r" #'aider-reset - :desc "Exit aidermacs" "x" #'aider-exit - )))) - -;; Add the setup function to appropriate hooks -(add-hook 'find-file-hook #'aider-doom-setup-keys) -(add-hook 'dired-mode-hook #'aider-doom-setup-keys) -(add-hook 'after-change-major-mode-hook #'aider-doom-setup-keys) - -(provide 'aider-doom) -;;; aider-doom.el ends here diff --git a/aider-models.el b/aider-models.el deleted file mode 100644 index fb89b8c316..0000000000 --- a/aider-models.el +++ /dev/null @@ -1,39 +0,0 @@ -;;; aider-models.el --- Model selection for aider.el -*- lexical-binding: t; -*- - -;;; Commentary: -;; Model selection functionality for aider.el - -;;; Code: - -(defgroup aider-models nil - "Model selection customization for aider." - :group 'aider) - -(defcustom aider-popular-models - '("anthropic/claude-3-5-sonnet-20241022" ;; really good in practical - "o3-mini" ;; very powerful - "gemini/gemini-exp-1206" ;; free - "r1" ;; performance match o1, price << claude sonnet. weakness: small context - "deepseek/deepseek-chat" ;; chatgpt-4o level performance, price is 1/100. weakness: small context - ) - "List of available AI models for selection. -Each model should be in the format expected by the aider command line interface. -Also based on aider LLM benchmark: https://aider.chat/docs/leaderboards/" - :type '(repeat string) - :group 'aider-models) - -(defun aider--select-model () - "Private function for model selection with completion." - (completing-read "Select AI model: " aider-popular-models nil t nil nil (car aider-popular-models))) - -;;;###autoload -(defun aider-change-model () - "Interactively select and change AI model in current aider session." - (interactive) - (let ((model (aider--select-model))) - (when model - (aider--send-command (format "/model %s" model) t)))) - -(provide 'aider-models) - -;;; aider-models.el ends here diff --git a/aidermacs-backend-comint.el b/aidermacs-backend-comint.el new file mode 100644 index 0000000000..7633b157c7 --- /dev/null +++ b/aidermacs-backend-comint.el @@ -0,0 +1,50 @@ +;;; aidermacsmacs-backend-comint.el --- Comint backend for aidermacs.el -*- lexical-binding: t; -*- + +;;; Commentary: +;; Comint backend implementation for aidermacs.el + +;;; Code: + +(require 'comint) + +(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) + +(defun aidermacs-run-aidermacs-comint (program args buffer-name) + "Create a comint-based buffer and run aidermacs PROGRAM with ARGS in BUFFER-NAME." + (let ((comint-terminfo-terminal "dumb")) + (unless (comint-check-proc buffer-name) + (apply 'make-comint-in-buffer "aidermacs" buffer-name program nil args) + (with-current-buffer buffer-name + (comint-mode) + (setq-local comint-input-sender 'aidermacs-input-sender) + (setq aidermacs--font-lock-buffer + (get-buffer-create (concat " *aidermacs-fontify" buffer-name))) + (add-hook 'kill-buffer-hook #'aidermacs-kill-buffer nil t) + (add-hook 'comint-output-filter-functions #'aidermacs-fontify-blocks 100 t) + (let ((local-map (make-sparse-keymap))) + (set-keymap-parent local-map comint-mode-map) + (define-key local-map (kbd aidermacs-comint-multiline-newline-key) #'comint-accumulate) + (use-local-map local-map)) + (font-lock-add-keywords nil aidermacs-font-lock-keywords t))))) + +(defun aidermacs--send-command-comint (buffer command &optional switch-to-buffer) + "Send COMMAND to the aidermacs comint BUFFER. +If SWITCH-TO-BUFFER is non-nil, switch to the buffer after sending." + (with-current-buffer buffer + (let ((process (get-buffer-process buffer)) + (inhibit-read-only t)) + (goto-char (process-mark process)) + (insert (propertize command + 'face 'aidermacs-command-text + 'font-lock-face 'aidermacs-command-text + 'rear-nonsticky t)) + (set-marker (process-mark process) (point)) + (comint-send-string process (concat command "\n"))))) + +(provide 'aidermacs-backend-comint) + +;;; aidermacs-backend-comint.el ends here diff --git a/aidermacs-backend-vterm.el b/aidermacs-backend-vterm.el new file mode 100644 index 0000000000..cd224c24c8 --- /dev/null +++ b/aidermacs-backend-vterm.el @@ -0,0 +1,32 @@ +;;; aidermacs-backend-vterm.el --- VTerm backend for aidermacs.el -*- lexical-binding: t; -*- + +;;; Commentary: +;; VTerm backend implementation for aidermacs.el + +;;; Code: + +(require 'vterm nil t) + +(defun aidermacs-run-aidermacs-vterm (program args buffer-name) + "Create a vterm-based buffer and run aidermacs PROGRAM with ARGS in BUFFER-NAME." + (unless (require 'vterm nil t) + (error "vterm package is not available")) + (unless (get-buffer buffer-name) + (let ((cmd (concat program " " (mapconcat 'identity args " ")))) + (setq vterm-shell cmd) + (setq vterm-buffer-name buffer-name) + (vterm) + (with-current-buffer buffer-name + (setq-local aidermacs-backend 'vterm) + (aidermacs-minor-mode 1))))) + +(defun aidermacs--send-command-vterm (buffer command &optional switch-to-buffer) + "Send COMMAND to the aidermacs vterm BUFFER. +If SWITCH-TO-BUFFER is non-nil, switch to the buffer after sending." + (with-current-buffer buffer + (vterm-send-string command) + (vterm-send-return))) + +(provide 'aidermacs-backend-vterm) + +;;; aidermacs-backend-vterm.el ends here diff --git a/aidermacs-backends.el b/aidermacs-backends.el new file mode 100644 index 0000000000..ee4d73cfda --- /dev/null +++ b/aidermacs-backends.el @@ -0,0 +1,49 @@ +;;; aidermacs-backends.el --- Backend implementations for aidermacs.el -*- lexical-binding: t; -*- + +;;; Commentary: +;; Backend dispatcher for aidermacs.el + +;;; Code: + +(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 will +launch a fully functional vterm buffer (with bracketed paste support) instead +of using a comint process." + :type '(choice (const :tag "Comint" comint) + (const :tag "VTerm" vterm)) + :group 'aidermacs-backends) + + +(require 'aidermacs-backend-comint) +(when (and (eq aidermacs-backend 'vterm) (not (featurep 'vterm))) + (require 'aidermacs-backend-vterm)) + + +;; Backend dispatcher functions +(defun aidermacs-run-aidermacs-backend (program args buffer-name) + "Run aidermacs using the selected backend. +PROGRAM is the aidermacs executable path, ARGS are command line arguments, +and BUFFER-NAME is the name for the aidermacs buffer." + (cond + ((eq aidermacs-backend 'vterm) + (aidermacs-run-aidermacs-vterm program args buffer-name)) + (t + (aidermacs-run-aidermacs-comint program args buffer-name)))) + +(defun aidermacs--send-command-backend (buffer command &optional switch-to-buffer) + "Send COMMAND to BUFFER using the appropriate backend. +If SWITCH-TO-BUFFER is non-nil, switch to the buffer after sending." + (cond + ((eq aidermacs-backend 'vterm) + (aidermacs--send-command-vterm buffer command switch-to-buffer)) + (t + (aidermacs--send-command-comint buffer command switch-to-buffer)))) + +(provide 'aidermacs-backends) + +;;; aidermacs-backends.el ends here diff --git a/aidermacs-doom.el b/aidermacs-doom.el new file mode 100644 index 0000000000..7fe88603ea --- /dev/null +++ b/aidermacs-doom.el @@ -0,0 +1,72 @@ +;;; aidermacs-doom.el --- Description -*- lexical-binding: t; no-byte-compile: t -*- +;; +;; This file is not part of GNU Emacs. +;; +;;; Commentary: +;; +;; Doom integration for aidermacs +;; +;;; Code: + +(defun aidermacs-doom-setup-keys () + "Setup aidermacs keybindings if the current buffer is in a git repository." + (when (and (featurep 'doom-keybinds) + (vc-backend (or (buffer-file-name) default-directory))) + (map! :leader + (:prefix ("A" . "aidermacs") + (:prefix ("a" . "Add") + :desc "Current file" "c" #'aidermacs-add-current-file + :desc "File read-only" "f" #'aidermacs-current-file-read-only + :desc "Files in window" "w" #'aidermacs-add-files-in-current-window + :desc "Add Same Type Files under dir" "d" #'aidermacs-add-same-type-files-under-dir + :desc "Batch direct marked files" "b" #'aidermacs-batch-add-dired-marked-files + ) + + (:prefix ("b" . "Buffer") + :desc "Switch to aidermacsmacs" "b" #'aidermacs-switch-to-buffer + :desc "Clear aidermacsmacs" "c" #'aidermacs-clear + ) + + (:prefix ("s" . "Send") + :desc "Line at cursor" "l" #'aidermacs-send-line-under-cursor + :desc "Paragraph at cursor, line by line" "p" #'aidermacs-send-region-by-line + :desc "Region as block" "r" #'aidermacs-send-region + ) + + (:prefix ("c" . "Code") + :desc "Architecture" "d" #'aidermacs-architect-discussion + :desc "Change" "c" #'aidermacs-code-change + :desc "Refactor Function or Region" "r" #'aidermacs-function-or-region-refactor + :desc "Implement Requirement in-place" "i" #'aidermacs-implement-todo + :desc "Undo change" "u" #'aidermacs-undo-last-change + :desc "Show last commit" "g" #'aidermacs-magit-show-last-commit + ) + + (:prefix ("d" . "Discuss") + :desc "Ask question" "a" #'aidermacs-ask-question + :desc "Explain Function or Region" "r" #'aidermacs-function-or-region-explain + :desc "Exception debugging" "e" #'aidermacs-debug-exception + ) + + (:prefix ("t" . "Test") + :desc "Write Unit Test" "w" #'aidermacs-write-unit-test + :desc "Fix Failed Test" "f" #'aidermacs-fix-failing-test-under-cursor + ) + + (:prefix ("z" . "Other") + :desc "General command" "c" #'aidermacs-general-command + :desc "Help" "h" #'aidermacs-help + ) + + :desc "Open aidermacsmacs" "o" #'aidermacs-run-aidermacs + :desc "Reset aidermacsmacs" "r" #'aidermacs-reset + :desc "Exit aidermacsmacs" "x" #'aidermacs-exit + )))) + +;; Add the setup function to appropriate hooks +(add-hook 'find-file-hook #'aidermacs-doom-setup-keys) +(add-hook 'dired-mode-hook #'aidermacs-doom-setup-keys) +(add-hook 'after-change-major-mode-hook #'aidermacs-doom-setup-keys) + +(provide 'aidermacs-doom) +;;; aidermacs-doom.el ends here diff --git a/aider-helm.el b/aidermacs-helm.el similarity index 61% rename from aider-helm.el rename to aidermacs-helm.el index c631471fa6..4dc5bfa2af 100644 --- a/aider-helm.el +++ b/aidermacs-helm.el @@ -1,21 +1,22 @@ -;;; aider-helm.el --- Helm completion for aider.el -*- lexical-binding: t; -*- +;;; aidermacs-helm.el --- Helm completion for aidermacs.el -*- lexical-binding: t; -*- -;; Author: Kang Tu <tni...@gmail.com> +;; Author: Mingde (Matthew) Zeng <matthew...@posteo.net> +;; Original Author: Kang Tu <tni...@gmail.com> ;; Version: 0.2.0 ;; Package-Requires: ((emacs "25.1") (helm "3.0")) ;; Keywords: convenience, tools -;; URL: https://github.com/tninja/aider.el +;; URL: https://github.com/MatthewZMD/aidermacs.el ;;; Commentary: -;; Optional Helm completion interface for aider.el -;; To use this, ensure both aider.el and helm are installed. +;; Optional Helm completion interface for aidermacs.el +;; To use this, ensure both aidermacs.el and helm are installed. ;;; Code: (require 'helm) (require 'cl-lib) ; For `cl-subseq` -(defun aider-helm-read-string-with-history (prompt history-file-name &optional initial-input) +(defun aidermacs-helm-read-string-with-history (prompt history-file-name &optional initial-input) "Read a string with Helm completion using specified history file. PROMPT is the prompt string. HISTORY-FILE-NAME is the base name for history file. @@ -44,19 +45,19 @@ INITIAL-INPUT is optional initial input string." (insert (prin1-to-string history-entries))))) input)) -(defun aider-helm-read-string (prompt &optional initial-input) - "Read a string with Helm completion for aider, showing historical inputs. +(defun aidermacs-helm-read-string (prompt &optional initial-input) + "Read a string with Helm completion for aidermacs, showing historical inputs. PROMPT is the prompt string. INITIAL-INPUT is optional initial input string." - (aider-helm-read-string-with-history prompt "aider-helm-read-string-history.el" initial-input)) + (aidermacs-helm-read-string-with-history prompt "aidermacs-helm-read-string-history.el" initial-input)) -(declare-function aider-read-string "aider") +(declare-function aidermacs-read-string "aidermacs") ;;;###autoload -(with-eval-after-load 'aider +(with-eval-after-load 'aidermacs (if (featurep 'helm) - (defalias 'aider-read-string 'aider-helm-read-string) - (message "Helm is not available. Please install helm package to use aider-helm features"))) + (defalias 'aidermacs-read-string 'aidermacs-helm-read-string) + (message "Helm is not available. Please install helm package to use aidermacs-helm features"))) -(provide 'aider-helm) -;;; aider-helm.el ends here +(provide 'aidermacs-helm) +;;; aidermacs-helm.el ends here diff --git a/aidermacs-models.el b/aidermacs-models.el new file mode 100644 index 0000000000..e0300e35c6 --- /dev/null +++ b/aidermacs-models.el @@ -0,0 +1,39 @@ +;;; aidermacs-models.el --- Model selection for aidermacs.el -*- lexical-binding: t; -*- + +;;; Commentary: +;; Model selection functionality for aidermacs.el + +;;; Code: + +(defgroup aidermacs-models nil + "Model selection customization for aidermacs." + :group 'aidermacs) + +(defcustom aidermacs-popular-models + '("anthropic/claude-3-5-sonnet-20241022" ;; really good in practical + "o3-mini" ;; very powerful + "gemini/gemini-exp-1206" ;; free + "r1" ;; performance match o1, price << claude sonnet. weakness: small context + "deepseek/deepseek-chat" ;; chatgpt-4o level performance, price is 1/100. weakness: small context + ) + "List of available AI models for selection. +Each model should be in the format expected by the aidermacs command line interface. +Also based on aidermacs LLM benchmark: https://aidermacs.chat/docs/leaderboards/" + :type '(repeat string) + :group 'aidermacs-models) + +(defun aidermacs--select-model () + "Private function for model selection with completion." + (completing-read "Select AI model: " aidermacs-popular-models nil t nil nil (car aidermacs-popular-models))) + +;;;###autoload +(defun aidermacs-change-model () + "Interactively select and change AI model in current aidermacs session." + (interactive) + (let ((model (aidermacs--select-model))) + (when model + (aidermacs--send-command (format "/model %s" model) t)))) + +(provide 'aidermacs-models) + +;;; aidermacs-models.el ends here diff --git a/aider-etc.el b/aidermacs-utils.el similarity index 88% rename from aider-etc.el rename to aidermacs-utils.el index 00c6d8de77..bea25e2f4e 100644 --- a/aider-etc.el +++ b/aidermacs-utils.el @@ -1,7 +1,7 @@ ;; New function to run `find-name-dired` from the Git repository root directory ;;;###autoload -(defun aider-repo-find-name-dired (pattern) +(defun aidermacs-repo-find-name-dired (pattern) "Run `find-name-dired` from the Git repository root directory with the given PATTERN." (interactive "sFind name (pattern): ") (let* ((git-repo-path (shell-command-to-string "git rev-parse --show-toplevel")) @@ -11,7 +11,7 @@ (find-name-dired repo-path pattern)))) ;;;###autoload -(defun aider-git-repo-root-dired () +(defun aidermacs-git-repo-root-dired () "Open a Dired buffer at the root of the current Git repository." (interactive) (let ((git-repo-path (shell-command-to-string "git rev-parse --show-toplevel"))) @@ -19,3 +19,5 @@ (message "The current buffer is not in a Git repository.") (let ((repo-path (string-trim git-repo-path))) (dired-other-window repo-path))))) + +(provide 'aidermacs-utils) diff --git a/aider.el b/aidermacs.el similarity index 52% rename from aider.el rename to aidermacs.el index 9b95d5ce77..082ed445a8 100644 --- a/aider.el +++ b/aidermacs.el @@ -1,13 +1,14 @@ -;;; aider.el --- aidermacs package for interactive conversation with aider -*- lexical-binding: t; -*- +;;; aidermacs.el --- aidermacsmacs package for interactive conversation with aidermacs -*- lexical-binding: t; -*- -;; Author: Kang Tu <tni...@gmail.com> +;; Author: Mingde (Matthew) Zeng <matthew...@posteo.net> +;; Original Author: Kang Tu <tni...@gmail.com> ;; Version: 0.2.0 ;; Package-Requires: ((emacs "26.1") (transient "0.3.0") (magit "2.1.0")) ;; Keywords: convenience, tools -;; URL: https://github.com/tninja/aider.el +;; URL: https://github.com/MatthewZMD/aidermacs.el ;;; Commentary: -;; This package provides an interactive interface to communicate with https://github.com/paul-gauthier/aider. +;; This package provides an interactive interface to communicate with https://github.com/paul-gauthier/aidermacs. ;;; Code: @@ -19,182 +20,182 @@ (require 'ansi-color) -(require 'aider-backends) -(require 'aider-models) +(require 'aidermacs-backends) +(require 'aidermacs-models) (when (featurep 'doom) - (require 'aider-doom)) + (require 'aidermacs-doom)) -(defgroup aider nil +(defgroup aidermacs nil "Customization group for the aidermacs package." - :prefix "aider-" + :prefix "aidermacs-" :group 'convenience) -(defcustom aider-program "aider" - "The name or path of the aider program." +(defcustom aidermacs-program "aider" + "The name or path of the aidermacs program." :type 'string - :group 'aider) + :group 'aidermacs) -(defcustom aider-args '("--model" "anthropic/claude-3-5-sonnet-20241022") +(defcustom aidermacs-args '("--model" "anthropic/claude-3-5-sonnet-20241022") "Arguments to pass to the aidermacs command." :type '(repeat string) - :group 'aider) + :group 'aidermacs) -(defcustom aider--switch-to-buffer-other-frame nil +(defcustom aidermacs--switch-to-buffer-other-frame nil "When non-nil, open aidermacs buffer in a new frame using `switch-to-buffer-other-frame'. When nil, use standard `display-buffer' behavior." :type 'boolean - :group 'aider) + :group 'aidermacs) -(defcustom aider-language-name-map '(("elisp" . "emacs-lisp") - ("bash" . "sh") - ("objective-c" . "objc") - ("objectivec" . "objc") - ("cpp" . "c++")) +(defcustom aidermacs-language-name-map '(("elisp" . "emacs-lisp") + ("bash" . "sh") + ("objective-c" . "objc") + ("objectivec" . "objc") + ("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 'aider) + :group 'aidermacs) -(defcustom aider-prompt-file-name ".aider.prompt.org" - "File name that will automatically enable aider-minor-mode when opened. +(defcustom aidermacs-prompt-file-name ".aidermacs.prompt.org" + "File name that will automatically enable aidermacs-minor-mode when opened. This is the file name without path." :type 'string - :group 'aider) + :group 'aidermacs) -(defvar aider-read-string-history nil - "History list for aider read string inputs.") +(defvar aidermacs-read-string-history nil + "History list for aidermacs read string inputs.") (if (bound-and-true-p savehist-loaded) - (add-to-list 'savehist-additional-variables 'aider-read-string-history) + (add-to-list 'savehist-additional-variables 'aidermacs-read-string-history) (add-hook 'savehist-mode-hook (lambda () - (add-to-list 'savehist-additional-variables 'aider-read-string-history)))) + (add-to-list 'savehist-additional-variables 'aidermacs-read-string-history)))) -(defface aider-command-separator +(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 aider." - :group 'aider) + "Face for command separator in aidermacs." + :group 'aidermacs) -(defface aider-command-text +(defface aidermacs-command-text '((t :inherit bold)) - "Face for commands sent to aider buffer." - :group 'aider) + "Face for commands sent to aidermacs buffer." + :group 'aidermacs) -(defface aider-search-replace-block +(defface aidermacs-search-replace-block '((t :inherit 'diff-refine-added :bold t)) "Face for search/replace block content." - :group 'aider) + :group 'aidermacs) -(defvar aider-font-lock-keywords - '(("^\x2500+\n?" 0 '(face aider-command-separator) t) +(defvar aidermacs-font-lock-keywords + '(("^\x2500+\n?" 0 '(face aidermacs-command-separator) t) ("^\x2500+" 0 '(face nil display (space :width 2))) ("^\\([0-9]+\\). " 0 font-lock-constant-face) - ("^>>>>>>> REPLACE" 0 'aider-search-replace-block t) - ("^<<<<<<< SEARCH" 0 'aider-search-replace-block t) + ("^>>>>>>> REPLACE" 0 'aidermacs-search-replace-block t) + ("^<<<<<<< SEARCH" 0 'aidermacs-search-replace-block t) ("^\\(```\\)\\([^[:space:]]*\\)" (1 'shadow t) (2 font-lock-builtin-face t)) - ("^=======$" 0 'aider-search-replace-block t)) - "Font lock keywords for aider buffer.") + ("^=======$" 0 'aidermacs-search-replace-block t)) + "Font lock keywords for aidermacs buffer.") ;;;###autoload -(defun aider-plain-read-string (prompt &optional initial-input) +(defun aidermacs-plain-read-string (prompt &optional initial-input) "Read a string from the user with PROMPT and optional INITIAL-INPUT. This function can be customized or redefined by the user." - (read-string prompt initial-input 'aider-read-string-history)) + (read-string prompt initial-input 'aidermacs-read-string-history)) ;;;###autoload -(defalias 'aider-read-string 'aider-plain-read-string) +(defalias 'aidermacs-read-string 'aidermacs-plain-read-string) (eval-and-compile ;; Ensure the alias is always available in both compiled and interpreted modes. - (defalias 'aider-read-string 'aider-plain-read-string)) + (defalias 'aidermacs-read-string 'aidermacs-plain-read-string)) -(defvar aider--add-file-read-only nil - "Set model parameters from `aider-menu' buffer-locally. +(defvar aidermacs--add-file-read-only nil + "Set model parameters from `aidermacs-menu' buffer-locally. Affects the system message too.") -(defun aider--get-add-command-prefix () - "Return the appropriate command prefix based on aider--add-file-read-only." - (if aider--add-file-read-only "/read-only" "/add")) +(defun aidermacs--get-add-command-prefix () + "Return the appropriate command prefix based on aidermacs--add-file-read-only." + (if aidermacs--add-file-read-only "/read-only" "/add")) -(defclass aider--add-file-type (transient-lisp-variable) - ((variable :initform 'aider--add-file-read-only) +(defclass aidermacs--add-file-type (transient-lisp-variable) + ((variable :initform 'aidermacs--add-file-read-only) (format :initform "%k %d %v") (reader :initform #'transient-lisp-variable--read-value)) - "Class for toggling aider--add-file-read-only.") + "Class for toggling aidermacs--add-file-read-only.") -(defclass aider--switch-to-buffer-type (transient-lisp-variable) - ((variable :initform 'aider--switch-to-buffer-other-frame) +(defclass aidermacs--switch-to-buffer-type (transient-lisp-variable) + ((variable :initform 'aidermacs--switch-to-buffer-other-frame) (format :initform "%k %d %v") (reader :initform #'transient-lisp-variable--read-value)) - "Class for toggling aider--switch-to-buffer-other-frame.") + "Class for toggling aidermacs--switch-to-buffer-other-frame.") -(transient-define-infix aider--infix-add-file-read-only () - "Toggle aider--add-file-read-only between nil and t." - :class 'aider--add-file-type +(transient-define-infix aidermacs--infix-add-file-read-only () + "Toggle aidermacs--add-file-read-only between nil and t." + :class 'aidermacs--add-file-type :key "@" :description "Read-only mode" :reader (lambda (_prompt _initial-input _history) - (not aider--add-file-read-only))) + (not aidermacs--add-file-read-only))) -(transient-define-infix aider--infix-switch-to-buffer-other-frame () - "Toggle aider--switch-to-buffer-other-frame between nil and t." - :class 'aider--switch-to-buffer-type +(transient-define-infix aidermacs--infix-switch-to-buffer-other-frame () + "Toggle aidermacs--switch-to-buffer-other-frame between nil and t." + :class 'aidermacs--switch-to-buffer-type :key "^" :description "Open in new frame" :reader (lambda (_prompt _initial-input _history) - (not aider--switch-to-buffer-other-frame))) + (not aidermacs--switch-to-buffer-other-frame))) ;; Transient menu for aidermacs commands ;; The instruction in the autoload comment is needed, see ;; https://github.com/magit/transient/issues/280. -;;;###autoload (autoload 'aider-transient-menu "aider" "Transient menu for aidermacs commands." t) -(transient-define-prefix aider-transient-menu () +;;;###autoload (autoload 'aidermacs-transient-menu "aidermacs" "Transient menu for aidermacsmacs commands." t) +(transient-define-prefix aidermacs-transient-menu () "Transient menu for aidermacs commands." ["aidermacs: AI Pair Programming" ["aidermacs Process" - (aider--infix-switch-to-buffer-other-frame) - ("a" "Run aidermacs" aider-run-aider) - ("z" "Switch to aidermacs Buffer" aider-switch-to-buffer) - ("o" "Select Model" aider-change-model) - ("l" "Clear aidermacs" aider-clear) - ("s" "Reset aidermacs" aider-reset) - ("x" "Exit aidermacs" aider-exit) + (aidermacs--infix-switch-to-buffer-other-frame) + ("a" "Run aidermacsmacs" aidermacs-run-aidermacs) + ("z" "Switch to aidermacsmacs Buffer" aidermacs-switch-to-buffer) + ("o" "Select Model" aidermacs-change-model) + ("l" "Clear aidermacsmacs" aidermacs-clear) + ("s" "Reset aidermacsmacs" aidermacs-reset) + ("x" "Exit aidermacsmacs" aidermacs-exit) ] ["Add File to aidermacs" - (aider--infix-add-file-read-only) - ("f" "Add Current File" aider-add-current-file) - ("R" "Add Current File Read-Only" aider-current-file-read-only) - ("w" "Add All Files in Current Window" aider-add-files-in-current-window) - ("d" "Add Same Type Files under dir" aider-add-same-type-files-under-dir) - ("b" "Batch Add Dired Marked Files" aider-batch-add-dired-marked-files) + (aidermacs--infix-add-file-read-only) + ("f" "Add Current File" aidermacs-add-current-file) + ("R" "Add Current File Read-Only" aidermacs-current-file-read-only) + ("w" "Add All Files in Current Window" aidermacs-add-files-in-current-window) + ("d" "Add Same Type Files under dir" aidermacs-add-same-type-files-under-dir) + ("b" "Batch Add Dired Marked Files" aidermacs-batch-add-dired-marked-files) ] ["Code Change" - ("t" "Architect Discuss and Change" aider-architect-discussion) - ("c" "Code Change" aider-code-change) - ("r" "Refactor Function or Region" aider-function-or-region-refactor) - ("i" "Implement Requirement in-place" aider-implement-todo) - ("U" "Write Unit Test" aider-write-unit-test) - ("T" "Fix Failing Test Under Cursor" aider-fix-failing-test-under-cursor) - ("m" "Show Last Commit with Magit" aider-magit-show-last-commit) - ("u" "Undo Last Change" aider-undo-last-change) + ("t" "Architect Discuss and Change" aidermacs-architect-discussion) + ("c" "Code Change" aidermacs-code-change) + ("r" "Refactor Function or Region" aidermacs-function-or-region-refactor) + ("i" "Implement Requirement in-place" aidermacs-implement-todo) + ("U" "Write Unit Test" aidermacs-write-unit-test) + ("T" "Fix Failing Test Under Cursor" aidermacs-fix-failing-test-under-cursor) + ("m" "Show Last Commit with Magit" aidermacs-magit-show-last-commit) + ("u" "Undo Last Change" aidermacs-undo-last-change) ] ["Discussion" - ("q" "Ask Question given Context" aider-ask-question) - ("y" "Go Ahead" aider-go-ahead) - ("e" "Explain Function or Region" aider-function-or-region-explain) - ("p" "Explain Symbol Under Point" aider-explain-symbol-under-point) - ("D" "Debug Exception" aider-debug-exception) + ("q" "Ask Question given Context" aidermacs-ask-question) + ("y" "Go Ahead" aidermacs-go-ahead) + ("e" "Explain Function or Region" aidermacs-function-or-region-explain) + ("p" "Explain Symbol Under Point" aidermacs-explain-symbol-under-point) + ("D" "Debug Exception" aidermacs-debug-exception) ] ["Other" - ("g" "General Command" aider-general-command) - ("Q" "Ask General Question" aider-general-question) - ("p" "Open Prompt File" aider-open-prompt-file) - ("h" "Help" aider-help) + ("g" "General Command" aidermacs-general-command) + ("Q" "Ask General Question" aidermacs-general-question) + ("p" "Open Prompt File" aidermacs-open-prompt-file) + ("h" "Help" aidermacs-help) ] ]) -(defun aider-buffer-name () +(defun aidermacs-buffer-name () "Generate the aidermacs buffer name based on the git repo or current buffer file path. If not in a git repository and no buffer file exists, an error is raised." (let ((git-repo-path (magit-toplevel)) @@ -214,123 +215,123 @@ If not in a git repository and no buffer file exists, an error is raised." (error "Not in a git repository and current buffer is not associated with a file"))))) ;;;###autoload -(defun aider-run-aider (&optional edit-args) - "Run aider process using the selected backend. -With the universal argument, prompt to edit aider-args before running." +(defun aidermacs-run-aidermacs (&optional edit-args) + "Run aidermacs process using the selected backend. +With the universal argument, prompt to edit aidermacs-args before running." (interactive "P") - (let* ((buffer-name (aider-buffer-name)) + (let* ((buffer-name (aidermacs-buffer-name)) (current-args (if edit-args - (split-string (read-string "Edit aider arguments: " - (mapconcat 'identity aider-args " "))) - aider-args))) - (aider-run-aider-backend aider-program current-args buffer-name) - (aider-switch-to-buffer))) - -(defun aider--send-command (command &optional switch-to-buffer) - "Send COMMAND to the corresponding aider process after performing necessary checks. + (split-string (read-string "Edit aidermacs arguments: " + (mapconcat 'identity aidermacs-args " "))) + aidermacs-args))) + (aidermacs-run-aidermacs-backend aidermacs-program current-args buffer-name) + (aidermacs-switch-to-buffer))) + +(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." - (if-let ((aider-buffer (get-buffer (aider-buffer-name)))) - (let ((processed-command (aider--process-message-if-multi-line command))) - (aider-reset-font-lock-state) - (aider--send-command-backend aider-buffer processed-command switch-to-buffer) + (if-let ((aidermacs-buffer (get-buffer (aidermacs-buffer-name)))) + (let ((processed-command (aidermacs--process-message-if-multi-line command))) + (aidermacs-reset-font-lock-state) + (aidermacs--send-command-backend aidermacs-buffer processed-command switch-to-buffer) (when switch-to-buffer - (aider-switch-to-buffer)) + (aidermacs-switch-to-buffer)) (sleep-for 0.2)) - (message "Buffer %s does not exist. Please start 'aider' first." (funcall #'aider-buffer-name)))) + (message "Buffer %s does not exist. Please start 'aidermacs' first." (funcall #'aidermacs-buffer-name)))) -(defun aider-kill-buffer () +(defun aidermacs-kill-buffer () "Clean-up fontify buffer." - (when (bufferp aider--font-lock-buffer) - (kill-buffer aider--font-lock-buffer))) + (when (bufferp aidermacs--font-lock-buffer) + (kill-buffer aidermacs--font-lock-buffer))) -(defun aider-input-sender (proc string) +(defun aidermacs-input-sender (proc string) "Reset font-lock state before executing a command." - (aider-reset-font-lock-state) - (comint-simple-send proc (aider--process-message-if-multi-line string))) + (aidermacs-reset-font-lock-state) + (comint-simple-send proc (aidermacs--process-message-if-multi-line string))) ;; Buffer-local variables for block processing state -(defvar-local aider--block-end-marker nil +(defvar-local aidermacs--block-end-marker nil "The end marker for the current block being processed.") -(defvar-local aider--block-start nil +(defvar-local aidermacs--block-start nil "The starting position of the current block being processed.") -(defvar-local aider--block-end nil +(defvar-local aidermacs--block-end nil "The end position of the current block being processed.") -(defvar-local aider--last-output-start nil - "an alternative to `comint-last-output-start' used in aider.") +(defvar-local aidermacs--last-output-start nil + "an alternative to `comint-last-output-start' used in aidermacs.") -(defvar-local aider--block-mode nil +(defvar-local aidermacs--block-mode nil "The major mode for the current block being processed.") -(defvar-local aider--font-lock-buffer nil +(defvar-local aidermacs--font-lock-buffer nil "Temporary buffer for fontification.") -(defconst aider-search-marker "<<<<<<< SEARCH") -(defconst aider-diff-marker "=======") -(defconst aider-replace-marker ">>>>>>> REPLACE") -(defconst aider-fence-marker "```") -(defvar aider-block-re - (format "^\\(?:\\(?1:%s\\|%s\\)\\|\\(?1:%s\\).+\\)$" aider-search-marker aider-diff-marker aider-fence-marker)) +(defconst aidermacs-search-marker "<<<<<<< SEARCH") +(defconst aidermacs-diff-marker "=======") +(defconst aidermacs-replace-marker ">>>>>>> REPLACE") +(defconst aidermacs-fence-marker "```") +(defvar aidermacs-block-re + (format "^\\(?:\\(?1:%s\\|%s\\)\\|\\(?1:%s\\).+\\)$" aidermacs-search-marker aidermacs-diff-marker aidermacs-fence-marker)) -(defun aider-reset-font-lock-state () +(defun aidermacs-reset-font-lock-state () "Reset font lock state to default for processing another a new src block." - (unless (equal aider--block-end-marker aider-diff-marker) + (unless (equal aidermacs--block-end-marker aidermacs-diff-marker) ;; if we are processing the other half of a SEARCH/REPLACE block, we need to ;; keep the mode - (setq aider--block-mode nil)) - (setq aider--block-end-marker nil - aider--last-output-start nil - aider--block-start nil - aider--block-end nil)) + (setq aidermacs--block-mode nil)) + (setq aidermacs--block-end-marker nil + aidermacs--last-output-start nil + aidermacs--block-start nil + aidermacs--block-end nil)) -(defun aider-fontify-blocks (_output) +(defun aidermacs-fontify-blocks (_output) "fontify search/replace blocks in comint output." (save-excursion - (goto-char (or aider--last-output-start + (goto-char (or aidermacs--last-output-start comint-last-output-start)) (beginning-of-line) ;; Continue processing existing block if we're in one - (when aider--block-start - (aider--fontify-block)) + (when aidermacs--block-start + (aidermacs--fontify-block)) - (setq aider--last-output-start nil) + (setq aidermacs--last-output-start nil) ;; Look for new blocks if we're not in one - (while (and (null aider--block-start) - (null aider--last-output-start) - (re-search-forward aider-block-re nil t)) + (while (and (null aidermacs--block-start) + (null aidermacs--last-output-start) + (re-search-forward aidermacs-block-re nil t)) ;; If it is code fence marker, we need to check if there is a SEARCH marker ;; directly after it - (when (equal (match-string 1) aider-fence-marker) + (when (equal (match-string 1) aidermacs-fence-marker) (let* ((next-line (min (point-max) (1+ (line-end-position)))) (line-text (buffer-substring next-line - (min (point-max) (+ next-line (length aider-search-marker)))))) - (cond ((equal line-text aider-search-marker) + (min (point-max) (+ next-line (length aidermacs-search-marker)))))) + (cond ((equal line-text aidermacs-search-marker) ;; Next line is a SEARCH marker. use that instead of the fence marker - (re-search-forward (format "^\\(%s\\)" aider-search-marker) nil t)) - ((string-prefix-p line-text aider-search-marker) + (re-search-forward (format "^\\(%s\\)" aidermacs-search-marker) nil t)) + ((string-prefix-p line-text aidermacs-search-marker) ;; Next line *might* be a SEARCH marker. Don't process more of ;; the buffer until we know for sure - (setq aider--last-output-start comint-last-output-start))))) + (setq aidermacs--last-output-start comint-last-output-start))))) - (unless aider--last-output-start + (unless aidermacs--last-output-start ;; Set up new block state - (setq aider--block-end-marker + (setq aidermacs--block-end-marker (pcase (match-string 1) - ((pred (equal aider-search-marker)) aider-diff-marker) - ((pred (equal aider-diff-marker)) aider-replace-marker) - ((pred (equal aider-fence-marker)) aider-fence-marker)) - aider--block-start (line-end-position) - aider--block-end (line-end-position) - aider--block-mode (aider--guess-major-mode)) + ((pred (equal aidermacs-search-marker)) aidermacs-diff-marker) + ((pred (equal aidermacs-diff-marker)) aidermacs-replace-marker) + ((pred (equal aidermacs-fence-marker)) aidermacs-fence-marker)) + aidermacs--block-start (line-end-position) + aidermacs--block-end (line-end-position) + aidermacs--block-mode (aidermacs--guess-major-mode)) ;; Set the major-mode of the font lock buffer - (let ((mode aider--block-mode)) - (with-current-buffer aider--font-lock-buffer + (let ((mode aidermacs--block-mode)) + (with-current-buffer aidermacs--font-lock-buffer (erase-buffer) (unless (eq mode major-mode) (condition-case e @@ -339,64 +340,64 @@ Dispatches to the appropriate backend." (error (message "aidermacs: failed to init major-mode `%s' for font-locking: %s" mode e)))))) ;; Process initial content - (aider--fontify-block))))) + (aidermacs--fontify-block))))) -(defun aider--fontify-block () +(defun aidermacs--fontify-block () "Fontify as much of the current source block as possible." (let* ((last-bol (save-excursion (goto-char (point-max)) (line-beginning-position))) - (last-output-start aider--block-end) + (last-output-start aidermacs--block-end) end-of-block-p) - (setq aider--block-end - (cond ((re-search-forward (concat "^" aider--block-end-marker "$") nil t) + (setq aidermacs--block-end + (cond ((re-search-forward (concat "^" aidermacs--block-end-marker "$") nil t) ;; Found the end of the block (setq end-of-block-p t) (line-beginning-position)) - ((string-prefix-p (buffer-substring last-bol (point-max)) aider--block-end-marker) + ((string-prefix-p (buffer-substring last-bol (point-max)) aidermacs--block-end-marker) ;; The end of the text *might* be the end marker. back up to ;; make sure we don't process it until we know for sure last-bol) ;; We can process till the end of the text (t (point-max)))) - ;; Append new content to temp buffer and fontify - (let ((new-content (buffer-substring-no-properties - last-output-start - aider--block-end)) - (pos aider--block-start) - (font-pos 0) - fontified) - - ;; Insert the new text and get the fontified result - (with-current-buffer aider--font-lock-buffer - (goto-char (point-max)) - (insert new-content) - (with-demoted-errors "aider block font lock error: %s" - (let ((inhibit-message t)) - (font-lock-ensure))) - (setq fontified (buffer-string))) - - ;; Apply the faces to the buffer - (remove-overlays aider--block-start aider--block-end) - (while (< pos aider--block-end) - (let* ((next-font-pos (or (next-property-change font-pos fontified) (length fontified))) - (next-pos (+ aider--block-start next-font-pos)) - (face (get-text-property font-pos 'face fontified))) - (ansi-color-apply-overlay-face pos next-pos face) - (setq pos next-pos - font-pos next-font-pos)))) - - ;; If we found the end marker, finalize the block - (when end-of-block-p - (when (equal aider--block-end-marker aider-diff-marker) - ;; we will need to process the other half of the SEARCH/REPLACE block. - ;; Backup so it will get matched - (beginning-of-line)) - (aider-reset-font-lock-state)))) - -(defun aider--guess-major-mode () + ;; Append new content to temp buffer and fontify + (let ((new-content (buffer-substring-no-properties + last-output-start + aidermacs--block-end)) + (pos aidermacs--block-start) + (font-pos 0) + fontified) + + ;; Insert the new text and get the fontified result + (with-current-buffer aidermacs--font-lock-buffer + (goto-char (point-max)) + (insert new-content) + (with-demoted-errors "aidermacs block font lock error: %s" + (let ((inhibit-message t)) + (font-lock-ensure))) + (setq fontified (buffer-string))) + + ;; Apply the faces to the buffer + (remove-overlays aidermacs--block-start aidermacs--block-end) + (while (< pos aidermacs--block-end) + (let* ((next-font-pos (or (next-property-change font-pos fontified) (length fontified))) + (next-pos (+ aidermacs--block-start next-font-pos)) + (face (get-text-property font-pos 'face fontified))) + (ansi-color-apply-overlay-face pos next-pos face) + (setq pos next-pos + font-pos next-font-pos)))) + + ;; If we found the end marker, finalize the block + (when end-of-block-p + (when (equal aidermacs--block-end-marker aidermacs-diff-marker) + ;; we will need to process the other half of the SEARCH/REPLACE block. + ;; Backup so it will get matched + (beginning-of-line)) + (aidermacs-reset-font-lock-state)))) + +(defun aidermacs--guess-major-mode () "Extract the major mode from fence markers or filename." (save-excursion (beginning-of-line) @@ -409,50 +410,50 @@ Dispatches to the appropriate backend." ;; check the previous line since this might be a SEARCH block (looking-at re)))) (let* ((lang (downcase (match-string 1))) - (mode (map-elt aider-language-name-map lang lang))) + (mode (map-elt aidermacs-language-name-map lang lang))) (intern-soft (concat mode "-mode")))) ;; check the file extension in auto-mode-alist (when (re-search-backward "^\\([^[:space:]]+\\)" (line-beginning-position -3) t) (let ((file (match-string 1))) (cdr (cl-assoc-if (lambda (re) (string-match re file)) auto-mode-alist)))) - aider--block-mode + aidermacs--block-mode 'fundamental-mode))) ;; Function to switch to the aidermacs buffer ;;;###autoload -(defun aider-switch-to-buffer () +(defun aidermacs-switch-to-buffer () "Switch to the aidermacs buffer. -When `aider--switch-to-buffer-other-frame' is non-nil, open in a new frame. +When `aidermacs--switch-to-buffer-other-frame' is non-nil, open in a new frame. If the current buffer is already the aidermacs buffer, do nothing." (interactive) - (if (string= (buffer-name) (aider-buffer-name)) + (if (string= (buffer-name) (aidermacs-buffer-name)) (message "Already in aidermacs buffer") - (if-let ((buffer (get-buffer (aider-buffer-name)))) - (if aider--switch-to-buffer-other-frame + (if-let ((buffer (get-buffer (aidermacs-buffer-name)))) + (if aidermacs--switch-to-buffer-other-frame (switch-to-buffer-other-frame buffer) (pop-to-buffer buffer)) - (message "aidermacs buffer '%s' does not exist." (aider-buffer-name))))) + (message "aidermacsmacs buffer '%s' does not exist." (aidermacs-buffer-name))))) ;; Function to reset the aidermacs buffer ;;;###autoload -(defun aider-clear () +(defun aidermacs-clear () "Send the command \"/clear\" to the aidermacs buffer." (interactive) - (aider--send-command "/clear")) + (aidermacs--send-command "/clear")) ;;;###autoload -(defun aider-reset () +(defun aidermacs-reset () "Send the command \"/reset\" to the aidermacs buffer." (interactive) - (aider--send-command "/reset")) + (aidermacs--send-command "/reset")) ;;;###autoload -(defun aider-exit () +(defun aidermacs-exit () "Send the command \"/exit\" to the aidermacs buffer." (interactive) - (aider--send-command "/exit")) + (aidermacs--send-command "/exit")) -(defun aider--comint-send-string-syntax-highlight (buffer text) +(defun aidermacs--comint-send-string-syntax-highlight (buffer text) "Send TEXT to the comint BUFFER with syntax highlighting. This function ensures proper syntax highlighting by inheriting face properties from the source buffer and maintaining proper process markers." @@ -462,54 +463,54 @@ from the source buffer and maintaining proper process markers." (goto-char (process-mark process)) ;; Insert text with proper face properties (insert (propertize text - 'face 'aider-command-text - 'font-lock-face 'aider-command-text - 'rear-nonsticky t)) + 'face 'aidermacs-command-text + 'font-lock-face 'aidermacs-command-text + 'rear-nonsticky t)) ;; Update process mark and send text (set-marker (process-mark process) (point)) (comint-send-string process text)))) -(defun aider--process-message-if-multi-line (str) +(defun aidermacs--process-message-if-multi-line (str) "Entering multi-line chat messages -https://aider.chat/docs/usage/commands.html#entering-multi-line-chat-messages -If STR contains newlines and isn't already wrapped in {aider...aider}, -wrap it in {aider\\nstr\\naider}. Otherwise return STR unchanged." +https://aidermacs.chat/docs/usage/commands.html#entering-multi-line-chat-messages +If STR contains newlines and isn't already wrapped in {aidermacs...aidermacs}, +wrap it in {aidermacs\\nstr\\naidermacs}. Otherwise return STR unchanged." (if (and (string-match-p "\n" str) - (not (string-match-p "^{aider\n.*\naider}$" str))) - (format "{aider\n%s\naider}" str) + (not (string-match-p "^{aidermacs\n.*\naidermacs}$" str))) + (format "{aidermacs\n%s\naidermacs}" str) str)) ;;;###autoload -(defun aider-add-or-read-current-file (command-prefix) - "Send the command \"COMMAND-PREFIX <current buffer file full path>\" to the corresponding aider comint buffer." +(defun aidermacs-add-or-read-current-file (command-prefix) + "Send the command \"COMMAND-PREFIX <current buffer file full path>\" to the corresponding aidermacs comint buffer." ;; Ensure the current buffer is associated with a file (if (not buffer-file-name) (message "Current buffer is not associated with a file.") (let* ((local-name (file-local-name - (expand-file-name buffer-file-name))) + (expand-file-name buffer-file-name))) (formatted-path (if (string-match-p " " local-name) - (format "\"%s\"" local-name) - local-name)) + (format "\"%s\"" local-name) + local-name)) (command (format "%s %s" command-prefix formatted-path))) ;; Use the shared helper function to send the command - (aider--send-command command)))) + (aidermacs--send-command command)))) -;; Function to send "/add <current buffer file full path>" to corresponding aider buffer +;; Function to send "/add <current buffer file full path>" to corresponding aidermacs buffer ;;;###autoload -(defun aider-add-current-file () - "Send the command \"/add <current buffer file full path>\" to the corresponding aider comint buffer." +(defun aidermacs-add-current-file () + "Send the command \"/add <current buffer file full path>\" to the corresponding aidermacs comint buffer." (interactive) - (aider-add-or-read-current-file (aider--get-add-command-prefix))) + (aidermacs-add-or-read-current-file (aidermacs--get-add-command-prefix))) ;;;###autoload -(defun aider-current-file-read-only () - "Send the command \"/read-only <current buffer file full path>\" to the corresponding aider comint buffer." +(defun aidermacs-current-file-read-only () + "Send the command \"/read-only <current buffer file full path>\" to the corresponding aidermacs comint buffer." (interactive) - (aider-add-or-read-current-file "/read-only")) + (aidermacs-add-or-read-current-file "/read-only")) ;; New function to add files in all buffers in current emacs window ;;;###autoload -(defun aider-add-files-in-current-window () +(defun aidermacs-add-files-in-current-window () "Add files in all buffers in the current Emacs window to the aidermacs buffer." (interactive) (let ((files (mapcar (lambda (buffer) @@ -519,93 +520,93 @@ wrap it in {aider\\nstr\\naider}. Otherwise return STR unchanged." (mapcar 'window-buffer (window-list))))) (setq files (delq nil files)) (if files - (let ((command (concat (aider--get-add-command-prefix) " " (mapconcat 'identity files " ")))) - (aider--send-command command nil)) + (let ((command (concat (aidermacs--get-add-command-prefix) " " (mapconcat 'identity files " ")))) + (aidermacs--send-command command nil)) (message "No files found in the current window.")))) -;; Function to send a custom command to corresponding aider buffer +;; Function to send a custom command to corresponding aidermacs buffer ;;;###autoload -(defun aider-general-command () - "Prompt the user to input COMMAND and send it to the corresponding aider comint buffer." +(defun aidermacs-general-command () + "Prompt the user to input COMMAND and send it to the corresponding aidermacs comint buffer." (interactive) - (let ((command (aider-read-string "Enter command to send to aidermacs: "))) + (let ((command (aidermacs-read-string "Enter command to send to aidermacsmacs: "))) ;; Use the shared helper function to send the command - (aider--send-command command t))) + (aidermacs--send-command command t))) ;; New function to get command from user and send it prefixed with "/code " ;;;###autoload -(defun aider-code-change () - "Prompt the user for a command and send it to the corresponding aider comint buffer prefixed with \"/code \"." +(defun aidermacs-code-change () + "Prompt the user for a command and send it to the corresponding aidermacs comint buffer prefixed with \"/code \"." (interactive) - (let ((command (aider-read-string "Enter code change requirement: "))) - (aider-send-command-with-prefix "/code " command))) + (let ((command (aidermacs-read-string "Enter code change requirement: "))) + (aidermacs-send-command-with-prefix "/code " command))) ;; New function to get command from user and send it prefixed with "/ask " ;;;###autoload -(defun aider-ask-question () - "Prompt the user for a command and send it to the corresponding aider comint buffer prefixed with \"/ask \". +(defun aidermacs-ask-question () + "Prompt the user for a command and send it to the corresponding aidermacs comint buffer prefixed with \"/ask \". If a region is active, append the region text to the question. If cursor is inside a function, include the function name as context." (interactive) - ;; Dispatch to general question if in aider buffer - (when (string= (buffer-name) (aider-buffer-name)) - (call-interactively 'aider-general-question) - (cl-return-from aider-ask-question)) + ;; Dispatch to general question if in aidermacs buffer + (when (string= (buffer-name) (aidermacs-buffer-name)) + (call-interactively 'aidermacs-general-question) + (cl-return-from aidermacs-ask-question)) (let* ((function-name (which-function)) (initial-input (when function-name (format "About function '%s': " function-name))) - (question (aider-read-string "Enter question to ask: " initial-input)) + (question (aidermacs-read-string "Enter question to ask: " initial-input)) (region-text (and (region-active-p) (buffer-substring-no-properties (region-beginning) (region-end)))) (command (if region-text (format "/ask %s: %s" question region-text) (format "/ask %s" question)))) - (aider-add-current-file) - (aider--send-command command t))) + (aidermacs-add-current-file) + (aidermacs--send-command command t))) ;;;###autoload -(defun aider-general-question () - "Prompt the user for a general question and send it to the corresponding aider comint buffer prefixed with \"/ask \"." +(defun aidermacs-general-question () + "Prompt the user for a general question and send it to the corresponding aidermacs comint buffer prefixed with \"/ask \"." (interactive) - (let ((question (aider-read-string "Enter general question to ask: "))) + (let ((question (aidermacs-read-string "Enter general question to ask: "))) (let ((command (format "/ask %s" question))) - (aider--send-command command t)))) + (aidermacs--send-command command t)))) ;; New function to get command from user and send it prefixed with "/help " ;;;###autoload -(defun aider-help () - "Prompt the user for a command and send it to the corresponding aider comint buffer prefixed with \"/help \"." +(defun aidermacs-help () + "Prompt the user for a command and send it to the corresponding aidermacs comint buffer prefixed with \"/help \"." (interactive) - (let ((command (aider-read-string "Enter help question: "))) - (aider-send-command-with-prefix "/help " command))) + (let ((command (aidermacs-read-string "Enter help question: "))) + (aidermacs-send-command-with-prefix "/help " command))) ;; New function to get command from user and send it prefixed with "/architect " ;;;###autoload -(defun aider-architect-discussion () - "Prompt the user for a command and send it to the corresponding aider comint buffer prefixed with \"/architect \"." +(defun aidermacs-architect-discussion () + "Prompt the user for a command and send it to the corresponding aidermacs comint buffer prefixed with \"/architect \"." (interactive) - (let ((command (aider-read-string "Enter architect discussion question: "))) - (aider-send-command-with-prefix "/architect " command))) + (let ((command (aidermacs-read-string "Enter architect discussion question: "))) + (aidermacs-send-command-with-prefix "/architect " command))) ;; New function to get command from user and send it prefixed with "/ask ", might be tough for AI at this moment ;;;###autoload -(defun aider-debug-exception () - "Prompt the user for a command and send it to the corresponding aider comint buffer prefixed with \"/debug \", +(defun aidermacs-debug-exception () + "Prompt the user for a command and send it to the corresponding aidermacs comint buffer prefixed with \"/debug \", replacing all newline characters except for the one at the end." (interactive) - (let ((command (aider-plain-read-string "Enter exception, can be multiple lines: "))) - (aider--send-command (concat "/ask Investigate the following exception, with current added files as context: " command) t))) + (let ((command (aidermacs-plain-read-string "Enter exception, can be multiple lines: "))) + (aidermacs--send-command (concat "/ask Investigate the following exception, with current added files as context: " command) t))) ;;;###autoload -(defun aider-go-ahead () - "Send the command \"go ahead\" to the corresponding aider comint buffer." +(defun aidermacs-go-ahead () + "Send the command \"go ahead\" to the corresponding aidermacs comint buffer." (interactive) - (aider--send-command "go ahead" t)) + (aidermacs--send-command "go ahead" t)) ;; New function to show the last commit using magit ;;;###autoload -(defun aider-magit-show-last-commit () +(defun aidermacs-magit-show-last-commit () "Show the last commit message using Magit. If Magit is not installed, report that it is required." (interactive) @@ -615,13 +616,13 @@ If Magit is not installed, report that it is required." ;; Modified function to get command from user and send it based on selected region ;;;###autoload -(defun aider-undo-last-change () +(defun aidermacs-undo-last-change () "Undo the last change made by aidermacs." (interactive) - (aider--send-command "/undo")) + (aidermacs--send-command "/undo")) ;;;###autoload -(defun aider-function-or-region-refactor () +(defun aidermacs-function-or-region-refactor () "Refactor code at point or region. If region is active, refactor that region. If point is in a function, refactor that function." @@ -629,26 +630,26 @@ If point is in a function, refactor that function." (if (use-region-p) (let* ((region-text (buffer-substring-no-properties (region-beginning) (region-end))) (function-name (which-function)) - (user-command (aider-read-string "Enter refactor instruction: ")) + (user-command (aidermacs-read-string "Enter refactor instruction: ")) (command (if function-name - (format "/architect \"in function %s, for the following code block, %s: %s\"\n" - function-name user-command region-text) - (format "/architect \"for the following code block, %s: %s\"\n" - user-command region-text)))) - (aider-add-current-file) - (aider--send-command command t)) + (format "/architect \"in function %s, for the following code block, %s: %s\"\n" + function-name user-command region-text) + (format "/architect \"for the following code block, %s: %s\"\n" + user-command region-text)))) + (aidermacs-add-current-file) + (aidermacs--send-command command t)) (if-let ((function-name (which-function))) (let* ((initial-input (format "refactor %s: " function-name)) - (user-command (aider-read-string "Enter refactor instruction: " initial-input)) + (user-command (aidermacs-read-string "Enter refactor instruction: " initial-input)) (command (format "/architect %s" user-command))) - (aider-add-current-file) - (aider--send-command command t)) + (aidermacs-add-current-file) + (aidermacs--send-command command t)) (message "No region selected and no function found at point.")))) ;; New function to explain the code in the selected region ;;;###autoload -(defun aider-region-explain () - "Get a command from the user and send it to the corresponding aider comint buffer based on the selected region. +(defun aidermacs-region-explain () + "Get a command from the user and send it to the corresponding aidermacs comint buffer based on the selected region. The command will be formatted as \"/ask \" followed by the text from the selected region." (interactive) (if (use-region-p) @@ -661,35 +662,35 @@ The command will be formatted as \"/ask \" followed by the text from the selecte processed-region-text) (format "/ask explain the following code block: %s" processed-region-text)))) - (aider-add-current-file) - (aider--send-command command t)) + (aidermacs-add-current-file) + (aidermacs--send-command command t)) (message "No region selected."))) ;; New function to ask aidermacs to explain the function under the cursor ;;;###autoload -(defun aider-function-explain () +(defun aidermacs-function-explain () "Ask aidermacs to explain the function under the cursor. Prompts user for specific questions about the function." (interactive) (if-let ((function-name (which-function))) (let* ((initial-input (format "explain %s: " function-name)) - (user-question (aider-read-string "Enter your question about the function: " initial-input)) + (user-question (aidermacs-read-string "Enter your question about the function: " initial-input)) (command (format "/ask %s" user-question))) - (aider-add-current-file) - (aider--send-command command t)) + (aidermacs-add-current-file) + (aidermacs--send-command command t)) (message "No function found at cursor position."))) ;;;###autoload -(defun aider-function-or-region-explain () - "Call aider-function-explain when no region is selected, otherwise call aider-region-explain." +(defun aidermacs-function-or-region-explain () + "Call aidermacs-function-explain when no region is selected, otherwise call aidermacs-region-explain." (interactive) (if (region-active-p) - (aider-region-explain) - (aider-function-explain))) + (aidermacs-region-explain) + (aidermacs-function-explain))) ;; New function to explain the symbol at line ;;;###autoload -(defun aider-explain-symbol-under-point () +(defun aidermacs-explain-symbol-under-point () "Ask aidermacs to explain symbol under point, given the code line as background info." (interactive) (let* ((symbol (thing-at-point 'symbol)) @@ -698,30 +699,30 @@ Prompts user for specific questions about the function." (line-end-position))) (prompt (format "/ask Please explain what '%s' means in the context of this code line: %s" symbol line))) - (aider-add-current-file) - (aider--send-command prompt t))) + (aidermacs-add-current-file) + (aidermacs--send-command prompt t))) -(defun aider-send-command-with-prefix (prefix command) +(defun aidermacs-send-command-with-prefix (prefix command) "Send COMMAND to the aidermacs buffer prefixed with PREFIX." - (aider-add-current-file) - (aider--send-command (concat prefix command) t)) + (aidermacs-add-current-file) + (aidermacs--send-command (concat prefix command) t)) ;;; functions for dired related ;; New function to add multiple Dired marked files to aidermacs buffer ;;;###autoload -(defun aider-batch-add-dired-marked-files () +(defun aidermacs-batch-add-dired-marked-files () "Add multiple Dired marked files to the aidermacs buffer with the \"/add\" command." (interactive) (let ((files (dired-get-marked-files))) (if files - (let ((command (concat (aider--get-add-command-prefix) " " (mapconcat 'expand-file-name files " ")))) - (aider--send-command command t)) + (let ((command (concat (aidermacs--get-add-command-prefix) " " (mapconcat 'expand-file-name files " ")))) + (aidermacs--send-command command t)) (message "No files marked in Dired.")))) ;; New function to add all files with same suffix as current file under current directory ;;;###autoload -(defun aider-add-same-type-files-under-dir () +(defun aidermacs-add-same-type-files-under-dir () "Add all files with same suffix as current file under current directory to aidermacs. If there are more than 40 files, refuse to add and show warning message." (interactive) @@ -736,15 +737,15 @@ If there are more than 40 files, refuse to add and show warning message." (if (> (length files) max-files) (message "Too many files (%d, > %d) found with suffix .%s. Aborting." (length files) max-files current-suffix) - (let ((command (concat (aider--get-add-command-prefix) " " (mapconcat 'identity files " ")))) - (aider--send-command command t)) + (let ((command (concat (aidermacs--get-add-command-prefix) " " (mapconcat 'identity files " ")))) + (aidermacs--send-command command t)) (message "Added %d files with suffix .%s" (length files) current-suffix))))) ;;; functions for test fixing ;;;###autoload -(defun aider-write-unit-test () +(defun aidermacs-write-unit-test () "Generate unit test code for current buffer. Do nothing if current buffer is not visiting a file. If current buffer filename contains 'test': @@ -764,12 +765,12 @@ Otherwise: (if function-name (if (string-match-p "test" function-name) (let* ((initial-input - (format "Please implement test function '%s'. Follow standard unit testing practices and make it a meaningful test. Do not use Mock if possible." - function-name)) - (user-command (aider-read-string "Test implementation instruction: " initial-input)) - (command (format "/architect %s" user-command))) - (aider-add-current-file) - (aider--send-command command t)) + (format "Please implement test function '%s'. Follow standard unit testing practices and make it a meaningful test. Do not use Mock if possible." + function-name)) + (user-command (aidermacs-read-string "Test implementation instruction: " initial-input)) + (command (format "/architect %s" user-command))) + (aidermacs-add-current-file) + (aidermacs--send-command command t)) (message "Current function '%s' does not appear to be a test function." function-name)) (message "Please place cursor inside a test function to implement."))) ;; Non-test file case @@ -778,41 +779,41 @@ Otherwise: (initial-input (if function-name (format "Please write unit test code for function '%s'. %s" - function-name common-instructions) + function-name common-instructions) (format "Please write unit test code for file '%s'. For each function %s" - (file-name-nondirectory buffer-file-name) common-instructions))) - (user-command (aider-read-string "Unit test generation instruction: " initial-input)) + (file-name-nondirectory buffer-file-name) common-instructions))) + (user-command (aidermacs-read-string "Unit test generation instruction: " initial-input)) (command (format "/architect %s" user-command))) - (aider-add-current-file) - (aider--send-command command t))))))) + (aidermacs-add-current-file) + (aidermacs--send-command command t))))))) ;;;###autoload -(defun aider-fix-failing-test-under-cursor () - "Report the current test failure to aider and ask it to fix the code. +(defun aidermacs-fix-failing-test-under-cursor () + "Report the current test failure to aidermacs and ask it to fix the code. This function assumes the cursor is on or inside a test function." (interactive) (if-let ((test-function-name (which-function))) (let* ((initial-input (format "The test '%s' is failing. Please analyze and fix the code to make the test pass. Don't break any other test" - test-function-name)) - (test-output (aider-read-string "Architect question: " initial-input)) + test-function-name)) + (test-output (aidermacs-read-string "Architect question: " initial-input)) (command (format "/architect %s" test-output))) - (aider-add-current-file) - (aider--send-command command t)) + (aidermacs-add-current-file) + (aidermacs--send-command command t)) (message "No test function found at cursor position."))) -(defun aider--is-comment-line (line) +(defun aidermacs--is-comment-line (line) "Check if LINE is a comment line based on current buffer's comment syntax. Returns non-nil if LINE starts with one or more comment characters, ignoring leading whitespace." (when comment-start (let ((comment-str (string-trim-right comment-start))) (string-match-p (concat "^[ \t]*" - (regexp-quote comment-str) - "+") - (string-trim-left line))))) + (regexp-quote comment-str) + "+") + (string-trim-left line))))) ;;;###autoload -(defun aider-implement-todo () +(defun aidermacs-implement-todo () "Implement TODO comments in current context. If region is selected, implement that specific region. If cursor is on a comment line, implement that specific comment. @@ -822,12 +823,12 @@ Otherwise implement TODOs for the entire current file." (if (not buffer-file-name) (message "Current buffer is not visiting a file.") (let* ((current-line (string-trim (thing-at-point 'line t))) - (is-comment (aider--is-comment-line current-line)) + (is-comment (aidermacs--is-comment-line current-line)) (function-name (which-function)) (region-text (when (region-active-p) - (buffer-substring-no-properties - (region-beginning) - (region-end)))) + (buffer-substring-no-properties + (region-beginning) + (region-end)))) (initial-input (cond (region-text @@ -842,70 +843,70 @@ Otherwise implement TODOs for the entire current file." (t (format "Please implement all TODO items in file '%s'. Keep the existing code structure and only implement the TODOs in comments." (file-name-nondirectory buffer-file-name))))) - (user-command (aider-read-string "TODO implementation instruction: " initial-input)) + (user-command (aidermacs-read-string "TODO implementation instruction: " initial-input)) (command (format "/architect %s" user-command))) - (aider-add-current-file) - (aider--send-command command t)))) + (aidermacs-add-current-file) + (aidermacs--send-command command t)))) ;;; functions for sending text blocks ;; New function to send "<line under cursor>" or region line by line to the aidermacs buffer ;;;###autoload -(defun aider-send-line-or-region () +(defun aidermacs-send-line-or-region () "Send text to the aidermacs buffer. If region is active, send the selected region line by line. Otherwise, send the line under cursor." (interactive) (if (region-active-p) - (aider-send-region-by-line) + (aidermacs-send-region-by-line) (let ((line (thing-at-point 'line t))) - (aider--send-command (string-trim line) t)))) + (aidermacs--send-command (string-trim line) t)))) ;;; New function to send the current selected region line by line to the aidermacs buffer ;;;###autoload -(defun aider-send-region-by-line () +(defun aidermacs-send-region-by-line () "Get the text of the current selected region, split them into lines, strip the newline character from each line, -for each non-empty line, send it to aider session. +for each non-empty line, send it to aidermacs session. If no region is selected, show a message." (interactive) (if (region-active-p) (let ((region-text (buffer-substring-no-properties - (region-beginning) - (region-end)))) + (region-beginning) + (region-end)))) (mapc (lambda (line) (unless (string-empty-p line) - (aider--send-command line t))) + (aidermacs--send-command line t))) (split-string region-text "\n" t))) (message "No region selected."))) ;;;###autoload -(defun aider-send-block-or-region () - "Send the current active region text or, if no region is active, send the current paragraph content to the aider session. +(defun aidermacs-send-block-or-region () + "Send the current active region text or, if no region is active, send the current paragraph content to the aidermacs session. When sending paragraph content, preserve cursor position and deactivate mark afterwards." (interactive) (if (region-active-p) (let ((region-text (buffer-substring-no-properties (region-beginning) (region-end)))) (unless (string-empty-p region-text) - (aider--send-command region-text t))) + (aidermacs--send-command region-text t))) (save-excursion ; preserve cursor position (let ((region-text (progn (mark-paragraph) ; mark paragraph (buffer-substring-no-properties (region-beginning) (region-end))))) (unless (string-empty-p region-text) - (aider--send-command region-text t)) + (aidermacs--send-command region-text t)) (deactivate-mark))))) ; deactivate mark after sending ;;;###autoload -(defun aider-open-prompt-file () - "Open aider prompt file under git repo root. +(defun aidermacs-open-prompt-file () + "Open aidermacs prompt file under git repo root. If file doesn't exist, create it with command binding help and sample prompt." (interactive) (let* ((git-root (magit-toplevel)) (prompt-file (when git-root - (expand-file-name aider-prompt-file-name git-root)))) + (expand-file-name aidermacs-prompt-file-name git-root)))) (if prompt-file (progn (find-file-other-window prompt-file) @@ -914,54 +915,54 @@ If file doesn't exist, create it with command binding help and sample prompt." (insert "# aidermacs Prompt File - Command Reference:\n") (insert "# C-c C-n or C-<return>: Send current line or selected region line by line\n") (insert "# C-c C-c: Send current block or selected region as a whole\n") - (insert "# C-c C-z: Switch to aider buffer\n\n") + (insert "# C-c C-z: Switch to aidermacs buffer\n\n") (insert "* Sample task:\n\n") (insert "/ask what this repo is about?\n") (save-buffer))) (message "Not in a git repository")))) ;; Define the keymap for aidermacs Minor Mode -(defvar aider-minor-mode-map +(defvar aidermacs-minor-mode-map (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-c C-n") 'aider-send-line-or-region) - (define-key map (kbd "C-<return>") 'aider-send-line-or-region) - (define-key map (kbd "C-c C-c") 'aider-send-block-or-region) - (define-key map (kbd "C-c C-z") 'aider-switch-to-buffer) + (define-key map (kbd "C-c C-n") 'aidermacs-send-line-or-region) + (define-key map (kbd "C-<return>") 'aidermacs-send-line-or-region) + (define-key map (kbd "C-c C-c") 'aidermacs-send-block-or-region) + (define-key map (kbd "C-c C-z") 'aidermacs-switch-to-buffer) map) "Keymap for aidermacs Minor Mode.") ;; Define the aidermacs Minor Mode ;;;###autoload -(define-minor-mode aider-minor-mode +(define-minor-mode aidermacs-minor-mode "Minor mode for aidermacs with keybindings." :lighter " aidermacs" - :keymap aider-minor-mode-map + :keymap aidermacs-minor-mode-map :override t) -;; Auto-enable aider-minor-mode for specific files -(defcustom aider-auto-mode-files +;; Auto-enable aidermacs-minor-mode for specific files +(defcustom aidermacs-auto-mode-files (list - ".aider.prompt.org" ; Default prompt file - ".aider.chat.md" ; Chat history file - ".aider-history.md") ; Alternative history file - "List of filenames that should automatically enable `aider-minor-mode'. + ".aidermacs.prompt.org" ; Default prompt file + ".aidermacs.chat.md" ; Chat history file + ".aidermacs-history.md") ; Alternative history file + "List of filenames that should automatically enable `aidermacs-minor-mode'. These are exact filename matches (including the dot prefix)." :type '(repeat string) - :group 'aider) + :group 'aidermacs) -(defun aider--should-enable-minor-mode-p (filename) - "Determine if aider-minor-mode should be enabled for FILENAME. -Returns t if the file matches any of the patterns in `aider-auto-mode-files'." +(defun aidermacs--should-enable-minor-mode-p (filename) + "Determine if aidermacs-minor-mode should be enabled for FILENAME. +Returns t if the file matches any of the patterns in `aidermacs-auto-mode-files'." (when filename (let ((base-name (file-name-nondirectory filename))) - (member base-name aider-auto-mode-files)))) + (member base-name aidermacs-auto-mode-files)))) (add-hook 'find-file-hook (lambda () (when (and buffer-file-name - (aider--should-enable-minor-mode-p buffer-file-name)) - (aider-minor-mode 1)))) + (aidermacs--should-enable-minor-mode-p buffer-file-name)) + (aidermacs-minor-mode 1)))) -(provide 'aider) +(provide 'aidermacs) -;;; aider.el ends here +;;; aidermacs.el ends here diff --git a/scratch.aider b/scratch.aider index 0750a347e8..7d530e229e 100644 --- a/scratch.aider +++ b/scratch.aider @@ -1,24 +1,24 @@ /ask what this repo is about? -# This repository appears to be focused on an Emacs package called "Aider," which -# provides an interactive interface for communicating with an AI tool (likely -# related to programming assistance). The package allows users to send commands -# and queries to the Aider program, which seems to facilitate AI pair programming -# or code assistance. +# This repository appears to be focused on an Emacs package called "Aidermacs," which +# provides an interactive interface for communicating with an AI tool (likely +# related to programming assistance). The package allows users to send commands +# and queries to the Aider program, which seems to facilitate AI pair programming +# or code assistance. -# Key features of the repository include: +# Key features of the repository include: -# • Interactive Commands: Users can run commands like adding files, asking -# questions, and performing code changes through a transient menu. -# • Integration with Git: The package can generate buffer names based on the -# current Git repository, indicating that it is designed to work within a -# version-controlled environment. -# • Custom Commands: Users can send custom commands to the Aider buffer, allowing -# for flexible interaction with the AI. -# • File Type Association: The package defines a major mode (aider-mode) for -# files with the .aider extension, which includes specific key bindings and -# functionalities tailored for those files. +# • Interactive Commands: Users can run commands like adding files, asking +# questions, and performing code changes through a transient menu. +# • Integration with Git: The package can generate buffer names based on the +# current Git repository, indicating that it is designed to work within a +# version-controlled environment. +# • Custom Commands: Users can send custom commands to the Aidermacs buffer, allowing +# for flexible interaction with the AI. +# • File Type Association: The package defines a major mode (aidermacs-mode) for +# files with the .aider extension, which includes specific key bindings and +# functionalities tailored for those files. -# Overall, the repository is aimed at enhancing the coding experience by -# leveraging AI assistance within the Emacs text editor. +# Overall, the repository is aimed at enhancing the coding experience by +# leveraging AI assistance within the Emacs text editor. diff --git a/test_aider.el b/test_aider.el deleted file mode 100644 index fa594386ab..0000000000 --- a/test_aider.el +++ /dev/null @@ -1,168 +0,0 @@ -(require 'ert) - -(ert-deftest aider-buffer-name-test () - "Test the aider-buffer-name function." - ;; Test normal git repo case - (cl-letf (((symbol-function 'magit-toplevel) - (lambda () "/path/to/git/repo/"))) - (should (equal (aider-buffer-name) "*aider:/path/to/git/repo/*"))) - - ;; Test when magit-toplevel returns nil but buffer has a file - (cl-letf (((symbol-function 'magit-toplevel) - (lambda () nil)) - ((symbol-function 'buffer-file-name) - (lambda () "/path/to/some/file.el"))) - (should (equal (aider-buffer-name) "*aider:/path/to/some/*"))) - - ;; Test when magit-toplevel returns fatal message but buffer has a file - (cl-letf (((symbol-function 'magit-toplevel) - (lambda () "fatal: not a git repository")) - ((symbol-function 'buffer-file-name) - (lambda () "/another/path/file.txt"))) - (should (equal (aider-buffer-name) "*aider:/another/path/*"))) - - ;; Test error case when not in git repo and no buffer file - (cl-letf (((symbol-function 'magit-toplevel) - (lambda () nil)) - ((symbol-function 'buffer-file-name) - (lambda () nil))) - (should-error (aider-buffer-name) :type 'error)) - ) - -(ert-deftest aider--process-message-if-multi-line-test () - "Test the aider--process-message-if-multi-line function." - ;; Test single line text - (should (equal (aider--process-message-if-multi-line "single line") - "single line")) - ;; Test multi-line text - (should (equal (aider--process-message-if-multi-line "line 1\nline 2") - "{aider\nline 1\nline 2\naider}")) - ;; Test empty string - (should (equal (aider--process-message-if-multi-line "") - "")) - ;; Test newline only - (should (equal (aider--process-message-if-multi-line "\n") - "{aider\n\n\naider}"))) - -(ert-deftest aider--get-add-command-prefix-test () - "Test the aider--get-add-command-prefix function." - ;; Test when read-only mode is off - (let ((aider--add-file-read-only nil)) - (should (equal (aider--get-add-command-prefix) "/add"))) - ;; Test when read-only mode is on - (let ((aider--add-file-read-only t)) - (should (equal (aider--get-add-command-prefix) "/read-only")))) - -(ert-deftest aider-region-refactor-generate-command-test () - "Test the aider-region-refactor-generate-command function." - ;; Test with function name - (should (equal (aider-region-refactor-generate-command - "code block" - "test_func" - "make it better") - "/architect \"in function test_func, for the following code block, make it better: code block\"\n")) - - ;; Test without function name - (should (equal (aider-region-refactor-generate-command - "code block" - nil - "make it better") - "/architect \"for the following code block, make it better: code block\"\n")) - - ;; Test multi-line code block - (should (equal (aider-region-refactor-generate-command - "line 1\nline 2" - "test_func" - "make it better") - "/architect \"in function test_func, for the following code block, make it better: line 1\nline 2\"\n")) - - ;; Test empty code block - (should (equal (aider-region-refactor-generate-command - "" - "test_func" - "make it better") - "/architect \"in function test_func, for the following code block, make it better: \"\n"))) - -(ert-deftest aider-font-lock-keywords-test () - "Test the aider-font-lock-keywords variable." - ;; Ensure font-lock-keywords contains correct patterns and faces - (should (equal (car (car aider-font-lock-keywords)) - "^\x2500+\n?")) - (should (equal (nth 2 (car aider-font-lock-keywords)) - '(face aider-command-separator))) - (should (equal (car (cadr aider-font-lock-keywords)) - "^\x2500+")) - (should (equal (nth 2 (cadr aider-font-lock-keywords)) - '(face nil display (space :width 2))))) - -(ert-deftest aider-write-unit-test-behavior () - "Test the behavior of aider-write-unit-test function." - ;; Test when buffer is not visiting a file - (with-temp-buffer - (should (progn (aider-write-unit-test) - (equal (current-message) "Current buffer is not visiting a file.")))) - - ;; Test when buffer is a test file - (with-temp-buffer - (let ((buffer-file-name "test_something.py")) - (should (progn (aider-write-unit-test) - (equal (current-message) "Current buffer appears to be a test file."))))) - - ;; Test when buffer is a regular file with no function under cursor - (with-temp-buffer - (let ((buffer-file-name "regular.py") - (aider-read-string-result "modified prompt")) - (cl-letf (((symbol-function 'which-function) (lambda () nil)) - ((symbol-function 'aider-read-string) - (lambda (prompt initial) aider-read-string-result)) - ((symbol-function 'aider-add-current-file) (lambda () t)) - ((symbol-function 'aider--send-command) (lambda (cmd switch) t))) - (aider-write-unit-test) - (should (not (equal (current-message) "Current buffer is not visiting a file."))) - (should (not (equal (current-message) "Current buffer appears to be a test file."))))))) - -(ert-deftest aider-faces-test () - "Test that aider faces are properly defined." - ;; Test aider-command-separator face - (should (face-differs-from-default-p 'aider-command-separator)) - ;; Test aider-command-text face - (should (face-differs-from-default-p 'aider-command-text))) - -;; Helper function to test if a face differs from the default face -(defun face-differs-from-default-p (face) - "Check if FACE has different properties from default face." - (let ((face-props (face-all-attributes face nil)) - (default-props (face-all-attributes 'default nil))) - (not (equal face-props default-props)))) - -(ert-deftest aider--is-comment-line-test () - "Test the aider--is-comment-line function." - ;; Test with semicolon comment style (Lisp) - (let ((comment-start ";")) - ;; Basic comment cases - (should (aider--is-comment-line "; comment")) - (should (aider--is-comment-line ";; double comment")) - (should (aider--is-comment-line ";;; triple comment")) - ;; Comments with leading whitespace - (should (aider--is-comment-line " ; indented comment")) - (should (aider--is-comment-line "\t; tabbed comment")) - ;; Non-comment cases - (should-not (aider--is-comment-line "code ; with comment")) - (should-not (aider--is-comment-line "regular code")) - (should-not (aider--is-comment-line ""))) - ;; Test with hash comment style (Python) - (let ((comment-start "#")) - (should (aider--is-comment-line "# python comment")) - (should (aider--is-comment-line " ## indented python comment")) - (should-not (aider--is-comment-line "code # with comment"))) - ;; Test with double slash comment style (C/Java) - (let ((comment-start "//")) - (should (aider--is-comment-line "// c style comment")) - (should (aider--is-comment-line " /// indented c comment")) - (should-not (aider--is-comment-line "code // with comment"))) - ;; Test with nil comment-start - (let ((comment-start nil)) - (should-not (aider--is-comment-line "; not a comment when no comment-start")) - (should-not (aider--is-comment-line "# not a comment when no comment-start")) - (should-not (aider--is-comment-line "// not a comment when no comment-start"))) - ) diff --git a/tests/test dir/a.kt b/tests/test dir/a.kt deleted file mode 100644 index 9ce06a81ea..0000000000 --- a/tests/test dir/a.kt +++ /dev/null @@ -1 +0,0 @@ -# dummy diff --git a/tests/test-aider-alias.el b/tests/test-aider-alias.el deleted file mode 100644 index a4ed22a4d9..0000000000 --- a/tests/test-aider-alias.el +++ /dev/null @@ -1,24 +0,0 @@ -;;; tests/test-aider-alias.el --- Tests for aider alias -;; -;; Author: Kang Tu -;; Version: 0.1 -;; License: MIT -;; -;;; Commentary: -;; This file contains automated tests using ERT for the aider alias. -;; It verifies that `aider-read-string` is correctly aliased to -;; `aider-plain-read-string` in both interpreted and compiled environments. -;; -;;; Code: - -(require 'ert) -(require 'aider) - -(ert-deftest aider-read-string-alias-test () - "Test that `aider-read-string` is aliased to `aider-plain-read-string`." - (should (eq (symbol-function 'aider-read-string) - (symbol-function 'aider-plain-read-string)))) - -(provide 'test-aider-alias) - -;;; tests/test-aider-alias.el ends here