-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathwacs-interactive.el
281 lines (245 loc) · 10.2 KB
/
wacs-interactive.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
;;; wacs-interactive.el --- Interactive commands for wacspace -*- lexical-binding: t -*-
;; Copyright © 2013-2014 Emanuel Evans
;; Author: Emanuel Evans <[email protected]>
;; This file is not part of GNU Emacs.
;;; Commentary:
;; Interactive commands for wacspace.el.
;;; License:
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License
;; as published by the Free Software Foundation; either version 3
;; of the License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Code:
(require 'wacs-util)
(require 'wacs-configuration)
;; Declare variables defined in wacs-configuration to keep the
;; compiler happy
(defvar wacs-regexp-buffer-switching)
(defvar wacs-project-base-file)
(defvar wacs-save-frame)
(defun wacs--run-winconf (conf-name)
"Run winconf with name CONF-NAME."
(delete-other-windows)
(-if-let (winconf (wacs--alist-get conf-name wacs--winconfs))
(let ((main-window (selected-window)))
(funcall winconf)
(select-window main-window)
main-window)
(error "No winconf with name: %s" conf-name)))
(defun wacs--set-frame (frame)
"Set the frame using the function set for FRAME."
(-if-let (frame-fn (wacs--alist-get frame wacs--frame-fns))
(funcall frame-fn)
(message "No frame fn specified for frame alignment %s" frame)))
(cl-defmacro wacs--with-property ((prop) &body body)
"Helper macro for using properties within configurations."
(let ((prop-keyword (intern (concat ":" (symbol-name prop)))))
`(let ((,prop (wacs--alist-get ,prop-keyword config)))
(when ,prop
,@body))))
(defun wacs--switch-to-buffer (buffer-string)
"Switch to buffer with name BUFFER-STRING.
If `wacs-regexp-buffer-switching' is set to t, BUFFER-STRING is
interpreted as an unescaped regexp."
(-if-let (buffer
(car (--filter (string= buffer-string
(buffer-name it))
(buffer-list))))
(switch-to-buffer buffer)
(if wacs-regexp-buffer-switching
(-if-let (buffer
(car
(--filter
(string-match-p (regexp-quote buffer-string)
(buffer-name it))
(buffer-list))))
(switch-to-buffer buffer)
(switch-to-buffer buffer-string))
(switch-to-buffer buffer-string))))
(defmacro wacs--update-local-vars ()
"Update local vars in the current buffer.
Variables from `wacs--persistent-local-vars' will be updated."
(cons 'progn
(-map (lambda (var)
`(setq-local ,var ,var))
wacs--persistent-local-vars)))
(defun wacs--set-up-windows (config main-window)
"Set up the windows according to the CONFIG.
MAIN-WINDOW is the window from which `wacspace' was called."
(-each (-take (length (window-list))
'(:main :aux1 :aux2 :aux3 :aux4 :aux5))
(lambda (win-key)
(-when-let (buffer-conf (wacs--alist-get win-key config))
(select-window main-window)
(other-window (string-to-number
(substring (symbol-name win-key) -1)))
(cond ((or (eq buffer-conf :main)
(and (consp buffer-conf) (eq (cdr buffer-conf) :main)))
(switch-to-buffer wacs-main-buffer))
((stringp buffer-conf)
(wacs--switch-to-buffer buffer-conf))
((symbolp buffer-conf)
(funcall buffer-conf))
;; Backwards-compatibility for (:buffer "foo") syntax
((and (consp buffer-conf) (eq (car buffer-conf) :buffer))
(wacs--switch-to-buffer (cdr buffer-conf)))
;; Backwards-compatibility for (:cmd bar) syntax
((and (consp buffer-conf) (eq (car buffer-conf) :cmd))
(funcall (cdr buffer-conf)))
(t (error "Invalid wacspace buffer configuration."))))
(wacs--update-local-vars)))
(wacs--switch-to-window-with-buffer wacs-main-buffer))
(defun wacs--set-up-workspace (config)
"Set up the workspace according to CONFIG."
(let ((wacs-project-base-file
(or (wacs--alist-get :base-file config)
wacs-project-base-file
(file-name-nondirectory (buffer-file-name))))
(wacs--project-name-fn (wacs--alist-get :project-name-fn
config)))
(wacs--with-property (before)
(save-window-excursion
(funcall before)))
(wacs--with-property (frame)
(wacs--set-frame frame))
(let ((main-window
(wacs--with-property (winconf)
(wacs--run-winconf winconf))))
(wacs--set-up-windows config main-window))
(wacs--with-property (run)
(save-window-excursion
(funcall run)))
(wacs--with-property (after)
(save-window-excursion
(funcall after)))
(wacs--with-property (after-switch)
(puthash (wacs-project-name)
after-switch
wacs--after-switch-fns)
(save-window-excursion
(funcall after-switch)))
(message "wacspace configured")))
(defun wacs--update-open-projects (buffer arg)
"Update `wacs--open-projects' with BUFFER and ARG."
(let ((project-name (wacs-project-name)))
(wacs--alist-put project-name
(cons buffer arg)
wacs--open-projects)))
;;;###autoload
(defun wacspace (&optional arg)
"Set up your Emacs workspace.
If there is a saved configuration with numeric prefix ARG,
restore that. Otherwise, set up your workspace based on your
wacspace configuration. If called with universal prefix
arg (\\[universal-argument]), force reconfiguration even if there
is a saved workspace."
(interactive "P")
(when (wacs--u-prefix? arg)
(wacs-clear-saved (current-buffer))
(setq arg nil))
(unless (wacspace-restore arg)
(-if-let* ((wacs-main-buffer (current-buffer))
(config (wacs--get-config arg)))
(progn (wacs--set-up-workspace config)
(wacspace-save arg))
(error
"No wacspace configuration available for the current mode"))))
;;;###autoload
(defun wacspace-save (&optional arg)
"Save the current window configuration with prefix ARG.
When wacspace is invoked in the future in any of the current
buffers with given prefix key, the current workspace will be
restored."
(interactive "P")
(let* ((config-symbol-alist
(gethash (current-buffer)
wacs--saved-workspaces))
(config (if wacs-save-frame
(current-frame-configuration)
(current-window-configuration)))
(current-buffers (-map 'window-buffer (window-list)))
(new-config-alist
(wacs--alist-put (or arg :default)
(cons config current-buffers)
config-symbol-alist)))
(--each (window-list)
(puthash (window-buffer it)
new-config-alist
wacs--saved-workspaces))
(wacs--update-open-projects (current-buffer) arg)
(message "wacspace saved")))
;;;###autoload
(defun wacspace-restore (&optional arg)
"Restore a window configuration saved with prefix key ARG.
Usually, you should call `wacspace' directly instead of this
function unless you want to skip the possibility of
configuration."
(let ((buffer (current-buffer))
(buffer-points
(-map (lambda (b) (cons b (wacs--buffer-point b))) (buffer-list))))
(ignore-errors
(-when-let (config (cadr
(assoc (or arg :default)
(gethash buffer
wacs--saved-workspaces))))
(if wacs-save-frame
(set-frame-configuration config)
(set-window-configuration config))
(--each (wacs--interesting-buffers)
(wacs--set-buffer-point it (wacs--alist-get it buffer-points)))
(wacs--switch-to-window-with-buffer buffer)
(wacs--update-open-projects (current-buffer) arg)
(message "wacspace restored")
t))))
(defun wacspace-switch-project ()
"Quickly switch between open projects."
(interactive)
(if (null wacs--open-projects)
(message "No open projects")
(let* ((project-names (-map 'car wacs--open-projects))
(project (completing-read "Project: " project-names
nil t nil nil
(if (= (length project-names) 1)
(car project-names)
(cadr project-names))))
(config (wacs--alist-get project wacs--open-projects))
(buffer (car config))
(last-prefix (cdr config)))
(switch-to-buffer buffer)
(wacspace last-prefix)
(-when-let (after-switch (gethash project
wacs--after-switch-fns))
(funcall after-switch)))))
(defun wacs-clear-saved (&optional buffer)
"Clear saved workspaces associated with BUFFER.
BUFFER can be a string or a buffer object. If called
interactively, will clear saved workspaces associated with the
current buffer."
(interactive)
(let ((buffer (or buffer (current-buffer))))
(-each (gethash buffer wacs--saved-workspaces)
(lambda (entry)
(-each (cddr entry)
(lambda (buffer)
(remhash buffer wacs--saved-workspaces)))))))
(defun wacs-clear-all-saved ()
"Clear all saved workspaces from this session."
(interactive)
(maphash (lambda (key _) (wacs-clear-saved key))
wacs--saved-workspaces))
(defun wacs--kill-buffer-hook ()
"Hook to clear saved associated workspaces when a buffer is killed."
(wacs-clear-saved (current-buffer)))
(add-hook 'kill-buffer-hook 'wacs--kill-buffer-hook)
(provide 'wacs-interactive)
;;; wacs-interactive.el ends here