-
Notifications
You must be signed in to change notification settings - Fork 22
/
filesets.el
2538 lines (2249 loc) · 84.7 KB
/
filesets.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
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
;;; filesets.el --- handle group of files
;; Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
;; Free Software Foundation, Inc.
;; Author: Thomas Link <sanobast-emacs@yahoo.de>
;; Maintainer: FSF
;; Keywords: filesets convenience
;; This file is part of GNU Emacs.
;; GNU Emacs 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.
;; GNU Emacs 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. If not, see <http://www.gnu.org/licenses/>.
;;; Code:
(defvar filesets-version "1.8.4")
(defvar filesets-homepage
"http://members.a1.net/t.link/CompEmacsFilesets.html")
;;; Commentary:
;; Define filesets, which can be opened or saved with the power of one or
;; two mouse clicks only. A fileset is either a list of files, a file
;; pattern, a base directory and a search pattern (for files), or an
;; inclusion group (i.e. a base file including other files).
;; Usage:
;; 1. Put (require 'filesets) and (filesets-init) in your .emacs file.
;; 2. Type ;; M-x filesets-edit or choose "Edit Filesets" from the menu.
;; 3. Save your customizations.
;; Caveat: Fileset names have to be unique.
;; Filesets.el adds a nifty filesets menu to your menubar. If you change
;; your filesets on the fly, don't forget to select "Save Filesets" from
;; the menu.
;; Pressing on the first item in the submenu will open all files at once.
;; Define your own function, e.g. browse-url, for opening a fileset's
;; files. Or define external viewers for opening files with other
;; programs. See `filesets-external-viewers'.
;; BTW, if you close a fileset, files, which have been changed, will
;; be silently saved. Change this behavior by setting
;; `filesets-save-buffer-fn'.
;;; Supported modes for inclusion groups (`filesets-ingroup-patterns'):
;; - Elisp
;; - Emacs-Wiki (simple names only)
;; - LaTeX
;;; Known bugs:
;;; To do:
;;- better handling of different customization scenarios
;; Data gathering should be better separated from building the menu
;; so that one could (1) use filesets without installing the menu
;; and (2) create new "frontends" to speedbar and others.
;; The functionality to call external viewers should be isolated in
;; an extra package and possibly integrated with the MIME
;; handling.
;;; Credits:
;; Helpful suggestions (but no significant code) were contributed by
;;- Christoph Conrad (at gmx de)
;;- Christian Ohler (at Informatik Uni-Oldenburg DE)
;;- Richard Stallman aka RMS (at gnu org)
;;- Per Abrahamsen aka abraham (at dina kvl dk)
;;; Code:
(eval-when-compile
(require 'cl))
;;; Some variables
(defvar filesets-menu-cache nil
"The whole filesets menu.")
(defvar filesets-cache-version nil
"Filesets' cached version number.")
(defvar filesets-cache-hostname nil
"Filesets' cached system name.")
(defvar filesets-ingroup-cache nil
"A plist containing files and their ingroup data.")
(defvar filesets-ingroup-files nil
"List of files already processed when searching for included files.")
(defvar filesets-has-changed-flag t
"Non-nil means some fileset definition has changed.")
(defvar filesets-submenus nil
"An association list with filesets menu data.")
(defvar filesets-updated-buffers nil
"A list of buffers with updated menu bars.")
(defvar filesets-menu-use-cached-flag nil
"Use cached data. See `filesets-menu-ensure-use-cached' for details.")
(defvar filesets-update-cache-file-flag nil
"Non-nil means the cache needs updating.")
(defvar filesets-ignore-next-set-default nil
"List of custom variables for which the next `set-default' will be ignored.")
(defvar filesets-output-buffer-flag nil
"Non-nil means the current buffer is an output buffer created by filesets.
Is buffer local variable.")
(defvar filesets-verbosity 1
"An integer defining the level of verbosity.
0 means no messages at all.")
(defvar filesets-menu-ensure-use-cached
(and (featurep 'xemacs)
(if (fboundp 'emacs-version>=)
(not (emacs-version>= 21 5))))
"Make sure (X)Emacs uses filesets' cache.
Well, if you use XEmacs (prior to 21.5?) custom.el is loaded after
init.el. This means that settings saved in the cache file (see
`filesets-menu-cache-file') will be overwritten by custom.el. In order
to ensure the use of the cache file, set this variable to t -- which is
the default for XEmacs prior to 21.5. If you want to change this value
put \"(setq filesets-menu-ensure-use-cached VALUE)\" into your startup
file -- before loading filesets.el.
So, when should you think about setting this value to t? If filesets.el
is loaded before user customizations. Thus, if (require 'filesets)
precedes the `custom-set-variables' command or, for XEmacs, if init.el
is loaded before custom.el, set this variable to t.")
;;; utils
(defun filesets-filter-list (lst cond-fn)
"Remove all elements not conforming to COND-FN from list LST.
COND-FN takes one argument: the current element."
; (remove* 'dummy lst :test (lambda (dummy elt)
; (not (funcall cond-fn elt)))))
(let ((rv nil))
(dolist (elt lst rv)
(when (funcall cond-fn elt)
(setq rv (append rv (list elt)))))))
(defun filesets-ormap (fsom-pred lst)
"Return the tail of LST for the head of which FSOM-PRED is non-nil."
(let ((fsom-lst lst)
(fsom-rv nil))
(while (and (not (null fsom-lst))
(null fsom-rv))
(if (funcall fsom-pred (car fsom-lst))
(setq fsom-rv fsom-lst)
(setq fsom-lst (cdr fsom-lst))))
fsom-rv))
(defun filesets-some (fss-pred fss-lst)
"Return non-nil if FSS-PRED is non-nil for any element of FSS-LST.
Like `some', return the first value of FSS-PRED that is non-nil."
(catch 'exit
(dolist (fss-this fss-lst nil)
(let ((fss-rv (funcall fss-pred fss-this)))
(when fss-rv
(throw 'exit fss-rv))))))
;(fset 'filesets-some 'some) ;; or use the cl function
(defun filesets-member (fsm-item fsm-lst &rest fsm-keys)
"Find the first occurrence of FSM-ITEM in FSM-LST.
It is supposed to work like cl's `member*'. At the moment only the :test
key is supported."
(let ((fsm-test (or (plist-get fsm-keys ':test)
(function equal))))
(filesets-ormap (lambda (fsm-this)
(funcall fsm-test fsm-item fsm-this))
fsm-lst)))
;(fset 'filesets-member 'member*) ;; or use the cl function
(defun filesets-sublist (lst beg &optional end)
"Get the sublist of LST from BEG to END - 1."
(let ((rv nil)
(i beg)
(top (or end
(length lst))))
(while (< i top)
(setq rv (append rv (list (nth i lst))))
(setq i (+ i 1)))
rv))
(defun filesets-select-command (cmd-list)
"Select one command from CMD-LIST -- a string with space separated names."
(let ((this (shell-command-to-string
(format "which --skip-alias %s 2> /dev/null | head -n 1"
cmd-list))))
(if (equal this "")
nil
(file-name-nondirectory (substring this 0 (- (length this) 1))))))
(defun filesets-which-command (cmd)
"Call \"which CMD\"."
(shell-command-to-string (format "which %s" cmd)))
(defun filesets-which-command-p (cmd)
"Call \"which CMD\" and return non-nil if the command was found."
(when (string-match (format "\\(/[^/]+\\)?/%s" cmd)
(filesets-which-command cmd))
cmd))
(defun filesets-message (level &rest args)
"Show a message only if LEVEL is greater or equal then `filesets-verbosity'."
(when (<= level (abs filesets-verbosity))
(apply 'message args)))
;;; config file
(defun filesets-save-config ()
"Save filesets' customizations."
(interactive)
(customize-save-customized))
(defun filesets-reset-fileset (&optional fileset no-cache)
"Reset the cached values for one or all filesets."
(if fileset
(setq filesets-submenus (lax-plist-put filesets-submenus fileset nil))
(setq filesets-submenus nil))
(setq filesets-has-changed-flag t)
(setq filesets-update-cache-file-flag (or filesets-update-cache-file-flag
(not no-cache))))
(defun filesets-set-config (fileset var val)
"Set-default wrapper function."
(filesets-reset-fileset fileset)
(set-default var val))
; (customize-set-variable var val))
; (filesets-build-menu))
;; It seems this is a workaround for the XEmacs issue described in the
;; doc-string of filesets-menu-ensure-use-cached. Under Emacs this is
;; essentially just `set-default'.
(defun filesets-set-default (sym val &optional init-flag)
"Set-default wrapper function used in conjunction with `defcustom'.
If SYM is in the list `filesets-ignore-next-set-default', delete
it from that list, and return nil. Otherwise, set the value of
SYM to VAL and return t. If INIT-FLAG is non-nil, set with
`custom-initialize-set', otherwise with `set-default'."
(let ((ignore-flag (member sym filesets-ignore-next-set-default)))
(if ignore-flag
(setq filesets-ignore-next-set-default
(delete sym filesets-ignore-next-set-default))
(if init-flag
(custom-initialize-set sym val)
(set-default sym val)))
(not ignore-flag)))
(defun filesets-set-default! (sym val)
"Call `filestes-set-default' and reset cached data (i.e. rebuild menu)."
(when (filesets-set-default sym val)
(filesets-reset-fileset)))
(defun filesets-set-default+ (sym val)
"Call `filestes-set-default' and reset filesets' standard menu."
(when (filesets-set-default sym val)
(setq filesets-has-changed-flag t)))
; (filesets-reset-fileset nil t)))
(defvar filesets-data)
(defun filesets-data-set-default (sym val)
"Set the default for `filesets-data'."
(if filesets-menu-use-cached-flag
(setq filesets-menu-use-cached-flag nil)
(when (default-boundp 'filesets-data)
(let ((modified-filesets
(filesets-filter-list val
(lambda (x)
(let ((name (car x))
(data (cdr x)))
(let ((elt (assoc name filesets-data)))
(or (not elt)
(not (equal data (cdr elt))))))))))
(dolist (x modified-filesets)
(filesets-reset-fileset (car x))))))
(filesets-set-default sym val))
;;; configuration
(defgroup filesets nil
"The fileset swapper."
:prefix "filesets-"
:group 'convenience
:version "22.1")
(defcustom filesets-menu-name "Filesets"
"Filesets' menu name."
:set (function filesets-set-default)
:type 'string
:group 'filesets)
(defcustom filesets-menu-path '("File") ; cf recentf-menu-path
"The menu under which the filesets menu should be inserted.
See `add-submenu' for documentation."
:set (function filesets-set-default)
:type '(choice (const :tag "Top Level" nil)
(sexp :tag "Menu Path"))
:version "23.1" ; was nil
:group 'filesets)
(defcustom filesets-menu-before "Open File..." ; cf recentf-menu-before
"The name of a menu before which this menu should be added.
See `add-submenu' for documentation."
:set (function filesets-set-default)
:type '(choice (string :tag "Name")
(const :tag "Last" nil))
:version "23.1" ; was "File"
:group 'filesets)
(defcustom filesets-menu-in-menu nil
"Use that instead of `current-menubar' as the menu to change.
See `add-submenu' for documentation."
:set (function filesets-set-default)
:type 'sexp
:group 'filesets)
(defcustom filesets-menu-shortcuts-flag t
"Non-nil means to prepend menus with hopefully unique shortcuts."
:set (function filesets-set-default!)
:type 'boolean
:group 'filesets)
(defcustom filesets-menu-shortcuts-marker "%_"
"String for marking menu shortcuts."
:set (function filesets-set-default!)
:type 'string
:group 'filesets)
;;(defcustom filesets-menu-cnvfp-flag nil
;; "*Non-nil means show \"Convert :pattern to :files\" entry for :pattern menus."
;; :set (function filesets-set-default!)
;; :type 'boolean
;; :group 'filesets)
(defcustom filesets-menu-cache-file
(locate-user-emacs-file "filesets-cache.el")
"File to be used for saving the filesets menu between sessions.
Set this to \"\", to disable caching of menus.
Don't forget to check out `filesets-menu-ensure-use-cached'."
:set (function filesets-set-default)
:type 'file
:group 'filesets)
(put 'filesets-menu-cache-file 'risky-local-variable t)
(defcustom filesets-menu-cache-contents
'(filesets-be-docile-flag
filesets-submenus
filesets-menu-cache
filesets-ingroup-cache)
"Stuff we want to save in `filesets-menu-cache-file'.
Possible uses: don't save configuration data in the main startup files
but in filesets's own cache. In this case add `filesets-data' to this
list.
There is a second reason for putting `filesets-data' on this list. If
you frequently add and remove buffers on the fly to :files filesets, you
don't need to save your customizations if `filesets-data' is being
mirrored in the cache file. In this case the version in the cache file
is the current one, and the version in your startup file will be
silently updated later on.
If you want caching to work properly, at least `filesets-submenus',
`filesets-menu-cache', and `filesets-ingroup-cache' should be in this
list.
Don't forget to check out `filesets-menu-ensure-use-cached'."
:set (function filesets-set-default)
:type '(repeat
(choice :tag "Variable"
(const :tag "filesets-submenus"
:value filesets-submenus)
(const :tag "filesets-menu-cache"
:value filesets-menu-cache)
(const :tag "filesets-ingroup-cache"
:value filesets-ingroup-cache)
(const :tag "filesets-data"
:value filesets-data)
(const :tag "filesets-external-viewers"
:value filesets-external-viewers)
(const :tag "filesets-ingroup-patterns"
:value filesets-ingroup-patterns)
(const :tag "filesets-be-docile-flag"
:value filesets-be-docile-flag)
(sexp :tag "Other" :value nil)))
:group 'filesets)
(defcustom filesets-cache-fill-content-hooks nil
"Hooks to run when writing the contents of filesets' cache file.
The hook is called with the cache file as current buffer and the cursor
at the last position. I.e. each hook has to make sure that the cursor is
at the last position.
Possible uses: If you don't want to save `filesets-data' in your normal
configuration file, you can add a something like this
\(lambda ()
\(insert (format \"(setq-default filesets-data '%S)\"
filesets-data))
\(newline 2))
to this hook.
Don't forget to check out `filesets-menu-ensure-use-cached'."
:set (function filesets-set-default)
:type 'hook
:group 'filesets)
(defcustom filesets-cache-hostname-flag nil
"Non-nil means cache the hostname.
If the current name differs from the cached one,
rebuild the menu and create a new cache file."
:set (function filesets-set-default)
:type 'boolean
:group 'filesets)
(defcustom filesets-cache-save-often-flag nil
"Non-nil means save buffer on every change of the filesets menu.
If this variable is set to nil and if Emacs crashes, the cache and
filesets-data could get out of sync. Set this to t if this happens from
time to time or if the fileset cache causes troubles."
:set (function filesets-set-default)
:type 'boolean
:group 'filesets)
(defcustom filesets-max-submenu-length 25
"Maximum length of submenus.
Set this value to 0 to turn menu splitting off. BTW, parts of submenus
will not be rewrapped if their length exceeds this value."
:set (function filesets-set-default)
:type 'integer
:group 'filesets)
(defcustom filesets-max-entry-length 50
"Truncate names of splitted submenus to this length."
:set (function filesets-set-default)
:type 'integer
:group 'filesets)
(defcustom filesets-browse-dir-function 'dired
"A function or command used for browsing directories.
When using an external command, \"%s\" will be replaced with the
directory's name.
Note: You have to manually rebuild the menu if you change this value."
:set (function filesets-set-default)
:type '(choice :tag "Function:"
(const :tag "dired"
:value dired)
(list :tag "Command"
:value ("" "%s")
(string :tag "Name")
(string :tag "Arguments"))
(function :tag "Function"
:value nil))
:group 'filesets)
(defcustom filesets-open-file-function 'filesets-find-or-display-file
"The function used for opening files.
`filesets-find-or-display-file' ... Filesets' default function for
visiting files. This function checks if an external viewer is defined
for a specific file type. Either this viewer, if defined, or
`find-file' will be used to visit a file.
`filesets-find-file' ... An alternative function that always uses
`find-file'. If `filesets-be-docile-flag' is true, a file, which isn't
readable, will not be opened.
Caveat: Changes will take effect only after rebuilding the menu."
:set (function filesets-set-default)
:type '(choice :tag "Function:"
(const :tag "filesets-find-or-display-file"
:value filesets-find-or-display-file)
(const :tag "filesets-find-file"
:value filesets-find-file)
(function :tag "Function"
:value nil))
:group 'filesets)
(defcustom filesets-save-buffer-function 'save-buffer
"The function used to save a buffer.
Caveat: Changes will take effect after rebuilding the menu."
:set (function filesets-set-default)
:type '(choice :tag "Function:"
(const :tag "save-buffer"
:value save-buffer)
(function :tag "Function"
:value nil))
:group 'filesets)
(defcustom filesets-find-file-delay
(if (and (featurep 'xemacs) gutter-buffers-tab-visible-p)
0.5
0)
"Delay before calling `find-file'.
This is for calls via `filesets-find-or-display-file'
or `filesets-find-file'.
Set this to 0, if you don't use XEmacs' buffer tabs."
:set (function filesets-set-default)
:type 'number
:group 'filesets)
(defcustom filesets-be-docile-flag nil
"Non-nil means don't complain if a file or a directory doesn't exist.
This is useful if you want to use the same startup files in different
computer environments."
:set (function filesets-set-default)
:type 'boolean
:group 'filesets)
(defcustom filesets-sort-menu-flag t
"Non-nil means sort the filesets menu alphabetically."
:set (function filesets-set-default)
:type 'boolean
:group 'filesets)
(defcustom filesets-sort-case-sensitive-flag t
"Non-nil means sorting of the filesets menu is case sensitive."
:set (function filesets-set-default)
:type 'boolean
:group 'filesets)
(defcustom filesets-tree-max-level 3
"Maximum scan depth for directory trees.
A :tree fileset is defined by a base directory the contents of which
will be recursively added to the menu. `filesets-tree-max-level' tells up
to which level the directory structure should be scanned/listed,
i.e. how deep the menu should be. Try something like
\(\"HOME -- only one level\"
\(:tree \"~\" \"^[^.].*[^~]$\")
\(:tree-max-level 1)
\(:filter-dirs-flag t))
\(\"HOME -- up to 3 levels\"
\(:tree \"~\" \"^[^.].*[^~]$\")
\(:tree-max-level 3)
\(:filter-dirs-flag t))
and it should become clear what this option is about. In any case,
including directory trees to the menu can take a lot of memory."
:set (function filesets-set-default)
:type 'integer
:group 'filesets)
(defcustom filesets-commands
`(("Isearch"
multi-isearch-files
(filesets-cmd-isearch-getargs))
("Isearch (regexp)"
multi-isearch-files-regexp
(filesets-cmd-isearch-getargs))
("Query Replace"
perform-replace
(filesets-cmd-query-replace-getargs))
("Query Replace (regexp)"
perform-replace
(filesets-cmd-query-replace-regexp-getargs))
("Grep <<selection>>"
"grep"
("-n " filesets-get-quoted-selection " " "<<file-name>>"))
("Run Shell Command"
filesets-cmd-shell-command
(filesets-cmd-shell-command-getargs)))
"Commands to run on filesets.
An association list of names, functions, and an argument list (or a
function that returns one) to be run on a filesets' files.
The argument <file-name> or <<file-name>> (quoted) will be replaced with
the filename."
:set (function filesets-set-default+)
:type '(repeat :tag "Commands"
(list :tag "Definition" :value ("")
(string "Name")
(choice :tag "Command"
(string :tag "String")
(function :tag "Function"))
(repeat :tag "Argument List"
(choice :tag "Arguments"
(sexp :tag "Sexp"
:value nil)
(string :tag "File Name"
:value "<file-name>")
(string :tag "Quoted File Name"
:value "<<file-name>>")
(function :tag "Function"
:value nil)))))
:group 'filesets)
(put 'filesets-commands 'risky-local-variable t)
(defcustom filesets-external-viewers
(let
;; ((ps-cmd (or (and (boundp 'my-ps-viewer) my-ps-viewer)
;; (filesets-select-command "ggv gv")))
;; (pdf-cmd (or (and (boundp 'my-ps-viewer) my-pdf-viewer)
;; (filesets-select-command "xpdf acroread")))
;; (dvi-cmd (or (and (boundp 'my-ps-viewer) my-dvi-viewer)
;; (filesets-select-command "xdvi tkdvi")))
;; (doc-cmd (or (and (boundp 'my-ps-viewer) my-doc-viewer)
;; (filesets-select-command "antiword")))
;; (pic-cmd (or (and (boundp 'my-ps-viewer) my-pic-viewer)
;; (filesets-select-command "gqview ee display"))))
((ps-cmd "ggv")
(pdf-cmd "xpdf")
(dvi-cmd "xdvi")
(doc-cmd "antiword")
(pic-cmd "gqview"))
`(("^.+\\..?html?$" browse-url
((:ignore-on-open-all t)))
("^.+\\.pdf$" ,pdf-cmd
((:ignore-on-open-all t)
(:ignore-on-read-text t)
(:constraint-flag ,pdf-cmd)))
("^.+\\.e?ps\\(.gz\\)?$" ,ps-cmd
((:ignore-on-open-all t)
(:ignore-on-read-text t)
(:constraint-flag ,ps-cmd)))
("^.+\\.dvi$" ,dvi-cmd
((:ignore-on-open-all t)
(:ignore-on-read-text t)
(:constraint-flag ,dvi-cmd)))
("^.+\\.doc$" ,doc-cmd
((:capture-output t)
(:ignore-on-read-text t)
(:constraint-flag ,doc-cmd)))
("^.+\\.\\(tiff\\|xpm\\|gif\\|pgn\\)$" ,pic-cmd
((:ignore-on-open-all t)
(:ignore-on-read-text t)
(:constraint-flag ,pic-cmd)))))
"Association list of file patterns and external viewers for use with
`filesets-find-or-display-file'.
Has the form ((FILE-PATTERN VIEWER PROPERTIES) ...), VIEWER being either a
function or a command name as string.
Properties is an association list determining filesets' behavior in
several conditions. Choose one from this list:
:ignore-on-open-all ... Don't open files of this type automatically --
i.e. on open-all-files-events or when running commands
:capture-output ... capture an external viewer output
:constraintp FUNCTION ... use this viewer only if FUNCTION returns non-nil
:constraint-flag SEXP ... use this viewer only if SEXP evaluates to non-nil
:open-hook HOOK ... run hooks after spawning the viewer -- mainly useful
in conjunction with :capture-output
:args (FORMAT-STRING or SYMBOL or FUNCTION) ... a list of arguments
\(defaults to (list \"%S\")) when using shell commands
Avoid modifying this variable and achieve minor speed-ups by setting the
variables my-ps-viewer, my-pdf-viewer, my-dvi-viewer, my-pic-viewer.
In order to view pdf or rtf files in an Emacs buffer, you could use these:
\(\"^.+\\\\.pdf\\\\'\" \"pdftotext\"
\((:capture-output t)
\(:args (\"%S - | fmt -w \" window-width))
\(:ignore-on-read-text t)
\(:constraintp (lambda ()
\(and \(filesets-which-command-p \"pdftotext\")
\(filesets-which-command-p \"fmt\"))))))
\(\"^.+\\\\.rtf\\\\'\" \"rtf2htm\"
\((:capture-output t)
\(:args (\"%S 2> /dev/null | w3m -dump -T text/html\"))
\(:ignore-on-read-text t)
\(:constraintp (lambda ()
\(and (filesets-which-command-p \"rtf2htm\")
\(filesets-which-command-p \"w3m\"))))))"
:set (function filesets-set-default)
:type '(repeat :tag "Viewer"
(list :tag "Definition"
:value ("^.+\\.suffix$" "")
(regexp :tag "Pattern")
(choice :tag "Viewer"
(symbol :tag "Function" :value nil)
(string :tag "Program" :value ""))
(repeat :tag "Properties"
(choice
(list :tag ":constraintp"
:value (:constraintp)
(const :format ""
:value :constraintp)
(function :tag "Function"))
(list :tag ":constraint-flag"
:value (:constraint-flag)
(const :format ""
:value :constraint-flag)
(sexp :tag "Symbol"))
(list :tag ":ignore-on-open-all"
:value (:ignore-on-open-all t)
(const :format ""
:value :ignore-on-open-all)
(boolean :tag "Boolean"))
(list :tag ":ignore-on-read-text"
:value (:ignore-on-read-text t)
(const :format ""
:value :ignore-on-read-text)
(boolean :tag "Boolean"))
(list :tag ":args"
:value (:args)
(const :format ""
:value :args)
(repeat :tag "List"
(choice :tag "Arguments"
(string :tag "String"
:value "")
(symbol :tag "Symbol"
:value nil)
(function :tag "Function"
:value nil))))
(list :tag ":open-hook"
:value (:open-hook)
(const :format ""
:value :open-hook)
(hook :tag "Hook"))
; (list :tag ":close-hook"
; :value (:close-hook)
; (const :format ""
; :value :close-hook)
; (hook :tag "Hook"))
(list :tag ":capture-output"
:value (:capture-output t)
(const :format ""
:value :capture-output)
(boolean :tag "Boolean"))))))
:group 'filesets)
(put 'filesets-external-viewers 'risky-local-variable t)
(defcustom filesets-ingroup-patterns
'(("^.+\\.tex$" t
(((:name "Package")
(:pattern "\\\\usepackage\\W*\\(\\[[^\]]*\\]\\W*\\)?{\\W*\\(.+\\)\\W*}")
(:match-number 2)
(:stub-flag t)
(:get-file-name (lambda (master file)
(filesets-which-file master
(concat file ".sty")
(filesets-convert-path-list
(or (getenv "MY_TEXINPUTS")
(getenv "TEXINPUTS")))))))
((:name "Include")
(:pattern "\\\\include\\W*{\\W*\\(.+\\)\\W*}")
(:get-file-name (lambda (master file)
(filesets-which-file master
(concat file ".tex")
(filesets-convert-path-list
(or (getenv "MY_TEXINPUTS")
(getenv "TEXINPUTS"))))))
(:scan-depth 5))
((:name "Input")
(:pattern "\\\\input\\W*{\\W*\\(.+\\)\\W*}")
(:stubp (lambda (a b) (not (filesets-files-in-same-directory-p a b))))
(:get-file-name (lambda (master file)
(filesets-which-file master
(concat file ".tex")
(filesets-convert-path-list
(or (getenv "MY_TEXINPUTS")
(getenv "TEXINPUTS"))))))
(:scan-depth 5))
((:name "Bibliography")
(:pattern "\\\\bibliography\\W*{\\W*\\(.+\\)\\W*}")
(:get-file-name (lambda (master file)
(filesets-which-file master
(concat file ".bib")
(filesets-convert-path-list
(or (getenv "MY_BIBINPUTS")
(getenv "BIBINPUTS")))))))))
("^.+\\.el$" t
(((:name "Require")
(:pattern "(require\\W+'\\(.+\\))")
(:stubp (lambda (a b) (not (filesets-files-in-same-directory-p a b))))
(:get-file-name (lambda (master file)
(filesets-which-file master
(concat file ".el")
load-path))))
((:name "Load")
(:pattern "(load\\(-library\\)?\\W+\"\\(.+\\)\")")
(:match-number 2)
(:get-file-name (lambda (master file)
(filesets-which-file master file load-path))))))
("^\\([A-ZÄÖÜ][a-zäöüß]+\\([A-ZÄÖÜ][a-zäöüß]+\\)+\\)$" t
(((:pattern "\\<\\([A-ZÄÖÜ][a-zäöüß]+\\([A-ZÄÖÜ][a-zäöüß]+\\)+\\)\\>")
(:scan-depth 5)
(:stubp (lambda (a b) (not (filesets-files-in-same-directory-p a b))))
(:case-sensitive t)
(:get-file-name (lambda (master file)
(filesets-which-file
master
file
(if (boundp 'emacs-wiki-directories)
emacs-wiki-directories
nil))))))))
"Inclusion group definitions.
Define how to find included file according to a file's mode (being
defined by a file pattern).
A valid entry has the form (FILE-PATTERN REMOVE-DUPLICATES-FLAG
CMD-DEF1 ...), CMD-DEF1 being a plist containing the fields :pattern
\(mandatory), :name, :get-file-name, :match-number, :scan-depth,
:preprocess, :case-sensitive.
File Pattern ... A regexp matching the file's name for which the
following rules should be applied.
Remove Duplicates ... If t, only the first occurrence of an included
file is retained. (See below for a full explanation.)
:name STRING ... This pattern's name.
:pattern REGEXP ... A regexp matching the command. This regexp has to
include a group that holds the name of the included file.
:get-file-name FUNCTION (default: `filesets-which-file') ... A function
that takes two arguments (the path of the master file and the name
of the included file) and returns a valid path or nil -- if the
subfile can't be found.
:match-number INTEGER (default: 1) ... The number of the match/group
in the pattern holding the subfile's name. 0 refers the whole
match, 1 to the first group.
:stubp FUNCTION ... If (FUNCTION MASTER INCLUDED-FILE) returns non-nil,
INCLUDED-FILE is a stub -- see below.
:stub-flag ... Files of this type are stubs -- see below.
:scan-depth INTEGER (default: 0) ... Whether included files should be
rescanned. Set this to 0 to disable re-scanning of included file.
:preprocess FUNCTION ... A function modifying a buffer holding the
master file so that pattern matching becomes easier. This is usually
used to narrow a buffer to the relevant region. This function could also
be destructive and simply delete non-relevant text.
:case-sensitive BOOLEAN (default: nil) ... Whether a pattern is
case-sensitive or not.
Stubs:
First, a stub is a file that shows up in the menu but will not be
included in an ingroup's file listing -- i.e. filesets will never
operate on this file automatically. Secondly, in opposition to normal
files stubs are not scanned for new inclusion groups. This is useful if
you want to have quick access to library headers.
In the menu, an asterisk is appended to the stub's name.
Remove Duplicates:
E.g. File A and file B refer to file X; X refers to A. If
you choose not to remove duplicates the tree would look like:
M + A - X - A ...
B - X - A ...
As you can see, there is some chance that you run in circles.
Nevertheless, up to some degree this could still be what you want.
With duplicates removed, it would be:
M + A - X
B"
:set (function filesets-set-default)
:type '(repeat
:tag "Include"
(list
:tag "Definition" :value ("^.+\\.suffix$" t)
(regexp :tag "File Pattern" :value "^.+\\.suffix$")
(boolean :tag "Remove Duplicates" :value t)
(repeat :tag "Commands"
(repeat :tag "Command"
(choice
:tag "Definition"
(list :tag ":name"
:value (:name "")
(const :format "" :value :name)
(string :tag "String"))
(list :tag ":pattern"
:value (:pattern "\\<CMD\\W*\\(.+\\)\\>")
(const :format "" :value :pattern)
(regexp :tag "RegExp"))
(list :tag ":get-file-name"
:value (:get-file-name)
(const :format "" :value :get-file-name)
(function :tag "Function"))
(list :tag ":match-number"
:value (:match-number 1)
(const :format "" :value :match-number)
(integer :tag "Integer"))
(list :tag ":stub-flag"
:value (:stub-flag t)
(const :format "" :value :stub-flag)
(boolean :tag "Boolean"))
(list :tag ":stubp"
:value (:stubp)
(const :format "" :value :stubp)
(function :tag "Function"))
(list :tag ":scan-depth"
:value (:scan-depth 0)
(const :format "" :value :scan-depth)
(integer :tag "Integer"))
(list :tag ":case-sensitive"
:value (:case-sensitive)
(const :format "" :value :case-sensitive)
(boolean :tag "Boolean"))
(list :tag ":preprocess"
:value (:preprocess)
(const :format "" :value :preprocess)
(function :tag "Function")))))))
:group 'filesets)
(put 'filesets-ingroup-patterns 'risky-local-variable t)
(defcustom filesets-data nil
"Fileset definitions.
A fileset is either a list of files, a file pattern, a base directory
and a search pattern (for files), or a base file. Changes to this
variable will take effect after rebuilding the menu.
Caveat: Fileset names have to be unique.
Example definition:
'\(\(\"My Wiki\"
\(:ingroup \"~/Etc/My-Wiki/WikiContents\"))
\(\"My Homepage\"
\(:pattern \"~/public_html/\" \"^.+\\\\.html$\")
\(:open filesets-find-file))
\(\"User Configuration\"
\(:files \"~/.xinitrc\"
\"~/.bashrc\"
\"~/.bash_profile\"))
\(\"HOME\"
\(:tree \"~\" \"^[^.].*[^~]$\")
\(:filter-dirs-flag t)))
`filesets-data' is a list of (NAME-AS-STRING . DEFINITION), DEFINITION
being an association list with the fields:
:files FILE-1 .. FILE-N ... a list of files belonging to a fileset
:ingroup FILE-NAME ... an inclusion group's base file.
:tree ROOT-DIR PATTERN ... a base directory and a file pattern
:pattern DIR PATTERN ... a base directory and a regexp matching
files in that directory. Usually,
PATTERN has the form '^REGEXP$'. Unlike
:tree, this form does not descend
recursively into subdirectories.
:filter-dirs-flag BOOLEAN ... is only used in conjunction with :tree.
:tree-max-level INTEGER ... recurse into directories this many levels
\(see `filesets-tree-max-level' for a full explanation)
:dormant-flag BOOLEAN ... non-nil means don't show this item in the
menu; dormant filesets can still be manipulated via commands available
from the minibuffer -- e.g. `filesets-open', `filesets-close', or
`filesets-run-cmd'
:dormant-p FUNCTION ... a function returning :dormant-flag
:open FUNCTION ... the function used to open file belonging to this
fileset. The function takes a file name as argument
:save FUNCTION ... the function used to save file belonging to this
fileset; it takes no arguments, but works on the current buffer.