-
-
Save Bad-ptr/aa6be231fdc22705ca19 to your computer and use it in GitHub Desktop.
Manage sets of perspectives, provide save/load functionality.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(require 'perspective) | |
(require 'tramp) | |
(defcustom persp-one-initial-set-for-all-frames nil | |
"If nil -- new frames get each own new `perspectives-set' on creation, | |
Otherwise -- new frames created with the same `perspectives-set'." | |
:type 'boolean | |
:group 'perspective-mode) | |
(defcustom persp-auto-name-initial-set t | |
"If t -- autogenerate name (instead of temp-name) for initial perspectives-set, | |
so it will be saved even if persp-save-and-load-only-named-sets is t." | |
:type 'boolean | |
:group 'perspective-mode) | |
(defcustom persp-save-and-load-only-named-sets t | |
"If nil -- save/load all perspective sets, otherwise save/load only named sets." | |
:type 'boolean | |
:group 'perspective-mode) | |
(defcustom persp-auto-kill-unnamed-sets t | |
"If t -- On frame deletion or persps-set-switch, kill the current perspectives-set | |
if it's name is nil and there is no other frame using this set." | |
:type 'boolean | |
:group 'perspective-mode) | |
(defcustom persp-last-autokilled-set-name "*last-autokilled*" | |
"Last autokilled perspectives-set saved with this name in perspectives-set-hash | |
(not actually killed until user kill another perspectives-set)." | |
:type 'boolean | |
:group 'perspective-mode) | |
(cl-defstruct (perspectives-set | |
(:conc-name persps-set-) | |
(:constructor make-persps-set-internal)) | |
(name) (temp-name) (p-last) (p-curr) | |
(p-hash (make-hash-table :test 'equal :size 10))) | |
(defvar persp-after-killed-hook nil | |
"A hook that's run just after a perspective is destroyed.") | |
(defvar perspectives-set-hash (make-hash-table :test 'equal :size 5) | |
"A hash containing `perspectives-set' structs. | |
The keys are the sets' names. The values are the `perspectives-set' structs.") | |
(make-variable-frame-local | |
(defvar persps-set-curr nil | |
"The perspectives-set of the current frame.")) | |
;; New funcs ----------- | |
(defmacro with-perspectives-set (name &rest body) | |
"Switch to the perspectives-set given by NAME while evaluating BODY." | |
(declare (indent 1)) | |
(let ((old (cl-gensym))) | |
`(progn | |
(let ((,old (when persps-set-curr (persps-set-get-name-or-gen persps-set-curr))) | |
(persp-auto-kill-unnamed-sets nil)) | |
;; (unwind-protect | |
(progn | |
(persps-set-switch ,name) | |
,@body) | |
(when ,old (persps-set-switch ,old)) | |
;;) | |
)))) | |
(defun persps-set-get-name-or-gen (p-set) | |
(when p-set | |
(or (persps-set-name p-set) | |
(persps-set-temp-name p-set) | |
(let* ((n (hash-table-count perspectives-set-hash)) | |
(gname (format "#%s" n))) | |
(while (gethash gname perspectives-set-hash) | |
(setq n (1+ n) | |
gname (format "#%s" n))) | |
(setf (persps-set-temp-name p-set) gname) | |
gname)))) | |
(defun make-persps-set (&rest args) | |
(let ((new (apply #'make-persps-set-internal args))) | |
(puthash (persps-set-get-name-or-gen new) new perspectives-set-hash) | |
new)) | |
(defun persps-set-shared-or-new-empty () | |
(or (and persp-one-initial-set-for-all-frames | |
(block 'fps | |
(maphash #'(lambda (k v) | |
(unless (string= k persp-last-autokilled-set-name) | |
(return-from 'fps v))) | |
perspectives-set-hash))) | |
(if (and persp-auto-name-initial-set | |
(< (hash-table-count perspectives-set-hash) 1)) | |
(make-persps-set :name persp-initial-frame-name) | |
(make-persps-set)))) | |
(defun persps-set-get-persp-curr-or-new () | |
(when persps-set-curr | |
(let ((p-c (persps-set-p-curr persps-set-curr))) | |
(if (and p-c (not (persp-killed-p p-c))) | |
p-c | |
;;(setf (persps-set-p-curr persps-set-curr) | |
(make-persp :name persp-initial-frame-name :buffers (list (current-buffer)) | |
:window-configuration (current-window-configuration) | |
:point-marker (point-marker)) | |
;;) | |
)))) | |
(defun persps-set-save-persp-last-curr () | |
(when persps-set-curr | |
(setf (persps-set-p-last persps-set-curr) persp-last) | |
(setf (persps-set-p-curr persps-set-curr) persp-curr))) | |
(defun persps-set-save-state () | |
(when persps-set-curr | |
(persps-set-save-persp-last-curr) | |
(persp-save))) | |
(defun persps-set-restore-persp-last-curr () | |
(when persps-set-curr | |
(setq persp-last (persps-set-p-last persps-set-curr)) | |
(setq persp-curr (persps-set-p-curr persps-set-curr)))) | |
(defun persps-set-deactivate () | |
(when persps-set-curr | |
(if (and persp-auto-kill-unnamed-sets | |
persps-set-curr | |
(null (persps-set-name persps-set-curr))) | |
(persps-set-kill (persps-set-get-name-or-gen persps-set-curr) t) | |
(persps-set-save-state)))) | |
(defun persps-set-activate (persps-set &optional dont-deactivate) | |
(when persps-set | |
(unless dont-deactivate | |
(persps-set-deactivate)) | |
(setq persps-set-curr persps-set | |
perspectives-hash (persps-set-p-hash persps-set) | |
persp-last (persps-set-p-last persps-set)) | |
(persp-activate (persps-set-get-persp-curr-or-new)))) | |
(defun persps-set-prompt (&optional default require-match) | |
(funcall persp-interactive-completion-function | |
(concat "Perspectives set name" | |
(if default (concat " (default " default ")") "") | |
": ") | |
(let ((ps-names)) | |
(maphash #'(lambda (ps-name ps) | |
(push ps-name ps-names)) | |
perspectives-set-hash) | |
ps-names) | |
nil require-match nil nil default)) | |
(defun persps-set-kill (name &optional auto-kill) | |
(interactive "i") | |
(unless name | |
(setq name (persps-set-prompt | |
(persps-set-get-name-or-gen persps-set-curr) | |
t))) | |
(when name | |
(let ((persps-set-to-kill (gethash (if auto-kill | |
persp-last-autokilled-set-name | |
name) | |
perspectives-set-hash)) | |
(persp-auto-kill-unnamed-sets nil) | |
(p-s-curr-name (persps-set-get-name-or-gen persps-set-curr))) | |
(with-perspectives-set (persps-set-get-name-or-gen persps-set-to-kill) | |
(maphash #'(lambda (persp-name _) (persp-kill persp-name)) | |
perspectives-hash)) | |
(remhash (persps-set-get-name-or-gen persps-set-to-kill) | |
perspectives-set-hash) | |
(when (and auto-kill | |
(not (string= name persp-last-autokilled-set-name))) | |
(let ((persps-set-to-save)) | |
(puthash persp-last-autokilled-set-name persps-set-to-save | |
perspectives-set-hash) | |
(setf (persps-set-name persps-set-to-save) | |
nil | |
(persps-set-temp-name persps-set-to-save) | |
persp-last-autokilled-set-name))) | |
(when (string= name p-s-curr-name) | |
(persps-set-activate (persps-set-shared-or-new-empty) t))))) | |
(defun persps-set-rename (newname) | |
(interactive "sNew name: ") | |
(if (gethash newname perspectives-set-hash) | |
(persp-error "Perspectives-set `%s' already exists" name) | |
(when persps-set-curr | |
(remhash (persps-set-get-name-or-gen persps-set-curr) perspectives-set-hash) | |
(setf (persps-set-temp-name persps-set-curr) nil | |
(persps-set-name persps-set-curr) newname) | |
(puthash newname persps-set-curr perspectives-set-hash)))) | |
(defun persps-set-switch (name) | |
(interactive "i") | |
(unless name (setq name (persps-set-prompt))) | |
(let ((new-set (or (gethash name perspectives-set-hash) | |
(make-persps-set :name name)))) | |
(if (eq new-set persps-set-curr) | |
name | |
(persps-set-activate new-set)))) | |
;; --- Hooks | |
(add-hook 'persp-switch-hook #'persps-set-save-persp-last-curr) | |
(add-hook 'persp-after-killed-hook #'persps-set-save-persp-last-curr) | |
(add-hook 'delete-frame-functions #'(lambda (f) | |
(when persp-mode | |
(persps-set-deactivate)))) | |
;; --- Changed funcs | |
(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)))) | |
(run-hooks 'persp-after-killed-hook)) | |
(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) (perspective-modestring) | |
(persps-set-curr))) | |
;; Don't set these variables in modify-frame-parameters | |
;; because that won't do anything if they've already been accessed | |
(persps-set-activate (persps-set-shared-or-new-empty)) | |
(when persp-show-modestring | |
(if (eq persp-show-modestring 'header) | |
(let ((val (or (default-value 'header-line-format) '("")))) | |
(unless (memq 'perspective-modestring val) | |
(set-default 'header-line-format (append val '(perspective-modestring))))) | |
(setq global-mode-string (or global-mode-string '(""))) | |
(unless (memq 'perspective-modestring global-mode-string) | |
(setq global-mode-string (append global-mode-string '(perspective-modestring))))) | |
(persp-update-modestring)))) | |
(cl-defun persp-buffer-in-other-p-frame (buffer) | |
(let ((tmp (persp-buffer-in-other-p buffer))) | |
(when tmp | |
(let ((pset (car-safe tmp))) | |
(when pset | |
(mapc #'(lambda (f) | |
(with-selected-frame | |
(when (eq persps-set-curr pset) | |
(cl-return-from persp-buffer-in-other-p-frame | |
(cons f (cdr tmp)))))) | |
(frame-list))))))) | |
(cl-defun persp-buffer-in-other-p (buffer) | |
"Returns nil if BUFFER is only in the current perspective. | |
Otherwise, returns (SET . NAME), the set and name of another | |
perspective that has the buffer. | |
Prefers perspectives in the current set of selected frame." | |
(mapc #'(lambda (pset) | |
(maphash #'(lambda (persp-name persp) | |
(when (and (not (and (equal pset persps-set-curr) | |
(equal (persp-name persp) (persp-name persp-curr)))) | |
(memq buffer (persp-buffers persp))) | |
(cl-return-from persp-buffer-in-other-p | |
(cons pset (persp-name persp))))) | |
(persps-set-p-hash pset))) | |
(let (ps-list first-item) | |
(maphash #'(lambda (pset-name pset) | |
(if (eq pset persps-set-curr) | |
(setq first-item pset) | |
(push pset ps-list))) | |
perspectives-set-hash) | |
(push first-item ps-list) | |
ps-list)) | |
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-frame buffer))) | |
(when (eq (car-safe other-persp) (selected-frame)) | |
(persp-switch (cdr other-persp))) | |
(switch-to-buffer buffer))))) | |
(defun persp-all-names (&optional not-frame) | |
(persp--all-names (with-selected-frame not-frame persps-set-curr))) | |
(defun persp--all-names (&optional not-pset) | |
"Return a list of the perspective names for all perspectives-sets. | |
Excludes NOT-PSET, if given." | |
(cl-reduce 'union | |
(let (persp-names) | |
(maphash #'(lambda (pset-name pset) | |
(unless (eq pset not-pset) | |
(let (temp-name-list) | |
(maphash #'(lambda (persp-name _) | |
(push persp-name temp-name-list)) | |
(persps-set-p-hash pset)) | |
(push temp-name-list persp-names)))) | |
perspectives-set-hash) | |
persp-names))) | |
(cl-defun persp-all-get (name not-frame) | |
(persp--all-get name (with-selected-frame not-frame persps-set-curr))) | |
(cl-defun persp--all-get (name not-set) | |
"Returns the list of buffers for a perspective named NAME from any | |
set other than NOT-SET. | |
This doesn't return the window configuration because those can't be | |
copied across frames." | |
(maphash #'(lambda (pset-name pset) | |
(unless (eq pset not-pset) | |
(let ((persp (gethash name (persps-set-p-hash pset)))) | |
(when persp | |
(cl-return-from persp-all-get (persp-buffers persp)))))) | |
perspectives-set-hash)) | |
(defun persp-warning (&rest args) | |
(message "[perspective-mode] Warning: %s" (apply #'format args))) | |
;; ---------------- Save --------------------- | |
(defcustom persp-save-dir (expand-file-name "persp-confs/" user-emacs-directory) | |
"The directory to/from where perspectives saved/loaded by default. | |
Autosave files are saved and loaded to/from this directory." | |
:group 'perspective-mode | |
:type 'directory) | |
(defcustom persp-auto-save-fname "persp-auto-save" | |
"Name of the file for auto save/load perspectives on the perspective-mode | |
deactivation or the emacs shutdown." | |
:group 'perspective-mode | |
:type 'string) | |
(defcustom persp-auto-save-num-of-backups 3 | |
"How many autosave file backups to keep." | |
:group 'perspective-mode | |
:type 'integer) | |
(defcustom persp-filter-save-buffers-functions | |
(list #'(lambda (b) (string-prefix-p " " (buffer-name b))) | |
#'(lambda (b) (string-prefix-p "*" (buffer-name b)))) | |
"If one of these functions returns t - a buffer will not be saved." | |
:group 'perspective-mode | |
:type '(repeat (function :tag "Function"))) | |
(defcustom persp-save-buffer-functions | |
(list #'(lambda (b) | |
(when (tramp-tramp-file-p (buffer-file-name b)) | |
`(def-buffer ,(buffer-name b) | |
,(persp-tramp-save-buffer-file-name b) | |
,(buffer-local-value 'major-mode b)))) | |
#'(lambda (b) | |
(block 'persp-skip-buffer | |
(dolist (f-f persp-filter-save-buffers-functions) | |
(when (funcall f-f b) | |
(return-from 'persp-skip-buffer 'skip))))) | |
#'(lambda (b) | |
`(def-buffer ,(buffer-name b) | |
,(buffer-file-name b) | |
,(buffer-local-value 'major-mode b)))) | |
"Convert a buffer to a structure that could be saved to a file. | |
If a function return nil -- follow to the next function in the list. | |
If a function return 'skip -- don't save a buffer." | |
:group 'perspective-mode | |
:type '(repeat (function :tag "Function"))) | |
(defun persp-tramp-save-buffer-file-name (b) | |
(let ((persp-tramp-file-name tramp-prefix-format) | |
(tmh (tramp-compute-multi-hops (tramp-dissect-file-name (buffer-file-name b))))) | |
(while tmh | |
(let* ((hop (car tmh)) | |
(method (tramp-file-name-method hop)) | |
(user (tramp-file-name-user hop)) | |
(host (tramp-file-name-host hop)) | |
(filename (tramp-file-name-localname hop))) | |
(setq persp-tramp-file-name (concat | |
persp-tramp-file-name | |
method tramp-postfix-method-format | |
user tramp-postfix-user-format | |
host (if (= (string-width filename) 0) | |
tramp-postfix-hop-format | |
(concat tramp-postfix-host-format filename))) | |
tmh (cdr tmh)))) | |
persp-tramp-file-name)) | |
(defun persp-buffer-list-to-savelist (bufferlist) | |
`(def-buffers | |
,(let (ret) | |
(mapc #'(lambda (b) | |
(block 'persp-buffer-to-savelist | |
(let (tmp) | |
(dolist (s-f persp-save-buffer-functions) | |
(setq tmp (funcall s-f b)) | |
(when tmp | |
(when (eq tmp 'skip) (return-from 'persp-buffer-to-savelist)) | |
(push tmp ret) | |
(return-from 'persp-buffer-to-savelist tmp)))))) | |
bufferlist) | |
ret))) | |
(defun persp-window-conf-to-savelist (persp) | |
`(def-winconf ,(if (not (version< emacs-version "24.3")) | |
(let (ret) | |
(with-perspective (persp-name persp) | |
(setq ret (window-state-get (frame-root-window) t))) | |
ret) | |
nil))) | |
(defun persp-local-vars-to-savelist (persp) | |
`(def-persp-locals ,(remove-if | |
#'(lambda (lvar) | |
(and (not (stringp lvar)) | |
(string-match-p "#<.*?>" | |
(prin1-to-string lvar)) | |
(persp-warning "The local variable %S of the perspective %s couldn't be saved." | |
lvar (persp-name persp)) | |
t)) | |
(persp-local-variables persp)))) | |
(defun persp-to-savelist (persp &optional buffer-names) | |
(let ((pbn (mapcar #'(lambda (b) (buffer-name b)) (persp-buffers persp)))) | |
(when buffer-names | |
(setq pbn | |
(delete-if-not #'(lambda (bn) | |
(memq bn buffer-names)) | |
pbn))) | |
`(def-persp ,(and persp (persp-name persp)) | |
(def-buffer-names ,pbn) | |
,(persp-window-conf-to-savelist persp) | |
,(persp-local-vars-to-savelist persp)))) | |
(defun persps-set-to-savelist (p-set &optional buffer-names) | |
`(def-persps-set ,(and p-set (persps-set-name p-set)) | |
(def-persps ,(let (p-sl) | |
(with-perspectives-set (persps-set-get-name-or-gen p-set) | |
(mapcar #'(lambda (persp) | |
(push (persp-to-savelist persp buffer-names) | |
p-sl)) | |
(let (persps-list) | |
(maphash #'(lambda (_ persp) | |
(push persp persps-list)) | |
(persps-set-p-hash p-set)) | |
persps-list))) | |
p-sl)))) | |
(defun persp-everything-to-savelist () | |
(let* ((buffers-savelist (persp-buffer-list-to-savelist (buffer-list))) | |
(buffers-namelist (mapcar #'(lambda (bl) (cadr bl)) | |
(cadr buffers-savelist)))) | |
`(def-persp-config | |
,buffers-savelist | |
(def-persps-sets | |
,(let (ps-sl) | |
(maphash #'(lambda (_ p-set) | |
(when (or (not persp-save-and-load-only-named-sets) | |
(persps-set-name p-set)) | |
(push (persps-set-to-savelist p-set buffers-namelist) | |
ps-sl))) | |
perspectives-set-hash) | |
ps-sl))))) | |
(defun persp-save-all-persps-state () | |
(mapc #'(lambda (f) | |
(with-selected-frame f | |
(persps-set-save-state))) | |
(frame-list))) | |
(defsubst persp-save-with-backups (fname) | |
(when (and (string= fname | |
(concat (expand-file-name persp-save-dir) | |
persp-auto-save-fname)) | |
(> persp-auto-save-num-of-backups 0)) | |
(do ((cur persp-auto-save-num-of-backups (1- cur)) | |
(prev (1- persp-auto-save-num-of-backups) (1- prev))) | |
((> 1 cur) nil) | |
(let ((cf (concat fname (number-to-string cur))) | |
(pf (concat fname (if (> prev 0) | |
(number-to-string prev) | |
"")))) | |
(when (file-exists-p pf) | |
(when (file-exists-p cf) | |
(delete-file cf)) | |
(rename-file pf cf t)))) | |
(when (file-exists-p fname) | |
(rename-file fname (concat fname (number-to-string 1)) t))) | |
(write-file fname nil)) | |
(cl-defun persp-save-state-to-file (&optional (fname persp-auto-save-fname)) | |
(interactive (list (read-file-name "Save perspectives to file: " | |
persp-save-dir))) | |
(when fname | |
(let* ((p-save-dir (or (file-name-directory fname) | |
(expand-file-name persp-save-dir))) | |
(p-save-file (concat p-save-dir (file-name-nondirectory fname)))) | |
(unless (and (file-exists-p p-save-dir) | |
(file-directory-p p-save-dir)) | |
(make-directory p-save-dir t)) | |
(if (not (and (file-exists-p p-save-dir) | |
(file-directory-p p-save-dir))) | |
(persp-error "Can't save perspectives -- `persp-save-dir'(%S) \ | |
does not exist or not a directory." p-save-dir) | |
(persp-save-all-persps-state) | |
(let ((savelist (persp-everything-to-savelist))) | |
(with-temp-buffer | |
(erase-buffer) | |
(goto-char (point-min)) | |
(insert (let ((print-length nil) | |
(print-level nil)) | |
(prin1-to-string savelist))) | |
(persp-save-with-backups p-save-file))))))) | |
;; ---------- Load ----------------------- | |
(defcustom persp-load-buffer-functions | |
(list #'(lambda (savelist) | |
(when (and savelist (eq (car savelist) 'def-buffer)) | |
(cl-destructuring-bind (name fname mode) (cdr savelist) | |
(let ((buf (get-buffer name))) | |
(if (buffer-live-p buf) | |
(if (or (null fname) | |
(string= fname (buffer-file-name buf))) | |
buf | |
(if (file-exists-p fname) | |
(setq buf (find-file-noselect fname)) | |
(persp-warning "The file %s no longer exists." fname) | |
(setq buf nil))) | |
(if (and fname (file-exists-p fname)) | |
(setq buf (find-file-noselect fname)) | |
(when fname | |
(persp-warning "The file %s no longer exists." fname)) | |
(setq buf (get-buffer-create name)))) | |
(when (buffer-live-p buf) | |
(with-current-buffer buf | |
(typecase mode | |
(function (when (and (not (eq major-mode mode)) | |
(not (eq major-mode 'not-loaded-yet))) | |
(funcall mode)))))) | |
buf))))) | |
"Restore a buffer from a saved structure. | |
If a function return nil -- follow to the next function in the list. | |
If a function return 'skip -- don't restore a buffer." | |
:group 'perspective-mode | |
:type '(repeat (function :tag "Function"))) | |
(defun persp-buffers-from-savelist (savelist) | |
(when (and savelist (eq (car savelist) 'def-buffers)) | |
(mapc #'(lambda (saved-buf) | |
(block 'persp-buffer-from-savelist | |
(let (tmp) | |
(dolist (l-f persp-load-buffer-functions) | |
(setq tmp (funcall l-f saved-buf)) | |
(when tmp | |
(when (eq tmp 'skip) | |
(return-from 'persp-buffer-from-savelist)) | |
(return-from 'persp-buffer-from-savelist)))))) | |
(cadr savelist)))) | |
(defun persp-window-conf-from-savelist (savelist) | |
(when (and savelist (eq (car savelist) 'def-winconf)) | |
(cadr savelist))) | |
(defun persp-local-vars-from-savelist (savelist) | |
(when (and savelist (eq (car savelist) 'def-persp-locals)) | |
(cadr savelist))) | |
(defun persp-get-or-new (name) | |
(or (gethash name perspectives-hash) | |
(make-persp :name name))) | |
(defun persp-from-savelist (savelist) | |
(when (and savelist (eq (car savelist) 'def-persp)) | |
(cl-destructuring-bind (name buffer-names wconf locals) (cdr savelist) | |
(let ((persp (persp-get-or-new name))) | |
(when (and buffer-names (eq (car buffer-names) 'def-buffer-names)) | |
(setf (persp-buffers persp) | |
(delete-if-not #'buffer-live-p | |
(mapcar #'(lambda (bn) (get-buffer bn)) (cadr buffer-names))))) | |
(when (and wconf (eq (car wconf) 'def-winconf)) | |
(let ((cwc (current-window-configuration))) | |
(window-state-put (persp-window-conf-from-savelist wconf) | |
(frame-root-window) 'safe) | |
(setf (persp-window-configuration persp) | |
(current-window-configuration)) | |
(with-current-buffer (window-buffer (selected-window)) | |
(setf (persp-point-marker persp) (point-marker)) | |
(setq special-win-conf (current-window-configuration) | |
special-point-marker (point-marker))) | |
(set-window-configuration cwc))) | |
(setf (persp-local-variables persp) (persp-local-vars-from-savelist locals)))))) | |
(defun persps-set-get-or-new (name) | |
(or (gethash name perspectives-set-hash) | |
(make-persps-set :name name))) | |
(defun persps-set-from-savelist (savelist) | |
(when (and savelist (eq (car savelist) 'def-persps-set)) | |
(cl-destructuring-bind (name persps) (cdr savelist) | |
(when (or (not persp-save-and-load-only-named-sets) | |
name) | |
(let ((p-set (persps-set-get-or-new name)) | |
(original-persp-save (symbol-function 'persp-save)) | |
(special-point-marker (point-marker)) | |
(special-win-conf (current-window-configuration))) | |
(flet ((persp-save () | |
(flet ((point-marker () | |
special-point-marker) | |
(current-window-configuration (&optional f) | |
special-win-conf)) | |
(funcall original-persp-save)))) | |
(with-perspectives-set (persps-set-get-name-or-gen p-set) | |
(when (and persps (eq (car persps) 'def-persps)) | |
(mapc #'(lambda (persp) | |
(persp-from-savelist persp)) | |
(cadr persps)))))))))) | |
(defun persp-everything-from-savelist (savelist) | |
(when (and savelist (eq (car savelist) 'def-persp-config)) | |
(cl-destructuring-bind (bufferlist persps-sets) (cdr savelist) | |
(persp-buffers-from-savelist bufferlist) | |
(when (and persps-sets (eq (car persps-sets) 'def-persps-sets)) | |
(mapc #'(lambda (p-set) | |
(persps-set-from-savelist p-set)) | |
(cadr persps-sets)))))) | |
(defun* persp-load-state-from-file (&optional (fname persp-auto-save-fname)) | |
(interactive (list (read-file-name "Load perspectives from file: " | |
persp-save-dir))) | |
(when fname | |
(let ((p-save-file (concat (or (file-name-directory fname) | |
(expand-file-name persp-save-dir)) | |
(file-name-nondirectory fname)))) | |
(if (not (file-exists-p p-save-file)) | |
(persp-warning "Load Error: No such file -- %S." p-save-file) | |
(let ((savelist (with-temp-buffer | |
(goto-char (point-min)) | |
(insert-file-contents p-save-file nil nil nil t) | |
(read (current-buffer))))) | |
(persp-everything-from-savelist savelist)))) | |
(mapc #'(lambda (f) | |
(with-selected-frame f | |
(when persp-curr | |
(set-window-configuration (persp-window-configuration persp-curr))))) | |
(frame-list)))) | |
;; -------- Auto Save/Load --------------- | |
(defcustom persp-auto-save-opt 1 | |
"This variable controls the autosave functionality of the persp-mode: | |
0 -- do not auto save; | |
1 -- save on the emacs shutdown and only if the persp-mode active;" | |
:group 'perspective-mode | |
:type '(choice (integer :tag "Do not save" :value 0) | |
(integer :tag "Save on exit" :value 1))) | |
(defcustom persp-auto-resume-time 3.0 | |
"Delay time in seconds before loading from the autosave file. If <= 0 -- do not autoresume." | |
:group 'perspective-mode | |
:type 'float) | |
(defun persp-auto-save-on-exit () | |
(when (and persp-mode (> persp-auto-save-opt 0)) | |
(persp-save-state-to-file persp-auto-save-fname))) | |
(defun persp-auto-resume () | |
(run-at-time persp-auto-resume-time nil | |
#'(lambda () | |
(when (> persp-auto-resume-time 0) | |
(persp-load-state-from-file))))) | |
(add-hook 'kill-emacs-hook #'persp-auto-save-on-exit) | |
(defun persp-auto-resume-and-remove-from-after-make-frame-functions (f) | |
(persp-auto-resume) | |
(remove-hook 'after-make-frame-functions #'persp-auto-resume-and-remove-from-after-make-frame-functions)) | |
(add-hook 'persp-mode-hook | |
#'(lambda () | |
(if (or noninteractive | |
(and (daemonp) | |
(null (cdr (frame-list))) | |
(eq (selected-frame) terminal-frame))) | |
(add-hook 'after-make-frame-functions #'persp-auto-resume-and-remove-from-after-make-frame-functions) | |
(persp-auto-resume)))) | |
(provide 'perspectives-set) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment