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

- Add `eca-chat-remove-workspace-root` command and `[-]` mode-line button to remove a workspace folder from a running session. Both 'add' and 'remove' is in the transient menu (`W a` / `W r`).
- Add `eca-chat-mode-line-format` for customizable chat mode line layout. #184
- Fix top-level `(require 'tab-line)` causing side effects when `eca-chat-tab-line` is nil. #195
- Add support for `chat/opened` server notification, enabling the `/fork` command to open forked chats as new tabs.
Expand Down
35 changes: 34 additions & 1 deletion eca-chat.el
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ Must be a valid model supported by server, check `eca-chat-select-model`."
:group 'eca)

(defcustom eca-chat-mode-line-format
'(:workspace-folders :add-workspace-button :spacer :init-progress " " :bg-jobs " " :elapsed-time " " :usage " " :trust)
'(:workspace-folders :add-workspace-button :remove-workspace-button :spacer :init-progress " " :bg-jobs " " :elapsed-time " " :usage " " :trust)
"Format for the ECA chat mode line.

When set to a list, each element is a module keyword or a
Expand All @@ -177,6 +177,7 @@ to separate left-aligned and right-aligned content.
Available modules:
`:workspace-folders' - project root paths
`:add-workspace-button' - clickable [+] button
`:remove-workspace-button' - clickable [-] button
`:title' - chat title
`:elapsed-time' - turn duration timer
`:usage' - token/cost info (see `eca-chat-usage-string-format')
Expand All @@ -196,6 +197,7 @@ This gives full control for powerline or doom-modeline users."
(string :tag "Literal string")
(const :tag "Workspace folders" :workspace-folders)
(const :tag "Add workspace button" :add-workspace-button)
(const :tag "Remove workspace button" :remove-workspace-button)
(const :tag "Background jobs" :bg-jobs)
(const :tag "Chat title" :title)
(const :tag "Elapsed time" :elapsed-time)
Expand Down Expand Up @@ -1762,13 +1764,38 @@ E is the mouse event."
(eca--session-add-workspace-folder session folder)
(force-mode-line-update))))

(defun eca-chat-remove-workspace-root ()
"Prompt for a workspace folder to remove from the current session.
Refuses when only one folder remains. In `merged' worktree mode, a
removed folder sharing its git-common-dir with another session folder
may be auto-re-added on the next buffer visit."
(interactive)
(when-let ((session (eca-session)))
(let ((folders (eca--session-workspace-folders session)))
(cond
((null folders)
(user-error "No workspace folders to remove"))
((<= (length folders) 1)
(user-error "Cannot remove the last workspace folder"))
(t
(let ((folder (completing-read "Remove workspace: " folders nil t)))
(eca--session-remove-workspace-folder session folder)
(force-mode-line-update)))))))

(defvar eca-chat--add-workspace-map
(let ((map (make-sparse-keymap)))
(define-key map [mode-line mouse-1]
#'eca-chat-add-workspace-root)
map)
"Keymap for the modeline [+] workspace button.")

(defvar eca-chat--remove-workspace-map
(let ((map (make-sparse-keymap)))
(define-key map [mode-line mouse-1]
#'eca-chat-remove-workspace-root)
map)
"Keymap for the modeline [-] workspace button.")

(defvar eca-chat--trust-toggle-map
(let ((map (make-sparse-keymap)))
(define-key map [mode-line mouse-1]
Expand Down Expand Up @@ -1813,6 +1840,12 @@ are in progress."
'mouse-face 'highlight
'help-echo "Add workspace folder"
'local-map eca-chat--add-workspace-map))
(:remove-workspace-button
(propertize " [-]"
'face 'shadow
'mouse-face 'highlight
'help-echo "Remove workspace folder"
'local-map eca-chat--remove-workspace-map))
(:bg-jobs
(when-let* ((jobs (eca--session-jobs session))
(running (seq-count (lambda (j) (string= "running" (plist-get j :status))) jobs)))
Expand Down
34 changes: 33 additions & 1 deletion eca-util.el
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,34 @@ workspace folders. Returns nil otherwise."
:removed [])))
(eca-info "Added workspace folder: %s" folder))))

(defun eca--session-remove-workspace-folder (session folder)
"Remove FOLDER from SESSION's workspace-folders and notify the server.
Refuses to remove the last remaining folder, since the Emacs client
resolves buffers to sessions by matching against workspace-folders and
an empty list would make the session unreachable. In `merged' worktree
mode, a removed folder whose git-common-dir still matches another
folder in the session can be auto-re-added by `eca-session' the next
time a buffer under it is visited."
(let* ((folder (expand-file-name folder))
(folders (eca--session-workspace-folders session)))
(cond
((not (--first (string= it folder) folders))
(eca-warn "Workspace folder not found: %s" folder))
((<= (length folders) 1)
(user-error "Cannot remove the last workspace folder"))
(t
(setf (eca--session-workspace-folders session)
(cl-remove-if (lambda (it) (string= it folder)) folders))
(eca-api-notify
session
:method "workspace/didChangeWorkspaceFolders"
:params (list :event
(list :added []
:removed (vector
(list :uri (eca--path-to-uri folder)
:name (file-name-nondirectory (directory-file-name folder)))))))
(eca-info "Removed workspace folder: %s" folder)))))

(defun eca-session ()
"Return the session related to root of current buffer otherwise nil."
(or (eca-get eca--sessions eca--session-id-cache)
Expand Down Expand Up @@ -327,7 +355,11 @@ Inheirits BASE-MAP."

["Server"
("S r" "Restart" eca-restart)
("S s" "Stop" eca-stop)]]))
("S s" "Stop" eca-stop)]

["Workspace"
("W a" "Add folder" eca-chat-add-workspace-root)
("W r" "Remove folder" eca-chat-remove-workspace-root)]]))

(defun eca-transient-menu ()
"Open the ECA transient menu.
Expand Down
Loading