branch: elpa/aidermacs
commit a2b7e239143606ecc1708bf9e750211e238eca11
Merge: fe34d0ceca 73f96304c1
Author: Matthew Zeng <matthew...@posteo.net>
Commit: GitHub <nore...@github.com>

    Merge pull request #34 from ianschenck/ian/profile-speed-up
    
    Optimize vterm output handling and add multiline input support
---
 aidermacs-backend-vterm.el | 177 ++++++++++++++++++++++++++++++++++++---------
 1 file changed, 143 insertions(+), 34 deletions(-)

diff --git a/aidermacs-backend-vterm.el b/aidermacs-backend-vterm.el
index 5fbef78148..b7c81ab2b4 100644
--- a/aidermacs-backend-vterm.el
+++ b/aidermacs-backend-vterm.el
@@ -18,46 +18,58 @@
 (defvar vterm-buffer-name)
 
 (defun aidermacs--vterm-check-finish-sequence-repeated (proc orig-filter 
start-point expected)
-  "Check for the finish sequence repeatedly in PROC's buffer.
+  "Check for the finish sequence in PROC's buffer.
 PROC is the process to check.  ORIG-FILTER is the original process filter.
 START-POINT is the starting position for output capture.  EXPECTED is the
-pattern to match.  Forces a vterm render and redisplay.  If the finish
-sequence is detected, store the output via `aidermacs--store-output`,
-restore ORIG-FILTER, and return t."
+pattern to match.  If the finish sequence is detected, store the output via
+`aidermacs--store-output`, restore ORIG-FILTER, and return t."
   (when (buffer-live-p (process-buffer proc))
     (with-current-buffer (process-buffer proc)
-      ;; Force vterm to update its display.
-      (when (fboundp 'vterm--render)
-        (vterm--render))
-      (force-window-update (selected-window))
-      (redisplay t)
-      (let* ((prompt-point (vterm--get-prompt-point))
-             (seq-start (or (save-excursion
-                              (goto-char prompt-point)
-                              (search-backward "\n" nil t))
-                            (point-min)))
-             (seq-end (or (save-excursion
-                            (goto-char prompt-point)
-                            (search-forward "\n" nil t))
-                          (point-max)))
-             (finish-seq (buffer-substring-no-properties seq-start seq-end)))
-        (when (and (string-match-p expected finish-seq)
-                   (< start-point prompt-point))
-          (let ((output (buffer-substring-no-properties start-point 
seq-start)))
-            (aidermacs--store-output (string-trim output)))
-          (set-process-filter proc orig-filter)
-          t)))))
+      ;; Only render vterm if needed - this is expensive
+      (when (and (fboundp 'vterm--render)
+                 (vterm--invalidate-p))
+        (condition-case nil
+            (vterm--render)
+          (error nil)))
+      
+      (let* ((prompt-point (condition-case nil
+                               (vterm--get-prompt-point)
+                             (error (point-max))))
+             ;; Only do these expensive operations if we have a new prompt
+             (has-new-prompt (< start-point prompt-point)))
+        
+        (when has-new-prompt
+          ;; Only search for boundaries when we have a new prompt
+          (let* ((seq-start (or (save-excursion
+                                  (goto-char prompt-point)
+                                  (condition-case nil
+                                      (search-backward "\n" nil t)
+                                    (error nil)))
+                                (point-min)))
+                 ;; Only get the prompt line, not the whole sequence
+                 (prompt-line (buffer-substring-no-properties 
+                               seq-start 
+                               (min (+ seq-start 200) (point-max)))))
+            
+            (when (string-match-p expected prompt-line)
+              (let ((output (buffer-substring-no-properties start-point 
seq-start)))
+                (aidermacs--store-output (string-trim output)))
+              (set-process-filter proc orig-filter)
+              t)))))))
 
 (defun aidermacs--vterm-output-advice (orig-fun &rest args)
   "Capture vterm output until the finish sequence appears.
 ORIG-FUN is the original function being advised.  ARGS are its arguments.
-This sets a temporary process filter and installs a repeating timer to
-force vterm to update until the expected finish sequence is detected."
+This sets a temporary process filter that checks for the finish sequence
+after each output chunk, reducing the need for timers."
   (if (and (bound-and-true-p aidermacs-minor-mode)
            (eq major-mode 'vterm-mode))
-      (let* ((start-point (vterm--get-prompt-point))
+      (let* ((start-point (condition-case nil
+                              (vterm--get-prompt-point)
+                            (error (point-min))))
              (proc (get-buffer-process (current-buffer)))
-             (expected "\n[^[:space:]]*>[[:space:]].*\n")
+             ;; Simplified pattern that just looks for a shell prompt
+             (expected "^[^[:space:]]*>[[:space:]]")
              (orig-filter (process-filter proc))
              (timer nil))
         ;; Set our temporary process filter.
@@ -66,15 +78,29 @@ force vterm to update until the expected finish sequence is 
detected."
          (lambda (proc string)
            ;; Call the original filter.
            (funcall orig-filter proc string)
-           ;; If we haven't yet started our repeating timer, do so.
+           
+           ;; Check immediately after receiving output
+           (when (aidermacs--vterm-check-finish-sequence-repeated
+                  proc orig-filter start-point expected)
+             (when timer
+               (cancel-timer timer)
+               (setq timer nil))
+             (return))
+           
+           ;; If we haven't found it yet, set up a timer with adaptive 
frequency
            (unless timer
              (setq timer (run-with-timer
                           0.05 0.05
                           (lambda ()
-                            (when 
(aidermacs--vterm-check-finish-sequence-repeated
-                                   proc orig-filter start-point expected)
+                            (cond
+                             ;; Found the prompt, we're done
+                             ((aidermacs--vterm-check-finish-sequence-repeated
+                               proc orig-filter start-point expected)
                               (cancel-timer timer)
-                              (setq timer nil))))))))
+                              (setq timer nil))
+                             
+                             ;; Just keep checking until we find the prompt
+                             )))))))
         (apply orig-fun args))
     (apply orig-fun args)))
 
