Skip to content
Open
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

- Respect `eca-chat-window-side` (and width/height) even when `eca-chat-use-side-window` is nil, displaying the chat on that side via `display-buffer-in-direction`. To avoid overlapping/duplicate windows, placement now reuses the window already showing the chat, otherwise replaces another visible chat window in place (new-tab behavior), and only opens a new window when there is nothing to reuse.
- Bugfix: keep the `:usage` and `:trust` mode-line segments visible after adding the context-usage bar. The right-alignment reserved space from `(length right)`, which counts the bar's pixel-width `display` spaces (and wide glyphs) as ~1 char each, so the segments overflowed off the right edge. It now measures the real rendered width via `string-pixel-width` and aligns flush to the right edge in pixels (Emacs 29+ only; on Emacs 28 right segments follow left without alignment).
- Bugfix: closing a chat (`kill-buffer`, `C-c C-k`, or the tab close button) now switches the chat window to a sibling chat (the previous tab, or the only one left) instead of falling back to an unrelated buffer like the settings buffer, and drops the dead chat from the session registry. `C-c C-k` (`eca-chat-reset`) only starts a fresh chat when the closed chat was the last one.
- Add `eca-chat-copy-at-point`, bound to `C-c C-w`. It copies the fenced code block at point, the assistant response at point, or the latest response as a fallback.
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,10 @@ Chat
- `eca-chat-parent-mode`: Set major-mode of chat parent, can be `markdown-mode`, `markdown-view-mode` or `gfm-view-mode` (default)
- `eca-chat-mode-hook`: Hooks to run after entering `eca-chat-mode`.
- `eca-chat-finished-hook`: Hooks to run after finishing a chat prompt.
- `eca-chat-use-side-window`: Whether the chat buffer is displayed in a side window or a normal window.
- `eca-chat-window-side`: On which side (`left`, `right`, `top`, `bottom`) the chat side window appears.
- `eca-chat-window-width`: Width of the chat side window when on the left or right.
- `eca-chat-window-height`: Height of the chat side window when on the top or bottom.
- `eca-chat-use-side-window`: Whether the chat buffer is displayed in a dedicated side window or a normal window. Either way it is placed on `eca-chat-window-side`.
- `eca-chat-window-side`: On which side (`left`, `right`, `top`, `bottom`) the chat window appears (respected for both side and normal windows).
- `eca-chat-window-width`: Width of the chat window when on the left or right.
- `eca-chat-window-height`: Height of the chat window when on the top or bottom.
- `eca-chat-focus-on-open`: Whether to focus the chat window when it opens.
- `eca-chat-auto-add-repomap`: Whether to automatically include repoMap context when opening ECA.
- `eca-chat-auto-add-cursor`: Whether to automatically track the cursor position and add it as context.
Expand Down
72 changes: 44 additions & 28 deletions eca-chat.el
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,10 @@ such as the Doom workspaces tabline to refresh external indicators.")

(defcustom eca-chat-window-side 'right
"Side of the frame where the ECA chat window should appear.
Can be `'left', `'right', `'top', or `'bottom'. This setting will only
be used when `eca-chat-use-side-window' is non-nil."
Can be `'left', `'right', `'top', or `'bottom'. This setting is respected
both when `eca-chat-use-side-window' is non-nil (dedicated side window)
and when it is nil (regular window displayed in the corresponding
direction)."
:type '(choice (const :tag "Left" left)
(const :tag "Right" right)
(const :tag "Top" top)
Expand All @@ -76,11 +78,13 @@ be used when `eca-chat-use-side-window' is non-nil."
:group 'eca)

(defcustom eca-chat-use-side-window t
"Whether to display ECA chat in a side window.
"Whether to display ECA chat in a dedicated side window.
When non-nil (default), ECA chat opens in a dedicated side window
controlled by `eca-chat-window-side' and related settings. When nil,
ECA chat opens in a regular buffer that follows standard
`display-buffer' behavior."
ECA chat opens in a regular window, still placed on the side given by
`eca-chat-window-side' with the corresponding width/height, but without
the side-window restrictions (so it can be split or replaced like any
other window)."
:type 'boolean
:group 'eca)

Expand Down Expand Up @@ -2642,30 +2646,42 @@ available, falling back to `string-width' on Emacsen without it."
(select-window (get-buffer-window (buffer-name))))

(defun eca-chat--display-buffer (buffer)
"Display BUFFER in a side window according to customization.
The window is displayed on the side specified by
`eca-chat-window-side' with dimensions from
`eca-chat-window-width' or `eca-chat-window-height'.
"Display BUFFER according to customization.
BUFFER is displayed on the side given by `eca-chat-window-side'
with the size from `eca-chat-window-width' or
`eca-chat-window-height'. When `eca-chat-use-side-window' is
non-nil a dedicated side window is used, otherwise a regular
window in the same direction.
If `eca-chat-focus-on-open' is non-nil, the window is selected."
(let ((window
(if eca-chat-use-side-window
;; Use side window
(let* ((side eca-chat-window-side)
(slot 0)
(window-parameters '((no-delete-other-windows . t)))
(display-action
`((display-buffer-in-side-window)
(side . ,side)
(slot . ,slot)
(dedicated . side)
,@(when (memq side '(left right))
`((window-width . ,eca-chat-window-width)))
,@(when (memq side '(top bottom))
`((window-height . ,eca-chat-window-height)))
(window-parameters . ,window-parameters))))
(display-buffer buffer display-action))
;; Use regular buffer
(display-buffer buffer))))
(let* ((side eca-chat-window-side)
(size (if (memq side '(left right))
`((window-width . ,eca-chat-window-width))
`((window-height . ,eca-chat-window-height))))
(display-action
(if eca-chat-use-side-window
;; Dedicated side window.
`((display-buffer-in-side-window)
(side . ,side)
(slot . 0)
(dedicated . side)
,@size
(window-parameters . ((no-delete-other-windows . t))))
;; Regular window in the same direction.
`((display-buffer-in-direction)
(direction . ,(pcase side ('top 'above) ('bottom 'below) (_ side)))
,@size)))
(window
(or
;; Already visible: keep it where it is.
(get-buffer-window buffer)
;; Another chat is visible: replace it (new-tab behavior).
(when-let* ((win (get-window-with-predicate
(lambda (w)
(buffer-local-value 'eca-chat--id (window-buffer w))))))
(set-window-buffer win buffer)
win)
;; Nothing to reuse: open a new window.
(display-buffer buffer display-action))))
;; Select the window to give it focus if configured to do so
(when (and window eca-chat-focus-on-open)
(select-window window))
Expand Down
Loading