diff --git a/core/autoload/sessions.el b/core/autoload/sessions.el new file mode 100644 index 000000000..af9a8dc24 --- /dev/null +++ b/core/autoload/sessions.el @@ -0,0 +1,121 @@ +;;; core/autoload/sessions.el -*- lexical-binding: t; -*- + +;; +;;; Helpers + +;;;###autoload +(defun doom-save-session (&optional file) + "TODO" + (setq file (expand-file-name (or file (doom-session-file)))) + (cond ((require 'persp-mode nil t) + (unless persp-mode (persp-mode +1)) + (setq persp-auto-save-opt 0) + (persp-save-state-to-file file)) + ((and (require 'frameset nil t) + (require 'restart-emacs nil t)) + (let ((frameset-filter-alist (append '((client . restart-emacs--record-tty-file)) + frameset-filter-alist)) + (desktop-base-file-name (file-name-nondirectory file)) + (desktop-dirname (file-name-directory file)) + (desktop-restore-eager t) + desktop-file-modtime) + (make-directory desktop-dirname t) + (desktop-save desktop-dirname t))) + ((error "No session backend to save session with")))) + +;;;###autoload +(defun doom-load-session (&optional file) + "TODO" + (setq file (expand-file-name (or file (doom-session-file)))) + (message "Attempting to load %s" file) + (cond ((require 'persp-mode nil t) + (unless persp-mode (persp-mode +1)) + (persp-load-state-from-file file)) + ((and (require 'frameset nil t) + (require 'restart-emacs nil t)) + (restart-emacs--restore-frames-using-desktop file)) + ((error "No session backend to load session with"))) + (select-frame-set-input-focus (selected-frame))) + +;;;###autoload +(defun doom-session-file () + "TODO" + (cond ((require 'persp-mode nil t) + (expand-file-name persp-auto-save-fname persp-save-dir)) + ((require 'desktop nil t) + (desktop-full-file-name)) + ((error "No session backend available")))) + + +;; +;;; Command line switch + +;;;###autoload +(defun doom-restore-session-handler (&rest _) + "TODO" + (doom-load-session)) + +;;;###autoload +(add-to-list 'command-switch-alist (cons "--restore" #'doom-restore-session-handler)) + + +;; +;;; Commands + +;;;###autoload +(defun doom/quickload-session () + "TODO" + (interactive) + (message "Restoring session...") + (doom-load-session) + (message "Session restored. Welcome back.")) + +;;;###autoload +(defun doom/quicksave-session () + "TODO" + (interactive) + (message "Saving session") + (doom-save-session) + (message "Saving session...DONE")) + +;;;###autoload +(defun doom/load-session (file) + "TODO" + (interactive + (let ((session-file (doom-session-file))) + (list (or (read-file-name "Session to restore: " + (file-name-directory session-file) + nil t + (file-name-nondirectory session-file)) + (user-error "No session selected. Aborting"))))) + (unless file + (error "No session file selected")) + (message "Loading '%s' session" file) + (doom-load-session file)) + +;;;###autoload +(defun doom/save-session (file) + "TODO" + (interactive + (let ((session-file (doom-session-file))) + (list (or (read-file-name "Save session to: " + (file-name-directory session-file) + nil nil + (file-name-nondirectory session-file)) + (user-error "No session selected. Aborting"))))) + (unless file + (error "No session file selected")) + (message "Saving '%s' session" file) + (doom-save-session file)) + +;;;###autoload +(defalias 'doom/restart #'restart-emacs) + +;;;###autoload +(defun doom/restart-and-restore (&optional debug) + "TODO" + (interactive "P") + (doom/quicksave-session) + (let (confirm-kill-emacs) + (restart-emacs + (delq nil (list (if debug "--debug-init") "--restore"))))) diff --git a/core/cli/autoloads.el b/core/cli/autoloads.el index fd49c2da8..9b1c0d9b3 100644 --- a/core/cli/autoloads.el +++ b/core/cli/autoloads.el @@ -37,9 +37,8 @@ it exists." (print! (bold (green "\nFinished!"))) (message "If you have a running Emacs Session, you will need to restart it or") (message "reload Doom for changes to take effect:\n") - (when (fboundp '+workspace/restart-emacs-then-restore) - (message " M-x +workspace/restart-emacs-then-restore")) - (message " M-x restart-emacs") + (message " M-x doom/restart-and-restore") + (message " M-x doom/restart") (message " M-x doom/reload")) (defun doom--do-load (&rest files) diff --git a/core/core.el b/core/core.el index e4e860990..1b5344a7b 100644 --- a/core/core.el +++ b/core/core.el @@ -253,6 +253,9 @@ and `doom-exit-window-hook'." async-byte-compile-log-file (concat doom-etc-dir "async-bytecomp.log") auto-save-list-file-name (concat doom-cache-dir "autosave") backup-directory-alist (list (cons "." (concat doom-cache-dir "backup/"))) + desktop-dirname (concat doom-etc-dir "desktop") + desktop-base-file-name "autosave" + desktop-base-lock-name "autosave-lock" pcache-directory (concat doom-cache-dir "pcache/") request-storage-directory (concat doom-cache-dir "request") server-auth-dir (concat doom-cache-dir "server/") diff --git a/modules/config/default/+emacs-bindings.el b/modules/config/default/+emacs-bindings.el index 221e634b0..198104fa8 100644 --- a/modules/config/default/+emacs-bindings.el +++ b/modules/config/default/+emacs-bindings.el @@ -91,9 +91,9 @@ :desc "Quit Emacs" "q" #'kill-emacs :desc "Save and quit Emacs" "Q" #'save-buffers-kill-terminal (:when (featurep! :feature workspaces) - :desc "Quit Emacs & forget session" "X" #'+workspace/kill-session-and-quit - :desc "Restart & restore Emacs" "r" #'+workspace/restart-emacs-then-restore) - :desc "Restart Emacs" "R" #'restart-emacs) + :desc "Quit Emacs & forget session" "X" #'+workspace/kill-session-and-quit) + :desc "Restart & restore Emacs" "r" #'doom/restart-and-restore + :desc "Restart Emacs" "R" #'doom/restart) ;; Snippets "&" nil ; yasnippet creates this prefix, we use a different one (:prefix ("s" . "snippets") @@ -123,15 +123,15 @@ :desc "Previous hunk" "[" #'git-gutter:previous-hunk) ;; Worspace and window management bindings (:prefix ("w". "workspaces") - :desc "Autosave session" "a" #'+workspace/save-session + :desc "Autosave session" "a" #'doom/quicksave-session :desc "Display workspaces" "d" #'+workspace/display :desc "Rename workspace" "r" #'+workspace/rename :desc "Create workspace" "c" #'+workspace/new :desc "Delete workspace" "k" #'+workspace/delete - :desc "Save session" "s" (λ! (let ((current-prefix-arg '(4))) (call-interactively #'+workspace/save-session))) + :desc "Save session" "s" #'doom/save-session :desc "Save workspace" "S" #'+workspace/save - :desc "Load session" "l" #'+workspace/load-session - :desc "Load last autosaved session" "L" #'+workspace/load-last-session + :desc "Load session" "l" #'doom/load-session + :desc "Load last autosaved session" "L" #'doom/quickload-session :desc "Kill other buffers" "o" #'doom/kill-other-buffers :desc "Undo window config" "u" #'winner-undo :desc "Redo window config" "U" #'winner-redo diff --git a/modules/config/default/+evil-bindings.el b/modules/config/default/+evil-bindings.el index 3b75ca111..560faeb10 100644 --- a/modules/config/default/+evil-bindings.el +++ b/modules/config/default/+evil-bindings.el @@ -544,14 +544,12 @@ :desc "Display tab bar" "TAB" #'+workspace/display :desc "New workspace" "n" #'+workspace/new :desc "Load workspace from file" "l" #'+workspace/load - :desc "Load a past session" "L" #'+workspace/load-session :desc "Save workspace to file" "s" #'+workspace/save - :desc "Autosave current session" "S" #'+workspace/save-session :desc "Switch workspace" "." #'+workspace/switch-to :desc "Delete session" "x" #'+workspace/kill-session :desc "Delete this workspace" "d" #'+workspace/delete :desc "Rename workspace" "r" #'+workspace/rename - :desc "Restore last session" "R" #'+workspace/load-last-session + :desc "Restore last session" "R" #'+workspace/restore-last-session :desc "Next workspace" "]" #'+workspace/switch-right :desc "Previous workspace" "[" #'+workspace/switch-left :desc "Switch to 1st workspace" "1" (λ! (+workspace/switch-to 0)) @@ -757,12 +755,15 @@ :desc "List project tasks" "t" #'+default/project-tasks :desc "Invalidate cache" "x" #'projectile-invalidate-cache) - (:prefix ("q" . "quit/restart") + (:prefix ("q" . "session") :desc "Quit Emacs" "q" #'evil-quit-all :desc "Save and quit Emacs" "Q" #'evil-save-and-quit - :desc "Quit Emacs & forget session" "X" #'+workspace/kill-session-and-quit - :desc "Restart & restore Emacs" "r" #'+workspace/restart-emacs-then-restore - :desc "Restart Emacs" "R" #'restart-emacs) + :desc "Quick save current session" "s" #'doom/quicksave-session + :desc "Restore last session" "l" #'doom/quickload-session + :desc "Save session to file" "S" #'doom/save-session + :desc "Restore session from file" "L" #'doom/load-session + :desc "Restart & restore Emacs" "r" #'doom/restart-and-restore + :desc "Restart Emacs" "R" #'doom/restart) (:when (featurep! :tools upload) (:prefix ("r" . "remote") diff --git a/modules/feature/workspaces/README.org b/modules/feature/workspaces/README.org index 29f8dec1a..877280479 100644 --- a/modules/feature/workspaces/README.org +++ b/modules/feature/workspaces/README.org @@ -61,9 +61,9 @@ in [[../../private/default/+evil-commands.el][private/default/+evil-commands.el] | ~+workspace/new~ | =SPC TAB n= | Create a new, blank workspace | | ~+workspace/display~ | =SPC TAB TAB= | Display open workspaces in the mode-line | | ~+workspace/load~ | =SPC TAB l= | Load a saved workspace into the current session | -| ~+workspace/load-session~ | =SPC TAB L= / =:sl[oad]= | Replace current session with a saved one | +| ~doom/quicksave-load~ | =SPC TAB L= / =:sl[oad]= | Replace current session with a saved one | | ~+workspace/save~ | =SPC TAB s= | Save the current workspace to a file | -| ~+workspace/save-session~ | =SPC TAB S= / =:ss[ave]= | Save current session | +| ~doom/quicksave-save~ | =SPC TAB S= / =:ss[ave]= | Save current session | | ~+workspace/switch-to~ | =SPC TAB .= | Switch to an open workspace | | ~+workspace/switch-left~ | =SPC TAB [= / =[ w= / =gT= | Switch to previous workspace | | ~+workspace/switch-right~ | =SPC TAB [= / =] w= / =gt= | Switch to next workspace | diff --git a/modules/feature/workspaces/autoload/evil.el b/modules/feature/workspaces/autoload/evil.el index 16f75c4e6..a18d06f74 100644 --- a/modules/feature/workspaces/autoload/evil.el +++ b/modules/feature/workspaces/autoload/evil.el @@ -1,20 +1,6 @@ ;;; feature/workspaces/autoload/evil.el -*- lexical-binding: t; -*- ;;;###if (featurep! :feature evil) -;;;###autoload (autoload '+workspace:save-session "feature/workspaces/autoload/evil" nil t) -(evil-define-command +workspace:save-session (&optional bang name) - "Ex wrapper around `+workspace/save-session'. If BANG, then autosave -(pointless if autosaving/loading is off). If NAME is nil, default to 'last'." - (interactive "") - (+workspace/save-session (if bang persp-auto-save-fname name))) - -;;;###autoload (autoload '+workspace:load-session "feature/workspaces/autoload/evil" nil t) -(evil-define-command +workspace:load-session (&optional bang name) - "Ex wrapper around `+workspace/load-session'. If BANG, then load last autosave -(pointless if autosaving/loading is off). If NAME is nil, defaults to 'last'." - (interactive "") - (+workspace/load-session (if bang persp-auto-save-fname name))) - ;;;###autoload (autoload '+workspace:save "feature/workspaces/autoload/evil" nil t) (evil-define-command +workspace:save (&optional name) "Ex wrapper around `+workspace/save-session'." diff --git a/modules/feature/workspaces/autoload/workspaces.el b/modules/feature/workspaces/autoload/workspaces.el index ebd1ffc0c..aa1f62e6f 100644 --- a/modules/feature/workspaces/autoload/workspaces.el +++ b/modules/feature/workspaces/autoload/workspaces.el @@ -112,14 +112,6 @@ Returns t if successful, nil otherwise." *persp-hash* (list name)) (+workspace-exists-p name)) -;;;###autoload -(defun +workspace-load-session (&optional name) - "Replace current session with the entire session named NAME. If NAME is nil, -use `persp-auto-save-fname'." - (mapc #'+workspace-delete (+workspace-list-names)) - (persp-load-state-from-file - (expand-file-name (or name persp-auto-save-fname) persp-save-dir))) - ;;;###autoload (defun +workspace-save (name) "Saves a single workspace (NAME) from the current session. Can be loaded again @@ -134,18 +126,6 @@ Returns t on success, nil otherwise." (and (member name (persp-list-persp-names-in-file fname)) t))) -;;;###autoload -(defun +workspace-save-session (&optional name) - "Save a whole session as NAME. If NAME is nil, use `persp-auto-save-fname'. -Return t on success, nil otherwise." - (let ((fname (expand-file-name (or name persp-auto-save-fname) - persp-save-dir))) - ;; disable auto-saving on kill-emacs if autosaving (i.e. name is nil) - (when (or (not name) - (string= name persp-auto-save-fname)) - (setq persp-auto-save-opt 0)) - (and (persp-save-state-to-file fname) t))) - ;;;###autoload (defun +workspace-new (name) "Create a new workspace named NAME. If one already exists, return nil. @@ -206,6 +186,9 @@ throws an error." ;; ;; Commands +;;;###autoload +(defalias '+workspace/restore-last-session #'doom/quickload-session) + ;;;###autoload (defun +workspace/load (name) "Load a workspace and switch to it. If called with C-u, try to reload the @@ -236,46 +219,6 @@ workspace." (+workspace-message (format "'%s' workspace saved" name) 'success) (+workspace-error (format "Couldn't save workspace %s" name)))) -;;;###autoload -(defun +workspace/load-session (&optional name) - "Load a session and switch to it. If called with C-u, try to load the last -session." - (interactive - (list - (unless current-prefix-arg - (completing-read - "Session to load: " - (directory-files persp-save-dir nil "^[^_.]") - nil t)))) - (condition-case ex - (let ((name (or name persp-auto-save-fname))) - (+workspace-load-session name) - (+workspace-message (format "'%s' workspace loaded" name) 'success)) - '(error (+workspace-error (cadr ex) t)))) - -;;;###autoload -(defun +workspace/load-last-session () - "Restore last session and switch to it." - (interactive) - (+workspace/load-session)) - -;;;###autoload -(defun +workspace/save-session (&optional name) - "Save the current session. If called with C-u, prompt you for the name to save -the session as." - (interactive - (list - (when current-prefix-arg - (completing-read - "Save session as: " - (directory-files persp-save-dir nil "^[^_.]"))))) - (condition-case-unless-debug ex - (let ((name (or name persp-auto-save-fname))) - (if (+workspace-save-session name) - (+workspace-message (format "Saved session as '%s'" name) 'success) - (error "Couldn't save session as '%s'" name))) - ('error (+workspace-error ex t)))) - ;;;###autoload (defun +workspace/rename (new-name) "Rename the current workspace." @@ -437,12 +380,6 @@ the next." (t (+workspace-error "Can't delete last workspace" t))))))) -;;;###autoload -(defun +workspace/restart-emacs-then-restore () - "Restarts Emacs, then restores the session." - (interactive) - (restart-emacs (list "--restore"))) - ;; ;; Tabs display in minibuffer diff --git a/modules/feature/workspaces/config.el b/modules/feature/workspaces/config.el index ff26bb85b..7bc243468 100644 --- a/modules/feature/workspaces/config.el +++ b/modules/feature/workspaces/config.el @@ -5,11 +5,6 @@ ;; it because it was unstable and slow; `persp-mode' is neither (and still ;; maintained). ;; -;; 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 -;; can be loaded later. -;; ;; NOTE persp-mode requires `workgroups' for file persistence in Emacs 24.4. (defvar +workspaces-main "main" @@ -20,11 +15,6 @@ `counsel-projectile-switch-project'. This function must take one argument: the new project directory.") -;; 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'.") - (defvar +workspaces-on-switch-project-behavior 'non-empty "Controls the behavior of workspaces when switching to a new project. @@ -35,11 +25,10 @@ t Always create a new workspace for the project associated with it. nil Never create a new workspace on project switch.") -;; If emacs is passed --restore, restore the last session on startup. This is -;; used by the `+workspace/restart-emacs-then-restore' command. -(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)) +;; 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'.") ;; @@ -64,29 +53,29 @@ 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 creates (`persp-nil-name') is - ;; special and doesn't 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)) - ;; Fix #319: the warnings buffer gets swallowed by creating - ;; `+workspaces-main', so we display it manually, if it exists. - (when-let* ((warnings (get-buffer "*Warnings*"))) - (save-excursion - (display-buffer-in-side-window - warnings '((window-height . shrink-window-if-larger-than-buffer)))))))))) + (persp-mode +1) + (unless noninteractive + (let (persp-before-switch-functions persp-activated-functions) + (with-selected-frame frame + ;; The default perspective persp-mode creates (`persp-nil-name') is + ;; special and doesn't 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)) + ;; Fix #319: the warnings buffer gets swallowed by creating + ;; `+workspaces-main', so we display it manually, if it exists. + (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) :config diff --git a/modules/ui/doom-dashboard/config.el b/modules/ui/doom-dashboard/config.el index d9cdc643b..e684795b4 100644 --- a/modules/ui/doom-dashboard/config.el +++ b/modules/ui/doom-dashboard/config.el @@ -44,11 +44,12 @@ Possible values: (defvar +doom-dashboard-menu-sections '(("Reload last session" :icon (all-the-icons-octicon "history" :face 'font-lock-keyword-face) - :when (and (bound-and-true-p persp-mode) - (file-exists-p (expand-file-name persp-auto-save-fname - persp-save-dir))) + :when (cond ((require 'persp-mode nil t) + (file-exists-p (expand-file-name persp-auto-save-fname persp-save-dir))) + ((require 'desktop nil t) + (file-exists-p (desktop-full-file-name)))) :face (:inherit (font-lock-keyword-face bold)) - :action +workspace/load-last-session) + :action doom/quickload-session) ("Open org-agenda" :icon (all-the-icons-octicon "calendar" :face 'font-lock-keyword-face) :when (fboundp 'org-agenda) @@ -107,8 +108,9 @@ PLIST can have the following properties: initial-buffer-choice (when (or (daemonp) (not (cl-loop for arg in (cdr command-line-args) - if (and (string-match-p "^[^-]" arg) - (file-exists-p arg)) + if (or (equal arg "--restore") + (and (string-match-p "^[^-]" arg) + (file-exists-p arg))) return t))) #'+doom-dashboard-initial-buffer))