branch: elpa/aidermacs commit 7a55f48c2836a363ebf8829a8953030f0de9b607 Author: Mingde (Matthew) Zeng <matthew...@posteo.net> Commit: Mingde (Matthew) Zeng <matthew...@posteo.net>
Move fontlock to aidermacs-comint and output management Signed-off-by: Mingde (Matthew) Zeng <matthew...@posteo.net> --- aidermacs-backend-comint.el | 183 ++++++++++++++++++++++++++++++++++++++++++ aidermacs-backend-vterm.el | 66 +++++++++++++--- aidermacs-backends.el | 49 +++++++++++- aidermacs-models.el | 2 +- aidermacs.el | 189 +------------------------------------------- 5 files changed, 290 insertions(+), 199 deletions(-) diff --git a/aidermacs-backend-comint.el b/aidermacs-backend-comint.el index 55c21eaa72..c03e298129 100644 --- a/aidermacs-backend-comint.el +++ b/aidermacs-backend-comint.el @@ -13,6 +13,185 @@ This allows for multi-line input without sending the command." :type 'string :group 'aidermacs) +(defface aidermacs-command-separator + '((((type graphic)) :strike-through t :extend t) + (((type tty)) :inherit font-lock-comment-face :underline t :extend t)) + "Face for command separator in aidermacs." + :group 'aidermacs) + +(defface aidermacs-command-text + '((t :inherit bold)) + "Face for commands sent to aidermacs buffer." + :group 'aidermacs) + +(defface aidermacs-search-replace-block + '((t :inherit 'diff-refine-added :bold t)) + "Face for search/replace block content." + :group 'aidermacs) + + +(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 'aidermacs-search-replace-block t) + ("^<<<<<<< SEARCH" 0 'aidermacs-search-replace-block t) + ("^\\(```\\)\\([^[:space:]]*\\)" (1 'shadow t) (2 font-lock-builtin-face t)) + ("^=======$" 0 'aidermacs-search-replace-block t)) + "Font lock keywords for aidermacs buffer.") + +(defvar-local aidermacs--font-lock-buffer nil + "Temporary buffer for fontification.") + +(defun aidermacs--comint-output-filter (output) + "Filter function for comint OUTPUT." + (when (not (string-empty-p output)) + (aidermacs--store-output output))) + +(defun aidermacs-reset-font-lock-state () + "Reset font lock state to default for processing another a new src block." + (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 aidermacs--block-mode nil)) + (setq aidermacs--block-end-marker nil + aidermacs--last-output-start nil + aidermacs--block-start nil + aidermacs--block-end nil)) + +(defun aidermacs-fontify-blocks (_output) + "Fontify search/replace blocks in comint output." + (save-excursion + (goto-char (or aidermacs--last-output-start + comint-last-output-start)) + (beginning-of-line) + + ;; Continue processing existing block if we're in one + (when aidermacs--block-start + (aidermacs--fontify-block)) + + (setq aidermacs--last-output-start nil) + ;; Look for new blocks if we're not in one + (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) 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 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\\)" 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 aidermacs--last-output-start comint-last-output-start))))) + + (unless aidermacs--last-output-start + ;; Set up new block state + (setq aidermacs--block-end-marker + (pcase (match-string 1) + ((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 aidermacs--block-mode)) + (with-current-buffer aidermacs--font-lock-buffer + (erase-buffer) + (unless (eq mode major-mode) + (condition-case e + (let ((inhibit-message t)) + (funcall mode)) + (error "aidermacs: failed to init major-mode `%s' for font-locking: %s" mode e))))) + + ;; Process initial content + (aidermacs--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 aidermacs--block-end) + end-of-block-p) + + (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)) 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 + 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) + (or + ;; check if the block has a language id + (when (let ((re "^```\\([^[:space:]]+\\)")) + (or (looking-at re) + (save-excursion + (forward-line -1) + ;; check the previous line since this might be a SEARCH block + (looking-at re)))) + (let* ((lang (downcase (match-string 1))) + (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)))) + aidermacs--block-mode + 'fundamental-mode))) + (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")) @@ -25,6 +204,7 @@ This allows for multi-line input without sending the command." (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) + (add-hook 'comint-output-filter-functions #'aidermacs--comint-output-filter) (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) @@ -37,6 +217,9 @@ 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)) + ;; Store command before sending + (setq aidermacs--last-command command + aidermacs--current-output nil) (goto-char (process-mark process)) (insert (propertize command 'face 'aidermacs-command-text diff --git a/aidermacs-backend-vterm.el b/aidermacs-backend-vterm.el index 49d1373dcb..8db110210d 100644 --- a/aidermacs-backend-vterm.el +++ b/aidermacs-backend-vterm.el @@ -5,10 +5,53 @@ ;;; Code: -(require 'vterm nil t) +(require 'vterm nil 'noerror) + +(defun aidermacs--vterm-output-advice (orig-fun &rest args) + "Capture output before and after executing `vterm-send-return'. +This advice records the current prompt position as START-POINT, +calls ORIG-FUN (with ARGS) and then waits until the expected finish +sequence appears. The expected finish sequence is defined as the +substring from (vterm--get-prompt-point) minus the length of the sequence +to (vterm--get-prompt-point). For example, if + + (buffer-substring-no-properties (- (vterm--get-prompt-point) 1) + (+ (vterm--get-prompt-point) 2)) +yields \"\n> \", +then the expected finish sequence is \"\n> \" (12 characters). + +Once that is detected, the output from START-POINT up to the beginning +of the finish sequence is captured and stored via `aidermacs--store-output`." + (let ((start-point (vterm--get-prompt-point)) + (proc (get-buffer-process (current-buffer))) + (expected "\n> ")) + ;; Store the original process filter + (let ((orig-filter (process-filter proc))) + ;; Set up our temporary filter + (set-process-filter + proc + (lambda (proc string) + ;; Call original filter first + (funcall orig-filter proc string) + ;; Then check for our finish sequence + (let* ((end-point (vterm--get-prompt-point)) + (seq-start (max 0 (- end-point 1))) + (seq-end (min (point-max) (+ end-point 2))) + (finish-seq (buffer-substring-no-properties seq-start seq-end))) + (when (and (string= finish-seq expected) + (not (= start-point end-point))) + ;; We found our finish sequence + (let ((output (buffer-substring-no-properties start-point end-point))) + (aidermacs--store-output (string-trim output)) + ;; Restore original filter + (set-process-filter proc orig-filter)))))) + ;; Execute original function + (apply orig-fun args)))) (defun aidermacs-run-aidermacs-vterm (program args buffer-name) - "Create a vterm-based buffer and run aidermacs PROGRAM with ARGS in BUFFER-NAME." + "Create a vterm-based buffer and run aidermacs PROGRAM with ARGS in BUFFER-NAME. +PROGRAM is the command to run, ARGS is a list of arguments, +and BUFFER-NAME is the name of the vterm buffer." (unless (require 'vterm nil t) (error "vterm package is not available")) (unless (get-buffer buffer-name) @@ -18,21 +61,26 @@ (cmd (mapconcat 'identity (append (list program mode) args) " ")) (vterm-buffer-name-orig vterm-buffer-name) (vterm-shell-orig vterm-shell)) + ;; Temporarily set globals so that the new buffer uses our values. (setq vterm-buffer-name buffer-name) (setq vterm-shell cmd) - (if (get-buffer buffer-name) - (switch-to-buffer buffer-name) - (with-current-buffer (vterm-other-window) - (aidermacs-minor-mode 1))) + (with-current-buffer (vterm-other-window) + (aidermacs-minor-mode 1) + (advice-add 'vterm-send-return :around #'aidermacs--vterm-output-advice) + ) + ;; Restore the original globals. (setq vterm-buffer-name vterm-buffer-name-orig) - (setq vterm-shell vterm-shell-orig)))) + (setq vterm-shell vterm-shell-orig))) + buffer-name) (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." +If SWITCH-TO-BUFFER is non-nil, switch to BUFFER after sending the command." (with-current-buffer buffer (vterm-send-string command) - (vterm-send-return))) + (vterm-send-return) + (when switch-to-buffer + (switch-to-buffer buffer)))) (provide 'aidermacs-backend-vterm) diff --git a/aidermacs-backends.el b/aidermacs-backends.el index 44093553e7..1c10c30a7f 100644 --- a/aidermacs-backends.el +++ b/aidermacs-backends.el @@ -5,6 +5,10 @@ ;;; Code: +(require 'aidermacs-backend-comint) +(when (require 'vterm nil 'noerror) + (require 'aidermacs-backend-vterm)) + (defgroup aidermacs-backends nil "Backend customization for aidermacs." :group 'aidermacs) @@ -18,11 +22,50 @@ of using a comint process." (const :tag "VTerm" vterm)) :group 'aidermacs-backends) +;; Core output management functionality +(defgroup aidermacs-output nil + "Output handling for aidermacs." + :group 'aidermacs) -(require 'aidermacs-backend-comint) -(when (require 'vterm nil t) - (require 'aidermacs-backend-vterm)) +(defcustom aidermacs-output-limit 10 + "Maximum number of output entries to keep in history." + :type 'integer + :group 'aidermacs-output) + +(defvar-local aidermacs--output-history nil + "List to store aidermacs output history. +Each entry is a cons cell (timestamp . output-text).") + +(defvar-local aidermacs--last-command nil + "Store the last command sent to aidermacs.") + +(defvar-local aidermacs--current-output nil + "Accumulator for current output being captured.") + +(defun aidermacs-get-output-history (&optional limit) + "Get the output history, optionally limited to LIMIT entries. +Returns a list of (timestamp . output-text) pairs, most recent first." + (let ((history aidermacs--output-history)) + (if limit + (seq-take history limit) + history))) + +(defun aidermacs-get-last-output () + "Get the most recent output from aidermacs." + (car (aidermacs-get-output-history 1))) + +(defun aidermacs-clear-output-history () + "Clear the output history." + (interactive) + (setq aidermacs--output-history nil)) +(defun aidermacs--store-output (output) + "Store OUTPUT in the history with timestamp." + (setq aidermacs--current-output output) + (push (cons (current-time) output) aidermacs--output-history) + (when (> (length aidermacs--output-history) aidermacs-output-limit) + (setq aidermacs--output-history + (seq-take aidermacs--output-history aidermacs-output-limit)))) ;; Backend dispatcher functions (defun aidermacs-run-aidermacs-backend (program args buffer-name) diff --git a/aidermacs-models.el b/aidermacs-models.el index 0cd13520e7..464cc3bbbe 100644 --- a/aidermacs-models.el +++ b/aidermacs-models.el @@ -12,7 +12,7 @@ (defcustom aidermacs-popular-models '("anthropic/claude-3-5-sonnet-20241022" ;; really good in practical "o3-mini" ;; very powerful - "gemini/gemini-2.0-flash" ;; free + "gemini/gemini-2.0-pro-exp-02-05" ;; 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 ) diff --git a/aidermacs.el b/aidermacs.el index 804abd42f6..ca362976b6 100644 --- a/aidermacs.el +++ b/aidermacs.el @@ -71,31 +71,6 @@ This is the file name without path." (lambda () (add-to-list 'savehist-additional-variables 'aidermacs-read-string-history)))) -(defface aidermacs-command-separator - '((((type graphic)) :strike-through t :extend t) - (((type tty)) :inherit font-lock-comment-face :underline t :extend t)) - "Face for command separator in aidermacs." - :group 'aidermacs) - -(defface aidermacs-command-text - '((t :inherit bold)) - "Face for commands sent to aidermacs buffer." - :group 'aidermacs) - -(defface aidermacs-search-replace-block - '((t :inherit 'diff-refine-added :bold t)) - "Face for search/replace block content." - :group 'aidermacs) - -(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 'aidermacs-search-replace-block t) - ("^<<<<<<< SEARCH" 0 'aidermacs-search-replace-block t) - ("^\\(```\\)\\([^[:space:]]*\\)" (1 'shadow t) (2 font-lock-builtin-face t)) - ("^=======$" 0 'aidermacs-search-replace-block t)) - "Font lock keywords for aidermacs buffer.") ;;;###autoload (defun aidermacs-plain-read-string (prompt &optional initial-input) @@ -222,8 +197,9 @@ With the universal argument, prompt to edit aidermacs-args before running." (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))) + (if (get-buffer buffer-name) + (aidermacs-switch-to-buffer) + (aidermacs-run-aidermacs-backend aidermacs-program current-args buffer-name)))) (defun aidermacs--send-command (command &optional switch-to-buffer) "Send COMMAND to the corresponding aidermacs process after performing necessary checks. @@ -273,149 +249,6 @@ Dispatches to the appropriate backend." (defvar aidermacs-block-re (format "^\\(?:\\(?1:%s\\|%s\\)\\|\\(?1:%s\\).+\\)$" aidermacs-search-marker aidermacs-diff-marker aidermacs-fence-marker)) -(defun aidermacs-reset-font-lock-state () - "Reset font lock state to default for processing another a new src block." - (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 aidermacs--block-mode nil)) - (setq aidermacs--block-end-marker nil - aidermacs--last-output-start nil - aidermacs--block-start nil - aidermacs--block-end nil)) - -(defun aidermacs-fontify-blocks (_output) - "fontify search/replace blocks in comint output." - (save-excursion - (goto-char (or aidermacs--last-output-start - comint-last-output-start)) - (beginning-of-line) - - ;; Continue processing existing block if we're in one - (when aidermacs--block-start - (aidermacs--fontify-block)) - - (setq aidermacs--last-output-start nil) - ;; Look for new blocks if we're not in one - (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) 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 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\\)" 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 aidermacs--last-output-start comint-last-output-start))))) - - (unless aidermacs--last-output-start - ;; Set up new block state - (setq aidermacs--block-end-marker - (pcase (match-string 1) - ((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 aidermacs--block-mode)) - (with-current-buffer aidermacs--font-lock-buffer - (erase-buffer) - (unless (eq mode major-mode) - (condition-case e - (let ((inhibit-message t)) - (funcall mode)) - (error "aidermacs: failed to init major-mode `%s' for font-locking: %s" mode e))))) - - ;; Process initial content - (aidermacs--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 aidermacs--block-end) - end-of-block-p) - - (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)) 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 - 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) - (or - ;; check if the block has a language id - (when (let ((re "^```\\([^[:space:]]+\\)")) - (or (looking-at re) - (save-excursion - (forward-line -1) - ;; check the previous line since this might be a SEARCH block - (looking-at re)))) - (let* ((lang (downcase (match-string 1))) - (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)))) - aidermacs--block-mode - 'fundamental-mode))) ;; Function to switch to the aidermacs buffer ;;;###autoload @@ -451,22 +284,6 @@ If the current buffer is already the aidermacs buffer, do nothing." (interactive) (aidermacs--send-command "/exit")) -(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." - (with-current-buffer buffer - (let ((process (get-buffer-process buffer)) - (inhibit-read-only t)) - (goto-char (process-mark process)) - ;; Insert text with proper face properties - (insert (propertize text - '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 aidermacs--process-message-if-multi-line (str) "Entering multi-line chat messages