diff --git a/CHANGELOG.md b/CHANGELOG.md index 81e2aa2..6332b2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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: `eca-chat-add-context-to-user-prompt` and `eca-chat-add-filepath-to-user-prompt` no longer error with `window-live-p, nil` when the chat buffer is not visible; the chat window is now displayed first. With a prefix arg (`C-u`) they add the context without selecting the chat window, leaving point where it was. #266 - Bugfix: don't move the cursor while the chat is streaming. `eca-chat-content-received` rendered without a `save-excursion`, so content streamed above the prompt (e.g. while "thinking") dragged point up to the middle of the buffer; point is now preserved. - 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). diff --git a/README.md b/README.md index 8e74276..f9807b2 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/eca-chat.el b/eca-chat.el index e8ec223..6253a0d 100644 --- a/eca-chat.el +++ b/eca-chat.el @@ -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) @@ -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) @@ -2685,30 +2689,42 @@ available, falling back to `string-width' on Emacsen without it." (select-window window)))) (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))