doomemacs/modules/feature/workspaces/config.el

183 lines
8.3 KiB
EmacsLisp
Raw Normal View History

;;; feature/workspaces/config.el -*- lexical-binding: t; -*-
2017-02-04 03:21:04 -05:00
;; `persp-mode' gives me workspaces, a workspace-restricted `buffer-list', and
2017-06-14 21:03:20 +02:00
;; file-based session persistence. I used workgroups2 before this, but abandoned
;; it because it was unstable and slow; `persp-mode' is neither (and still
;; maintained).
;;
2017-06-14 21:03:20 +02:00
;; By default, sessions are autosaved, but not autoloaded. Use :ss or
;; `+workspace/save-session' to save, and :sl or `+workspace/load-session' to
;; load the last autosaved session. You can give sessions a custom name so they
2017-06-14 21:03:20 +02:00
;; can be loaded later.
;;
2017-09-02 16:39:51 +02:00
;; NOTE persp-mode requires `workgroups' for file persistence in Emacs 24.4.
(defvar +workspaces-main "main"
2017-04-11 08:31:00 -04:00
"The name of the primary and initial workspace, which cannot be deleted or
renamed.")
(defvar +workspaces-switch-project-function #'doom-project-find-file
"The function to run after `projectile-switch-project' or
`counsel-projectile-switch-project'. This function must take one argument: the
new project directory.")
2018-02-02 19:50:25 -05:00
;; FIXME actually use this for wconf bookmark system
(defvar +workspaces-data-file "_workspaces"
"The basename of the file to store single workspace perspectives. Will be
stored in `persp-save-dir'.")
;; If emacs is passed --restore, restore the last session on startup. This is
;; particularly useful for the `+workspace/restart-emacs-then-restore' command.
2018-05-13 23:16:06 +02:00
(defun +workspaces-restore-last-session (&rest _)
(add-hook 'emacs-startup-hook #'+workspace/load-session 'append))
(add-to-list 'command-switch-alist (cons "--restore" #'+workspaces-restore-last-session))
2017-02-04 03:21:04 -05:00
2017-04-11 08:31:00 -04:00
;;
;; Plugins
;;
(def-package! persp-mode
:defer t
:init
(defun +workspaces|init ()
;; Remove default buffer predicate so persp-mode can put in its own
(setq default-frame-alist
(delq (assq 'buffer-predicate default-frame-alist)
default-frame-alist))
(add-hook 'after-make-frame-functions #'+workspaces|init-frame)
(require 'persp-mode)
(unless (daemonp)
(+workspaces|init-frame (selected-frame))))
(defun +workspaces|init-frame (frame)
"Make sure a main workspace exists and is switched to, if FRAME isn't in any
workspace. Also ensures that the *Warnings* buffer will be visible in main.
Uses `+workspaces-main' to determine the name of the main workspace."
(unless persp-mode
(persp-mode +1))
(unless noninteractive
(let (persp-before-switch-functions persp-activated-functions)
(with-selected-frame frame
;; The default perspective persp-mode makes (defined by
;; `persp-nil-name') is special and doesn't actually represent a real
;; persp object, so buffers can't really be assigned to it, among
;; other quirks. We create a *real* main workspace to fill this role.
(unless (persp-get-by-name +workspaces-main)
(persp-add-new +workspaces-main))
;; Switch to it if we aren't auto-loading the last session
(when (and (string= (safe-persp-name (get-current-persp)) persp-nil-name)
(= persp-auto-resume-time -1))
(persp-frame-switch +workspaces-main frame)
;; We want to know where we are in every new daemon frame
(when (daemonp)
(run-at-time 0.1 nil #'+workspace/display))
;; The warnings buffer gets swallowed by creating
;; `+workspaces-main', so we display it manually, if it exists (fix
;; #319).
(when-let* ((warnings (get-buffer "*Warnings*")))
(save-excursion
(display-buffer-in-side-window
warnings '((window-height . shrink-window-if-larger-than-buffer))))))))))
(add-hook 'doom-post-init-hook #'+workspaces|init t)
2017-02-19 18:40:39 -05:00
:config
(setq persp-autokill-buffer-on-remove 'kill-weak
persp-nil-hidden t
persp-auto-save-fname "autosave"
persp-save-dir (concat doom-etc-dir "workspaces/")
persp-set-last-persp-for-new-frames t
persp-switch-to-added-buffer nil
persp-remove-buffers-from-nil-persp-behaviour nil
persp-auto-resume-time -1 ; Don't auto-load on startup
persp-auto-save-opt (if noninteractive 0 1)) ; auto-save on kill
2017-02-04 03:21:04 -05:00
2018-03-22 06:30:49 -04:00
;; bootstrap
(defun +workspaces|init-persp-mode ()
(cond (persp-mode
;; Ensure `persp-kill-buffer-query-function' is last in
;; kill-buffer-query-functions
(remove-hook 'kill-buffer-query-functions 'persp-kill-buffer-query-function)
(add-hook 'kill-buffer-query-functions 'persp-kill-buffer-query-function t)
;; Ensure buffers we've opened/switched to are auto-added to the
;; current perspective
(add-hook 'doom-after-switch-buffer-hook #'+workspaces|auto-add-buffer)
;; Remap `buffer-list' to current workspace's buffers in
;; `doom-buffer-list'
(advice-add #'doom-buffer-list :override #'+workspace-buffer-list))
(t
(remove-hook 'doom-after-switch-buffer-hook #'+workspaces|auto-add-buffer)
(advice-remove #'doom-buffer-list #'+workspace-buffer-list))))
(add-hook 'persp-mode-hook #'+workspaces|init-persp-mode)
2018-03-22 06:30:49 -04:00
2018-05-13 23:16:06 +02:00
(defun +workspaces|leave-nil-perspective (&rest _)
(when (string= (+workspace-current-name) persp-nil-name)
(+workspace-switch (or (if (+workspace-p +workspace--last) +workspace--last)
(car (+workspace-list-names))
+workspaces-main)
'auto-create)))
2018-05-13 23:16:06 +02:00
(add-hook 'persp-after-load-state-functions #'+workspaces|leave-nil-perspective)
;; Modify `delete-window' to close the workspace if used on the last window
(define-key! persp-mode-map
[remap restart-emacs] #'+workspace/restart-emacs-then-restore
[remap delete-window] #'+workspace/close-window-or-workspace
[remap evil-delete-window] #'+workspace/close-window-or-workspace)
;; only auto-save when real buffers are present
(advice-add #'persp-asave-on-exit :around #'+workspaces*autosave-real-buffers)
2018-03-22 06:30:49 -04:00
;; On `doom/cleanup-session', delete buffers associated with no perspectives
(add-hook 'doom-cleanup-hook #'+workspaces|cleanup-unassociated-buffers)
2017-06-28 15:16:30 +02:00
;; per-frame workspaces
(setq persp-init-frame-behaviour t
persp-init-new-frame-behaviour-override nil
persp-interactive-init-frame-behaviour-override #'+workspaces|associate-frame
persp-emacsclient-init-frame-behaviour-override #'+workspaces|associate-frame)
;; delete frame associated with workspace, if it exists
(add-hook 'delete-frame-functions #'+workspaces|delete-associated-workspace)
2017-06-28 15:16:30 +02:00
2018-03-22 06:30:49 -04:00
;; per-project workspaces, but reuse current workspace if empty
(setq projectile-switch-project-action #'+workspaces|set-project-action
counsel-projectile-switch-project-action #'+workspaces|switch-to-project)
(add-hook 'projectile-after-switch-project-hook #'+workspaces|switch-to-project)
;;
;; eshell
(persp-def-buffer-save/load
:mode 'eshell-mode :tag-symbol 'def-eshell-buffer
:save-vars '(major-mode default-directory))
;; compile
(persp-def-buffer-save/load
:mode 'compilation-mode :tag-symbol 'def-compilation-buffer
:save-vars
'(major-mode default-directory compilation-directory compilation-environment compilation-arguments))
;; Restore indirect buffers
(defvar +workspaces--indirect-buffers-to-restore nil)
(persp-def-buffer-save/load
:tag-symbol 'def-indirect-buffer
:predicate #'buffer-base-buffer
:save-function (lambda (buf tag vars)
(list tag (buffer-name buf) vars
(buffer-name (buffer-base-buffer))))
:load-function (lambda (savelist &rest _rest)
(destructuring-bind
(buf-name vars base-buf-name &rest _rest) (cdr savelist)
(push (cons buf-name base-buf-name)
+workspaces--indirect-buffers-to-restore)
nil)))
(defun +workspaces|reload-indirect-buffers (&rest _)
(dolist (ibc +workspaces--indirect-buffers-to-restore)
(let* ((nbn (car ibc))
(bbn (cdr ibc))
(bb (get-buffer bbn)))
(when bb
(when (get-buffer nbn)
(setq nbn (generate-new-buffer-name nbn)))
(make-indirect-buffer bb nbn t))))
(setq +workspaces--indirect-buffers-to-restore nil))
(add-hook 'persp-after-load-state-functions #'+workspaces|reload-indirect-buffers))