branch: elpa/aidermacs
commit dfbe5ba5fe731faf28302017da3c41e1aa9e6e4b
Author: Kang Tu <[email protected]>
Commit: Kang Tu <[email protected]>
add aider.el, update comment
---
aider.el | 230 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 230 insertions(+)
diff --git a/aider.el b/aider.el
new file mode 100644
index 0000000000..749c4ea95f
--- /dev/null
+++ b/aider.el
@@ -0,0 +1,230 @@
+;;; aider.el --- Aider package for interactive conversation with OpenAI -*-
lexical-binding: t; -*-
+
+;; Author: Kang Tu <[email protected]>
+;; Version: 0.1.0
+;; Package-Requires: ((emacs "24.1") (transient "0.3.0"))
+;; Keywords: convenience, tools
+;; URL: https://github.com/tninja/aider.el
+
+;;; Commentary:
+;; This package provides an interactive interface to communicate with
https://github.com/paul-gauthier/aider.
+
+;;; Code:
+
+(require 'transient)
+
+(defgroup aider nil
+ "Customization group for the Aider package."
+ :prefix "aider-"
+ :group 'convenience)
+
+
+(defcustom aider-deepseek-api-key deepseek-api-key
+ "DeepSeek API key for Aider."
+ :type 'string
+ :group 'aider)
+
+(defcustom aider-args '("--deepseek")
+ "Arguments to pass to the Aider command."
+ :type '(repeat string)
+ :group 'aider)
+
+;; (defcustom aider-openai-api-key chatgpt-shell-openai-key
+;; "OpenAI API key for Aider."
+;; :type 'string
+;; :group 'aider)
+
+;; (defcustom aider-args '("--model" "gpt-4o-mini")
+;; "Arguments to pass to the Aider command."
+;; :type '(repeat string)
+;; :group 'aider)
+
+;; Define the keymap for Aider commands
+(defvar aider-global-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map (kbd "a") 'run-aider)
+ (define-key map (kbd "z") 'aider-switch-to-buffer)
+ (define-key map (kbd "s") 'aider-add-current-file)
+ (define-key map (kbd "d") 'aider-drop-current-file)
+ (define-key map (kbd "c") 'aider-send-command)
+ (define-key map (kbd "d") 'aider-code-command)
+ (define-key map (kbd "q") 'aider-ask-question)
+ (define-key map (kbd "t") 'aider-architect-command)
+ (define-key map (kbd "e") 'aider-region-code-command)
+ (define-key map (kbd "u") 'aider-undo-last-change)
+ (define-key map (kbd "r") 'aider-reset-command)
+ (define-key map (kbd "m") 'aider-transient-menu)
+ map)
+ "Global keymap for Aider commands.")
+
+;; Activate the global keymap
+(define-key global-map (kbd "C-c a") aider-global-map)
+
+;; Transient menu for Aider commands
+(transient-define-prefix aider-transient-menu ()
+ "Transient menu for Aider commands."
+ ["Aider Menu"
+ ["Aider process"
+ ("a" "Run Aider" run-aider)
+ ("s" "Add Current File" aider-add-current-file)
+ ("z" "Switch to Aider Buffer" aider-switch-to-buffer)
+ ("d" "Drop Current File" aider-drop-current-file)
+ ]
+ ["Code change"
+ ("d" "Code Change" aider-code-command)
+ ("e" "Region Code Change" aider-region-code-command)
+ ("u" "Undo Last Change" aider-undo-last-change) ;; Menu item for undo last
change
+ ]
+ ["Discussion"
+ ("q" "Ask Question" aider-ask-question)
+ ("t" "Architect Discussion" aider-architect-command)
+ ]
+ ["Other"
+ ("r" "Reset Aider" aider-reset-command) ;; Menu item for reset command
+ ("c" "General Command" aider-send-command)
+ ]
+ ])
+
+
+(defun aider-buffer-name ()
+ "Generate the Aider buffer name based on the path from the home folder to
the git repo of the current active buffer using a git command."
+ (let* ((buffer-file-path (buffer-file-name))
+ (git-repo-path (shell-command-to-string "git rev-parse
--show-toplevel"))
+ (home-path (expand-file-name "~"))
+ (relative-path (substring git-repo-path (length home-path))))
+ (format "*aider:%s*" (concat "~" (replace-regexp-in-string "\n" ""
relative-path)))))
+
+(defun run-aider ()
+ "Create a comint-based buffer and run 'aider' for interactive conversation."
+ (interactive)
+ (let* ((buffer-name (aider-buffer-name))
+ (command "aider"))
+ ;; Check if the buffer already has a running process
+ (unless (comint-check-proc buffer-name)
+ ;; Create a new comint buffer and start the process
+ ;; (setenv "OPENAI_API_KEY" aider-openai-api-key)
+ (setenv "DEEPSEEK_API_KEY" aider-deepseek-api-key)
+ (apply 'make-comint-in-buffer "aider" buffer-name command nil aider-args)
+ ;; Optionally, you can set the mode or add hooks here
+ (with-current-buffer buffer-name
+ (comint-mode)
+ ))
+ ;; Switch to the buffer
+ (aider-add-current-file)
+ (pop-to-buffer buffer-name)))
+
+;; Function to switch to the Aider buffer
+(defun aider-switch-to-buffer ()
+ "Switch to the Aider buffer."
+ (interactive)
+ (let ((buffer (get-buffer (aider-buffer-name))))
+ (if buffer
+ (pop-to-buffer buffer)
+ (message "Aider buffer '%s' does not exist." (aider-buffer-name)))))
+
+;; Function to reset the Aider buffer
+(defun aider-reset-command ()
+ "Send the command \"/reset\" to the Aider buffer."
+ (interactive)
+ (aider--send-command "/reset"))
+
+;; Shared helper function to send commands to *aider* buffer
+(defun aider--send-command (command)
+ "Send COMMAND to the *aider* comint buffer after performing necessary checks.
+COMMAND should be a string representing the command to send."
+ ;; Check if the *aider* buffer exists
+ (if-let ((aider-buffer (get-buffer (aider-buffer-name))))
+ (let ((aider-process (get-buffer-process aider-buffer)))
+ ;; Check if the *aider* buffer has an active process
+ (if (and aider-process (comint-check-proc aider-buffer))
+ (progn
+ ;; Ensure the command ends with a newline
+ (unless (string-suffix-p "\n" command)
+ (setq command (concat command "\n")))
+ ;; Send the command to the aider process
+ (comint-send-string aider-buffer command)
+ ;; Provide feedback to the user
+ (message "Sent command to *aider*: %s" (string-trim command))
+ (aider-switch-to-buffer))
+ (message "No active process found in buffer %s."
(aider-buffer-name))))
+ (message "Buffer %s does not exist. Please start 'aider' first."
(aider-buffer-name))))
+
+;; Function to send "/add <current buffer file full path>" to *aider* buffer
+(defun aider-add-current-file ()
+ "Send the command \"/add <current buffer file full path>\" to the *aider*
comint buffer."
+ (interactive)
+ ;; Ensure the current buffer is associated with a file
+ (if (not buffer-file-name)
+ (message "Current buffer is not associated with a file.")
+ (let ((file-path (expand-file-name buffer-file-name)))
+ ;; Construct the command
+ (let ((command (format "/add %s" file-path)))
+ ;; Use the shared helper function to send the command
+ (aider--send-command command)))))
+
+;; New function to send "/drop <current buffer file full path>" to *aider*
buffer
+(defun aider-drop-current-file ()
+ "Send the command \"/drop <current buffer file full path>\" to the *aider*
comint buffer."
+ (interactive)
+ ;; Ensure the current buffer is associated with a file
+ (if (not buffer-file-name)
+ (message "Current buffer is not associated with a file.")
+ (let ((file-path (expand-file-name buffer-file-name)))
+ ;; Construct the command
+ (let ((command (format "/drop %s" file-path)))
+ ;; Use the shared helper function to send the command
+ (aider--send-command command)))))
+
+;; Function to send a custom command to *aider* buffer
+(defun aider-send-command (command)
+ "Prompt the user to input COMMAND and send it to the *aider* comint buffer.
+COMMAND is a string representing the command to send."
+ (interactive
+ (list (read-string "Enter command to send to aider: ")))
+ ;; Use the shared helper function to send the command
+ (aider--send-command command))
+
+;; New function to get command from user and send it prefixed with "/code "
+(defun aider-code-command ()
+ "Prompt the user for a command and send it to the *aider* comint buffer
prefixed with \"/code \"."
+ (interactive)
+ (let ((command (read-string "Enter code command: ")))
+ (aider--send-command (concat "/code " command))))
+
+;; New function to get command from user and send it prefixed with "/ask "
+(defun aider-ask-question ()
+ "Prompt the user for a command and send it to the *aider* comint buffer
prefixed with \"/ask \"."
+ (interactive)
+ (let ((command (read-string "Enter ask command: ")))
+ (aider--send-command (concat "/ask " command))))
+
+;; New function to get command from user and send it prefixed with "/architect
"
+(defun aider-architect-command ()
+ "Prompt the user for a command and send it to the *aider* comint buffer
prefixed with \"/architect \"."
+ (interactive)
+ (let ((command (read-string "Enter architect command: ")))
+ (aider--send-command (concat "/architect " command))))
+
+;; Modified function to get command from user and send it based on selected
region
+(defun aider-undo-last-change ()
+ "Undo the last change made by Aider."
+ (interactive)
+ (aider--send-command "/undo"))
+
+(defun aider-region-code-command ()
+ "Get a command from the user and send it to the *aider* comint buffer based
on the selected region.
+The command will be formatted as \"/code \" followed by the user command and
the text from the selected region."
+ (interactive)
+ (if (use-region-p)
+ (let* ((region-text (buffer-substring-no-properties (region-beginning)
(region-end)))
+ (processed-region-text (replace-regexp-in-string "\n" "\\\\n"
region-text))
+ (user-command (read-string "Enter your command: "))
+ (function-name (or (which-function) "unknown function"))
+ (command (format "/code \"in function %s, for the following code
block, %s: %s\""
+ function-name user-command
processed-region-text)))
+ (aider--send-command command))
+ (message "No region selected.")))
+
+(provide 'aider)
+
+;;; aider.el ends here