Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

- Bugfix: a forward deletion of the first prompt character now works instead of dinging. `eca-chat--key-pressed-deletion` guarded the prompt boundary by rejecting any deletion at the prompt-field start regardless of direction, so `C-d` and, under evil, the `~` (`evil-invert-char`), `r`, `x` and `s` operators left the first char in place (`~` prepended the inverted char instead of replacing it). At the prompt-field start the guard now blocks only backward-deleting commands (`delete-backward-char`, `backward-kill-word`, `evil-delete-backward-char`, ...); deletions above the prompt (context/progress area) and everywhere else are unchanged.
- Show the shell/git command breakdown in tool call approvals: raw command plus per-command lines colored by remember state (green saved, yellow not yet), and `Accept and remember` lists the command keys it would newly save (e.g. `git status, rg`), hiding when nothing new would be remembered.

- Support TRAMP / remote hosts: the eca server starts on the remote host, the binary is resolved from the remote `PATH`, and paths are translated both ways using mappings derived from TRAMP workspace folders (explicit `eca-local-to-remote-prefix-map` entries win). Auto-install is local only: install `eca` on the remote or point `eca-custom-command` at it. #270
Expand Down
31 changes: 29 additions & 2 deletions eca-chat.el
Original file line number Diff line number Diff line change
Expand Up @@ -1857,6 +1857,23 @@ scrolling is suppressed so the view does not jump."
(-some-> (eca-chat--prompt-context-field-ov)
(overlay-start)))

(defconst eca-chat--prompt-boundary-guard-commands
'(delete-backward-char
backward-delete-char
backward-delete-char-untabify
backward-kill-word
evil-delete-backward-word
evil-delete-back-to-indentation
evil-delete-backward-char)
"Backward-deleting commands blocked at the prompt-field start.
At the start itself, `eca-chat--key-pressed-deletion' dings only for
these commands (and for a negative delete count). Forward deletions
there - `delete-char' with a positive count, as used by
`evil-invert-char' (~), `evil-replace' (r), `evil-delete-char' (x) and
`evil-substitute' (s) - remove the first prompt character and fall
through to the wrapped command. Above the prompt field the guard is
unconditional.")

(defun eca-chat--key-pressed-deletion (side-effect-fn &rest args)
"Apply SIDE-EFFECT-FN with ARGS before point.
Unless at the prompt field boundary.
Expand Down Expand Up @@ -1896,9 +1913,19 @@ the prompt/context line."
(setf (nth 2 args) nil) ;; do not delete prompt line passing nil argument
(apply side-effect-fn args))

;; start of the prompt
;; start of the prompt - keep the guard everywhere above the
;; prompt-field start, but at the start itself block only *backward*
;; deletions. A forward deletion there (`delete-char' from
;; evil-invert-char ~, evil-replace r, evil-delete-char x,
;; evil-substitute s, or C-d) removes the first prompt char, which
;; is legitimate; everything else is unchanged. A negative count
;; (e.g. `C-u - C-d') makes `delete-char' delete backward, so still
;; guard that.
((and prompt-ov
(or (<= (point) (overlay-start prompt-ov))
(or (< (point) (overlay-start prompt-ov))
(and (= (point) (overlay-start prompt-ov))
(or (memq this-command eca-chat--prompt-boundary-guard-commands)
(and (integerp (car args)) (< (car args) 0))))
(and (eq 'backward-kill-word this-command)
(string-blank-p (buffer-substring-no-properties
(overlay-start prompt-ov) (point))))))
Expand Down
26 changes: 25 additions & 1 deletion test/eca-chat-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,31 @@ does not treat the first line as metadata. Returns FN's value."
(expect side-effect-called :to-be nil)
(expect (eca-chat-test--prompt-text buf)
:to-equal "hello")))
(kill-buffer buf))))))
(kill-buffer buf))))

(it "allows forward deletion at prompt field start"
;; Regression: a forward `delete-char' at the prompt start - plain C-d
;; and, under evil, `evil-invert-char' (~), `evil-replace' (r),
;; `evil-delete-char' (x) and `evil-substitute' (s) - must remove the
;; first prompt char. The boundary guard used to block it too, so ~
;; prepended the inverted char instead of replacing it in place.
(dolist (cmd '(delete-char evil-invert-char evil-replace
evil-delete-char evil-substitute))
(let ((buf (eca-chat-test--make-prompt-buffer "hello")))
(unwind-protect
(with-current-buffer buf
(goto-char (eca-chat--prompt-field-start-point))
(let ((this-command cmd)
(side-effect-called nil))
(eca-chat--key-pressed-deletion
(lambda (n &optional _)
(setq side-effect-called t)
(delete-char n))
1)
(expect side-effect-called :to-be t)
(expect (eca-chat-test--prompt-text buf)
:to-equal "ello")))
(kill-buffer buf)))))))

(describe "eca-chat--protect-non-prompt"

Expand Down
Loading