3434Can be `'left' , `'right' , `'top' , or `'bottom' . This setting will only
3535be used when `eca-chat-use-side-window' is non-nil."
3636 :type '(choice (const :tag " Left" left)
37- (const :tag " Right" right)
38- (const :tag " Top" top)
39- (const :tag " Bottom" bottom))
37+ (const :tag " Right" right)
38+ (const :tag " Top" top)
39+ (const :tag " Bottom" bottom))
4040 :group 'eca )
4141
4242(defcustom eca-chat-window-width 0.40
@@ -164,14 +164,14 @@ Must be a valid model supported by server, check `eca-chat-select-model`."
164164(defcustom eca-chat-diff-tool 'smerge
165165 " Select the method for displaying file-change diffs in ECA chat."
166166 :type '(choice (const :tag " Side-by-side Ediff" ediff)
167- (const :tag " Merge-style Smerge" smerge))
167+ (const :tag " Merge-style Smerge" smerge))
168168 :group 'eca )
169169
170170(defcustom eca-chat-tool-call-prepare-throttle 'smart
171171 " Throttle strategy for handling `toolCallPrepare` events.
172172Possible values: `all` or `smart` (default)."
173173 :type '(choice (const :tag " Process all updates" all)
174- (const :tag " Smart throttle" smart))
174+ (const :tag " Smart throttle" smart))
175175 :group 'eca )
176176
177177(defcustom eca-chat-tool-call-prepare-update-interval 5
@@ -441,6 +441,11 @@ Must be a positive integer."
441441 (eca-dissoc 'empty )))
442442 empty-chat-buffer)))
443443
444+ (defun eca-chat--insert (&rest contents )
445+ " Insert CONTENTS reseting undo-list to avoid buffer inconsistencies."
446+ (apply #'insert contents)
447+ (setq-local buffer-undo-list nil ))
448+
444449(defmacro eca-chat--allow-write (&rest body )
445450 " Execute BODY allowing write to buffer."
446451 `(let ((inhibit-read-only t ))
@@ -569,15 +574,15 @@ Must be a positive integer."
569574 " Insert the prompt and context string adding overlay metadatas."
570575 (let ((prompt-area-ov (make-overlay (line-beginning-position ) (1+ (line-beginning-position )) (current-buffer ))))
571576 (overlay-put prompt-area-ov 'eca-chat-prompt-area t ))
572- (insert eca-chat-prompt-separator)
577+ (eca-chat-- insert eca-chat-prompt-separator)
573578 (let ((progress-area-ov (make-overlay (line-beginning-position ) (line-end-position ) (current-buffer ) nil t )))
574579 (overlay-put progress-area-ov 'eca-chat-progress-area t )
575- (insert " \n " )
580+ (eca-chat-- insert " \n " )
576581 (move-overlay progress-area-ov (overlay-start progress-area-ov) (1- (overlay-end progress-area-ov))))
577582 (let ((context-area-ov (make-overlay (line-beginning-position ) (line-end-position ) (current-buffer ) nil t )))
578583 (overlay-put context-area-ov 'eca-chat-context-area t )
579- (insert (propertize eca-chat-context-prefix 'font-lock-face 'eca-chat-context-unlinked-face ))
580- (insert " \n " )
584+ (eca-chat-- insert (propertize eca-chat-context-prefix 'font-lock-face 'eca-chat-context-unlinked-face ))
585+ (eca-chat-- insert " \n " )
581586 (move-overlay context-area-ov (overlay-start context-area-ov) (1- (overlay-end context-area-ov))))
582587 (let ((prompt-field-ov (make-overlay (line-beginning-position ) (1+ (line-beginning-position )) (current-buffer ))))
583588 (overlay-put prompt-field-ov 'eca-chat-prompt-field t )
@@ -587,7 +592,7 @@ Must be a positive integer."
587592 " Clear the chat for SESSION."
588593 (erase-buffer )
589594 (remove-overlays (point-min ) (point-max ))
590- (insert " \n " )
595+ (eca-chat-- insert " \n " )
591596 (eca-chat--insert-prompt-string)
592597 (eca-chat--refresh-context))
593598
@@ -615,7 +620,7 @@ Otherwise to a not loading state."
615620 (overlay-put prompt-field-ov 'before-string (propertize eca-chat-prompt-prefix-loading 'font-lock-face 'default ))
616621 (save-excursion
617622 (goto-char (overlay-start prompt-field-ov))
618- (insert stop-text)))
623+ (eca-chat-- insert stop-text)))
619624 (progn
620625 (overlay-put prompt-field-ov 'before-string (propertize eca-chat-prompt-prefix 'font-lock-face 'eca-chat-prompt-prefix-face ))
621626 (save-excursion
@@ -626,7 +631,7 @@ Otherwise to a not loading state."
626631 " Set the chat prompt to be TEXT."
627632 (-some-> (eca-chat--prompt-field-start-point) (goto-char ))
628633 (delete-region (point ) (point-max ))
629- (insert text))
634+ (eca-chat-- insert text))
630635
631636(defun eca-chat--cycle-history (n )
632637 " Cycle history by N."
@@ -650,7 +655,7 @@ Otherwise to a not loading state."
650655 " Insert a newline character at point."
651656 (interactive )
652657 (when (>= (point ) (eca-chat--prompt-field-start-point))
653- (insert " \n " )))
658+ (eca-chat-- insert " \n " )))
654659
655660(defun eca-chat--prompt-field-ov ()
656661 " Return the overlay for the prompt field."
@@ -983,7 +988,7 @@ If `eca-chat-focus-on-open' is non-nil, the window is selected."
983988 (when eca-chat--last-user-message-pos
984989 (save-excursion
985990 (goto-char eca-chat--last-user-message-pos)
986- (insert content))))
991+ (eca-chat-- insert content))))
987992
988993(defun eca-chat--add-text-content (text &optional overlay-key overlay-value )
989994 " Add TEXT to the chat current position.
@@ -997,7 +1002,7 @@ Add a overlay before with OVERLAY-KEY = OVERLAY-VALUE if passed."
9971002 (overlay-put ov overlay-key overlay-value)
9981003 (when (eq overlay-key 'eca-chat--user-message-id )
9991004 (overlay-put ov 'eca-chat--timestamp (float-time )))))
1000- (insert text)
1005+ (eca-chat-- insert text)
10011006 (point ))))
10021007
10031008(defun eca-chat--expandable-content-at-point ()
@@ -1035,21 +1040,21 @@ Applies LABEL-FACE to label and CONTENT-FACE to content."
10351040 (let* ((context-start (eca-chat--prompt-area-start-point))
10361041 (start-point (1- context-start)))
10371042 (goto-char start-point)
1038- (unless (bolp ) (insert " \n " ))
1043+ (unless (bolp ) (eca-chat-- insert " \n " ))
10391044 (let ((ov-label (make-overlay (point ) (point ) (current-buffer ))))
10401045 (overlay-put ov-label 'eca-chat--expandable-content-id id)
10411046 (overlay-put ov-label 'eca-chat--expandable-content-toggle nil )
1042- (insert (propertize (eca-chat--propertize-only-first-word label
1043- 'line-prefix (unless (string-empty-p content)
1044- eca-chat-expandable-block-open-symbol))
1045- 'keymap (let ((km (make-sparse-keymap )))
1046- (define-key km (kbd " <mouse-1>" ) (lambda () (eca-chat--expandable-content-toggle id)))
1047- (define-key km (kbd " <tab>" ) (lambda () (eca-chat--expandable-content-toggle id)))
1048- km)
1049- 'help-echo " mouse-1 / tab / RET: expand/collapse" ))
1050- (insert " \n " )
1047+ (eca-chat-- insert (propertize (eca-chat--propertize-only-first-word label
1048+ 'line-prefix (unless (string-empty-p content)
1049+ eca-chat-expandable-block-open-symbol))
1050+ 'keymap (let ((km (make-sparse-keymap )))
1051+ (define-key km (kbd " <mouse-1>" ) (lambda () (eca-chat--expandable-content-toggle id)))
1052+ (define-key km (kbd " <tab>" ) (lambda () (eca-chat--expandable-content-toggle id)))
1053+ km)
1054+ 'help-echo " mouse-1 / tab / RET: expand/collapse" ))
1055+ (eca-chat-- insert " \n " )
10511056 (let* ((start-point (point ))
1052- (_ (insert " \n " ))
1057+ (_ (eca-chat-- insert " \n " ))
10531058 (ov-content (make-overlay start-point start-point (current-buffer ) nil t )))
10541059 (overlay-put ov-content 'eca-chat--expandable-content-content (propertize content 'line-prefix " " ))
10551060 (overlay-put ov-label 'eca-chat--expandable-content-ov-content ov-content))))))
@@ -1070,23 +1075,23 @@ Applies LABEL-FACE to label and CONTENT-FACE to content."
10701075 ; ; Refresh the label line (cheap even when appending)
10711076 (goto-char (overlay-start ov-label))
10721077 (delete-region (point ) (1- (overlay-start ov-content)))
1073- (insert (propertize (eca-chat--propertize-only-first-word label
1074- 'line-prefix (unless (string-empty-p new-content)
1075- (if open?
1076- eca-chat-expandable-block-close-symbol
1077- eca-chat-expandable-block-open-symbol)))
1078- 'help-echo " mouse-1 / RET / tab: expand/collapse" ))
1078+ (eca-chat-- insert (propertize (eca-chat--propertize-only-first-word label
1079+ 'line-prefix (unless (string-empty-p new-content)
1080+ (if open?
1081+ eca-chat-expandable-block-close-symbol
1082+ eca-chat-expandable-block-open-symbol)))
1083+ 'help-echo " mouse-1 / RET / tab: expand/collapse" ))
10791084 (when open?
10801085 (if append-content?
10811086 ; ; Fast path: just append the delta to the visible content
10821087 (progn
10831088 (goto-char (overlay-end ov-content))
1084- (insert delta))
1089+ (eca-chat-- insert delta))
10851090 ; ; Replace the whole visible content
10861091 (progn
10871092 (delete-region (overlay-start ov-content) (overlay-end ov-content))
10881093 (goto-char (overlay-start ov-content))
1089- (insert new-content))))))))
1094+ (eca-chat-- insert new-content))))))))
10901095
10911096(defun eca-chat--expandable-content-toggle (id &optional force? close? )
10921097 " Toggle the expandable-content of ID.
@@ -1113,7 +1118,7 @@ If FORCE? decide to CLOSE? or not."
11131118 (put-text-property (point ) (line-end-position )
11141119 'line-prefix eca-chat-expandable-block-close-symbol)
11151120 (goto-char (overlay-start ov-content))
1116- (insert content " \n " )
1121+ (eca-chat-- insert content " \n " )
11171122 (overlay-put ov-label 'eca-chat--expandable-content-toggle t ))))
11181123 close?) ))
11191124
@@ -1182,11 +1187,11 @@ Show parent upwards if HIDE-FILENAME? is non nil."
11821187 (let ((ov (eca-chat--prompt-progress-field-ov)))
11831188 (goto-char (overlay-start ov))
11841189 (delete-region (point ) (overlay-end ov)))
1185- (insert (propertize (if (string-empty-p eca-chat--progress-text)
1186- eca-chat-prompt-separator
1187- (concat eca-chat-prompt-separator " \n " eca-chat--progress-text))
1188- 'font-lock-face 'eca-chat-system-messages-face )
1189- eca-chat--spinner-string)))))
1190+ (eca-chat-- insert (propertize (if (string-empty-p eca-chat--progress-text)
1191+ eca-chat-prompt-separator
1192+ (concat eca-chat-prompt-separator " \n " eca-chat--progress-text))
1193+ 'font-lock-face 'eca-chat-system-messages-face )
1194+ eca-chat--spinner-string)))))
11901195
11911196(defun eca-chat--context->str (context &optional static? )
11921197 " Convert CONTEXT to a presentable str in buffer.
@@ -1263,9 +1268,9 @@ If STATIC? return strs with no dynamic values."
12631268 (goto-char ))
12641269 (delete-region (point ) (line-end-position ))
12651270 (seq-doseq (context eca-chat--context)
1266- (insert (eca-chat--context->str context))
1267- (insert " " ))
1268- (insert (propertize eca-chat-context-prefix 'font-lock-face 'eca-chat-context-unlinked-face ))))
1271+ (eca-chat-- insert (eca-chat--context->str context))
1272+ (eca-chat-- insert " " ))
1273+ (eca-chat-- insert (propertize eca-chat-context-prefix 'font-lock-face 'eca-chat-context-unlinked-face ))))
12691274
12701275(defconst eca-chat--kind->symbol
12711276 '((" file" . file)
@@ -1321,7 +1326,7 @@ If STATIC? return strs with no dynamic values."
13211326
13221327(defun eca-chat--completion-prompts-annotate (item-label )
13231328 " Annotate prompt ITEM-LABEL."
1324- (-let (((&plist :description description :arguments args)
1329+ (-let (((&plist :description description :arguments args)
13251330 (get-text-property 0 'eca-chat-completion-item item-label)))
13261331 (concat " (" (string-join (--map (plist-get it :name ) args) " , " )
13271332 " ) "
@@ -1341,8 +1346,8 @@ Add text property to prompt text to match context."
13411346 (search-backward eca-chat-context-prefix (line-beginning-position ) t )))
13421347 (end-pos (point )))
13431348 (delete-region start-pos end-pos)
1344- (insert (eca-chat--context->str context 'static ))))
1345- (insert " " ))
1349+ (eca-chat-- insert (eca-chat--context->str context 'static ))))
1350+ (eca-chat-- insert " " ))
13461351
13471352(defun eca-chat--completion-file-from-prompt-exit-function (item _status )
13481353 " Add to files the selected ITEM."
@@ -1351,23 +1356,23 @@ Add text property to prompt text to match context."
13511356 (search-backward eca-chat-filepath-prefix (line-beginning-position ) t )))
13521357 (end-pos (point )))
13531358 (delete-region start-pos end-pos)
1354- (insert (eca-chat--filepath->str (plist-get file :path ) nil )))
1355- (insert " " ))
1359+ (eca-chat-- insert (eca-chat--filepath->str (plist-get file :path ) nil )))
1360+ (eca-chat-- insert " " ))
13561361
13571362(defun eca-chat--completion-prompt-exit-function (item _status )
13581363 " Finish prompt completion for ITEM."
13591364 (-let* (((&plist :arguments arguments) (get-text-property 0 'eca-chat-completion-item item)))
13601365 (when (> (length arguments) 0 )
13611366 (seq-doseq (arg arguments)
13621367 (-let (((&plist :name name :description description :required required) arg))
1363- (insert " " )
1368+ (eca-chat-- insert " " )
13641369 (let ((arg-text (read-string (format " Arg: %s \n Description: %s \n Value%s : "
13651370 name
13661371 description
13671372 (if required " " " (leave blank for default)" )))))
13681373 (if (and arg-text (string-match-p " " arg-text))
1369- (insert (format " \" %s \" " arg-text))
1370- (insert arg-text)))))
1374+ (eca-chat-- insert (format " \" %s \" " arg-text))
1375+ (eca-chat-- insert arg-text)))))
13711376 (end-of-line ))))
13721377
13731378(defun eca-chat--context-to-completion (context )
@@ -1548,8 +1553,8 @@ string."
15481553 (goto-char (eca-chat--prompt-field-start-point))
15491554 (goto-char (line-end-position ))
15501555 (when (= (line-beginning-position ) (line-end-position ))
1551- (insert " " ))
1552- (insert text)))
1556+ (eca-chat-- insert " " ))
1557+ (eca-chat-- insert text)))
15531558
15541559; ; Public
15551560
@@ -1562,7 +1567,6 @@ string."
15621567 (read-only-mode -1 )
15631568 (setq-local eca-chat--history '())
15641569 (setq-local eca-chat--history-index -1 )
1565- (buffer-disable-undo )
15661570
15671571 ; ; Show diff blocks in markdown-mode with colors.
15681572 (setq-local markdown-fontify-code-blocks-natively t )
@@ -1582,9 +1586,9 @@ string."
15821586 (when (eq 0 (length (string-trim (buffer-string ))))
15831587 (save-excursion
15841588 (goto-char (point-min ))
1585- (insert " \n " )
1586- (insert (propertize (eca--session-chat-welcome-message session)
1587- 'font-lock-face 'eca-chat-welcome-face ))
1589+ (eca-chat-- insert " \n " )
1590+ (eca-chat-- insert (propertize (eca--session-chat-welcome-message session)
1591+ 'font-lock-face 'eca-chat-welcome-face ))
15881592 (eca-chat--insert-prompt-string)))
15891593
15901594 ; ; TODO is there a better way to do that?
@@ -1729,8 +1733,8 @@ Calls CB with the resulting message."
17291733 (cond
17301734 ((eq action 'metadata )
17311735 '(metadata (category . eca-capf)
1732- (display-sort-function . identity)
1733- (cycle-sort-function . identity)))
1736+ (display-sort-function . identity)
1737+ (cycle-sort-function . identity)))
17341738 ((eq (car-safe action) 'boundaries ) nil )
17351739 (t
17361740 (complete-with-action action (funcall candidates-fn) probe pred))))
@@ -2419,7 +2423,7 @@ if ARG is current prefix, ask for file, otherwise drop current file."
24192423 (line-beginning-position )
24202424 (line-end-position ))))
24212425 (eca-chat--with-current-buffer (eca-chat--get-last-buffer session)
2422- (insert transcription)
2426+ (eca-chat-- insert transcription)
24232427 (newline )
24242428 (eca-chat--key-pressed-return))))
24252429 nil t )
@@ -2435,7 +2439,7 @@ If MSG has :timestamp, prepends [HH:MM] to the text."
24352439 (let ((timestamp (plist-get msg :timestamp ))
24362440 (text (plist-get msg :text )))
24372441 (if timestamp
2438- (format " [%s ] %s "
2442+ (format " [%s ] %s "
24392443 (format-time-string " %H:%M" timestamp)
24402444 text)
24412445 text)))
@@ -2488,12 +2492,12 @@ Returns selected message plist or nil if no messages or cancelled."
24882492 (dolist (msg (reverse messages))
24892493 (puthash (eca-chat--format-message-for-completion msg) msg table))
24902494 (when-let ((choice (completing-read
2491- prompt
2492- (lambda (string pred action )
2493- (if (eq action 'metadata )
2494- `(metadata (display-sort-function . identity))
2495- (complete-with-action action (hash-table-keys table) string pred)))
2496- nil t )))
2495+ prompt
2496+ (lambda (string pred action )
2497+ (if (eq action 'metadata )
2498+ `(metadata (display-sort-function . identity))
2499+ (complete-with-action action (hash-table-keys table) string pred)))
2500+ nil t )))
24972501 (gethash choice table)))))
24982502
24992503;;;### autoload
0 commit comments