branch: elpa/aidermacs commit d60824add8884a2baf4bb73344983f2026bc4b1a Author: Kang Tu <tni...@gmail.com> Commit: GitHub <nore...@github.com>
Refactor: combine function under cursor function and region based function as single one (#34) * feat(aider): add explain-symbol-under-point command * feat: add function to explain function under cursor * feat(aider): add support for Gemini model and function explanation command * docs(readme): reorganize and expand code explanation features * refactor: remove unused syntax table code in buffer setup * feat: add function to add files with same suffix under current dir * feat: Add function to add same-type files under a directory to Aider * feat: Add command to add files of same type under current directory * refactor: Use helper function for adding/reading current file in aider.el * refactor: Comment out unused functions in aider-main-menu-map * refactor: move git repository functions to separate file * refactor: Combine region and function explain commands into one * feat: Add function to refactor region or function based on selection * feat: Add function to refactor function or region based on selection * refactor(ui): simplify code change menu options * refactor(doom): update menu itemsc * feat: append region text to question in `aider-ask-question` * fix: Escape newlines in region text for `aider-ask-question` * refactor: Extract string escaping logic into dedicated function This commit refactors the string escaping logic in `aider.el` into a dedicated function, `aider--escape-string-for-aider`. This improves code readability, maintainability, and reusability by centralizing the escaping logic in one place. The function replaces newline characters with escaped newlines, and is now used in `aider-ask-question`, `aider-debug-exception`, `aider-region-refactor-generate-command`, and `aider-region-explain`. The `aider-plain-read-string` function no longer per [...] * refactor: Centralize newline escaping in `aider--send-command` * fix: Remove extra parenthesis in message call in aider--send-command * bug fix * refactor: Remove unnecessary parentheses in `aider-send-command` * refactor: Refactor aider-send-paragraph to send lines individually * refactor: Use dash library for list manipulation * bug fix * fix: improve `aider-send-paragraph` to handle newlines correctly * fix: Send whole buffer content when no files are added to chat * ``` refactor: Only send non-empty lines in `aider-send-paragraph` ``` * refactor: Send non-empty lines to aider in `aider-send-current-test` * refactor: Improve `aider-send-paragraph` to send whole paragraph to aider * refactor: remove dash.el dependency --------- Co-authored-by: Kang Tu <kang...@apple.com> --- aider-doom.el | 36 ++++++++++++++----------- aider.el | 86 ++++++++++++++++++++++++++++++++++++++--------------------- 2 files changed, 75 insertions(+), 47 deletions(-) diff --git a/aider-doom.el b/aider-doom.el index bfd6f0d476..d2a149000d 100644 --- a/aider-doom.el +++ b/aider-doom.el @@ -16,40 +16,44 @@ (:prefix ("A" . "Aider") (: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 - :desc "Find files in repo" "g" #'aider-repo-find-name-dired - :desc "Open repo root" "d" #'aider-git-repo-root-dired) + ) (:prefix ("b" . "Buffer") + :desc "Open Aider" "o" #'aider-run-aider :desc "Switch to Aider" "b" #'aider-switch-to-buffer - :desc "Clear Aider" "c" #'aider-clear) + :desc "Clear Aider" "c" #'aider-clear + :desc "Reset Aider" "r" #'aider-reset + :desc "Exit Aider" "x" #'aider-exit + ) (:prefix ("s" . "Send") - :desc "File read-only" "f" #'aider-current-file-read-only :desc "Line at cursor" "l" #'aider-send-line-under-cursor - :desc "Paragraph at cursor" "p" #'aider-send-paragraph) + :desc "Paragraph at cursor" "p" #'aider-send-paragraph + ) (:prefix ("c" . "Code") + :desc "Architecture" "d" #'aider-architect-discussion :desc "Change" "c" #'aider-code-change - :desc "Refactor region" "r" #'aider-region-refactor - :desc "Undo change" "u" #'aider-undo-last-change) + :desc "Refactor Function or Region" "r" #'aider-function-or-region-refactor + :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 "Architecture" "d" #'aider-architect-discussion - :desc "Region explanation" "r" #'aider-region-explain - :desc "Exception debugggin" "e" #'aider-debug-exception) + :desc "Explain Function or Region" "r" #'aider-function-or-region-explain + :desc "Exception debugging" "e" #'aider-debug-exception + ) (:prefix ("z" . "Other") :desc "General command" "c" #'aider-general-command :desc "Help" "h" #'aider-help - :desc "Show last commit" "g" #'aider-magit-show-last-commit) - - - :desc "Open Aider" "o" #'aider-run-aider - :desc "Reset Aider" "r" #'aider-reset - :desc "Exit Aider" "x" #'aider-exit)))) + ) + )))) ;; Add the setup function to appropriate hooks (add-hook 'find-file-hook #'aider-doom-setup-keys) diff --git a/aider.el b/aider.el index a0f9560ed2..6a219bcc4e 100644 --- a/aider.el +++ b/aider.el @@ -12,9 +12,9 @@ ;;; Code: (require 'comint) +(require 'dired) (require 'transient) (require 'which-func) -(require 'dired) (defgroup aider nil "Customization group for the Aider package." @@ -42,13 +42,16 @@ "Font lock keywords for aider buffer.") +(defun aider--escape-string-for-aider (str) + "Escape special characters in STR for sending to Aider. +Currently, this function replaces newlines with \\\\n." + (replace-regexp-in-string "\n" "\\\\n" str)) + ;;;###autoload (defun aider-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." - (let* ((input (read-string prompt initial-input)) - (processed-input (replace-regexp-in-string "\n" "\\\\n" input))) - processed-input)) + (read-string prompt initial-input)) (defalias 'aider-read-string 'aider-plain-read-string) @@ -74,24 +77,21 @@ This function can be customized or redefined by the user." ("b" "Batch Add Dired Marked Files" aider-batch-add-dired-marked-files) ] ["Code Change" - ("c" "Code Change" aider-code-change) ("t" "Architect Discuss and Change" aider-architect-discussion) - ("r" "Refactor Code in Selected Region" aider-region-refactor) - ("R" "Refactor Function Under Cursor" aider-function-refactor) + ("c" "Code Change" aider-code-change) + ("r" "Refactor Function or Region" aider-function-or-region-refactor) ("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) ] ["Discussion" ("q" "Ask Question" aider-ask-question) - ("e" "Explain Code in Selected Region" aider-region-explain) - ("E" "Explain Function Under Cursor" aider-function-explain) - ("p" "Explain Symbol Under Cursor" aider-explain-symbol-under-point) + ("e" "Explain Function or Region" aider-function-or-region-explain) ("D" "Debug Exception" aider-debug-exception) ] ["Other" ("g" "General Command" aider-general-command) - ("h" "Help" aider-help) ;; Menu item for help command + ("h" "Help" aider-help) ] ]) @@ -205,21 +205,20 @@ If not in a git repository, an error is raised." COMMAND should be a string representing the command to send." ;; Check if the corresponding aider buffer exists (if-let ((aider-buffer (get-buffer (aider-buffer-name)))) - (let ((aider-process (get-buffer-process aider-buffer))) + (let* ((command (aider--escape-string-for-aider command)) + (aider-process (get-buffer-process aider-buffer))) ;; Check if the corresponding 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 - (aider--comint-send-large-string aider-buffer command) + (aider--comint-send-large-string aider-buffer (concat command "\n")) ;; Provide feedback to the user ;; (message "Sent command to aider buffer: %s" (string-trim command)) (when switch-to-buffer (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)))) + (message "Buffer %s does not exist. Please start 'aider' first." (aider-buffer-name)) + )) ;;;###autoload (defun aider-add-or-read-current-file (command-prefix) @@ -282,10 +281,16 @@ COMMAND should be a string representing the command to send." ;; 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 \"." + "Prompt the user for a command and send it to the corresponding aider comint buffer prefixed with \"/ask \". +If a region is active, append the region text to the question." (interactive) - (let ((command (aider-read-string "Enter question to ask: "))) - (aider-send-command-with-prefix "/ask " command))) + (let ((question (aider-read-string "Enter question to ask: ")) + (region-text (and (region-active-p) (buffer-substring-no-properties (region-beginning) (region-end))))) + (let ((command (if region-text + (format "/ask %s: %s" question region-text) + (format "/ask %s" question)))) + (aider-add-current-file) + (aider--send-command command t)))) ;; New function to get command from user and send it prefixed with "/help " ;;;###autoload @@ -331,7 +336,7 @@ If Magit is not installed, report that it is required." (defun aider-region-refactor-generate-command (region-text function-name user-command) "Generate the command string based on REGION-TEXT, FUNCTION-NAME, and USER-COMMAND." - (let ((processed-region-text (replace-regexp-in-string "\n" "\\\\n" region-text))) + (let ((processed-region-text region-text)) (if function-name (format "/architect \"in function %s, for the following code block, %s: %s\"\n" function-name user-command processed-region-text) @@ -366,6 +371,14 @@ The command will be formatted as \"/architect \" followed by the user command an (aider--send-command command t)) (message "No region selected."))) +;;;###autoload +(defun aider-function-or-region-refactor () + "Call aider-function-refactor when no region is selected, otherwise call aider-region-refactor." + (interactive) + (if (region-active-p) + (aider-region-refactor) + (aider-function-refactor))) + ;; New function to explain the code in the selected region ;;;###autoload (defun aider-region-explain () @@ -375,7 +388,7 @@ The command will be formatted as \"/ask \" followed by the text from the selecte (if (use-region-p) (let* ((region-text (buffer-substring-no-properties (region-beginning) (region-end))) (function-name (which-function)) - (processed-region-text (replace-regexp-in-string "\n" "\\\\n" region-text)) + (processed-region-text region-text) (command (if function-name (format "/ask in function %s, explain the following code block: %s" function-name @@ -397,6 +410,14 @@ The command will be formatted as \"/ask \" followed by the text from the selecte (aider--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." + (interactive) + (if (region-active-p) + (aider-region-explain) + (aider-function-explain))) + ;; New function to explain the symbol at line ;;;###autoload (defun aider-explain-symbol-under-point () @@ -480,16 +501,19 @@ This function assumes the cursor is on or inside a test function." ;;; New function to send the current paragraph to the Aider buffer ;;;###autoload (defun aider-send-paragraph () - "Send the current paragraph to the Aider buffer." + "Get the whole text of the current paragraph, split them into lines, + strip the newline character from each line, + for each non-empty line, send it to aider session" (interactive) - (let ((paragraph (buffer-substring-no-properties - (save-excursion - (backward-paragraph) - (point)) - (save-excursion - (forward-paragraph) - (point))))) - (aider--send-command (string-trim paragraph) t))) + (let ((paragraph (save-excursion + (backward-paragraph) + (let ((start (point))) + (forward-paragraph) + (buffer-substring-no-properties start (point)))))) + (mapc (lambda (line) + (unless (string-empty-p line) + (aider--send-command line t))) + (split-string paragraph "\n" t)))) ;; Define the keymap for Aider Minor Mode (defvar aider-minor-mode-map