fix(workspaces): projectile/project integration

And hardcoded behaviors in tabspaces' API.
This commit is contained in:
Henrik Lissner 2025-09-23 14:38:19 -04:00
parent 065b95288e
commit c57f2dfc82
No known key found for this signature in database
GPG key ID: B60957CA074D39A3
3 changed files with 91 additions and 1 deletions

View file

@ -374,6 +374,7 @@
:desc "New named workspace" "N" #'+workspaces/new-named
:desc "Restore last session" "R" #'+workspaces/restore-last-session
:desc "Save session" "s" #'+workspaces/save-session
:desc "Open project workspace" "p" #'+workspaces/open-in-project
:desc "Kill this workspace" "k" #'+workspaces/kill
:desc "Kill other workspaces" "K" #'+workspaces/kill-others
:desc "Kill session" "X" #'+workspaces/kill-session

View file

@ -233,4 +233,71 @@ its associated frame, if one exists) and move to the next."
(call-interactively #'+workspaces/kill))
((user-error "Can't delete last workspace"))))
;;;###autoload
(defun +workspaces/open-in-project (project &optional force?)
"Open an existing or new workspace for PROJECT.
Afterwards, executes `+workspaces-switch-project-function', if set.
If FORCE?, always create a workspace, even if it already exists."
(interactive
(list (if-let* ((projects (projectile-relevant-known-projects)))
(projectile-completing-read "Switch to project: " projects)
(user-error "There are no known projects"))
current-prefix-arg))
(let* ((project (expand-file-name project))
(existing-tab-names (tabspaces--list-tabspaces))
(original-tab-name (or (cdr (assoc project tabspaces-project-tab-map))
(tabspaces-generate-descriptive-tab-name project existing-tab-names)))
(tab-name original-tab-name)
(session (tabspaces--get-project-session-file-for-restore project))
(project-directory project) ; Use the full path as the project directory
(project-exists (cl-member project projectile-known-projects :test #'file-equal-p))
(create-new-tab (or force? (not (member tab-name existing-tab-names)))))
(with-current-buffer (doom-fallback-buffer)
(setq-local default-directory project-directory))
(cond
;; If there is no tab nor project, create both
((not project-exists)
(message "Creating new workspace for project...")
(tab-bar-new-tab)
(tab-bar-rename-tab tab-name)
(projectile-add-known-project project-directory)
(switch-to-buffer (doom-fallback-buffer)))
;; If project and tab exist, but we want a new tab
((and project-exists
(member tab-name existing-tab-names)
create-new-tab)
(message "Creating new workspace for known project...")
(let ((new-tab-name (generate-unique-numbered-tab-name tab-name existing-tab-names)))
(tab-bar-new-tab)
(tab-bar-rename-tab new-tab-name)
(setq tab-name new-tab-name)
(switch-to-buffer (doom-fallback-buffer))))
;; If project and tab exist, switch to it
((and project-exists
(member tab-name existing-tab-names))
(message "Switching to existing project workspace...")
(tab-bar-switch-to-tab tab-name))
;; If project exists, but no corresponding tab, open a new tab
(project-exists
(message "Creating new workspace for existing project...")
(tab-bar-new-tab)
(tab-bar-rename-tab tab-name)
(if (file-exists-p session)
(tabspaces-restore-session session)
(switch-to-buffer (doom-fallback-buffer))))
((user-error "Failed to find the project or create a workspace")))
;; Update tabspaces-project-tab-map (only for the main tab, not numbered duplicates)
(unless (string-match-p "<[0-9]+>$" tab-name)
(setf (alist-get project-directory tabspaces-project-tab-map nil nil #'equal)
tab-name))))
;;; workspaces.el ends here

View file

@ -38,4 +38,26 @@
;; Per-workspace `winner-mode' history
(add-to-list 'window-persistent-parameters '(winner-ring . t)))
(add-to-list 'window-persistent-parameters '(winner-ring . t))
;; HACK: Tabspaces is hardcoded to equate the root of a version controlled
;; project as the only kind of project root, and lacks integration with
;; project.el or projectile. This fixes that.
;; REVIEW: PR this upstream!
(defadvice! +workspaces--project-root-a (fn &rest args)
:around #'tabspaces-save-current-project-session
:around #'tabspaces--get-project-session-file
(letf! ((#'vc-root-dir #'doom-project-root))
(apply fn args)))
(defadvice! +workspaces--project-name-a (fn &rest args)
:around #'tabspaces--project-name
(or (doom-project-root)
(apply fn args)))
;; HACK: `tabspaces-open-or-create-project-and-workspace' has *so* much
;; hardcoded and opinionated behavior, so I replace it with a version that
;; cooperates with projectile/project, the dashboard, our custom
;; `tabspaces-session-project-session-store', and fixes its bugs.
;; REVIEW: Issues should be raised upstream!
(advice-add #'tabspaces-open-or-create-project-and-workspace
:override #'+workspaces/open-in-project))