diff --git a/CHANGELOG.develop b/CHANGELOG.develop index 353b1451b..198680e93 100644 --- a/CHANGELOG.develop +++ b/CHANGELOG.develop @@ -511,7 +511,16 @@ Other: - Fixed bug that could overwrites customize variables (thanks to bmag) - Don't suggest ~SPC q r~ if =restart-emacs= package is missing after an update (thanks to Keshav Kini) - - spacemacs-layouts: Add Projectile project buffers to new perspectives. + - spacemacs-layouts: Improvements to ~SPC l l~ (=spacemacs/helm-perspectives= + or =spacemacs/ivy-spacemacs-layouts=): + - If the user selects a project that does not already have a layout and then + quits without selecting a file or buffer, kill the new layout. + - When creating a new layout, add any buffers that belong to the project. + - spacemacs-layouts: Improvements to ~SPC p l~ + (=spacemacs/helm-persp-switch-project= or + =spacemacs/ivy-persp-switch-project=): + - Fixed the default action to display the home buffer in the new layout. + - Added actions to copy the current layout or create a new project layout. - Resolve symlinks in warning message about duplicate layers (thanks to Ben Gamari) - Check toggle condition in status function (thanks to Eivind Fonn) diff --git a/layers/+completion/ivy/funcs.el b/layers/+completion/ivy/funcs.el index d06d51895..6ea13b175 100644 --- a/layers/+completion/ivy/funcs.el +++ b/layers/+completion/ivy/funcs.el @@ -413,6 +413,8 @@ If match is found \(default) Select layout c: Close Layout(s) <- mark with C-SPC to close more than one-window k: Kill Layout(s) +n: Copy current layout +p: Create project layout If match is not found Creates layout @@ -422,13 +424,7 @@ Closing doesn't kill buffers inside the layout while killing layouts does." (ivy-read "Layouts: " (persp-names) :caller 'spacemacs/ivy-spacemacs-layouts - :action (lambda (name) - (let ((persp-reset-windows-on-nil-window-conf t)) - (persp-switch name) - (unless - (member name - (persp-names-current-frame-fast-ordered)) - (spacemacs/home)))))) + :action 'spacemacs//create-persp-with-home-buffer)) (defun spacemacs/ivy-spacemacs-layout-buffer () "Switch to layout buffer using ivy." diff --git a/layers/+completion/ivy/packages.el b/layers/+completion/ivy/packages.el index d7d6183c2..f965d133b 100644 --- a/layers/+completion/ivy/packages.el +++ b/layers/+completion/ivy/packages.el @@ -300,7 +300,10 @@ (ivy-set-actions 'spacemacs/ivy-spacemacs-layouts '(("c" persp-kill-without-buffers "Close layout(s)") - ("k" persp-kill "Kill layout(s)"))) + ("k" persp-kill "Kill layout(s)") + ("n" persp-copy "Copy Current Layout") + ("p" spacemacs//create-persp-with-current-project-buffers + "Create Project Layout"))) ;; TODO: better handling of C and X bindings for ivy ;; check ivy/pre-init-persp-mode (spacemacs/transient-state-register-remove-bindings 'layouts diff --git a/layers/+spacemacs/spacemacs-layouts/funcs.el b/layers/+spacemacs/spacemacs-layouts/funcs.el index 5bb04378d..388704a6f 100644 --- a/layers/+spacemacs/spacemacs-layouts/funcs.el +++ b/layers/+spacemacs/spacemacs-layouts/funcs.el @@ -327,6 +327,76 @@ format so they are supported by the :bindings ,@bindings)))) + +;; Persp and Projectile integration + +(defmacro spacemacs||switch-layout (name &rest props) + "Switch to the perspective called NAME. + +Available PROPS: + +`:init EXPRESSIONS' + One or more forms, which will be evaluated after switching to perspective + NAME if the perspective did not already exist." + (declare (indent 1)) + (let ((init (spacemacs/mplist-get-values props :init))) + `(let ((persp-reset-windows-on-nil-window-conf t) + (persp-already-exists (persp-with-name-exists-p ,name))) + (persp-switch ,name) + (unless persp-already-exists + ,@init)))) + +(defun spacemacs//create-persp-with-current-project-buffers (name) + "Create new perspective with project buffers. + +If perspective NAME does not already exist, create it and add any +buffers that belong to the current buffer's project." + (if (persp-with-name-exists-p name) + (message "There is already a perspective named %s" name) + (if-let ((project (projectile-project-p))) + (spacemacs||switch-layout name + :init + (persp-add-buffer (projectile-project-buffers project) + (persp-get-by-name name) nil nil)) + (message "Current buffer does not belong to a project")))) + +(defmacro spacemacs||switch-project-persp (name &rest body) + "Switch to persp and execute BODY with hook to add project buffers. + +Switch to perspective NAME, and then evaluate the forms in BODY. +If the perspective did not already exist, then BODY will be +evaluated with `projectile-after-switch-project-hook' bound to +add a hook that adds the current project's buffers to the +perspective. If the user quits during the evaluation of BODY, +the new perspective will be killed." + (declare (indent 1)) + `(let ((projectile-after-switch-project-hook + projectile-after-switch-project-hook)) + (spacemacs||switch-layout ,name + :init + (add-hook 'projectile-after-switch-project-hook + (lambda () + (let ((persp (persp-get-by-name ,name))) + (when (persp-p persp) + (persp-add-buffer (projectile-project-buffers + (expand-file-name ,name)) + persp nil nil))))) + (condition-case nil + (progn + ,@body) + (quit (persp-kill-without-buffers ,name)))))) + + +;; Helm and Ivy common functions + +(defun spacemacs//create-persp-with-home-buffer (name) + "Switch to perspective and display the Spacemacs home buffer. + +If perspective NAME does not already exist, create it and display +the Spacemacs home buffer. If the perspective already exists, +just switch to it." + (spacemacs||switch-layout name :init (spacemacs/home))) + ;; Helm integration @@ -371,11 +441,11 @@ perspectives does." :requires-pattern t :action '(("Create new perspective" . - (lambda (name) - (let ((persp-reset-windows-on-nil-window-conf t)) - (persp-switch name) - (unless (member name (persp-names-current-frame-fast-ordered)) - (spacemacs/home)))))))))) + spacemacs//create-persp-with-home-buffer) + ("Create new perspective with buffers from current project" . + spacemacs//create-persp-with-current-project-buffers) + ("Create new perspective with buffers from current perspective" . + persp-copy)))))) ;; ability to use helm find files but also adds to current perspective (defun spacemacs/helm-persp-close () @@ -410,7 +480,17 @@ perspectives does." (mapcar 'persp-kill (helm-marked-candidates)))))))) +(defun spacemacs//helm-persp-switch-project-action (project) + "Default action for `spacemacs/helm-persp-switch-project'." + (spacemacs||switch-project-persp project + (let ((projectile-completion-system 'helm) + (helm-quit-hook (append helm-quit-hook + (lambda () + (persp-kill-without-buffers project))))) + (projectile-switch-project-by-name project)))) + (defun spacemacs/helm-persp-switch-project (arg) + "Select a project layout using Helm." (interactive "P") (helm :sources @@ -423,33 +503,27 @@ perspectives does." :fuzzy-match helm-projectile-fuzzy-match :mode-line helm-read-file-name-mode-line-string :action '(("Switch to Project Perspective" . - (lambda (project) - (let ((persp-reset-windows-on-nil-window-conf t)) - (persp-switch project) - (let ((projectile-completion-system 'helm)) - (projectile-switch-project-by-name project))))))) + spacemacs//helm-persp-switch-project-action))) :buffer "*Helm Projectile Layouts*")) ;; Ivy integration -(defun spacemacs/ivy-persp-switch-project-advice (project) - (let ((persp-reset-windows-on-nil-window-conf t)) - (persp-switch project))) +(defun spacemacs//ivy-persp-switch-project-action (project) + "Default action for `spacemacs/ivy-persp-switch-project'." + (spacemacs||switch-project-persp project + (counsel-projectile-switch-project-action project))) (defun spacemacs/ivy-persp-switch-project (arg) + "Select a project layout using Ivy." (interactive "P") (require 'counsel-projectile) - (advice-add 'counsel-projectile-switch-project-action - :before #'spacemacs/ivy-persp-switch-project-advice) (ivy-read "Switch to Project Perspective: " (if (projectile-project-p) (cons (abbreviate-file-name (projectile-project-root)) (projectile-relevant-known-projects)) projectile-known-projects) - :action #'counsel-projectile-switch-project-action - :caller 'spacemacs/ivy-persp-switch-project) - (advice-remove 'counsel-projectile-switch-project-action - 'spacemacs/ivy-persp-switch-project-advice)) + :action #'spacemacs//ivy-persp-switch-project-action + :caller 'spacemacs/ivy-persp-switch-project)) ;; Eyebrowse @@ -689,11 +763,3 @@ containing the buffer." (append (persp-parameter 'gui-eyebrowse-window-configs persp) (persp-parameter 'term-eyebrowse-window-configs persp))) (eyebrowse--rename-window-config-buffers window-config old new))))) - - -;; Persp and Projectile integration - -(defun spacemacs//add-project-buffers-to-persp (persp _persp-hash) - (if-let ((buffers (and (projectile-project-p) - (projectile-project-buffers)))) - (persp-add-buffer buffers persp nil nil))) diff --git a/layers/+spacemacs/spacemacs-layouts/packages.el b/layers/+spacemacs/spacemacs-layouts/packages.el index e99a48dd1..1a32c8c0d 100644 --- a/layers/+spacemacs/spacemacs-layouts/packages.el +++ b/layers/+spacemacs/spacemacs-layouts/packages.el @@ -219,8 +219,6 @@ (defadvice persp-activate (before spacemacs//save-toggle-layout activate) (setq spacemacs--last-selected-layout persp-last-persp-name)) (add-hook 'persp-mode-hook 'spacemacs//layout-autosave) - (add-hook 'persp-created-functions - #'spacemacs//add-project-buffers-to-persp) (advice-add 'persp-load-state-from-file :before 'spacemacs//layout-wait-for-modeline) ;; Override SPC TAB to only change buffers in perspective (spacemacs/set-leader-keys