Commit 5d3de7d5ef31

Vincent Demeester <vincent@sbr.pm>
2017-05-04 23:07:07
Add perspectives mode to switch for workspaces
Signed-off-by: Vincent Demeester <vincent@sbr.pm>
1 parent 718f933
.emacs.d/elpa/perspective-20160609.1444/perspective-autoloads.el
@@ -0,0 +1,34 @@
+;;; perspective-autoloads.el --- automatically extracted autoloads
+;;
+;;; Code:
+(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path))))
+
+;;;### (autoloads nil "perspective" "perspective.el" (22795 38174
+;;;;;;  592574 89000))
+;;; Generated autoloads from perspective.el
+
+(defvar persp-mode nil "\
+Non-nil if Persp mode is enabled.
+See the `persp-mode' command
+for a description of this minor mode.
+Setting this variable directly does not take effect;
+either customize it (see the info node `Easy Customization')
+or call the function `persp-mode'.")
+
+(custom-autoload 'persp-mode "perspective" nil)
+
+(autoload 'persp-mode "perspective" "\
+Toggle perspective mode.
+When active, keeps track of multiple 'perspectives',
+named collections of buffers and window configurations.
+
+\(fn &optional ARG)" t nil)
+
+;;;***
+
+;; Local Variables:
+;; version-control: never
+;; no-byte-compile: t
+;; no-update-autoloads: t
+;; End:
+;;; perspective-autoloads.el ends here
.emacs.d/elpa/perspective-20160609.1444/perspective-pkg.el
@@ -0,0 +1,2 @@
+;;; -*- no-byte-compile: t -*-
+(define-package "perspective" "20160609.1444" "switch between named \"perspectives\" of the editor" '((cl-lib "0.5")) :url "http://github.com/nex3/perspective-el" :keywords '("workspace" "convenience" "frames"))
.emacs.d/elpa/perspective-20160609.1444/perspective.el
@@ -0,0 +1,926 @@
+;;; perspective.el --- switch between named "perspectives" of the editor
+
+;; Copyright (C) 2008-2015 Natalie Weizenbaum <nex342@gmail.com>
+;;
+;; Licensed under the same terms as Emacs and under the MIT license.
+
+;; Author: Natalie Weizenbaum <nex342@gmail.com>
+;; URL: http://github.com/nex3/perspective-el
+;; Package-Version: 20160609.1444
+;; Package-Requires: ((cl-lib "0.5"))
+;; Version: 1.12
+;; Created: 2008-03-05
+;; By: Natalie Weizenbaum <nex342@gmail.com>
+;; Keywords: workspace, convenience, frames
+
+;;; Commentary:
+
+;; This package provides tagged workspaces in Emacs, similar to
+;; workspaces in windows managers such as Awesome and XMonad (and
+;; somewhat similar to multiple desktops in Gnome or Spaces in OS X).
+
+;; perspective.el provides multiple workspaces (or "perspectives") for
+;; each Emacs frame.  This makes it easy to work on many separate projects
+;; without getting lost in all the buffers.
+
+;; Each perspective is composed of a window configuration and a set of
+;; buffers.  Switching to a perspective activates its window
+;; configuration, and when in a perspective only its buffers are
+;; available by default.
+
+(require 'cl-lib)
+
+;; 'cl' is still required because the use of 'lexical-let'.  'lexical-let' has
+;; been deprecated since emacs 24.1, and it should be replaced with true
+;; lexical bindings.  For more information, please see
+;; https://www.gnu.org/software/emacs/manual/html_node/cl/
+;; Obsolete-Lexical-Binding.html
+(require 'cl)
+
+(defvar ido-temp-list)
+
+;;; Code:
+
+(defgroup perspective-mode 'nil
+  "Customization for Perspective mode"
+  :group 'frames)
+
+(defcustom persp-initial-frame-name "main"
+  "Name used for the initial perspective when enabling `persp-mode'."
+  :type 'string
+  :group 'perspective-mode)
+
+(defcustom persp-show-modestring t
+  "Determines if `persp-modestring' is shown in the modeline.
+If the value is 'header, `persp-modestring' is shown in the
+header line instead."
+  :group 'perspective-mode
+  :type '(choice (const :tag "Off" nil)
+                 (const :tag "Modeline" t)
+                 (const :tag "Header" 'header)))
+
+(defcustom persp-modestring-dividers '("[" "]" "|")
+  "Plist of strings used to created `persp-modestring'.
+First string is the start of the modestring, second is the
+closing of the mode string, and the last is the divider between
+perspectives."
+  :group 'perspective-mode
+  :type '(list (string :tag "Open")
+               (string :tag "Close")
+               (string :tag "Divider")))
+
+(defcustom persp-mode-prefix-key (kbd "C-x x")
+  "Prefix key to activate perspective-map"
+  :group 'perspective-mode
+  :set (lambda (sym value)
+	 (when (and (bound-and-true-p persp-mode-map)
+		    (bound-and-true-p perspective-map))
+           (persp-mode-set-prefix-key value))
+	 (set-default sym value))
+  :type 'key-sequence)
+
+(defcustom persp-switch-wrap t
+  "Whether `persp-next' and `persp-prev' should wrap."
+  :group 'perspective-mode
+  :type 'boolean)
+
+;; This is only available in Emacs >23,
+;; so we redefine it here for compatibility.
+(unless (fboundp 'with-selected-frame)
+  (defmacro with-selected-frame (frame &rest body)
+    "Execute the forms in BODY with FRAME as the selected frame.
+The value returned is the value of the last form in BODY.
+See also `with-temp-buffer'."
+    (declare (indent 1) (debug t))
+    (let ((old-frame (make-symbol "old-frame"))
+          (old-buffer (make-symbol "old-buffer")))
+      `(let ((,old-frame (selected-frame))
+             (,old-buffer (current-buffer)))
+         (unwind-protect
+             (progn (select-frame ,frame)
+                    ,@body)
+           (if (frame-live-p ,old-frame)
+               (select-frame ,old-frame))
+           (if (buffer-live-p ,old-buffer)
+               (set-buffer ,old-buffer)))))))
+
+(defmacro persp-frame-local-let (bindings &rest body)
+  "Like `let', but properly handles frame-local variables.
+Bind variables according to BINDINGS then eval BODY.
+
+In Emacs >= 23.2, frame-local variables are not reset after a
+`let' expression.  This hacks around that by manually resetting
+them in Emacs >= 23.2.  In older versions, this is identical to
+`let'."
+  (declare (indent 1))
+  (if (or (< emacs-major-version 23)
+          (and (= emacs-major-version 23) (< emacs-minor-version 2)))
+      `(let ,bindings ,@body)
+    (let ((binding-syms (mapcar (lambda (binding) (list (car binding) (cl-gensym))) bindings)))
+      ;; Each binding-sym is a pair (ORIGINAL-VALUE . WAS-BOUND).
+      `(let ,(mapcar (lambda (binding)
+                       (list (cadr binding)
+                             (let ((name (car binding)))
+                               `(cons (when (boundp ',name) ,name)
+                                      (boundp ',name)))))
+                     binding-syms)
+         (unwind-protect
+             (progn ,@(mapcar (lambda (binding) `(setq ,(car binding) ,(cadr binding))) bindings)
+                    ,@body)
+           ;; After the body, reset the original value of each binding sym if
+           ;; there was one, unbind it if there wasn't.
+           ,@(mapcar (lambda (binding)
+                       `(if (cdr ,(cadr binding))
+                            (setq ,(car binding) (car ,(cadr binding)))
+                          (makunbound ',(car binding)))) binding-syms))))))
+
+(cl-defstruct (perspective
+               (:conc-name persp-)
+               (:constructor make-persp-internal))
+  name buffers killed local-variables
+  (buffer-history buffer-name-history)
+  (window-configuration (current-window-configuration))
+  (point-marker (point-marker)))
+
+(defalias 'persp-killed-p 'persp-killed
+  "Return whether the perspective CL-X has been killed.")
+
+(defvar persp-interactive-completion-function
+  (if ido-mode 'ido-completing-read 'completing-read)
+  "The function which is used by perspective.el to interactivly complete user input")
+
+(defvar persp-before-switch-hook nil
+  "A hook that's run before `persp-switch'.
+Run with the previous perspective as `persp-curr'.")
+
+(defvar persp-switch-hook nil
+  "A hook that's run after `persp-switch'.
+Run with the newly created perspective as `persp-curr'.")
+
+(defvar persp-mode-hook nil
+  "A hook that's run after `persp-mode' has been activated.")
+
+(defvar persp-created-hook nil
+  "A hook that's run after a perspective has been created.
+Run with the newly created perspective as `persp-curr'.")
+
+(defvar persp-killed-hook nil
+  "A hook that's run just before a perspective is destroyed.
+Run with the perspective to be destroyed as `persp-curr'.")
+
+(defvar persp-activated-hook nil
+  "A hook that's run after a perspective has been activated.
+Run with the activated perspective active.")
+
+(defvar persp-mode-map (make-sparse-keymap)
+  "Keymap for perspective-mode.")
+
+(define-prefix-command 'perspective-map)
+(define-key persp-mode-map persp-mode-prefix-key 'perspective-map)
+
+(define-key perspective-map (kbd "s") 'persp-switch)
+(define-key perspective-map (kbd "k") 'persp-remove-buffer)
+(define-key perspective-map (kbd "c") 'persp-kill)
+(define-key perspective-map (kbd "r") 'persp-rename)
+(define-key perspective-map (kbd "a") 'persp-add-buffer)
+(define-key perspective-map (kbd "A") 'persp-set-buffer)
+(define-key perspective-map (kbd "b") 'persp-switch-to-buffer)
+(define-key perspective-map (kbd "i") 'persp-import)
+(define-key perspective-map (kbd "n") 'persp-next)
+(define-key perspective-map (kbd "<right>") 'persp-next)
+(define-key perspective-map (kbd "p") 'persp-prev)
+(define-key perspective-map (kbd "<left>") 'persp-prev)
+(define-key perspective-map persp-mode-prefix-key 'persp-switch-last)
+
+(defun persp-mode-set-prefix-key (newkey)
+  "Set the prefix key to activate persp-mode"
+  (substitute-key-definition 'perspective-map nil persp-mode-map)
+  (define-key persp-mode-map newkey 'perspective-map))
+
+;; make-variable-frame-local is obsolete according to the docs,
+;; but I don't want to have to manually munge frame-parameters
+;; all the time so I'm using it anyway.
+(make-variable-frame-local
+ (defvar perspectives-hash nil
+   "A hash containing all perspectives. The keys are the
+perspectives' names. The values are persp structs,
+with the fields NAME, WINDOW-CONFIGURATION, BUFFERS,
+BUFFER-HISTORY, KILLED, POINT-MARKER, and LOCAL-VARIABLES.
+
+NAME is the name of the perspective.
+
+WINDOW-CONFIGURATION is the configuration given by
+`current-window-configuration' last time the perspective was
+saved (if this isn't the current perspective, this is when the
+perspective was last active).
+
+BUFFERS is a list of buffer objects that are associated with this
+perspective.
+
+BUFFER-HISTORY is the list of buffer history values for this
+perspective.
+
+KILLED is non-nil if the perspective has been killed.
+
+POINT-MARKER is the point position in the active buffer.
+Otherwise, when multiple windows are visiting the same buffer,
+all but one of their points will be overwritten.
+
+LOCAL-VARIABLES is an alist from variable names to their
+perspective-local values."))
+
+(make-variable-frame-local
+ (defvar persp-curr nil
+   "The current perspective."))
+
+(make-variable-frame-local
+ (defvar persp-recursive nil
+   "The current perspective before beginning a recursive edit."))
+
+(make-variable-frame-local
+ (defvar persp-last nil
+   "The last perspective accessed before the current perspective."))
+
+(make-variable-frame-local
+ (defvar persp-modestring nil
+   "The string displayed in the modeline representing the perspectives."))
+(put 'persp-modestring 'risky-local-variable t)
+
+(defvar persp-protected nil
+  "Whether a perspective error should cause persp-mode to be disabled.
+Dynamically bound by `persp-protect'.")
+
+(defface persp-selected-face
+  '((t (:weight bold :foreground "Blue")))
+  "The face used to highlight the current perspective on the modeline.")
+
+(defmacro persp-protect (&rest body)
+  "Wrap BODY to disable persp-mode when it errors out.
+This prevents the persp-mode from completely breaking Emacs."
+  (declare (indent 0))
+  (let ((persp-protected t))
+    `(condition-case err
+         (progn ,@body)
+       (persp-error
+        (message "Fatal persp-mode error: %S" err)
+        (persp-mode -1)))))
+
+(defun persp-error (&rest args)
+  "Like `error', but marks it as a persp-specific error.
+Used along with `persp-protect' to ensure that persp-mode doesn't
+bring down Emacs."
+  (if persp-protected
+      (signal 'persp-error (list (apply 'format args)))
+    (apply 'error args)))
+
+(defun check-persp (persp)
+  "Raise an error if PERSP has been killed."
+  (cond
+   ((not persp)
+    (persp-error "Expected perspective, was nil"))
+   ((persp-killed-p persp)
+    (persp-error "Using killed perspective `%s'" (persp-name persp)))))
+
+(defmacro make-persp (&rest args)
+  "Create a new perspective struct and put it in `perspectives-hash'.
+
+ARGS is a list of keyword arguments followed by an optional BODY.
+The keyword arguments set the fields of the perspective struct.
+If BODY is given, it is executed to set the window configuration
+for the perspective."
+  (declare (indent defun))
+  (let ((keywords))
+    (while (keywordp (car args))
+      (dotimes (_ 2) (push (pop args) keywords)))
+    (setq keywords (reverse keywords))
+    `(let ((persp (make-persp-internal ,@keywords)))
+       (when persp-curr
+         (setf (persp-local-variables persp) (persp-local-variables persp-curr)))
+       (puthash (persp-name persp) persp perspectives-hash)
+       (with-perspective (persp-name persp)
+         ,(when args
+            ;; Body form given
+            `(save-excursion ,@args))
+         (run-hooks 'persp-created-hook))
+       persp)))
+
+(defun persp-save ()
+  "Save the current perspective state.
+Specifically, save the current window configuration and
+perspective-local variables to `persp-curr'"
+  (when persp-curr
+    (setf (persp-local-variables persp-curr)
+          (mapcar
+           (lambda (c)
+             (let ((name (car c)))
+               (list name (symbol-value name))))
+           (persp-local-variables persp-curr)))
+    (setf (persp-buffer-history persp-curr) buffer-name-history)
+    (setf (persp-window-configuration persp-curr) (current-window-configuration))
+    (setf (persp-point-marker persp-curr) (point-marker))))
+
+(defun persp-names ()
+  "Return a list of the names of all perspectives, sorted alphabetically."
+  (sort
+   (cl-loop for name being the hash-keys of perspectives-hash
+            collect name)
+   'string<))
+
+(defun persp-all-names (&optional not-frame)
+  "Return a list of the perspective names for all frames.
+Excludes NOT-FRAME, if given."
+  (cl-reduce 'union
+             (mapcar
+              (lambda (frame)
+                (unless (equal frame not-frame)
+                  (with-selected-frame frame (persp-names))))
+              (frame-list))))
+
+(defun persp-prompt (&optional default require-match)
+  "Prompt for the name of a perspective.
+
+DEFAULT is a default value for the prompt.
+
+REQUIRE-MATCH can take the same values as in `completing-read'."
+  (funcall persp-interactive-completion-function (concat "Perspective name"
+                           (if default (concat " (default " default ")") "")
+                           ": ")
+                   (persp-names)
+                   nil require-match nil nil default))
+
+(defmacro with-perspective (name &rest body)
+  "Switch to the perspective given by NAME while evaluating BODY."
+  (declare (indent 1))
+  (let ((old (cl-gensym)))
+    `(progn
+       (let ((,old (when persp-curr (persp-name persp-curr)))
+             (last-persp-cache persp-last))
+         (unwind-protect
+             (progn
+               (persp-switch ,name)
+               ,@body)
+           (when ,old (persp-switch ,old)))
+         (setq persp-last last-persp-cache)))))
+
+(defun persp-new (name)
+  "Return a new perspective with name NAME.
+The new perspective will start with only an `initial-major-mode'
+buffer called \"*scratch* (NAME)\"."
+  (make-persp :name name
+    (switch-to-buffer (concat "*scratch* (" name ")"))
+    (funcall initial-major-mode)
+    (delete-other-windows)))
+
+(defun persp-reactivate-buffers (buffers)
+  "Raise BUFFERS to the top of the most-recently-selected list.
+Returns BUFFERS with all non-living buffers removed.
+
+See also `other-buffer'."
+  (cl-loop for buf in (reverse buffers)
+           if (not (null (buffer-name buf)))
+           collect buf into living-buffers
+           and do (switch-to-buffer buf)
+           finally return (reverse living-buffers)))
+
+(defun persp-set-local-variables (vars)
+  "Set the local variables given in VARS.
+VARS should be an alist of variable names to values."
+  (dolist (var vars) (apply 'set var)))
+
+(defun persp-intersperse (list interspersed-val)
+  "Intersperse a value into a list.
+Return a new list made from taking LIST and inserting
+INTERSPERSED-VAL between every pair of items.
+
+For example, (persp-intersperse '(1 2 3) 'a) gives '(1 a 2 a 3)."
+  (reverse
+   (cl-reduce
+    (lambda (list el) (if list (cl-list* el interspersed-val list) (list el)))
+    list :initial-value nil)))
+
+(defconst persp-mode-line-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map [mode-line down-mouse-1] 'persp-mode-line-click)
+    map))
+
+(defun persp-mode-line-click (event)
+  "Select the clicked perspective.
+EVENT is the click event triggering this function call."
+  (interactive "e")
+  (persp-switch (format "%s" (car (posn-string (event-start event))))))
+
+(defun persp-update-modestring ()
+  "Update `persp-modestring' to reflect the current perspectives.
+Has no effect when `persp-show-modestring' is nil."
+  (when persp-show-modestring
+    (let ((open (list (nth 0 persp-modestring-dividers)))
+          (close (list (nth 1 persp-modestring-dividers)))
+          (sep (nth 2 persp-modestring-dividers)))
+     (setq persp-modestring
+           (append open
+                   (persp-intersperse (mapcar 'persp-format-name
+                                              (persp-names)) sep)
+                   close)))))
+
+(defun persp-format-name (name)
+  "Format the perspective name given by NAME for display in `persp-modestring'."
+  (let ((string-name (format "%s" name)))
+    (if (equal name (persp-name persp-curr))
+        (propertize string-name 'face 'persp-selected-face)
+      (propertize string-name
+                  'local-map persp-mode-line-map
+                  'mouse-face 'mode-line-highlight))))
+
+(defun persp-get-quick (char &optional prev)
+  "Return the name of the first perspective that begins with CHAR.
+Perspectives are sorted alphabetically.
+
+PREV can be the name of a perspective.  If it's passed,
+this will try to return the perspective alphabetically after PREV.
+This is used for cycling between perspectives."
+  (persp-get-quick-helper char prev (persp-names)))
+
+(defun persp-get-quick-helper (char prev names)
+  (if (null names) nil
+    (let ((name (car names)))
+      (cond
+       ((and (null prev) (eq (string-to-char name) char)) name)
+       ((equal name prev)
+        (if (and (not (null (cdr names))) (eq (string-to-char (cadr names)) char))
+            (cadr names)
+          (persp-get-quick char)))
+       (t (persp-get-quick-helper char prev (cdr names)))))))
+
+(defun persp-switch-last ()
+  "Switch to the perspective accessed before the current one."
+  (interactive)
+  (unless persp-last
+    (error "There is no last perspective"))
+  (persp-switch (persp-name persp-last)))
+
+(defun persp-switch (name)
+  "Switch to the perspective given by NAME.
+If it doesn't exist, create a new perspective and switch to that.
+
+Switching to a perspective means that all buffers associated with
+that perspective are reactivated (see `persp-reactivate-buffers'),
+the perspective's window configuration is restored, and the
+perspective's local variables are set."
+  (interactive "i")
+  (if (null name) (setq name (persp-prompt (and persp-last (persp-name persp-last)))))
+  (if (and persp-curr (equal name (persp-name persp-curr))) name
+    (let ((persp (gethash name perspectives-hash)))
+      (setq persp-last persp-curr)
+      (when (null persp)
+        (setq persp (persp-new name)))
+      (run-hooks 'persp-before-switch-hook)
+      (persp-activate persp)
+      name))
+  (run-hooks 'persp-switch-hook))
+
+(defun persp-activate (persp)
+  "Activate the perspective given by the persp struct PERSP."
+  (check-persp persp)
+  (persp-save)
+  (setq persp-curr persp)
+  (persp-set-local-variables (persp-local-variables persp))
+  (persp-reactivate-buffers (persp-buffers persp))
+  (setq buffer-name-history (persp-buffer-history persp))
+  (set-window-configuration (persp-window-configuration persp))
+  (goto-char (persp-point-marker persp))
+  (persp-update-modestring)
+  (run-hooks 'persp-activated-hook))
+
+(defun persp-switch-quick (char)
+  "Switch to the first perspective, alphabetically, that begins with CHAR.
+
+Sets `this-command' (and thus `last-command') to (persp-switch-quick . CHAR).
+
+See `persp-switch', `persp-get-quick'."
+  (interactive "c")
+  (let ((persp (if (and (consp last-command) (eq (car last-command) this-command))
+                   (persp-get-quick char (cdr last-command))
+                 (persp-get-quick char))))
+    (setq this-command (cons this-command persp))
+    (if persp (persp-switch persp)
+      (persp-error (concat "No perspective name begins with " (string char))))))
+
+(defun persp-next ()
+  "Switch to next perspective (to the right)."
+  (interactive)
+  (let* ((names (persp-names))
+         (pos (cl-position (persp-name persp-curr) names)))
+    (cond
+     ((null pos) (persp-find-some))
+     ((= pos (1- (length names)))
+      (if persp-switch-wrap (persp-switch (nth 0 names))))
+     (t (persp-switch (nth (1+ pos) names))))))
+
+(defun persp-prev ()
+  "Switch to previous perspective (to the left)."
+  (interactive)
+  (let* ((names (persp-names))
+         (pos (cl-position (persp-name persp-curr) names)))
+    (cond
+     ((null pos) (persp-find-some))
+     ((= pos 0)
+      (if persp-switch-wrap (persp-switch (nth (1- (length names)) names))))
+     (t (persp-switch (nth (1- pos) names))))))
+
+(defun persp-find-some ()
+  "Return the name of a valid perspective.
+
+This function tries to return the \"most appropriate\"
+perspective to switch to.  It tries:
+
+  * The perspective given by `persp-last'.
+  * The main perspective.
+  * The first existing perspective, alphabetically.
+
+If none of these perspectives can be found, this function will
+create a new main perspective and return \"main\"."
+  (cond
+   (persp-last (persp-name persp-last))
+   ((gethash persp-initial-frame-name perspectives-hash) persp-initial-frame-name)
+   ((> (hash-table-count perspectives-hash) 0) (car (persp-names)))
+   (t (persp-activate
+       (make-persp :name persp-initial-frame-name :buffers (buffer-list)
+         :window-configuration (current-window-configuration)
+         :point-marker (point-marker)))
+      persp-initial-frame-name)))
+
+(defun persp-add-buffer (buffer)
+  "Associate BUFFER with the current perspective.
+
+See also `persp-switch' and `persp-remove-buffer'."
+  (interactive
+   (list
+    (let ((read-buffer-function nil))
+      (read-buffer "Add buffer to perspective: "))))
+  (let ((buffer (get-buffer buffer)))
+    (unless (memq buffer (persp-buffers persp-curr))
+      (push buffer (persp-buffers persp-curr)))))
+
+(defun persp-set-buffer (buffer-name)
+  "Associate BUFFER-NAME with the current perspective and remove it from any other."
+  (interactive
+   (list
+    (let ((read-buffer-function nil))
+      (read-buffer "Set buffer to perspective: "))))
+  (cond ((get-buffer buffer-name)
+         (persp-add-buffer buffer-name)
+         (cl-loop for other-persp = (persp-buffer-in-other-p (get-buffer buffer-name))
+                  while other-persp
+                  do (with-perspective (cdr other-persp)
+                                       (persp-remove-buffer buffer-name))))
+        (t (message "buffer %s doesn't exist" buffer-name))))
+
+(cl-defun persp-buffer-in-other-p (buffer)
+  "Returns nil if BUFFER is only in the current perspective.
+Otherwise, returns (FRAME . NAME), the frame and name of another
+perspective that has the buffer.
+
+Prefers perspectives in the selected frame."
+  (cl-loop for frame in (sort (frame-list) (lambda (frame1 frame2) (eq frame2 (selected-frame))))
+           do (cl-loop for persp being the hash-values of (with-selected-frame frame perspectives-hash)
+                       if (and (not (and (equal frame (selected-frame))
+                                         (equal (persp-name persp) (persp-name persp-curr))))
+                               (memq buffer (persp-buffers persp)))
+                       do (cl-return-from persp-buffer-in-other-p
+                            (cons frame (persp-name persp)))))
+  nil)
+
+(defun persp-switch-to-buffer (buffer-or-name)
+  "Like `switch-to-buffer', but switches to another perspective if necessary."
+  (interactive
+   (list
+    (let ((read-buffer-function nil))
+      (read-buffer-to-switch "Switch to buffer: "))))
+  (let ((buffer (window-normalize-buffer-to-switch-to buffer-or-name)))
+    (if (memq buffer (persp-buffers persp-curr))
+        (switch-to-buffer buffer)
+      (let ((other-persp (persp-buffer-in-other-p buffer)))
+        (when (eq (car-safe other-persp) (selected-frame))
+          (persp-switch (cdr other-persp)))
+        (switch-to-buffer buffer)))))
+
+(defun persp-remove-buffer (buffer)
+  "Disassociate BUFFER with the current perspective.
+
+See also `persp-switch' and `persp-add-buffer'."
+  (interactive "bRemove buffer from perspective: \n")
+  (setq buffer (get-buffer buffer))
+  (cond ((not (buffer-live-p buffer)))
+        ;; Only kill the buffer if no other perspectives are using it
+        ((not (persp-buffer-in-other-p buffer))
+         (kill-buffer buffer))
+        ;; Make the buffer go away if we can see it.
+        ;; TODO: Is it possible to tell if it's visible at all,
+        ;;       rather than just the current buffer?
+        ((eq buffer (current-buffer)) (bury-buffer))
+        (t (bury-buffer buffer)))
+  (setf (persp-buffers persp-curr) (remq buffer (persp-buffers persp-curr))))
+
+(defun persp-kill (name)
+  "Kill the perspective given by NAME.
+
+Killing a perspective means that all buffers associated with that
+perspective and no others are killed."
+  (interactive "i")
+  (if (null name) (setq name (persp-prompt (persp-name persp-curr) t)))
+  (with-perspective name
+    (run-hooks 'persp-killed-hook)
+    (mapc 'persp-remove-buffer (persp-buffers persp-curr))
+    (setf (persp-killed persp-curr) t))
+  (remhash name perspectives-hash)
+  (persp-update-modestring)
+  (when (equal name (persp-name persp-last))
+    (setq persp-last nil))
+  (when (equal name (persp-name persp-curr))
+    ;; Don't let persp-last get set to the deleted persp.
+    (persp-frame-local-let ((persp-last persp-last)) (persp-switch (persp-find-some)))))
+
+(defun persp-rename (name)
+  "Rename the current perspective to NAME."
+  (interactive "sNew name: ")
+  (if (gethash name perspectives-hash)
+      (persp-error "Perspective `%s' already exists" name)
+    (remhash (persp-name persp-curr) perspectives-hash)
+    (puthash name persp-curr perspectives-hash)
+    (setf (persp-name persp-curr) name)
+    (persp-update-modestring)))
+
+(cl-defun persp-all-get (name not-frame)
+  "Returns the list of buffers for a perspective named NAME from any
+frame other than NOT-FRAME.
+
+This doesn't return the window configuration because those can't be
+copied across frames."
+  (dolist (frame (frame-list))
+    (unless (equal frame not-frame)
+      (with-selected-frame frame
+        (let ((persp (gethash name perspectives-hash)))
+          (if persp (cl-return-from persp-all-get (persp-buffers persp))))))))
+
+(defun persp-read-buffer (prompt &optional def require-match)
+  "A replacement for the built-in `read-buffer'.
+Meant to be used with `read-buffer-function'. Return the name of
+the buffer selected, only selecting from buffers within the
+current perspective.
+
+With a prefix arg, uses the old `read-buffer' instead."
+  (persp-protect
+    (let ((read-buffer-function nil))
+      (if current-prefix-arg
+          (read-buffer prompt def require-match)
+        ;; Most of this is taken from `minibuffer-with-setup-hook',
+        ;; slightly modified because it's not a macro.
+        ;; The only functional difference is that the append argument
+        ;; to add-hook is t, so that it'll be run after the hook added
+        ;; by `read-buffer-to-switch'.
+        (let ((rb-completion-table (persp-complete-buffer))
+              (persp-read-buffer-hook))
+          (setq persp-read-buffer-hook
+                (lambda ()
+                  (remove-hook 'minibuffer-setup-hook persp-read-buffer-hook)
+                  (setq minibuffer-completion-table rb-completion-table)))
+          (unwind-protect
+              (progn
+                (add-hook 'minibuffer-setup-hook persp-read-buffer-hook t)
+                (read-buffer prompt def require-match))
+            (remove-hook 'minibuffer-setup-hook persp-read-buffer-hook)))))))
+
+(defun persp-complete-buffer ()
+  "Perform completion on all buffers within the current perspective."
+  (lexical-let ((persp-names (mapcar 'buffer-name (persp-buffers persp-curr))))
+    (apply-partially 'completion-table-with-predicate
+                     (or minibuffer-completion-table 'internal-complete-buffer)
+                     (lambda (name)
+                       (member (if (consp name) (car name) name) persp-names))
+                     nil)))
+
+(cl-defun persp-import (name &optional dont-switch)
+  "Import a perspective named NAME from another frame.  If DONT-SWITCH
+is non-nil or with prefix arg, don't switch to the new perspective."
+  ;; TODO: Have some way of selecting which frame the perspective is imported from.
+  (interactive "i\nP")
+  (unless name
+    (setq name (funcall persp-interactive-completion-function
+                        "Import perspective: " (persp-all-names (selected-frame)) nil t)))
+  (if (and (gethash name perspectives-hash)
+           (not (yes-or-no-p (concat "Perspective `" name "' already exits. Continue? "))))
+      (cl-return-from persp-import))
+  (let ((buffers (persp-all-get name (selected-frame)))
+        persp)
+    (if (null buffers)
+        (persp-error "Perspective `%s' doesn't exist in another frame" name))
+    (setq persp (make-persp :name name :buffers buffers
+                            (switch-to-buffer (cl-loop for buffer in buffers
+                                                       if (buffer-live-p buffer)
+                                                       return buffer))
+                            (delete-other-windows)))
+    (if dont-switch
+        (persp-update-modestring)
+      (persp-activate persp))))
+
+(defadvice switch-to-buffer (after persp-add-buffer-adv)
+  "Add BUFFER to the current perspective.
+
+See also `persp-add-buffer'."
+  (persp-protect
+    (let ((buf (ad-get-arg 0)))
+      (when buf
+        (persp-add-buffer buf)))))
+
+(defadvice display-buffer (after persp-add-buffer-adv)
+  "Add BUFFER to the perspective for the frame on which it's displayed.
+
+See also `persp-add-buffer'."
+  (persp-protect
+    (when ad-return-value
+      (let ((buf (ad-get-arg 0))
+            (frame (window-frame ad-return-value)))
+        (when (and buf frame)
+          (with-selected-frame frame
+            (persp-add-buffer buf)))))))
+
+(defadvice set-window-buffer (after persp-add-buffer-adv)
+  "Add BUFFER to the perspective for window's frame.
+
+See also `persp-add-buffer'."
+  (persp-protect
+    (let ((buf (ad-get-arg 1))
+          (frame (window-frame (ad-get-arg 0))))
+      (when (and buf frame)
+        (with-selected-frame frame
+          (persp-add-buffer buf))))))
+
+(defadvice switch-to-prev-buffer (around persp-ensure-buffer-in-persp)
+  "Ensure that the selected buffer is in WINDOW's perspective."
+  (let* ((window (window-normalize-window window t))
+         (frame (window-frame window))
+         (old-buffer (window-buffer window)))
+    ad-do-it
+
+    (let ((buffer (window-buffer window)))
+      (with-selected-frame frame
+        (unless (memq buffer (persp-buffers persp-curr))
+          ;; If a buffer from outside this perspective was selected, it's because
+          ;; this perspective is out of buffers. For lack of any better option, we
+          ;; recreate the scratch buffer.
+          ;;
+          ;; If we were just in a scratch buffer, change the name slightly.
+          ;; Otherwise our new buffer will get deleted too.
+          (let ((name (concat "*scratch* (" (persp-name persp-curr) ")")))
+            (when (and bury-or-kill (equal name (buffer-name old-buffer)))
+              (setq name (concat "*scratch*  (" (persp-name persp-curr) ")")))
+            (with-selected-window window
+              (switch-to-buffer name)
+              (funcall initial-major-mode))))))))
+
+(defadvice recursive-edit (around persp-preserve-for-recursive-edit)
+  "Preserve the current perspective when entering a recursive edit."
+  (persp-protect
+    (persp-save)
+    (persp-frame-local-let ((persp-recursive persp-curr))
+      (let ((old-hash (copy-hash-table perspectives-hash)))
+        ad-do-it
+        ;; We want the buffer lists that were created in the recursive edit,
+        ;; but not the window configurations
+        (maphash (lambda (key new-persp)
+                   (let ((persp (gethash key old-hash)))
+                     (when persp
+                       (setf (persp-buffers persp) (persp-buffers new-persp)))))
+                 perspectives-hash)
+        (setq perspectives-hash old-hash)))))
+
+(defadvice exit-recursive-edit (before persp-restore-after-recursive-edit)
+  "Restore the old perspective when exiting a recursive edit."
+  (persp-protect
+    (if persp-recursive (persp-switch (persp-name persp-recursive)))))
+
+;;;###autoload
+(define-minor-mode persp-mode
+  "Toggle perspective mode.
+When active, keeps track of multiple 'perspectives',
+named collections of buffers and window configurations."
+  :global t
+  :keymap persp-mode-map
+  (if persp-mode
+      (persp-protect
+        (ad-activate 'switch-to-buffer)
+        (ad-activate 'display-buffer)
+        (ad-activate 'set-window-buffer)
+        (ad-activate 'switch-to-prev-buffer)
+        (ad-activate 'recursive-edit)
+        (ad-activate 'exit-recursive-edit)
+        (add-hook 'after-make-frame-functions 'persp-init-frame)
+        (add-hook 'ido-make-buffer-list-hook 'persp-set-ido-buffers)
+        (setq read-buffer-function 'persp-read-buffer)
+        (mapc 'persp-init-frame (frame-list))
+        (setf (persp-buffers persp-curr) (buffer-list))
+
+        (run-hooks 'persp-mode-hook))
+    (ad-deactivate-regexp "^persp-.*")
+    (remove-hook 'after-make-frame-functions 'persp-init-frame)
+    (remove-hook 'ido-make-buffer-list-hook 'persp-set-ido-buffers)
+    (setq read-buffer-function nil)
+    (setq perspectives-hash nil)
+    (setq global-mode-string (delq 'persp-modestring global-mode-string))))
+
+(defun persp-init-frame (frame)
+  "Initialize the perspectives system in FRAME.
+By default, this uses the current frame."
+  (with-selected-frame frame
+    (modify-frame-parameters
+     frame
+     '((perspectives-hash) (persp-curr) (persp-last) (persp-recursive) (persp-modestring)))
+
+    ;; Don't set these variables in modify-frame-parameters
+    ;; because that won't do anything if they've already been accessed
+    (setq perspectives-hash (make-hash-table :test 'equal :size 10))
+
+    (when persp-show-modestring
+      (if (eq persp-show-modestring 'header)
+          (let ((val (or (default-value 'header-line-format) '(""))))
+            (unless (memq 'persp-modestring val)
+              (set-default 'header-line-format (append val '(persp-modestring)))))
+        (setq global-mode-string (or global-mode-string '("")))
+        (unless (memq 'persp-modestring global-mode-string)
+          (setq global-mode-string (append global-mode-string '(persp-modestring)))))
+      (persp-update-modestring))
+
+    (persp-activate
+     (make-persp :name persp-initial-frame-name :buffers (list (current-buffer))
+       :window-configuration (current-window-configuration)
+       :point-marker (point-marker)))))
+
+(defun persp-make-variable-persp-local (variable)
+  "Make VARIABLE become perspective-local.
+This means that whenever a new perspective is switched into, the
+variable will take on its local value for that perspective.  When
+a new perspective is created, the variable will inherit its value
+from the current perspective at time of creation."
+  (unless (assq variable (persp-local-variables persp-curr))
+    (let ((entry (list variable (symbol-value variable))))
+      (dolist (frame (frame-list))
+        (cl-loop for persp being the hash-values of (with-selected-frame frame perspectives-hash)
+                 do (push entry (persp-local-variables persp)))))))
+
+(defmacro persp-setup-for (name &rest body)
+  "Add code that should be run to set up the perspective named NAME.
+Whenever a new perspective named NAME is created, runs BODY in
+it. In addition, if one exists already, runs BODY in it immediately."
+  (declare (indent 1))
+  `(progn
+     (add-hook 'persp-created-hook
+               (lambda ()
+                 (when (string= (persp-name persp-curr) ,name)
+                   ,@body))
+               'append)
+     (when (gethash ,name perspectives-hash)
+       (with-perspective ,name ,@body))))
+
+(defun persp-set-ido-buffers ()
+  "Restrict the ido buffer to the current perspective."
+  (let ((persp-names
+         (remq nil (mapcar 'buffer-name (persp-buffers persp-curr))))
+        (indices (make-hash-table :test 'equal)))
+    (cl-loop for elt in ido-temp-list
+             for i upfrom 0
+             do (puthash elt i indices))
+    (setq ido-temp-list
+          (sort (intersection persp-names ido-temp-list)
+                (lambda (a b)
+                  (< (gethash a indices)
+                     (gethash b indices)))))))
+
+(defun quick-perspective-keys ()
+  "Bind quick key commands to switch to perspectives.
+All C-S-letter key combinations are bound to switch to the first
+perspective beginning with the given letter."
+  (cl-loop for c from ?a to ?z
+           do (define-key persp-mode-map
+                (read-kbd-macro (concat "C-S-" (string c)))
+                `(lambda ()
+                   (interactive)
+                   (persp-switch-quick ,c)))))
+
+(defun persp-turn-off-modestring ()
+  "Deactivate the perspective modestring."
+  (interactive)
+  (setq persp-modestring nil)
+  (setq persp-show-modestring nil))
+
+(defun persp-turn-on-modestring ()
+  "Activate the perspective modestring."
+  (interactive)
+  (setq persp-show-modestring t)
+  (persp-update-modestring))
+
+(provide 'perspective)
+
+;; Local Variables:
+;; indent-tabs-mode: nil
+;; End:
+;;; perspective.el ends here
.emacs.d/elpa/perspective-20160609.1444/perspective.elc
Binary file
.emacs.d/emacs.el
@@ -450,6 +450,38 @@
   (fullframe ibuffer ibuffer-quit)
   (fullframe list-packages quit-window))
 
+(use-package perspective
+  :ensure t
+  :bind* (("M-m SPC s" . persp-switch)
+    ("M-m SPC n" . persp-next)
+    ("M-m SPC p" . persp-prev)
+    ("M-m SPC r" . persp-rename)
+    ("M-m SPC k" . pers-kill)
+    ("M-m SPC o" . custom-persp/org)
+    ("M-m SPC d d" . custom-persp/docker)
+    ("M-m SPC d p" . custom-persp/pipeline))
+  :config
+  (persp-mode t)
+  (defmacro custom-persp (name &rest body)
+    `(let ((initialize (not (gethash ,name perspectives-hash)))
+           (current-perspective persp-curr))
+    (persp-switch ,name)
+    (when initialize ,@body)
+    (setq persp-last current-perspective)))
+  (defun custom-persp/org ()
+    (interactive)
+    (custom-persp "org"
+                  (find-file (expand-file-name "todos/personal.org" org-root-directory))))
+  (defun custom-persp/docker ()
+    (interactive)
+    (custom-persp "docker"
+                  (find-file (substitute-env-in-file-name "$HOME/go/src/github.com/docker/docker"))))
+  (defun custom-persp/pipeline ()
+    (interactive)
+    (custom-persp "docker"
+                  (find-file (substitute-env-in-file-name "$HOME/go/src/github.com/docker/pipeline"))))
+  )
+
 (use-package apropospriate-theme
   :ensure t
   :config
.emacs.d/emacs.org
@@ -1031,6 +1031,46 @@
         (fullframe list-packages quit-window))
     #+END_SRC
 
+** Perspectives
+
+   Let's use perspective to be able to set quick workspace
+   (pre-defined, or on-the-fly).
+
+   #+BEGIN_SRC emacs-lisp
+     (use-package perspective
+       :ensure t
+       :bind* (("M-m SPC s" . persp-switch)
+   	 ("M-m SPC n" . persp-next)
+   	 ("M-m SPC p" . persp-prev)
+   	 ("M-m SPC r" . persp-rename)
+   	 ("M-m SPC k" . pers-kill)
+   	 ("M-m SPC o" . custom-persp/org)
+   	 ("M-m SPC d d" . custom-persp/docker)
+   	 ("M-m SPC d p" . custom-persp/pipeline))
+       :config
+       (persp-mode t)
+       (defmacro custom-persp (name &rest body)
+         `(let ((initialize (not (gethash ,name perspectives-hash)))
+                (current-perspective persp-curr))
+         (persp-switch ,name)
+         (when initialize ,@body)
+         (setq persp-last current-perspective)))
+       (defun custom-persp/org ()
+         (interactive)
+         (custom-persp "org"
+                       (find-file (expand-file-name "todos/personal.org" org-root-directory))))
+       (defun custom-persp/docker ()
+         (interactive)
+         (custom-persp "docker"
+                       (find-file (substitute-env-in-file-name "$HOME/go/src/github.com/docker/docker"))))
+       (defun custom-persp/pipeline ()
+         (interactive)
+         (custom-persp "docker"
+                       (find-file (substitute-env-in-file-name "$HOME/go/src/github.com/docker/pipeline"))))
+       )
+   #+END_SRC
+   
+
 * Visual ๐Ÿ˜Ž
 ** Color theme(s)
 
@@ -1043,7 +1083,9 @@
        (load-theme 'apropospriate-dark t))
    #+END_SRC
 
-   Also use =all-the-icon= ๐Ÿ‘ผ
+** Mode Line
+
+   First use =all-the-icon= ๐Ÿ‘ผ
 
    #+BEGIN_SRC emacs-lisp
      (use-package all-the-icons
@@ -1053,8 +1095,7 @@
        :config (add-hook 'dired-mode-hook 'all-the-icons-dired-mode))
    #+END_SRC
 
-
-** Mode Line
+   Update modeline to use =all-the-icons= (see [[https://github.com/domtronn/all-the-icons.el/wiki/Spaceline][this link]])
 
    [[https://github.com/TheBB/spaceline][Spaceline]] is similar to the [[http://spacemacs.org][Spacemacs]] mode-line. I like it. It's pretty cool.