@@ -94,16 +120,99 @@ BUFFER-NAME is the name for the vterm buffer."
            (vterm-shell-orig vterm-shell))
       (with-current-buffer (vterm-other-window)
         (aidermacs-minor-mode 1)
-        (advice-add 'vterm-send-return :around 
#'aidermacs--vterm-output-advice))))
+        (advice-add 'vterm-send-return :around 
#'aidermacs--vterm-output-advice)
+        ;; Set a reasonable scrollback limit to prevent memory issues
+        (setq-local vterm-max-scrollback 5000)
+        ;; Set up multi-line key binding
+        (let ((map (make-sparse-keymap)))
+          (set-keymap-parent map (current-local-map))
+          (define-key map (kbd aidermacs-vterm-multiline-newline-key) 
#'aidermacs-vterm-insert-newline)
+          (define-key map (kbd aidermacs-vterm-multiline-send-key) 
#'aidermacs-vterm-send-multi-line)
+          (define-key map (kbd "C-c C-k") #'aidermacs-vterm-cancel-multi-line)
+          (use-local-map map))
+        ;; Add cleanup hook
+        (add-hook 'kill-buffer-hook #'aidermacs--vterm-cleanup nil t))))
   buffer-name)
 
+(defvar-local aidermacs--vterm-active-timer nil
+  "Store the active timer for vterm output processing.")
+
+(defvar-local aidermacs--vterm-multi-line-input nil
+  "Accumulated multi-line input in vterm mode.")
+
+(defvar-local aidermacs--vterm-multi-line-mode nil
+  "Non-nil when in multi-line input mode in vterm.")
+
+(defcustom aidermacs-vterm-multiline-newline-key "S-<return>"
+  "Key binding to enter a newline without sending in vterm."
+  :type 'string
+  :group 'aidermacs)
+
+(defcustom aidermacs-vterm-multiline-send-key "C-<return>"
+  "Key binding to send multi-line input in vterm mode."
+  :type 'string
+  :group 'aidermacs)
+
 (defun aidermacs--send-command-vterm (buffer command)
   "Send command to the aidermacs vterm buffer.
 BUFFER is the target buffer to send to.  COMMAND is the text to send."
   (with-current-buffer buffer
+    ;; Cancel any existing timer to prevent resource leaks
+    (when aidermacs--vterm-active-timer
+      (cancel-timer aidermacs--vterm-active-timer)
+      (setq aidermacs--vterm-active-timer nil))
+    ;; Reset multiline mode if active
+    (aidermacs-vterm-reset-multi-line-state)
     (vterm-send-string command)
     (vterm-send-return)))
 
+(defun aidermacs-vterm-insert-newline ()
+  "Insert a newline in vterm multi-line input."
+  (interactive)
+  (if aidermacs--vterm-multi-line-mode
+      (progn
+        (setq aidermacs--vterm-multi-line-input 
+              (concat aidermacs--vterm-multi-line-input "\n"))
+        (let ((inhibit-read-only t))
+          (vterm-insert "\n")))
+    ;; If not in multi-line mode, enter it
+    (setq aidermacs--vterm-multi-line-mode t
+          aidermacs--vterm-multi-line-input "")
+    (let ((inhibit-read-only t))
+      (vterm-insert "\n[multi-line mode] (Use Shift+Enter for new line, 
Ctrl+Enter to send)\n"))))
+
+(defun aidermacs-vterm-send-multi-line ()
+  "Send accumulated multi-line input in vterm."
+  (interactive)
+  (when aidermacs--vterm-multi-line-mode
+    (let ((input (string-trim aidermacs--vterm-multi-line-input)))
+      (setq aidermacs--vterm-multi-line-mode nil
+            aidermacs--vterm-multi-line-input nil)
+      ;; Format and send the input
+      (vterm-send-string (format "{aidermacs\n%s\naidermacs}" input))
+      (vterm-send-return))))
+
+(defun aidermacs-vterm-cancel-multi-line ()
+  "Cancel multiline input mode in vterm."
+  (interactive)
+  (when aidermacs--vterm-multi-line-mode
+    (setq aidermacs--vterm-multi-line-mode nil
+          aidermacs--vterm-multi-line-input nil)
+    (let ((inhibit-read-only t))
+      (vterm-insert "\n[multi-line mode canceled]\n"))))
+
+(defun aidermacs-vterm-reset-multi-line-state ()
+  "Reset multi-line state variables."
+  (setq aidermacs--vterm-multi-line-mode nil
+        aidermacs--vterm-multi-line-input nil))
+
+(defun aidermacs--vterm-cleanup ()
+  "Clean up vterm resources when buffer is killed."
+  (when aidermacs--vterm-active-timer
+    (cancel-timer aidermacs--vterm-active-timer)
+    (setq aidermacs--vterm-active-timer nil))
+  (aidermacs-vterm-reset-multi-line-state))
+
 (provide 'aidermacs-backend-vterm)
 
 ;;; aidermacs-backend-vterm.el ends here

Reply via email to