-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathbufferlo.el
More file actions
5130 lines (4616 loc) · 227 KB
/
bufferlo.el
File metadata and controls
5130 lines (4616 loc) · 227 KB
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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;;; bufferlo.el --- Frame/Tab Local Buffer Lists with Persistence -*- lexical-binding: t -*-
;; Copyright (C) 2021-2025 Free Software Foundation, Inc.
;; Author: Florian Rommel <mail@florommel.de>
;; Stephane Marks <shipmints@gmail.com>
;; Maintainer: Florian Rommel <mail@florommel.de>
;; Stephane Marks <shipmints@gmail.com>
;; Url: https://github.com/florommel/bufferlo
;; Created: 2021-09-15
;; Version: 1.2
;; Package-Requires: ((emacs "29.1"))
;; Keywords: buffer frame tabs local
;; 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 this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; With bufferlo, every frame and tab (i.e., `tab-bar-mode' tab) has an
;; additional manageable local buffer list. A buffer is added to the local
;; buffer list when displayed in the frame/tab (e.g., by opening a new file in
;; the tab or by switching to the buffer from the global buffer list).
;; Using Emacs's built-in buffer-list frame parameter, bufferlo integrates
;; seamlessly with all standard frame and tab management facilities, including
;; undeletion of frames and tabs, tab duplication and moving, frame cloning,
;; and session persistence with `desktop' (though bufferlo frame and tab
;; bookmarks offer an alternative persistence method).
;; Bufferlo provides extensive management functions for its local lists and
;; offers features on top of switch-buffer functions, buffer menu, and
;; `ibuffer'. You can configure any command that selects a buffer to use the
;; local buffer list via bufferlo-anywhere-mode.
;; In addition, bufferlo offers lightweight Emacs bookmarks-based persistence
;; for frames, tabs, and sets of frames/tabs to help you manage your transient
;; workflows. Bufferlo bookmarks are compatible with built-in features such
;; as `bookmark-bmenu-list' and third-party packages such as consult which
;; offers consult-bookmark for interactive bookmark selection.
;;; Code:
(require 'seq)
(require 'tab-bar)
(require 'desktop)
(require 'bookmark)
(require 'ibuffer)
(require 'ibuf-ext)
(defgroup bufferlo nil
"Manage frame/tab-local buffer lists."
:group 'convenience)
(defcustom bufferlo-menu-bar-show t
"Show the bufferlo menu on the menu bar."
:package-version '(bufferlo . "1.1")
:type 'boolean)
(defcustom bufferlo-menu-bar-list-buffers 'both
"Show simple and/or `ibuffer' buffer list menu items.
Set to \\='both to show both.
Set to \\='simple to show simple only.
Set to \\='ibuffer to show `ibuffer' only."
:package-version '(bufferlo . "1.1")
:type '(radio (const :tag "Show both simple and `ibuffer'" both)
(const :tag "Show simple only" simple)
(const :tag "Show `ibuffer' only" ibuffer)))
(defcustom bufferlo-prefer-local-buffers t
"Use the frame `buffer-predicate' to prefer local buffers.
Without this option, buffers from across all frames are
presented. This means that a local buffer will be preferred to be
displayed when the current buffer disappears (buried or killed).
This also influences `next-buffer' and `previous-buffer'.
Set to \\='tabs for `next-buffer' and `previous-buffer' to respect
buffers local to the current tab, otherwise they will cycle
through buffers across the frame.
This variable must be set before enabling `bufferlo-mode'."
:package-version '(bufferlo . "0.8")
:type '(radio (const :tag "Prefer local buffers" t)
(const :tag "Prefer local tab buffers" tabs)
(const :tag "Display all buffers" nil)))
(defcustom bufferlo-include-buried-buffers t
"Include buried buffers in the local list (`bufferlo-buffer-list').
Use `bufferlo-bury' to remove and bury a buffer if this is set to t."
:package-version '(bufferlo . "0.8")
:type 'boolean)
(defcustom bufferlo-include-buffer-filters nil
"Buffers that should always get included in a new tab or frame.
This is a list of regular expressions that match buffer names.
This is applied on frame and tab creation. Included buffers can be
explicitly removed later.
This overrides buffers excluded by `bufferlo-exclude-buffer-filters'."
:package-version '(bufferlo . "0.8")
:type '(repeat regexp))
(defcustom bufferlo-exclude-buffer-filters '(".*")
"Buffers that should always get excluded in a new tab or frame.
This is a list of regular expressions that match buffer names.
This is applied on frame and tab creation. Excluded buffers can be
added explicitly later. Use `bufferlo-hidden-buffers' to permanently
hide buffers from the local list.
Buffers included by `bufferlo-include-buffer-filters' take precedence."
:package-version '(bufferlo . "0.8")
:type '(repeat regexp))
(defcustom bufferlo-hidden-buffers nil
"List of regexps matching names of buffers to hide in the local buffer lists.
Matching buffers are hidden even if displayed in the current frame or tab."
:package-version '(bufferlo . "0.8")
:type '(repeat regexp))
(defcustom bufferlo-kill-buffers-exclude-filters
'("\\` " "\\`\\*Messages\\*\\'" "\\`\\*scratch\\*\\'")
"Buffers that should not be killed by `bufferlo-kill-buffers'.
This is a list of regular expressions that match buffer names."
:package-version '(bufferlo . "0.8")
:type '(repeat regexp))
(defcustom bufferlo-kill-buffers-prompt nil
"If non-nil, confirm before killing local or orphan buffers."
:package-version '(bufferlo . "1.1")
:type 'boolean)
(defcustom bufferlo-kill-modified-buffers-policy nil
"Bufferlo behavior when killing modified or process buffers.
This policy applies to all bufferlo functions that entail killing buffers,
e.g., `bufferlo-kill-buffers', `bufferlo-kill-orphan-buffers',
`bufferlo-tab-close-kill-buffers', `bufferlo-delete-frame-kill-buffers'.
This policy is useful if `shell-mode' or `eshell-mode' buffers are
active in a bufferlo-controlled frame or tab.
nil means default Emacs behavior which may prompt. This may have
side effects.
\\='retain-modified means bufferlo will leave modified buffers as
is.
\\='retain-modified-kill-without-file-name will leave modified buffers
as is BUT will kill buffers that have no file name; this includes shell,
hidden, and special buffers that can not normally be saved.
\\='kill-modified instructs bufferlo to kill modified buffers
without remorse including those with running processes such as
`shell-mode' buffers."
:package-version '(bufferlo . "1.1")
:type '(radio (const :tag "Retain modified buffers" retain-modified)
(const :tag "Retain modified buffers BUT kill buffers without file names"
retain-modified-kill-without-file-name)
(const :tag "Kill modified buffers without prompting"
kill-modified)
(const :tag "Default Emacs behavior (will prompt)" nil)))
(defcustom bufferlo-bookmark-inhibit-bookmark-point nil
"If non-nil, inhibit point in bookmarks.
This is useful if `save-place-mode' mode is enabled and you want it to
handle the place restoration in bufferlo bookmarks."
:package-version '(bufferlo . "1.1")
:type 'boolean)
(defcustom bufferlo-bookmark-buffers-exclude-filters nil
"Buffers that should be excluded from bufferlo bookmarks.
This is a list of regular expressions to filter buffer names.
Buffers included by `bufferlo-bookmark-buffers-include-filters' take
precedence."
:package-version '(bufferlo . "1.1")
:type '(repeat regexp))
(defcustom bufferlo-bookmark-buffers-include-filters nil
"Buffers that should be stored in bufferlo bookmarks.
This is a list of regular expressions to filter buffer names.
This overrides buffers excluded by `bufferlo-bookmark-buffers-exclude-filters'."
:package-version '(bufferlo . "1.1")
:type '(repeat regexp))
(defcustom bufferlo-bookmark-frame-load-make-frame nil
"Frame bookmark loading frame and geometry policy.
If nil, reuse the existing frame.
If non-nil, create a new frame to hold a loaded frame bookmark.
If \\='restore-geometry, create a new frame and restore the frame
geometry to that when it was last saved.
If \\='reuse-restore-geometry, reuse the existing frame and restore the
frame geometry to that when it was last saved."
:package-version '(bufferlo . "1.1")
:type '(radio (const :tag "Make a new frame" t)
(const :tag "Make a new frame and restore its geometry"
restore-geometry)
(const :tag "Reuse the current frame, and restore its geometry"
reuse-restore-geometry)
(const :tag "Reuse the current frame" nil)))
(defcustom bufferlo-delete-frame-kill-buffers-prompt nil
"If non-nil, confirm before deleting the frame and killing buffers."
:package-version '(bufferlo . "1.1")
:type 'boolean)
(defcustom bufferlo-close-tab-kill-buffers-prompt nil
"If non-nil, confirm before closing the tab and killing buffers."
:package-version '(bufferlo . "1.1")
:type 'boolean)
(defcustom bufferlo-bookmark-frame-save-on-delete nil
"Control automatically saving the frame bookmark on frame deletion.
nil does not save the frame bookmark when deleting the frame.
t always saves the frame bookmark when deleting the frame.
Prompts for a new bookmark name if the frame is not associated with a bookmark.
\\='when-bookmarked saves the bookmark only if the frame is already associated
with a current bookmark.
\\='on-kill-buffers behaves like t but only for the function
`bufferlo-delete-frame-kill-buffers'.
\\='on-kill-buffers-when-bookmarked behaves like \\='when-bookmarked but only
for `bufferlo-delete-frame-kill-buffers'."
:package-version '(bufferlo . "1.1")
:type '(radio (const :tag "Do not save" nil)
(const :tag "Always save" t)
(const :tag "Only if the frame is already associated with a bookmark"
when-bookmarked)
(const :tag "Only if killing buffers"
on-kill-buffers)
(const :tag "Only if killing buffers and associated with a bookmark"
on-kill-buffers-when-bookmarked)))
(defcustom bufferlo-bookmark-tab-save-on-close nil
"Control automatically saving the tab bookmark on tab deletion.
nil does not save the tab bookmark when closing the tab.
t always saves the tab bookmark when closing the tab.
Prompts for a new bookmark name if the tab is not associated with a bookmark.
\\='when-bookmarked saves the bookmark only if the tab is already associated
with a current bookmark.
\\='on-kill-buffers behaves like t but only for the function
`bufferlo-tab-close-kill-buffers'.
\\='on-kill-buffers-when-bookmarked behaves like \\='when-bookmarked but only
for `bufferlo-tab-close-kill-buffers'."
:package-version '(bufferlo . "1.1")
:type '(radio (const :tag "Do not save" nil)
(const :tag "Always save" t)
(const :tag "Only if the tab is already associated with a bookmark"
when-bookmarked)
(const :tag "Only if killing buffers"
on-kill-buffers)
(const :tag "Only if killing buffers and associated with a bookmark"
on-kill-buffers-when-bookmarked)))
(defcustom bufferlo-bookmark-frame-load-policy 'prompt
"Control loading a frame bookmark into a already-bookmarked frame.
\\='prompt allows you to select a policy interactively.
\\='disallow-replace prevents accidental replacement of
already-bookmarked frames, with the exception that a bookmarked
frame may be reloaded to restore its state.
\\='replace-frame-retain-current-bookmark replaces the frame
content using the existing frame bookmark name.
\\='replace-frame-adopt-loaded-bookmark replaces the frame content
and adopts the loaded bookmark name.
\\='merge adds new frame bookmark tabs to the existing frame,
retaining the existing bookmark name.
This policy is useful when
`bufferlo-bookmark-frame-load-make-frame' is not enabled or frame
loading is not overridden with a prefix argument that suppresses
making a new frame."
:package-version '(bufferlo . "1.1")
:type '(radio (const :tag "Prompt" prompt)
(const :tag "Disallow" disallow-replace)
(const :tag "Replace frame, retain current bookmark name"
replace-frame-retain-current-bookmark)
(const :tag "Replace frame, adopt loaded bookmark name"
replace-frame-adopt-loaded-bookmark)
(const :tag "Merge" merge)))
(defcustom bufferlo-bookmark-frame-duplicate-policy 'prompt
"Control duplicate active frame bookmarks.
These options help you manage and avoid potential confusion from
duplicate active bookmarks.
\\='prompt allows you to select a policy interactively.
\\='allow allows duplicates.
\\='clear silently clears the frame bookmark.
\\='clear-warn issues a warning message about the frame losing
its bookmark.
\\='ignore bypasses the duplicate.
\\='raise will raise the frame with the existing bookmark. Raise does
not apply when restoring bookmark sets.
Note: \\='raise is considered \\='clear during `bookmark-set' loading."
:package-version '(bufferlo . "1.1")
:type '(radio (const :tag "Prompt" prompt)
(const :tag "Allow" allow)
(const :tag "Clear (silently)" clear)
(const :tag "Clear (with message)" clear-warn)
(const :tag "Ignore" ignore)
(const :tag "Raise" raise)))
(defcustom bufferlo-bookmark-frame-persist-frame-name nil
"If non-nil, store frame's name in its bookmark, restore when loading."
:package-version '(bufferlo . "1.2")
:type 'boolean)
(defcustom bufferlo-bookmark-tab-restore-explicit-name t
"If non-nil, restore the tab's explicit name when loading a tab bookmark."
:package-version '(bufferlo . "1.2")
:type 'boolean)
(defcustom bufferlo-bookmarks-load-tabs-make-frame nil
"If non-nil, make a new frame for tabs loaded by `bufferlo-bookmarks-load'.
If nil, tab bookmarks are loaded into the current frame."
:package-version '(bufferlo . "1.1")
:type 'boolean)
(defcustom bufferlo-bookmark-restore-tab-groups nil
"Control the restoration of tab groups in frame and tab bookmarks
nil never restores tab groups.
t always restores tab groups.
\\='tabs only restores tab groups for tab bookmarks.
\\='frames only restores tab groups for frame bookmarks."
:package-version '(bufferlo . "1.2")
:type '(radio (const :tag "Never" nil)
(const :tag "Always" t)
(const :tag "Only for tab bookmarks" tabs)
(const :tag "Only for frame bookmarks" frames)))
(defcustom bufferlo-bookmark-tab-replace-policy 'replace
"Control whether loaded tabs replace current tabs or occupy new tabs.
\\='prompt allows you to select a policy interactively.
\\='replace clears the current tab and overwrites its content
with the loaded tab.
\\='new loads tab bookmarks into new tabs, honoring the user
option `tab-bar-new-tab-to'."
:package-version '(bufferlo . "1.1")
:type '(radio (const :tag "Prompt" prompt)
(const :tag "Replace" replace)
(const :tag "New" new)))
(defcustom bufferlo-bookmark-tab-duplicate-policy 'prompt
"Control duplicate active tab bookmarks.
These options help you manage and avoid potential confusion from
duplicate active bookmarks.
\\='prompt allows you to select a policy interactively.
\\='allow allows duplicates.
\\='clear silently clears the tab bookmark which is natural
reified frame bookmark behavior.
\\='clear-warn issues a warning message about the tab losing its
bookmark.
\\='ignore bypasses the duplicate.
\\='raise raises the first found existing tab bookmark and its frame.
Raise does not apply when restoring bookmark sets or tabs embedded
within a frame bookmark.
Note: \\='raise is considered \\='clear during `bookmark-set' loading."
:package-version '(bufferlo . "1.1")
:type '(radio (const :tag "Prompt" prompt)
(const :tag "Allow" allow)
(const :tag "Clear (silently)" clear)
(const :tag "Clear (with message)" clear-warn)
(const :tag "Ignore" ignore)
(const :tag "Raise" raise)))
(defcustom bufferlo-bookmark-tab-in-bookmarked-frame-policy 'prompt
"Control how a tab bookmark is loaded into an already-bookmarked frame.
This also warns about setting a new frame bookmark on a frame
that has tab bookmarks, and vice versa setting a tab bookmark on
a bookmarked frame.
\\='clear will silently clear the tab bookmark which is natural
reified frame bookmark behavior.
\\='clear-warn issues a warning message about the tab losing its
bookmark.
\\='allow will retain the tab bookmark to enable it to be saved
or updated. Note that the frame bookmark always supersedes the tab
bookmark if the frame bookmark is saved."
:package-version '(bufferlo . "1.1")
:type '(radio (const :tag "Prompt" prompt)
(const :tag "Allow" allow)
(const :tag "Clear (silently)" clear)
(const :tag "Clear (with message)" clear-warn)))
(defcustom bufferlo-bookmark-tab-failed-buffer-policy nil
"Control failed buffer bookmark restore handling.
This controls the handling of buffers in the bookmark's local buffer list
whose individual bookmark could not be restored (e.g., because the file
does not exist anymore) and for buffers that were not bookmarkable at all.
\\='placeholder creates a unique placeholder buffer in place of the
buffer that could not be restored. By default, the placeholder buffer has
a special name. This buffer will not have a file associated with it.
Each bookmark gets its own unique buffer name.
\\='placeholder-orig creates a placeholder buffer with the original
buffer name. This buffer will not have a file associated with it.
If a buffer with the same name already exists, bufferlo does not create a
placeholder buffer but uses this buffer instead.
Use a string to select or create the buffer named by the string; e.g.,
\"*scratch*\".
Use a function that returns a buffer. The function is passed the
original buffer name that failed to load.
nil does not create placeholder buffers for failed bookmarks. However,
similar to the \\='placeholder-orig policy, if a buffer with the same name
exists in the Emacs session, bufferlo will use this buffer.
If all buffers fail to restore and no matching existing buffers are found,
the default buffer shown will be chosen by Emacs."
:package-version '(bufferlo . "1.1")
:type '(radio (const :tag "Placeholder" placeholder)
(const :tag "Placeholder w/failed buffer name" placeholder-orig)
(string :tag "Buffer to select")
(function :tag "Function to call")
(const :tag "Ignore" nil)))
(defcustom bufferlo-bookmarks-save-duplicates-policy 'prompt
"Control duplicates when saving all bookmarks.
\\='prompt allows you to select a policy interactively.
\\='allow will save potentially differing content for the same
bookmark name multiple times with the last-one-saved taking
precedence. A warning message indicates the names of duplicate
bookmarks.
\\='disallow prevents the potentially confusing of overwriting
bookmark content for the same bookmark names. A warning message
indicates the names of duplicate bookmarks.
Note: When using bufferlo's auto-save feature, and to avoid
repeated prompts and warnings, it is best to choose policies in
advance that prevent duplicate frame and tab bookmarks."
:package-version '(bufferlo . "1.1")
:type '(radio (const :tag "Prompt" prompt)
(const :tag "Allow" allow)
(const :tag "Disallow" disallow)))
(defcustom bufferlo-bookmarks-save-frame-policy 'all
"Control bufferlo bookmarks save frame selection behavior.
\\='current saves bookmarks on the current frame only.
\\='other saves bookmarks on non-current frames.
\\='all saves bookmarks across all frames."
:package-version '(bufferlo . "1.1")
:type '(radio (const :tag "Current frame" current)
(const :tag "Other frames" other)
(const :tag "All frames" all)))
(defcustom bufferlo-bookmarks-save-predicate-functions
(list #'bufferlo-bookmarks-save-all-p)
"Functions to filter active bufferlo bookmarks to save.
These are applied when `bufferlo-bookmarks-auto-save-interval' is > 0,
or manually via `bufferlo-bookmarks-save'. Functions are passed the
bufferlo bookmark name and invoked until the first positive result. Set
to `#'bufferlo-bookmarks-save-all-p' to save all bookmarks or provide
your own predicates (note: be sure to remove
`#'bufferlo-bookmarks-save-all-p' from the list)."
:package-version '(bufferlo . "1.1")
:type 'hook)
(defcustom bufferlo-bookmarks-load-predicate-functions nil
"Functions to filter stored bufferlo bookmarks to load.
These are applied in `bufferlo-bookmarks-load' which might also
be invoked at Emacs startup time using `window-setup-hook'.
Functions are passed the bufferlo bookmark name and invoked until
the first positive result. Set to
`#'bufferlo-bookmarks-load-all-p' to load all bookmarks or
provide your own predicates."
:package-version '(bufferlo . "1.1")
:type 'hook)
(defcustom bufferlo-bookmarks-save-at-emacs-exit 'nosave
"Bufferlo can save active bookmarks at Emacs exit.
\\='nosave does not save any active bookmarks.
\\='all saves all active bufferlo bookmarks.
\\='pred honors the filter predicates
in `bufferlo-bookmarks-save-predicate-functions'."
:package-version '(bufferlo . "1.1")
:type '(radio (const :tag "Do not save at exit" nosave)
(const :tag "Predicate-filtered bookmarks" pred)
(const :tag "All bookmarks" all)))
(defcustom bufferlo-bookmarks-load-at-emacs-startup 'noload
"Bufferlo can load stored bookmarks at Emacs startup.
\\='noload does not load any stored bookmarks.
\\='all loads all stored bufferlo bookmarks.
\\='pred honors the filter predicates in
`bufferlo-bookmarks-load-predicate-functions'.
Note that `bufferlo-mode' must be enabled before
`window-setup-hook' is invoked for this policy to take effect."
:package-version '(bufferlo . "1.1")
:type '(radio (const :tag "Do not load at startup" noload)
(const :tag "Predicate-filtered bookmarks" pred)
(const :tag "All bookmarks" all)))
(defcustom bufferlo-bookmarks-load-at-emacs-startup-tabs-make-frame nil
"If nil, the initial frame is reused for restored tabs.
If non-nil, a new frame is created for restored tabs."
:package-version '(bufferlo . "1.1")
:type 'boolean)
(defcustom bufferlo-ibuffer-bind-local-buffer-filter t
"If non-nil, bind the local buffer filter and the orphan filter in ibuffer.
The local buffer filter is bound to \"/ l\" and the orphan filter to \"/ L\"."
:package-version '(bufferlo . "0.8")
:type 'boolean)
(defcustom bufferlo-ibuffer-bind-keys t
"If non-nil, bind ibuffer convenience keys for bufferlo functions."
:package-version '(bufferlo . "1.1")
:type 'boolean)
(defcustom bufferlo-local-scratch-buffer-name "*local scratch*"
"Base name of the local scratch buffer.
Multiple frame/tabs will use `generate-new-buffer-name' (which
appends \"<N>\" to the name) in order to get a unique buffer.
Local scratch buffers are optional and not used by default.
Use the following functions to create and work with them:
`bufferlo-create-local-scratch-buffer',
`bufferlo-get-local-scratch-buffer',
`bufferlo-switch-to-local-scratch-buffer',
and `bufferlo-toggle-local-scratch-buffer'.
For example, create a dedicated local scratch buffer for all tabs and frames:
(setq \\='tab-bar-new-tab-choice #\\='bufferlo-create-local-scratch-buffer)
(add-hook \\='after-make-frame-functions
#\\='bufferlo-switch-to-local-scratch-buffer)
You can set this to \"*scratch*\"."
:package-version '(bufferlo . "0.8")
:type 'string)
(defcustom bufferlo-local-scratch-buffer-initial-major-mode nil
"The initial major mode for local scratch buffers.
If nil, the local scratch buffers' major mode is set to `initial-major-mode'."
:package-version '(bufferlo . "0.8")
:type 'function)
(defcustom bufferlo-anywhere-filter '(switch-to-buffer
bufferlo-switch-to-buffer
bufferlo-find-buffer
bufferlo-find-buffer-switch)
"The functions that use the local buffer list in `bufferlo-anywhere-mode'.
If `bufferlo-anywhere-filter-type' is set to `exclude', this is an exclude
filter (i.e., determines the functions that do not use the local buffer list).
If `bufferlo-anywhere-filter-type' is set to `include' (or any other value),
this is an include filter.
The value can either be a list of functions, or t (for all functions),
or a custom filter function that takes a function symbol as its argument and
returns whether the probed function should be filtered (non-nil) or
not-filtered (nil)."
:package-version '(bufferlo . "0.8")
:type '(choice (repeat :tag "Filter specific functions" function)
(const :tag "All functions" t)
(function :tag "Custom filter function")))
(defcustom bufferlo-anywhere-filter-type 'exclude
"Determines whether `bufferlo-anywhere-filter' is an include or exclude filter.
Set this to `include' or `exclude'."
:package-version '(bufferlo . "0.8")
:type '(radio (const :tag "Include filter" include)
(const :tag "Exclude filter" exclude)))
(defcustom bufferlo-bookmark-map-functions nil
"Functions to call for every local buffer when making a tab bookmark.
Each function takes a bookmark record as its argument. The corresponding
buffer is set as current buffer. Every function should return a valid
bookmark record or nil. The first function gets the buffer's default
bookmark record or nil if it is not bookmarkable. Subsequent functions
receive the bookmark record that the previous function returned as their
argument. The bookmark record of the last function is used as the
effective record. If the last function returns nil, no record for the
respective buffer is included in the frame or tab bookmark.
These functions are also called when creating a frame bookmark, since a
frame bookmark is a collection of tab bookmarks."
:package-version '(bufferlo . "0.8")
:type 'hook)
(defcustom bufferlo-bookmark-tab-handler-functions nil
"Abnormal hooks to call after a bookmark tab is handled.
Each function takes the following arguments:
bookmark-name: source bookmark name
effective-bookmark-name: nil, if tab bookmark cleared
tab: the handled tab
restored-buffer-names: list of restored buffer names
skipped-buffer-names: list of skipped buffer names"
:package-version '(bufferlo . "1.1")
:type 'hook)
(defcustom bufferlo-bookmark-frame-handler-functions nil
"Abnormal hooks to call after a bookmark frame is handled.
Each function takes the following arguments:
bookmark-name: source bookmark name
effective-bookmark-name: nil, if frame bookmark cleared
new-frame-p: t if this is a new frame, nil if a reused frame
frame: the handled frame"
:package-version '(bufferlo . "1.1")
:type 'hook)
(defcustom bufferlo-bookmark-set-handler-functions nil
"Abnormal hooks to call after a bookmark set is handled.
Each function takes the following arguments:
bookmark-name: source bookmark name
set-bookmark-names: bookmark names handled"
:package-version '(bufferlo . "1.1")
:type 'hook)
(defvar bufferlo--desktop-advice-active nil)
(defvar bufferlo--desktop-advice-active-force nil)
(defvar bufferlo--clear-buffer-lists-active nil)
(defvar bufferlo--bookmarks-auto-save-timer nil
"Timer to save bufferlo bookmarks.
This is controlled by `bufferlo-bookmarks-auto-save-interval'.")
(defun bufferlo--bookmarks-auto-save-timer-maybe-cancel ()
"Cancel and clear the bufferlo bookmark auto-save timer, if set."
(when (timerp bufferlo--bookmarks-auto-save-timer)
(cancel-timer bufferlo--bookmarks-auto-save-timer))
(setq bufferlo--bookmarks-auto-save-timer nil))
(defvar bufferlo-bookmarks-auto-save-interval) ; byte compiler
(defun bufferlo--bookmarks-auto-save-timer-maybe-start ()
"Start the bufferlo auto-save bookmarks timer, if needed."
(bufferlo--bookmarks-auto-save-timer-maybe-cancel)
(when (and (integerp bufferlo-bookmarks-auto-save-interval)
(> bufferlo-bookmarks-auto-save-interval 0))
(setq bufferlo--bookmarks-auto-save-timer
(run-with-timer
bufferlo-bookmarks-auto-save-interval
nil ; We reschedule in `bufferlo-bookmarks-save'.
#'bufferlo-bookmarks-save))))
(defcustom bufferlo-bookmarks-auto-save-interval 0
"Save bufferlo bookmarks every interval of this many seconds.
Set to 0 to disable the timer. Units are whole integer seconds."
:package-version '(bufferlo . "1.1")
:type 'natnum
:set (lambda (sym val)
(set-default sym val)
(bufferlo--bookmarks-auto-save-timer-maybe-start)))
(defcustom bufferlo-bookmarks-auto-save-messages nil
"Control messages from the interval auto saver.
\\=nil inhibits all messages.
\\=t shows all messages.
\\='saved shows a message only when bookmarks have been saved.
\\='notsaved shows a message only when bookmarks have not been saved."
:package-version '(bufferlo . "1.1")
:type '(radio (const :tag "None" nil)
(const :tag "All" t)
(const :tag "Saved only" saved)
(const :tag "Not-saved only" notsaved)))
(defcustom bufferlo-set-restore-geometry-policy 'all
"Bufferlo frame restoration geometry policy.
This affects frame bookmarks inside a bookmark set.
\\='all restores both frame and tab bookmark frame geometries.
\\='frames restores only frame bookmark geometry.
\\='tab-frame restores only tab bookmark logical frame geometry."
:package-version '(bufferlo . "1.1")
:type '(radio (const :tag "All" all)
(const :tag "Frames" frames)
(const :tag "Tabs" tab-frames)))
(defcustom bufferlo-set-restore-tabs-reuse-init-frame nil
"Restore first tabs from a bookmark set's frame to the current frame.
This affects the first frame of tab bookmarks from a bookmark set.
Subsequent frames of tab bookmarks are restored to their own frames."
:package-version '(bufferlo . "1.1")
:type '(radio (const :tag "Reuse" reuse)
(const :tag "Reuse & reset geometry" reuse-reset-geometry)
(const :tag "New frame" nil)))
(defcustom bufferlo-set-restore-ignore-already-active nil
"Ignore already active bookmarks when restoring a bookmark set.
\\='prompt offers to bulk ignore loading active bookmarks
\\='ignore always ignores loading active bookmarks
\\='nil means default to potential duplicate bookmark policies"
:package-version '(bufferlo . "1.1")
:type '(radio (const :tag "Prompt" prompt)
(const :tag "Ignore" ignore)
(const :tag "Default duplicate policies" nil)))
(defcustom bufferlo-frameset-restore-geometry 'bufferlo
"Frameset restore geometry handling control.
\\='native uses Emacs built-in geometry handling.
\\='bufferlo uses bufferlo's geometry handling.
Set to nil to ignore geometry handling."
:package-version '(bufferlo . "1.1")
:type '(radio (const :tag "Emacs" native)
(const :tag "Bufferlo" bufferlo)
(const :tag "Ignore" nil)))
(defcustom bufferlo-frameset-save-filter nil
"Extra Emacs frame parameters to filter before saving a `frameset'.
Use this if you define custom frame parameters, or you use
packages that do, and you want to avoid storing such parameters
in bufferlo framesets."
:package-version '(bufferlo . "1.1")
:type '(repeat symbol))
(defcustom bufferlo-frameset-restore-filter nil
"Extra Emacs frame parameters to filter before restoring a `frameset'.
Use this if you define custom frame parameters, or you use
packages that do, and you want to ensure they are filtered in
advance of restoring bufferlo framesets."
:package-version '(bufferlo . "1.1")
:type '(repeat symbol))
(defcustom bufferlo-frameset-restore-function
#'bufferlo-frameset-restore-default
"Function to restore a frameset, which see `frameset-restore'.
It defaults to `bufferlo-frameset-restore-default'.
The function accepts a single parameter, the `frameset' to restore."
:package-version '(bufferlo . "1.1")
:type 'function)
(defcustom bufferlo-frameset-restore-parameters-function
#'bufferlo-frameset-restore-parameters-default
"Function to create parameters for `frameset-restore', which see.
The function should create a plist of the form:
(list :reuse-frames value
:force-display value
:force-onscreen value
:cleanup-frames value)
where each property is as documented by `frameset-restore'.
It defaults to `bufferlo-frameset-restore-parameters-default'."
:package-version '(bufferlo . "1.1")
:type 'function)
(defcustom bufferlo-frame-geometry-function
#'bufferlo-frame-geometry-default
"Function to produce a bufferlo-frame-geometry alist.
It defaults to `bufferlo-frame-geometry-default'.
The function takes one parameter, FRAME, for which geometry is to
be ascertained. See `bufferlo-frame-geometry-default' for
the returned alist form.
Replace this function with your own if the default produces
suboptimal results for your platform."
:package-version '(bufferlo . "1.1")
:type 'function)
(defcustom bufferlo-set-frame-geometry-function
#'bufferlo-set-frame-geometry-default
"Function to set frame geometry based on bufferlo-frame-geometry alist.
It defaults to `bufferlo-set-frame-geometry-default', which see for
parameters.
Replace this function with your own if the default produces
suboptimal results for your platform."
:package-version '(bufferlo . "1.1")
:type 'function)
(defcustom bufferlo-frame-sleep-for 0
"Window manager catch-up delay for changing frame parameters.
Delay is specified in seconds using `sleep-for', which see.
GTK/GNOME seems to need 0.3 seconds. YMMV.
No delay seems needed on macOS."
:package-version '(bufferlo . "1.1")
:type 'number)
(defcustom bufferlo-mode-line-prefix "Bfl"
"Bufferlo mode-line prefix."
:package-version '(bufferlo . "1.1")
:type 'string)
(defcustom bufferlo-mode-line-left-prefix "["
"Bufferlo mode-line left-hand prefix for an active tab/frame/set bookmark."
:package-version '(bufferlo . "1.1")
:type 'string)
(defcustom bufferlo-mode-line-right-suffix "]"
"Bufferlo mode-line right-hand prefix for an active tab/frame/set bookmark."
:package-version '(bufferlo . "1.1")
:type 'string)
(defcustom bufferlo-mode-line-frame-prefix "F:"
"Bufferlo mode-line frame prefix."
:package-version '(bufferlo . "1.1")
:type 'string)
(defcustom bufferlo-mode-line-tab-prefix "T:"
"Bufferlo mode-line tab prefix."
:package-version '(bufferlo . "1.1")
:type 'string)
(defcustom bufferlo-mode-line-set-active-prefix "S"
"Bufferlo mode-line frame prefix."
:package-version '(bufferlo . "1.1")
:type 'string)
(defcustom bufferlo-mode-line-delimiter "|"
"Delimiter between multiple bookmarks."
:package-version '(bufferlo . "1.1")
:type 'string)
(defvar bufferlo-mode) ; byte compiler
(defvar bufferlo-mode-line-menu) ; byte compiler
(defun bufferlo--mode-line-format-helper (abm str face)
"Bufferlo mode-line helper to add face and mouse features.
Where ABM is the current active bookmark, STR is the mode-line
string, FACE is the face for STR."
(propertize
str 'face face
'mouse-face 'mode-line-highlight
'help-echo (lambda (&rest _)
(concat
(unless (equal abm "")
(format "Active bufferlo bookmark: %s\n" abm))
"mouse-1: Display minor mode menu\n"
"mouse-2: Show help for minor mode"))
'keymap (let ((map (make-sparse-keymap)))
(define-key map [mode-line down-mouse-1]
bufferlo-mode-line-menu)
(define-key map [mode-line down-mouse-3]
bufferlo-mode-line-menu)
(define-key map [mode-line mouse-2]
(lambda ()
(interactive)
(describe-function 'bufferlo-mode)))
map)))
(defvar bufferlo--active-sets) ; byte compiler
(defun bufferlo--active-set-name-for-bookmark (bookmark-name)
"Return the active set name that BOOKMARK-NAME belongs to, or nil."
(unless (null bookmark-name)
(catch :set-name
(dolist (set bufferlo--active-sets)
(when (member bookmark-name
(alist-get 'bufferlo-bookmark-names (cdr set)))
(throw :set-name (car set)))))))
(defun bufferlo-mode-line-format ()
"Bufferlo mode-line format to display the current active frame or tab bookmark."
(when bufferlo-mode
(let* ((fbm (frame-parameter nil 'bufferlo-bookmark-frame-name))
(tbm (alist-get 'bufferlo-bookmark-tab-name
(tab-bar--current-tab-find
(frame-parameter nil 'tabs))))
(set-active (> (length bufferlo--active-sets) 0))
(cache (window-parameter nil 'bufferlo--mode-line-cache)))
(if (equal (cdr cache) (list fbm tbm set-active))
(car cache)
(let* ((abm (concat (when fbm (format "%s (Frame)" fbm))
(when (and fbm tbm) ", ")
(when tbm (format "%s (Tab)" tbm))))
(str (concat
(bufferlo--mode-line-format-helper
abm bufferlo-mode-line-prefix 'bufferlo-mode-line-face)
(when (and bufferlo-mode-line-left-prefix
(or set-active fbm tbm))
(bufferlo--mode-line-format-helper
abm
bufferlo-mode-line-left-prefix
'bufferlo-mode-line-face))
(when set-active
(bufferlo--mode-line-format-helper
abm
(concat bufferlo-mode-line-set-active-prefix
(bufferlo--active-set-name-for-bookmark fbm)
(bufferlo--active-set-name-for-bookmark tbm)
(when (or fbm tbm) bufferlo-mode-line-delimiter))
'bufferlo-mode-line-set-face))
(when fbm
(bufferlo--mode-line-format-helper
abm
(concat bufferlo-mode-line-frame-prefix
fbm
(when tbm bufferlo-mode-line-delimiter))
'bufferlo-mode-line-frame-bookmark-face))
(when tbm
(bufferlo--mode-line-format-helper
abm
(concat bufferlo-mode-line-tab-prefix tbm)
'bufferlo-mode-line-tab-bookmark-face))
(when (and bufferlo-mode-line-right-suffix
(or set-active fbm tbm))
(bufferlo--mode-line-format-helper
abm
bufferlo-mode-line-right-suffix
'bufferlo-mode-line-face))))
(str (if (string-empty-p str) "" (concat " " str))))
(set-window-parameter nil 'bufferlo--mode-line-cache
(list str fbm tbm set-active))
str)))))
(defvar bufferlo-mode-line)
(defun bufferlo--reset-mode-line ()
"Remove the current bufferlo mode line entry."
(setq mode-line-misc-info (delq (assq 'bufferlo-mode mode-line-misc-info)
mode-line-misc-info)))
(defun bufferlo--set-mode-line ()
"Set the current bufferlo mode line entry."
(setq mode-line-misc-info (cons (list 'bufferlo-mode bufferlo-mode-line)
mode-line-misc-info)))
(defcustom bufferlo-mode-line '(:eval (bufferlo-mode-line-format))
"Bufferlo mode line definition."
:package-version '(bufferlo . "1.1")
:type 'sexp
:set (lambda (variable value)
(bufferlo--reset-mode-line) ; do before we overwrite the value
(set-default variable value)
(bufferlo--set-mode-line))
:initialize #'custom-initialize-default
:risky t)
(defgroup bufferlo-faces nil
"Faces used in `bufferlo-mode'."
:package-version '(bufferlo . "1.1")
:group 'bufferlo
:group 'faces)
(defface bufferlo-mode-line-face nil
"`bufferlo-mode' mode-line base face."
:package-version '(bufferlo . "1.1"))