Skip to content

Commit 910f70e

Browse files
committed
Release: v0.6
2 parents 12e2c3a + 7db998d commit 910f70e

File tree

5 files changed

+309
-90
lines changed

5 files changed

+309
-90
lines changed

README.org

+22-4
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33
[[https://elpa.gnu.org/packages/activities.html][file:https://elpa.gnu.org/packages/activities.svg]]
44

5-
Inspired by Genera's and KDE's concepts of "activities", this library allows the user to select an "activity", the loading of which restores a window configuration into a ~tab-bar~ tab or frame, along with the buffers shown in each window. Saving an activity saves the state for later restoration. Switching away from an activity saves the last-used state for later switching back to, while still allowing the activity's initial or default state to be restored on demand. Resuming an activity loads the last-used state, or the initial/default state when a universal argument is provided.
5+
Inspired by Genera's and KDE's concepts of "activities", this Emacs library allows the user to manage frames/tabs, windows, and buffers according to their purpose. An "activity" comprises a frame or tab, its window configuration, and the buffers displayed in them--its "state"; this state would be related to a certain task the user performs at various times, such as developing a certain software project, reading and writing email, working with one's Org mode system, etc.
66

7-
The implementation uses the bookmark system to save buffers' states--that is, any major mode that supports the bookmark system is compatible. A buffer whose major mode does not support the bookmark system (or does not support it well enough to restore useful state) is not compatible and can't be fully restored, or perhaps not at all; but solving that is as simple as implementing bookmark support for the mode, which is usually trivial.
7+
"Suspending" an activity saves the activity's state and closes its frame/tab; the user would do this when finished with the activity's task for the time being. "Resuming" the activity restores its buffers and windows to its frame/tab; the user would do this when ready to resume the task at a later time. This saves the user from having to manually arrange the same windows and buffers each time the task is to be done.
88

9-
Integration with Emacs's ~tab-bar-mode~ is provided: a window configuration or can be restored to a ~tab-bar~ tab or to a frame.
9+
Each activity saves two states: the default state, set when the activity is defined by the user, and the last-used state, which was how the user left it when the activity was suspended (or when Emacs exited, etc). This allows the user to resume the activity where the task was left off, while also allowing it to be reverted to the default state, providing a consistent entry point into the activity.
10+
11+
Internally, the Emacs ~bookmark~ library is used to save and restore buffers' states--that is, any major mode that supports the bookmark system is compatible. A buffer whose major mode does not support the bookmark system (or does not support it well enough to restore useful state) is not compatible and can't be fully restored, or perhaps not at all; but solving that is as simple as implementing bookmark support for the mode, which is often trivial.
1012

1113
Various hooks are (or will be--feedback is welcome) provided, both globally and per-activity, so that the user can define functions to be called when an activity is saved, restored, or switched from/to. For example, this could be used to limit the set of buffers offered for switching to within an activity, or to track the time spent in an activity.
1214

@@ -64,6 +66,7 @@ This is the recommended configuration, in terms of a ~use-package~ form to be pl
6466
("C-x C-a C-k" . activities-kill)
6567
;; This binding mirrors, e.g. "C-x t RET".
6668
("C-x C-a RET" . activities-switch)
69+
("C-x C-a b" . activities-switch-buffer)
6770
("C-x C-a g" . activities-revert)
6871
("C-x C-a l" . activities-list)))
6972
#+END_SRC
@@ -115,6 +118,8 @@ Key bindings are, as always, ultimately up to the user. However, in [[Configura
115118
+ ~activities-resume~ (~C-x C-a C-a~) :: Resume an activity, switching to a new frame or tab for its window configuration, and restoring its buffers. With prefix argument, restore its default state rather than its last.
116119
+ ~activities-revert~ (~C-x C-a g~) :: Revert an activity to its default state.
117120
+ ~activities-switch~ (~C-x C-a RET~) :: Switch to an already-active activity.
121+
+ ~activities-switch-buffer~ (~C-x C-a b~) :: Switch to a buffer associated with the current activity (or, with prefix argument, another activity).
122+
+ ~activities-rename~ :: Rename an activity.
118123
+ ~activities-discard~ :: Discard an activity permanently.
119124
+ ~activities-save-all~ :: Save all active activities' states. (~activities-mode~ does this automatically, so this command should rarely be needed.)
120125

@@ -126,7 +131,7 @@ When option ~activities-bookmark-store~ is enabled, an Emacs bookmark is stored
126131

127132
+ How is this different from [[https://github.com/alphapapa/burly.el][Burly.el]] or [[https://github.com/alphapapa/bufler.el/][Bufler.el]]? :: Burly is a well-polished tool for restoring window and frame configurations, which could be considered an incubator for some of the ideas furthered here. Bufler's ~bufler-workspace~ library uses Burly to provide some similar functionality, which is at an exploratory stage. ~activities~ hopes to provide a longer-term solution more suitable for integration into Emacs.
128133

129-
+ How does this differ from "workspace" packages? :: Yes, there are many Emacs packages that provide "workspace"-like features in one way or another. To date, only Burly and Bufler seem to offer the ability to restore one across Emacs sessions. As mentioned, ~activities~ is intended to be more refined and easier to use (e.g. automatically saving activities' states when ~activities-mode~ is enabled). Comparisons to other packages are left to the reader; suffice to say that ~activities~ is intended to provide what other tools haven't, in an idiomatic, intuitive way. (Feedback is welcome.)
134+
+ How does this differ from "workspace" packages? :: Yes, there are many Emacs packages that provide "workspace"-like features in one way or another. To date, only Burly and Bufler seem to offer the ability to restore one across Emacs sessions, including non-file-backed buffers. As mentioned, ~activities~ is intended to be more refined and easier to use (e.g. automatically saving activities' states when ~activities-mode~ is enabled). Comparisons to other packages are left to the reader; suffice to say that ~activities~ is intended to provide what other tools haven't, in an idiomatic, intuitive way. (Feedback is welcome.)
130135

131136
+ How does this differ from the built-in ~desktop-mode~? :: As best this author can tell, ~desktop-mode~ saves and restores one set of buffers, with various options to control its behavior. It does not use ~bookmark~ internally, which prevents it from restoring non-file-backed buffers. As well, it is not intended to be used on-demand to switch between sets of buffers, windows, or frames (i.e. "activities").
132137

@@ -138,6 +143,19 @@ When option ~activities-bookmark-store~ is enabled, an Emacs bookmark is stored
138143

139144
* Changelog
140145

146+
** v0.6
147+
148+
*Additions*
149+
+ Command ~activities-switch-buffer~ switches to a buffer associated with the current activity (or, with prefix argument, another activity). (A buffer is considered to be associated with an activity if it has been displayed in its tab. Note that this feature currently requires ~activities-tabs-mode~.)
150+
+ Command ~activities-rename~ renames an activity.
151+
+ Option ~activities-after-switch-functions~, a hook called after switching to an activity.
152+
+ Option ~activities-set-frame-name~ sets the frame name after switching to an activity. ([[https://github.com/alphapapa/activities.el/issues/33][#33]]. Thanks to [[https://github.com/jdtsmith][JD Smith]].)
153+
+ Option ~activities-kill-buffers~, when suspending an activity, kills buffers that were only shown in that activity.
154+
155+
*Changes*
156+
+ Default time format in activities list.
157+
+ When saving all activities, don't persist to disk for each activity. ([[https://github.com/alphapapa/activities.el/issues/34][#34]]. Thanks to [[https://github.com/yrns][Al M.]] for reporting.)
158+
141159
** v0.5.1
142160

143161
*Fixes*

activities-list.el

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"Activities list buffer."
3333
:group 'activities)
3434

35-
(defcustom activities-list-time-format "%c"
35+
(defcustom activities-list-time-format "%Y-%m-%d %H:%M:%S"
3636
"Time format for `activities-list' buffer."
3737
:type 'string)
3838

activities-tabs.el

+70-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
"Records the original value of `tab-bar-tab-face-function'.
3636
When `activities-tabs-mode' is enabled.")
3737

38+
(defvar activities-kill-buffers)
39+
3840
;;;; Customization
3941

4042
(defgroup activities-tabs nil
@@ -67,10 +69,12 @@ accordingly."
6769
(activities--set . activities-tabs-activity--set)
6870
(activities--switch . activities-tabs--switch)
6971
(activities-current . activities-tabs-current)
70-
(activities-close . activities-tabs-close))))
72+
(activities-close . activities-tabs-close)
73+
(activities-switch-buffer . activities-tabs--switch-buffer))))
7174
(if activities-tabs-mode
7275
(progn
7376
(tab-bar-mode 1)
77+
(add-hook 'window-configuration-change-hook #'activities-tabs--window-configuration-change)
7478
(advice-add #'activities-resume :before #'activities-tabs-before-resume)
7579
(pcase-dolist (`(,symbol . ,function) override-map)
7680
(advice-add symbol :override function))
@@ -83,22 +87,87 @@ accordingly."
8387
(unless activities-tabs-tab-bar-tab-face-function-original
8488
(setf activities-tabs-tab-bar-tab-face-function-original tab-bar-tab-face-function
8589
tab-bar-tab-face-function #'activities-tabs--tab-bar-tab-face-function)))
90+
(remove-hook 'window-configuration-change-hook #'activities-tabs--window-configuration-change)
8691
(advice-remove #'activities-resume #'activities-tabs-before-resume)
8792
(pcase-dolist (`(,symbol . ,function) override-map)
8893
(advice-remove symbol function))
8994
(when activities-tabs-tab-bar-tab-face-function-original
9095
(setf tab-bar-tab-face-function activities-tabs-tab-bar-tab-face-function-original
9196
activities-tabs-tab-bar-tab-face-function-original nil)))))
9297

98+
;;;; Commands
99+
100+
(defun activities-tabs--switch-buffer (activity)
101+
"Switch to a buffer associated with ACTIVITY.
102+
Interactively, select from buffers associated with ACTIVITY; or,
103+
with prefix argument, choose another activity."
104+
(interactive
105+
(list (if current-prefix-arg
106+
(activities-completing-read)
107+
(or (activities-current) (activities-completing-read)))))
108+
;; Much code borrowed from `read-buffer-to-switch', which see.
109+
(let* ((tab (activities-tabs--tab activity))
110+
(activity-buffers (activities-tabs--tab-parameter 'activities-buffer-list tab))
111+
(current-buffer-name (buffer-name (current-buffer)))
112+
(rbts-completion-table
113+
(apply-partially
114+
#'completion-table-with-predicate
115+
#'internal-complete-buffer
116+
(lambda (buffer-name)
117+
(let ((buffer-name (if (consp buffer-name) (car buffer-name) buffer-name)))
118+
(and (not (equal buffer-name current-buffer-name))
119+
(cl-member buffer-name activity-buffers :key #'buffer-name))))
120+
nil))
121+
(selected-buffer
122+
(minibuffer-with-setup-hook
123+
(lambda ()
124+
(setq-local minibuffer-completion-table rbts-completion-table)
125+
(if (and (boundp 'icomplete-with-completion-tables)
126+
(listp icomplete-with-completion-tables))
127+
(setq-local icomplete-with-completion-tables
128+
(cons rbts-completion-table
129+
icomplete-with-completion-tables))))
130+
(read-buffer "Switch to activity buffer:" (other-buffer (current-buffer))
131+
(confirm-nonexistent-file-or-buffer)))))
132+
(switch-to-buffer selected-buffer)))
133+
93134
;;;; Functions
94135

95136
(cl-defun activities-tabs-close (activity)
96137
"Close ACTIVITY.
97138
Its state is not saved, and its frames, windows, and tabs are
98139
closed."
99140
(activities--switch activity)
141+
(activities-tabs--kill-buffers)
100142
(tab-bar-close-tab))
101143

144+
(defun activities-tabs--window-configuration-change ()
145+
"Add frame's windows' buffers to the current tab's `buffer-list' parameter."
146+
(cl-assert tab-bar-mode)
147+
(let ((tab (tab-bar--current-tab-find)))
148+
(walk-windows (lambda (window)
149+
(cl-pushnew (window-buffer window)
150+
(alist-get 'activities-buffer-list (cdr tab)))))))
151+
152+
(defun activities-tabs--kill-buffers ()
153+
;; TODO: Frame parameter name should be prefixed with `activities'.
154+
"Kill buffers that are only in the current tab's buffer list.
155+
Only does so when `activities-kill-buffers' is non-nil."
156+
(when activities-kill-buffers
157+
(let* ((all-tabs (funcall tab-bar-tabs-function))
158+
(current-tab (tab-bar--current-tab-find))
159+
(tab-buffers
160+
(cl-reduce
161+
(lambda (acc tab)
162+
(seq-difference acc (activities-tabs--tab-parameter 'activities-buffer-list tab)))
163+
(remove current-tab all-tabs)
164+
:initial-value (activities-tabs--tab-parameter 'activities-buffer-list current-tab)))
165+
(target-buffers (cl-remove-if (lambda (buffer)
166+
(run-hook-with-args-until-success
167+
'activities-anti-kill-predicates buffer))
168+
tab-buffers)))
169+
(mapc #'kill-buffer target-buffers))))
170+
102171
(defun activities-tabs--switch (activity)
103172
"Switch to ACTIVITY.
104173
Selects its tab, making one if needed. Its state is not changed."

0 commit comments

Comments
 (0)