aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJuanma Barranquero2003-04-26 23:41:59 +0000
committerJuanma Barranquero2003-04-26 23:41:59 +0000
commitbe9e7056daaf9112afc394fea96fe3fe67b26070 (patch)
tree323883f02fb1164e02258ee4002adc28672accf7
parent59b2ee6929bad652fab10c85addc02e7394b1bf6 (diff)
downloademacs-be9e7056daaf9112afc394fea96fe3fe67b26070.tar.gz
emacs-be9e7056daaf9112afc394fea96fe3fe67b26070.zip
Major rewrite. The code is reordered, cleaner and faster.
Introduced new options to automatically cleanup the recent list, and to handle filename transformation (for example to use true filenames). (recentf-version): New constant. (recentf-save-file-header): Moved. (recentf-data-cache): New variable. (recentf-update-menu-p, recentf-initialized-p): Removed. (recentf-menu-customization-changed): Moved. Doc fix. (recentf-max-saved-items): Doc fix. (recentf-save-file): Doc fix. No more expand filename here. (recentf-exclude, recentf-menu-action) (recentf-menu-filter): Doc fix. (recentf-menu-append-commands-flag): Renamed from... (recentf-menu-append-commands-p): Made obsolete. (recentf-keep-non-readable-files-flag): Renamed from... (recentf-keep-non-readable-files-p): Made obsolete. (recentf-auto-cleanup, recentf-filename-handler): New options. (recentf-string-equal, recentf-string-lessp) (recentf-string-member): New functions. (recentf-trunc-list): Moved. (recentf-dump-variable): Moved. Better code and output format. (recentf-auto-cleanup-timer): New variable. (recentf-auto-cleanup): New function. (recentf-push, recentf-expand-file-name): New functions. (recentf-add-file): In-lined. Use above functions. (recentf-remove-if-non-readable): In-lined. Expand file name. (recentf-find-file): Use `recentf-remove-if-non-readable'. (recentf-directory-compare): Moved. Use `recentf-string-equal' and `recentf-string-lessp' to do comparisons. (recentf-menu-items-for-commands) (recentf-menu-filter-commands): Moved. (recentf-elements, recentf-make-menu-element) (recentf-menu-element-item, recentf-menu-element-value) (recentf-set-menu-element-item, recentf-set-menu-element-value) (recentf-sub-menu-element-p, recentf-make-default-menu-element) (recentf-menu-elements): In-lined. Some doc fix. (recentf-apply-menu-filter): Better code. (recentf-make-menu-items): Doc fix. Use `recentf-menu-append-commands-flag'. (recentf-make-menu-item): In-lined. Better code. (recentf-clear-data): New function. (recentf-sort-ascending, recentf-sort-descending) (recentf-sort-basenames-ascending) (recentf-sort-basenames-descending) (recentf-sort-directories-ascending) (recentf-sort-directories-descending) (recentf-show-basenames-ascending) (recentf-show-basenames-descending: In-lined. Better code. Some doc fix. (recentf-show-basenames) (recentf-relative-filter): Better code. Doc fix. (recentf-arrange-by-rule-subfilter): Doc fix. Improved :set code. (recentf-match-rule-p): Use filename instead of file-path. (recentf-arrange-by-rule, recentf-build-mode-rules) (recentf-arrange-by-mode, recentf-build-dir-rules) (recentf-file-name-nondir) (recentf-filter-changer-alist): Some doc fix and code cleanup. (recentf-filter-changer-goto-next): Doc fix. Call `recentf-clear-data'. (recentf-filter-changer-get-current) (recentf-filter-changer-get-next): In-lined. Doc fix and better code. (recentf-filter-changer): Doc fix and better code. (recentf-cancel-dialog): Doc fix. (recentf-dialog-mode-map): Initialized in defvar. (recentf-dialog-mode): Doc fix. (recentf-track-opened-file): Renamed from... (recentf-add-file-hook): Removed. (recentf-track-closed-file): Renamed from... (recentf-remove-file-hook): Removed. (recentf-update-menu-hook): Removed. Replaced by... (recentf-update-menu): New. Better catch unnecessary updates. Display a message on error. (recentf-used-hooks): New constant. (recentf-enabled-p): New function. (recentf-edit-selected-items) (recentf-open-files-action) (recentf-open-files-item-shift): Doc fix. (recentf-edit-list-action) (recentf-open-files-item): Doc fix. Code cleanup. (recentf-edit-list, recentf-open-files) (recentf-open-more-files): Likewise. Removed autoload cookie. (recentf-save-list, recentf-cleanup): Likewise. Moved. (recentf-load-list): New command. (recentf-mode): Better code. Does nothing if enabling the already enabled mode.
-rw-r--r--lisp/recentf.el1423
1 files changed, 754 insertions, 669 deletions
diff --git a/lisp/recentf.el b/lisp/recentf.el
index f7683e30698..698e848ce6a 100644
--- a/lisp/recentf.el
+++ b/lisp/recentf.el
@@ -1,17 +1,21 @@
1;;; recentf.el --- setup a menu of recently opened files 1;;; recentf.el --- setup a menu of recently opened files
2 2
3;; Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. 3;; Copyright (C) 1999, 2000, 2001, 2002, 2003
4;; Free Software Foundation, Inc.
4 5
5;; Author: David Ponce <david@dponce.com> 6;; Author: David Ponce <david@dponce.com>
6;; Created: July 19 1999 7;; Created: July 19 1999
7;; Keywords: customization 8;; Maintainer: FSF
9;; Keywords: files
10
11(defconst recentf-version "$Revision$")
8 12
9;; This file is part of GNU Emacs. 13;; This file is part of GNU Emacs.
10 14
11;; GNU Emacs is free software; you can redistribute it and/or modify 15;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by 16;; it under the terms of the GNU General Public License as published
13;; the Free Software Foundation; either version 2, or (at your option) 17;; by the Free Software Foundation; either version 2, or (at your
14;; any later version. 18;; option) any later version.
15 19
16;; GNU Emacs is distributed in the hope that it will be useful, 20;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of 21;; but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -26,49 +30,31 @@
26;;; Commentary: 30;;; Commentary:
27 31
28;; This package maintains a menu for visiting files that were operated 32;; This package maintains a menu for visiting files that were operated
29;; on recently. When enabled a new "Open Recent" submenu is displayed 33;; on recently. When enabled a new "Open Recent" submenu is displayed
30;; in the "Files" menu. The recent files list is automatically saved 34;; in the "Files" menu. The recent files list is automatically saved
31;; across Emacs sessions. You can customize the number of recent 35;; across Emacs sessions. You can customize the number of recent
32;; files displayed, the location of the menu and others options (see 36;; files displayed, the location of the menu and others options (see
33;; the source code for details). To install and use, put the file on 37;; the source code for details).
34;; your Emacs-Lisp load path and add the following into your ~/.emacs 38
35;; startup file: 39;;; History:
36;; 40;;
37;; (require 'recentf)
38;; (recentf-mode 1)
39 41
40;;; Code: 42;;; Code:
41
42(require 'easymenu) 43(require 'easymenu)
43(require 'wid-edit) 44(require 'wid-edit)
45(require 'timer)
44 46
45(defconst recentf-save-file-header 47;;; Internal data
46 ";;; Automatically generated by `recentf' on %s.\n" 48;;
47 "Header to be written into the `recentf-save-file'.")
48
49(defvar recentf-list nil 49(defvar recentf-list nil
50 "List of recently opened files.") 50 "List of recently opened files.")
51 51
52(defvar recentf-update-menu-p t 52(defvar recentf-data-cache nil
53 "Non-nil if the recentf menu must be updated.") 53 "Cache of data used to build the recentf menu.
54 54The menu is rebuilt when this data has changed.")
55(defvar recentf-initialized-p nil 55
56 "Non-nil if recentf already initialized.") 56;;; Customization
57 57;;
58;; IMPORTANT: This function must be defined before the following defcustoms
59;; because it is used in their :set clause. To avoid byte-compiler warnings
60;; the `symbol-value' function is used to access the `recentf-menu-path'
61;; and `recentf-menu-title' values.
62(defun recentf-menu-customization-changed (sym val)
63 "Function called when menu customization has changed.
64It removes the recentf menu and forces its complete redrawing."
65 (when recentf-initialized-p
66 (easy-menu-remove-item nil
67 (symbol-value 'recentf-menu-path)
68 (symbol-value 'recentf-menu-title))
69 (setq recentf-update-menu-p t))
70 (custom-set-default sym val))
71
72(defgroup recentf nil 58(defgroup recentf nil
73 "Maintain a menu of recently opened files." 59 "Maintain a menu of recently opened files."
74 :version "21.1" 60 :version "21.1"
@@ -80,20 +66,30 @@ You should define the options of your own filters in this group."
80 :group 'recentf) 66 :group 'recentf)
81 67
82(defcustom recentf-max-saved-items 20 68(defcustom recentf-max-saved-items 20
83 "*Maximum number of items saved to `recentf-save-file'." 69 "*Maximum number of items of the recent list that will be saved.
70nil means to save the whole list.
71See the command `recentf-save-list'."
84 :group 'recentf 72 :group 'recentf
85 :type 'integer) 73 :type 'integer)
86 74
87(defcustom recentf-save-file (expand-file-name "~/.recentf") 75(defcustom recentf-save-file "~/.recentf"
88 "*File to save `recentf-list' into." 76 "*File to save the recent list into."
89 :group 'recentf 77 :group 'recentf
90 :type 'file) 78 :type 'file)
91 79
92(defcustom recentf-exclude nil 80(defcustom recentf-exclude nil
93 "*List of regexps for filenames excluded from `recentf-list'." 81 "*List of regexps for filenames excluded from the recent list."
94 :group 'recentf 82 :group 'recentf
95 :type '(repeat regexp)) 83 :type '(repeat regexp))
96 84
85(defun recentf-menu-customization-changed (variable value)
86 "Function called when the recentf menu customization has changed.
87Set VARIABLE with VALUE, and force a rebuild of the recentf menu."
88 (when (featurep 'recentf)
89 ;; Unavailable until recentf has been loaded.
90 (recentf-clear-data))
91 (set-default variable value))
92
97(defcustom recentf-menu-title "Open Recent" 93(defcustom recentf-menu-title "Open Recent"
98 "*Name of the recentf menu." 94 "*Name of the recentf menu."
99 :group 'recentf 95 :group 'recentf
@@ -118,11 +114,7 @@ If nil add it at end of menu (see also `easy-menu-change')."
118 114
119(defcustom recentf-menu-action 'recentf-find-file 115(defcustom recentf-menu-action 'recentf-find-file
120 "*Function to invoke with a filename item of the recentf menu. 116 "*Function to invoke with a filename item of the recentf menu.
121The default action `recentf-find-file' calls `find-file' to edit an 117The default is to call `recentf-find-file' to edit the selected file."
122existing file. If the file does not exist or is not readable, it is
123not edited and its name is removed from `recentf-list'. You can use
124`find-file' instead to open non-existing files and keep them in the
125list of recently opened files."
126 :group 'recentf 118 :group 'recentf
127 :type 'function 119 :type 'function
128 :set 'recentf-menu-customization-changed) 120 :set 'recentf-menu-customization-changed)
@@ -137,73 +129,220 @@ list of recently opened files."
137 "*Function used to filter files displayed in the recentf menu. 129 "*Function used to filter files displayed in the recentf menu.
138nil means no filter. The following functions are predefined: 130nil means no filter. The following functions are predefined:
139 131
140- `recentf-sort-ascending' to sort menu items in ascending order. 132- `recentf-sort-ascending'
141- `recentf-sort-descending' to sort menu items in descending order. 133 Sort menu items in ascending order.
142- `recentf-sort-basenames-ascending' to sort file names in descending order. 134- `recentf-sort-descending'
143- `recentf-sort-basenames-descending' to sort file names in descending order. 135 Sort menu items in descending order.
144- `recentf-sort-directories-ascending' to sort directories in ascending order. 136- `recentf-sort-basenames-ascending'
145- `recentf-sort-directories-descending' to sort directories in descending order. 137 Sort menu items by filenames sans directory in ascending order.
146- `recentf-show-basenames' to show file names (no directories) in menu items. 138- `recentf-sort-basenames-descending'
147- `recentf-show-basenames-ascending' to show file names in ascending order. 139 Sort menu items by filenames sans directory in descending order.
148- `recentf-show-basenames-descending' to show file names in descending order. 140- `recentf-sort-directories-ascending'
149- `recentf-relative-filter' to show file names relative to `default-directory'. 141 Sort menu items by directories in ascending order.
150- `recentf-arrange-by-rule' to show sub-menus following user defined rules. 142- `recentf-sort-directories-descending'
151- `recentf-arrange-by-mode' to show a sub-menu for each major mode. 143 Sort menu items by directories in descending order.
152- `recentf-arrange-by-dir' to show a sub-menu for each directory. 144- `recentf-show-basenames'
153- `recentf-filter-changer' to manage a ring of filters. 145 Show filenames sans directory in menu items.
154 146- `recentf-show-basenames-ascending'
155The filter function is called with one argument, the list of menu elements 147 Show filenames sans directory in ascending order.
156used to build the menu and must return a new list of menu elements (see 148- `recentf-show-basenames-descending'
157`recentf-make-menu-element' for menu element form)." 149 Show filenames sans directory in descending order.
150- `recentf-relative-filter'
151 Show filenames relative to `default-directory'.
152- `recentf-arrange-by-rule'
153 Show sub-menus following user defined rules.
154- `recentf-arrange-by-mode'
155 Show a sub-menu for each major mode.
156- `recentf-arrange-by-dir'
157 Show a sub-menu for each directory.
158- `recentf-filter-changer'
159 Manage a ring of filters.
160
161The filter function is called with one argument, the list of menu
162elements used to build the menu and must return a new list of menu
163elements (see `recentf-make-menu-element' for menu element form)."
158 :group 'recentf 164 :group 'recentf
159 :type '(radio (const nil) 165 :type '(radio (const nil)
160 (function-item recentf-sort-ascending) 166 (function-item recentf-sort-ascending)
161 (function-item recentf-sort-descending) 167 (function-item recentf-sort-descending)
162 (function-item recentf-sort-basenames-ascending) 168 (function-item recentf-sort-basenames-ascending)
163 (function-item recentf-sort-basenames-descending) 169 (function-item recentf-sort-basenames-descending)
164 (function-item recentf-sort-directories-ascending) 170 (function-item recentf-sort-directories-ascending)
165 (function-item recentf-sort-directories-descending) 171 (function-item recentf-sort-directories-descending)
166 (function-item recentf-show-basenames) 172 (function-item recentf-show-basenames)
167 (function-item recentf-show-basenames-ascending) 173 (function-item recentf-show-basenames-ascending)
168 (function-item recentf-show-basenames-descending) 174 (function-item recentf-show-basenames-descending)
169 (function-item recentf-relative-filter) 175 (function-item recentf-relative-filter)
170 (function-item recentf-arrange-by-rule) 176 (function-item recentf-arrange-by-rule)
171 (function-item recentf-arrange-by-mode) 177 (function-item recentf-arrange-by-mode)
172 (function-item recentf-arrange-by-dir) 178 (function-item recentf-arrange-by-dir)
173 (function-item recentf-filter-changer) 179 (function-item recentf-filter-changer)
174 function) 180 function)
175 :set 'recentf-menu-customization-changed) 181 :set 'recentf-menu-customization-changed)
176 182
177(defcustom recentf-menu-append-commands-p t 183(defcustom recentf-menu-append-commands-flag t
178 "*If not-nil command items are appended to the menu." 184 "*non-nil means to append command items to the menu."
179 :group 'recentf 185 :group 'recentf
180 :type 'boolean 186 :type 'boolean
181 :set 'recentf-menu-customization-changed) 187 :set 'recentf-menu-customization-changed)
182 188
183(defcustom recentf-keep-non-readable-files-p nil 189(defvaralias 'recentf-menu-append-commands-p
184 "*If nil (default), non-readable files are not kept in `recentf-list'." 190 'recentf-menu-append-commands-flag)
191(make-obsolete-variable 'recentf-menu-append-commands-p
192 'recentf-menu-append-commands-flag
193 "21.4")
194
195(defcustom recentf-keep-non-readable-files-flag nil
196 "*non-nil means to keep non readable files in the recent list."
185 :group 'recentf 197 :group 'recentf
186 :type 'boolean 198 :type 'boolean)
187 :require 'recentf 199
188 :initialize 'custom-initialize-default 200(defvaralias 'recentf-keep-non-readable-files-p
189 :set (lambda (sym val) 201 'recentf-keep-non-readable-files-flag)
190 (if val 202(make-obsolete-variable 'recentf-keep-non-readable-files-p
191 (remove-hook 'kill-buffer-hook 'recentf-remove-file-hook) 203 'recentf-keep-non-readable-files-flag
192 (add-hook 'kill-buffer-hook 'recentf-remove-file-hook)) 204 "21.4")
193 (custom-set-default sym val))) 205
206(defcustom recentf-auto-cleanup 'mode
207 "*Define when to automatically cleanup the recent list.
208The following values can be set:
209
210- `mode'
211 Cleanup when turning the mode on (default).
212- `never'
213 Never cleanup the list automatically.
214- A number
215 Cleanup each time Emacs has been idle that number of seconds.
216- A time string
217 Cleanup at specified time string, for example at \"11:00pm\".
218
219Setting this variable directly does not take effect;
220use \\[customize].
221
222See also the command `recentf-cleanup', that can be used to manually
223cleanup the list."
224 :group 'recentf
225 :type '(radio (const :tag "When mode enabled"
226 :value mode)
227 (const :tag "Never"
228 :value never)
229 (number :tag "When idle that seconds"
230 :value 300)
231 (string :tag "At time"
232 :value "11:00pm"))
233 :set (lambda (variable value)
234 (set-default variable value)
235 (when (featurep 'recentf)
236 ;; Unavailable until recentf has been loaded.
237 (recentf-auto-cleanup))))
194 238
195(defcustom recentf-load-hook nil 239(defcustom recentf-load-hook nil
196 "*Normal hook run at end of loading the `recentf' package." 240 "*Normal hook run at end of loading the `recentf' package."
197 :group 'recentf 241 :group 'recentf
198 :type 'hook) 242 :type 'hook)
199 243
200;;;; 244(defcustom recentf-filename-handler nil
201;;;; Common functions 245 "Function to call to process filename handled by recentf.
202;;;; 246It is passed a filename to give a chance to transform it.
247If it returns nil, the filename is left unchanged."
248 :group 'recentf
249 :type 'function)
250
251;;; Utilities
252;;
203(defconst recentf-case-fold-search 253(defconst recentf-case-fold-search
204 (memq system-type '(vax-vms windows-nt cygwin)) 254 (memq system-type '(vax-vms windows-nt cygwin))
205 "Non-nil if recentf searches and matches should ignore case.") 255 "Non-nil if recentf searches and matches should ignore case.")
206 256
257(defsubst recentf-string-equal (s1 s2)
258 "Return non-nil if strings S1 and S2 have identical contents.
259Ignore case if `recentf-case-fold-search' is non-nil."
260 (if recentf-case-fold-search
261 (string-equal (downcase s1) (downcase s2))
262 (string-equal s1 s2)))
263
264(defsubst recentf-string-lessp (s1 s2)
265 "Return non-nil if string S1 is less than S2 in lexicographic order.
266Ignore case if `recentf-case-fold-search' is non-nil."
267 (if recentf-case-fold-search
268 (string-lessp (downcase s1) (downcase s2))
269 (string-lessp s1 s2)))
270
271(defun recentf-string-member (elt list)
272 "Return non-nil if ELT is an element of LIST.
273The value is actually the tail of LIST whose car is ELT.
274ELT must be a string and LIST a list of strings.
275Ignore case if `recentf-case-fold-search' is non-nil."
276 (while (and list (not (recentf-string-equal elt (car list))))
277 (setq list (cdr list)))
278 list)
279
280(defsubst recentf-trunc-list (l n)
281 "Return from L the list of its first N elements."
282 (let (nl)
283 (while (and l (> n 0))
284 (setq nl (cons (car l) nl)
285 n (1- n)
286 l (cdr l)))
287 (nreverse nl)))
288
289(defun recentf-dump-variable (variable &optional limit)
290 "Insert a \"(setq VARIABLE value)\" in the current buffer.
291When the value of VARIABLE is a list, optional argument LIMIT
292specifies a maximum number of elements to insert. By default insert
293the full list."
294 (let ((value (symbol-value variable)))
295 (if (atom value)
296 (insert (format "\n(setq %S %S)\n" variable value))
297 (when (and (integerp limit) (> limit 0))
298 (setq value (recentf-trunc-list value limit)))
299 (insert (format "\n(setq %S\n '(" variable))
300 (dolist (e value)
301 (insert (format "\n %S" e)))
302 (insert "\n ))\n"))))
303
304(defvar recentf-auto-cleanup-timer nil
305 "Timer used to automatically cleanup the recent list.
306See also the option `recentf-auto-cleanup'.")
307
308(defun recentf-auto-cleanup ()
309 "Automatic cleanup of the recent list."
310 (when (timerp recentf-auto-cleanup-timer)
311 (cancel-timer recentf-auto-cleanup-timer))
312 (when recentf-mode
313 (setq recentf-auto-cleanup-timer
314 (cond
315 ((eq 'mode recentf-auto-cleanup)
316 (recentf-cleanup)
317 nil)
318 ((numberp recentf-auto-cleanup)
319 (run-with-idle-timer
320 recentf-auto-cleanup t 'recentf-cleanup))
321 ((stringp recentf-auto-cleanup)
322 (run-at-time
323 recentf-auto-cleanup nil 'recentf-cleanup))))))
324
325;;; File functions
326;;
327(defsubst recentf-push (filename)
328 "Push FILENAME into the recent list, if it isn't there yet.
329If it is there yet, move it at the beginning of the list.
330If `recentf-case-fold-search' is non-nil, ignore case when comparing
331filenames."
332 (let ((m (recentf-string-member filename recentf-list)))
333 (and m (setq recentf-list (delq (car m) recentf-list)))
334 (push filename recentf-list)))
335
336(defsubst recentf-expand-file-name (name)
337 "Convert filename NAME to absolute, and canonicalize it.
338See also the function `expand-file-name'.
339If defined, call the function `recentf-filename-handler' to post
340process the canonical name."
341 (let* ((filename (expand-file-name name)))
342 (or (and recentf-filename-handler
343 (funcall recentf-filename-handler filename))
344 filename)))
345
207(defun recentf-include-p (filename) 346(defun recentf-include-p (filename)
208 "Return t if FILENAME match none of the `recentf-exclude' regexps." 347 "Return t if FILENAME match none of the `recentf-exclude' regexps."
209 (let ((case-fold-search recentf-case-fold-search) 348 (let ((case-fold-search recentf-case-fold-search)
@@ -212,86 +351,111 @@ used to build the menu and must return a new list of menu elements (see
212 (setq rl (cdr rl))) 351 (setq rl (cdr rl)))
213 (null rl))) 352 (null rl)))
214 353
215(defun recentf-add-file (filename) 354(defsubst recentf-add-file (filename)
216 "Add or move FILENAME at the beginning of `recentf-list'. 355 "Add or move FILENAME at the beginning of the recent list.
217Does nothing if FILENAME matches one of the `recentf-exclude' regexps." 356Does nothing it if it matches any of the `recentf-exclude' regexps."
218 (let ((filename (expand-file-name filename))) 357 (setq filename (recentf-expand-file-name filename))
219 (when (recentf-include-p filename) 358 (when (recentf-include-p filename)
220 (setq recentf-list (cons filename (delete filename recentf-list))) 359 (recentf-push filename)))
221 (setq recentf-update-menu-p t))))
222 360
223(defun recentf-remove-if-non-readable (filename) 361(defsubst recentf-remove-if-non-readable (filename)
224 "Remove FILENAME from `recentf-list' if not readable." 362 "Remove FILENAME from the recent list, if file is not readable.
363Return non-nil if FILENAME has been removed."
225 (unless (file-readable-p filename) 364 (unless (file-readable-p filename)
226 (setq recentf-list (delete filename recentf-list)) 365 (let ((m (recentf-string-member
227 (setq recentf-update-menu-p t))) 366 (recentf-expand-file-name filename) recentf-list)))
367 (and m (setq recentf-list (delq (car m) recentf-list))))))
228 368
229(defun recentf-find-file (filename) 369(defun recentf-find-file (filename)
230 "Edit file FILENAME using `find-file'. 370 "Edit file FILENAME using `find-file'.
231If FILENAME is not readable it is removed from `recentf-list'." 371If the file does not exist or is non readable, and
232 (if (file-readable-p filename) 372`recentf-keep-non-readable-files-flag' is nil, it is not edited and
233 (find-file filename) 373its name is removed from the recent list."
234 (progn 374 (if (and (not recentf-keep-non-readable-files-flag)
235 (message "File `%s' not found." filename) 375 (recentf-remove-if-non-readable filename))
236 (setq recentf-list (delete filename recentf-list)) 376 (message "File `%s' not found" filename)
237 (setq recentf-update-menu-p t)))) 377 (find-file filename)))
238 378
239(defun recentf-trunc-list (l n) 379(defsubst recentf-directory-compare (f1 f2)
240 "Return a list of the first N elements of L." 380 "Compare absolute filenames F1 and F2.
241 (let ((lh nil)) 381First compare directories, then filenames sans directory.
242 (while (and l (> n 0)) 382Return non-nil if F1 is less than F2."
243 (setq lh (cons (car l) lh)) 383 (let ((d1 (file-name-directory f1))
244 (setq n (1- n)) 384 (d2 (file-name-directory f2)))
245 (setq l (cdr l))) 385 (if (recentf-string-equal d1 d2)
246 (nreverse lh))) 386 (recentf-string-lessp (file-name-nondirectory f1)
387 (file-name-nondirectory f2))
388 (recentf-string-lessp d1 d2))))
389
390;;; Menu building
391;;
392(defvar recentf-menu-items-for-commands
393 (list ["Cleanup list"
394 recentf-cleanup
395 :help "Remove all non-readable and excluded files from the recent list"
396 :active t]
397 ["Edit list..."
398 recentf-edit-list
399 :help "Edit the files that are kept in the recent list"
400 :active t]
401 ["Save list now"
402 recentf-save-list
403 :help "Save the list of recently opened files now"
404 :active t]
405 ["Options..."
406 (customize-group "recentf")
407 :help "Customize recently opened files menu and options"
408 :active t]
409 )
410 "List of menu items for recentf commands.")
247 411
248(defun recentf-elements (n) 412(defvar recentf-menu-filter-commands nil
249 "Return a list of the first N elements of `recentf-list'." 413 "This variable can be used by menu filters to setup their own command menu.
414If non-nil it must contain a list of valid menu-items to be appended
415to the recent file list part of the menu. Before calling a menu
416filter function this variable is reset to nil.")
417
418(defsubst recentf-elements (n)
419 "Return a list of the first N elements of the recent list."
250 (recentf-trunc-list recentf-list n)) 420 (recentf-trunc-list recentf-list n))
251 421
252(defun recentf-make-menu-element (menu-item menu-value) 422(defsubst recentf-make-menu-element (menu-item menu-value)
253 "Create a new menu-element. 423 "Create a new menu-element.
254 424A menu element is a pair (MENU-ITEM . MENU-VALUE), where MENU-ITEM is
255A menu element is a pair (MENU-ITEM . MENU-VALUE) where: 425the menu item string displayed. MENU-VALUE is the file to be open
256 426when the corresponding MENU-ITEM is selected. Or it is a
257- - MENU-ITEM is the menu item string displayed. 427pair (SUB-MENU-TITLE . MENU-ELEMENTS) where SUB-MENU-TITLE is a
258- - MENU-VALUE is the path used to open the file when the 428sub-menu title and MENU-ELEMENTS is the list of menu elements in the
259 corresponding MENU-ITEM is selected. Or it is 429sub-menu."
260 a pair (SUB-MENU-TITLE . MENU-ELEMENTS) where
261 SUB-MENU-TITLE is a sub-menu title and
262 MENU-ELEMENTS is the list of menu elements in
263 the sub-menu."
264 (cons menu-item menu-value)) 430 (cons menu-item menu-value))
265 431
266(defun recentf-menu-element-item (e) 432(defsubst recentf-menu-element-item (e)
267 "Return the item part of the menu-element E." 433 "Return the item part of the menu-element E."
268 (car e)) 434 (car e))
269 435
270(defun recentf-menu-element-value (e) 436(defsubst recentf-menu-element-value (e)
271 "Return the value part of the menu-element E." 437 "Return the value part of the menu-element E."
272 (cdr e)) 438 (cdr e))
273 439
274(defun recentf-set-menu-element-item (e item) 440(defsubst recentf-set-menu-element-item (e item)
275 "Change the item part of menu-element E to ITEM." 441 "Change the item part of menu-element E to ITEM."
276 (setcar e item)) 442 (setcar e item))
277 443
278(defun recentf-set-menu-element-value (e value) 444(defsubst recentf-set-menu-element-value (e value)
279 "Change the value part of menu-element E to VALUE." 445 "Change the value part of menu-element E to VALUE."
280 (setcdr e value)) 446 (setcdr e value))
281 447
282(defun recentf-sub-menu-element-p (e) 448(defsubst recentf-sub-menu-element-p (e)
283 "Return non-nil if menu-element E defines a sub-menu." 449 "Return non-nil if menu-element E defines a sub-menu."
284 (consp (recentf-menu-element-value e))) 450 (consp (recentf-menu-element-value e)))
285 451
286(defun recentf-make-default-menu-element (file-path) 452(defsubst recentf-make-default-menu-element (file)
287 "Make a new default menu element (MENU-ITEM . MENU-VALUE). 453 "Make a new default menu element with FILE.
288Do so for the given recent file path FILE-PATH. MENU-ITEM and 454This a menu element (FILE . FILE)."
289MENU-VALUE are set to FILE-PATH. See also 455 (recentf-make-menu-element file file))
290`recentf-make-menu-element'."
291 (recentf-make-menu-element file-path file-path))
292 456
293(defun recentf-menu-elements (n) 457(defsubst recentf-menu-elements (n)
294 "Return a list of the first N default menu elements from `recentf-list'. 458 "Return a list of the first N default menu elements from the recent list.
295See also `recentf-make-default-menu-element'." 459See also `recentf-make-default-menu-element'."
296 (mapcar 'recentf-make-default-menu-element 460 (mapcar 'recentf-make-default-menu-element
297 (recentf-elements n))) 461 (recentf-elements n)))
@@ -301,69 +465,31 @@ See also `recentf-make-default-menu-element'."
301It takes care of sub-menu elements in L and recursively apply FILTER 465It takes care of sub-menu elements in L and recursively apply FILTER
302to them. It is guaranteed that FILTER receives only a list of single 466to them. It is guaranteed that FILTER receives only a list of single
303menu-elements (no sub-menu)." 467menu-elements (no sub-menu)."
304 (if (and (functionp filter) l) 468 (if (and l (functionp filter))
305 (let ((case-fold-search recentf-case-fold-search) 469 (let ((case-fold-search recentf-case-fold-search)
306 menu-element sub-menu-elements single-elements) 470 elts others)
307 ;; split L in two sub-listes: 471 ;; split L into two sub-listes, one of sub-menus elements and
308 ;; one of sub-menus elements and 472 ;; another of single menu elements.
309 ;; one of single menu elements 473 (dolist (elt l)
310 (while l 474 (if (recentf-sub-menu-element-p elt)
311 (setq menu-element (car l)) 475 (push elt elts)
312 (if (recentf-sub-menu-element-p menu-element) 476 (push elt others)))
313 (setq sub-menu-elements 477 ;; Apply FILTER to single elements.
314 (cons menu-element sub-menu-elements)) 478 (when others
315 (setq single-elements 479 (setq others (funcall filter (nreverse others))))
316 (cons menu-element single-elements))) 480 ;; Apply FILTER to sub-menu elements.
317 (setq l (cdr l))) 481 (setq l nil)
318 ;; apply FILTER to the list of single menu elements 482 (dolist (elt elts)
319 (if single-elements
320 (setq single-elements (funcall filter
321 (nreverse single-elements))))
322 ;; apply FILTER to sub-menu menu element list
323 (setq l sub-menu-elements)
324 (setq sub-menu-elements nil)
325 (while l
326 (setq menu-element (car l))
327 (recentf-set-menu-element-value 483 (recentf-set-menu-element-value
328 menu-element 484 elt (recentf-apply-menu-filter
329 (recentf-apply-menu-filter 485 filter (recentf-menu-element-value elt)))
330 filter 486 (push elt l))
331 (recentf-menu-element-value menu-element))) 487 ;; Return the new filtered menu element list.
332 (setq sub-menu-elements (cons menu-element sub-menu-elements)) 488 (nconc l others))
333 (setq l (cdr l)))
334 ;; build and return the new filtered menu element list
335 (nconc sub-menu-elements single-elements))
336 l)) 489 l))
337 490
338(defvar recentf-menu-items-for-commands
339 (list ["Cleanup list"
340 recentf-cleanup
341 :help "Remove all non-readable and excluded files from the recent list"
342 :active t]
343 ["Edit list..."
344 recentf-edit-list
345 :help "Edit the files that are kept in the recent list"
346 :active t]
347 ["Save list now"
348 recentf-save-list
349 :help "Save the list of recently opened files now"
350 :active t]
351 ["Options..."
352 (customize-group "recentf")
353 :help "Customize recently opened files menu and options"
354 :active t]
355 )
356 "List of menu items for recentf commands.")
357
358(defvar recentf-menu-filter-commands nil
359 "This variable can be used by menu filters to setup their own command menu.
360
361If non-nil it must contain a list of valid menu-items to be appended
362to the recent file list part of the menu. Before calling a menu
363filter function this variable is reset to nil.")
364
365(defun recentf-make-menu-items () 491(defun recentf-make-menu-items ()
366 "Make menu items from `recentf-list'." 492 "Make menu items from the recent list."
367 (setq recentf-menu-filter-commands nil) 493 (setq recentf-menu-filter-commands nil)
368 (let ((file-items 494 (let ((file-items
369 (mapcar 'recentf-make-menu-item 495 (mapcar 'recentf-make-menu-item
@@ -380,149 +506,128 @@ filter function this variable is reset to nil.")
380 (and recentf-menu-filter-commands 506 (and recentf-menu-filter-commands
381 (cons "---" 507 (cons "---"
382 recentf-menu-filter-commands)) 508 recentf-menu-filter-commands))
383 (and recentf-menu-append-commands-p 509 (and recentf-menu-append-commands-flag
384 (cons "---" 510 (cons "---"
385 recentf-menu-items-for-commands))))) 511 recentf-menu-items-for-commands)))))
386 512
387(defun recentf-make-menu-item (menu-element) 513(defsubst recentf-make-menu-item (elt)
388 "Make a menu item from MENU-ELEMENT (see `recentf-make-menu-element')." 514 "Make a menu item from menu element ELT."
389 (let ((menu-item (recentf-menu-element-item menu-element)) 515 (let ((item (recentf-menu-element-item elt))
390 (menu-value (recentf-menu-element-value menu-element))) 516 (value (recentf-menu-element-value elt)))
391 (if (recentf-sub-menu-element-p menu-element) 517 (if (recentf-sub-menu-element-p elt)
392 (cons menu-item (mapcar 'recentf-make-menu-item menu-value)) 518 (cons item (mapcar 'recentf-make-menu-item value))
393 (vector menu-item 519 (vector item (list recentf-menu-action value)
394 (list recentf-menu-action menu-value) 520 :help (concat "Open " value)
395 :help (concat "Open " menu-value)
396 :active t)))) 521 :active t))))
397 522
398;;;; 523(defun recentf-clear-data ()
399;;;; Predefined menu filter functions 524 "Clear data used to build the recentf menu.
400;;;; 525This force a rebuild of the menu."
401 526 (easy-menu-remove-item nil recentf-menu-path recentf-menu-title)
402(defun recentf-sort-ascending (l) 527 (setq recentf-data-cache nil))
528
529;;; Predefined menu filters
530;;
531(defsubst recentf-sort-ascending (l)
403 "Sort the list of menu elements L in ascending order. 532 "Sort the list of menu elements L in ascending order.
404The MENU-ITEM part of each menu element is compared." 533The MENU-ITEM part of each menu element is compared."
405 (sort (copy-sequence l) 534 (sort (copy-sequence l)
406 (function 535 #'(lambda (e1 e2)
407 (lambda (e1 e2) 536 (recentf-string-lessp
408 (string-lessp (recentf-menu-element-item e1) 537 (recentf-menu-element-item e1)
409 (recentf-menu-element-item e2)))))) 538 (recentf-menu-element-item e2)))))
410 539
411(defun recentf-sort-descending (l) 540(defsubst recentf-sort-descending (l)
412 "Sort the list of menu elements L in descending order. 541 "Sort the list of menu elements L in descending order.
413The MENU-ITEM part of each menu element is compared." 542The MENU-ITEM part of each menu element is compared."
414 (sort (copy-sequence l) 543 (sort (copy-sequence l)
415 (function 544 #'(lambda (e1 e2)
416 (lambda (e1 e2) 545 (recentf-string-lessp
417 (string-lessp (recentf-menu-element-item e2) 546 (recentf-menu-element-item e2)
418 (recentf-menu-element-item e1)))))) 547 (recentf-menu-element-item e1)))))
419 548
420(defun recentf-sort-basenames-ascending (l) 549(defsubst recentf-sort-basenames-ascending (l)
421 "Sort the list of menu elements L in ascending order. 550 "Sort the list of menu elements L in ascending order.
422Only file names (without directories) are compared." 551Only filenames sans directory are compared."
423 (sort (copy-sequence l) 552 (sort (copy-sequence l)
424 (function 553 #'(lambda (e1 e2)
425 (lambda (e1 e2) 554 (recentf-string-lessp
426 (string-lessp 555 (file-name-nondirectory (recentf-menu-element-value e1))
427 (file-name-nondirectory (recentf-menu-element-value e1)) 556 (file-name-nondirectory (recentf-menu-element-value e2))))))
428 (file-name-nondirectory (recentf-menu-element-value e2)))))))
429 557
430(defun recentf-sort-basenames-descending (l) 558(defsubst recentf-sort-basenames-descending (l)
431 "Sort the list of menu elements L in descending order. 559 "Sort the list of menu elements L in descending order.
432Only file names (without directories) are compared." 560Only filenames sans directory are compared."
433 (sort (copy-sequence l) 561 (sort (copy-sequence l)
434 (function 562 #'(lambda (e1 e2)
435 (lambda (e1 e2) 563 (recentf-string-lessp
436 (string-lessp 564 (file-name-nondirectory (recentf-menu-element-value e2))
437 (file-name-nondirectory (recentf-menu-element-value e2)) 565 (file-name-nondirectory (recentf-menu-element-value e1))))))
438 (file-name-nondirectory (recentf-menu-element-value e1))))))) 566
439 567(defsubst recentf-sort-directories-ascending (l)
440(defun recentf-directory-compare (p1 p2)
441 "Compare directories then filenames in paths P1 and P2.
442Return non-nil if P1 is less than P2."
443 (let ((d1 (file-name-directory p1))
444 (f1 (file-name-nondirectory p1))
445 (d2 (file-name-directory p2))
446 (f2 (file-name-nondirectory p2)))
447 (if (string= d1 d2)
448 (string-lessp f1 f2)
449 (string-lessp d1 d2))))
450
451(defun recentf-sort-directories-ascending (l)
452 "Sort the list of menu elements L in ascending order. 568 "Sort the list of menu elements L in ascending order.
453Compares directories then filenames to order the list." 569Compares directories then filenames to order the list."
454 (sort (copy-sequence l) 570 (sort (copy-sequence l)
455 (function 571 #'(lambda (e1 e2)
456 (lambda (e1 e2) 572 (recentf-directory-compare
457 (recentf-directory-compare (recentf-menu-element-value e1) 573 (recentf-menu-element-value e1)
458 (recentf-menu-element-value e2)))))) 574 (recentf-menu-element-value e2)))))
459 575
460(defun recentf-sort-directories-descending (l) 576(defsubst recentf-sort-directories-descending (l)
461 "Sort the list of menu elements L in descending order. 577 "Sort the list of menu elements L in descending order.
462Compares directories then filenames to order the list." 578Compares directories then filenames to order the list."
463 (sort (copy-sequence l) 579 (sort (copy-sequence l)
464 (function 580 #'(lambda (e1 e2)
465 (lambda (e1 e2) 581 (recentf-directory-compare
466 (recentf-directory-compare (recentf-menu-element-value e2) 582 (recentf-menu-element-value e2)
467 (recentf-menu-element-value e1)))))) 583 (recentf-menu-element-value e1)))))
468 584
469(defun recentf-show-basenames (l) 585(defun recentf-show-basenames (l &optional no-dir)
470 "Filter the list of menu elements L to show only file names (no directories) 586 "Filter the list of menu elements L to show filenames sans directory.
471in the menu. When file names are duplicated their directory component is added." 587When a filename is duplicated, it is appended a sequence number if
472 (let ((names (mapcar (function 588optional argument NO-DIR is non-nil, or its directory otherwise."
473 (lambda (item) 589 (let (filtered-names filtered-list full name counters sufx)
474 (file-name-nondirectory 590 (dolist (elt l (nreverse filtered-list))
475 (recentf-menu-element-value item)))) 591 (setq full (recentf-menu-element-value elt)
476 l)) 592 name (file-name-nondirectory full))
477 (dirs (mapcar (function 593 (if (not (member name filtered-names))
478 (lambda (item) 594 (push name filtered-names)
479 (file-name-directory 595 (if no-dir
480 (recentf-menu-element-value item)))) 596 (if (setq sufx (assoc name counters))
481 l)) 597 (setcdr sufx (1+ (cdr sufx)))
482 (pathes (mapcar 'recentf-menu-element-value l)) 598 (setq sufx 1)
483 (pos -1) 599 (push (cons name sufx) counters))
484 item filtered-items filtered-list) 600 (setq sufx (file-name-directory full)))
485 (while names 601 (setq name (format "%s(%s)" name sufx)))
486 (setq item (car names)) 602 (push (recentf-make-menu-element name full) filtered-list))))
487 (setq names (cdr names)) 603
488 (setq pos (1+ pos)) 604(defsubst recentf-show-basenames-ascending (l)
489 (setq filtered-list 605 "Filter the list of menu elements L to show filenames sans directory.
490 (cons (recentf-make-menu-element 606Filenames are sorted in ascending order.
491 (if (or (member item names) (member item filtered-items)) 607This filter combines the `recentf-sort-basenames-ascending' and
492 (concat item " (" (nth pos dirs) ")")
493 item)
494 (nth pos pathes))
495 filtered-list))
496 (setq filtered-items (cons item filtered-items)))
497 (nreverse filtered-list)))
498
499(defun recentf-show-basenames-ascending (l)
500 "Filter the list of menu elements L.
501Show only file names in the menu, sorted in ascending order. This
502filter combines the `recentf-sort-basenames-ascending' and
503`recentf-show-basenames' filters." 608`recentf-show-basenames' filters."
504 (recentf-show-basenames (recentf-sort-basenames-ascending l))) 609 (recentf-show-basenames (recentf-sort-basenames-ascending l)))
505 610
506(defun recentf-show-basenames-descending (l) 611(defsubst recentf-show-basenames-descending (l)
507 "Filter the list of menu elements L. 612 "Filter the list of menu elements L to show filenames sans directory.
508Show only file names in the menu, sorted in descending order. This 613Filenames are sorted in descending order.
509filter combines the `recentf-sort-basenames-descending' and 614This filter combines the `recentf-sort-basenames-descending' and
510`recentf-show-basenames' filters." 615`recentf-show-basenames' filters."
511 (recentf-show-basenames (recentf-sort-basenames-descending l))) 616 (recentf-show-basenames (recentf-sort-basenames-descending l)))
512 617
513(defun recentf-relative-filter (l) 618(defun recentf-relative-filter (l)
514 "Filter the list of `recentf-menu-elements' L. 619 "Filter the list of menu-elements L to show relative filenames.
515Show filenames relative to `default-directory'." 620Filenames are relative to the `default-directory'."
516 (setq recentf-update-menu-p t) ; force menu update 621 (mapcar #'(lambda (menu-element)
517 (mapcar (function 622 (let* ((ful (recentf-menu-element-value menu-element))
518 (lambda (menu-element) 623 (rel (file-relative-name ful default-directory)))
519 (let* ((ful-path (recentf-menu-element-value menu-element)) 624 (if (string-match "^\\.\\." rel)
520 (rel-path (file-relative-name ful-path))) 625 menu-element
521 (if (string-match "^\\.\\." rel-path) 626 (recentf-make-menu-element rel ful))))
522 menu-element
523 (recentf-make-menu-element rel-path ful-path)))))
524 l)) 627 l))
525 628
629;;; Rule based menu filters
630;;
526(defcustom recentf-arrange-rules 631(defcustom recentf-arrange-rules
527 '( 632 '(
528 ("Elisp files (%d)" ".\\.el$") 633 ("Elisp files (%d)" ".\\.el$")
@@ -561,163 +666,144 @@ defined."
561 :set 'recentf-menu-customization-changed) 666 :set 'recentf-menu-customization-changed)
562 667
563(defcustom recentf-arrange-by-rule-subfilter nil 668(defcustom recentf-arrange-by-rule-subfilter nil
564 "*Function used by `recentf-arrange-by-rule' to filter sub-menu elements. 669 "*Function called by a rule based filter to filter sub-menu elements.
565nil means no filter. See also `recentf-menu-filter'. You can't use 670nil means no filter. See also `recentf-menu-filter'.
566`recentf-arrange-by-rule' itself here!" 671You can't use another rule based filter here."
567 :group 'recentf-filters 672 :group 'recentf-filters
568 :type '(choice (const nil) function) 673 :type '(choice (const nil) function)
569 :set (lambda (sym val) 674 :set (lambda (variable value)
570 (if (eq val 'recentf-arrange-by-rule) 675 (when (memq value '(recentf-arrange-by-rule
571 (error "Can't use `recentf-arrange-by-rule' itself here!") 676 recentf-arrange-by-mode
572 (recentf-menu-customization-changed sym val)))) 677 recentf-arrange-by-dir))
573 678 (error "Recursive use of a rule based filter"))
574(defun recentf-match-rule-p (matcher file-path) 679 (recentf-menu-customization-changed variable value)))
575 "Return non-nil if FILE-PATH match the rule specified by MATCHER. 680
681(defun recentf-match-rule-p (matcher filename)
682 "Return non-nil if the rule specified by MATCHER match FILENAME.
576See `recentf-arrange-rules' for details on MATCHER." 683See `recentf-arrange-rules' for details on MATCHER."
577 (if (stringp matcher) 684 (if (stringp matcher)
578 (string-match matcher file-path) 685 (string-match matcher filename)
579 (while (and (consp matcher) 686 (while (and (consp matcher)
580 (not (string-match (car matcher) file-path))) 687 (not (string-match (car matcher) filename)))
581 (setq matcher (cdr matcher))) 688 (setq matcher (cdr matcher)))
582 matcher)) 689 matcher))
583 690
584(defun recentf-arrange-by-rule (l) 691(defun recentf-arrange-by-rule (l)
585 "Filter the list of menu-elements L. 692 "Filter the list of menu-elements L.
586Arrange them in sub-menus following rules in `recentf-arrange-rules'." 693Arrange them in sub-menus following rules in `recentf-arrange-rules'."
587 (let ((sub-menus-number (length recentf-arrange-rules))) 694 (if (not recentf-arrange-rules)
588 (if (> sub-menus-number 0) 695 l
589 (let ((sub-menus (apply 'vector 696 (let ((menus (mapcar #'(lambda (r) (list (car r)))
590 (mapcar (function 697 recentf-arrange-rules))
591 (lambda (pair) 698 menu others min file rules elts count)
592 (list (car pair)))) 699 (dolist (elt l)
593 recentf-arrange-rules))) 700 (setq file (recentf-menu-element-value elt)
594 other-menu-elements index min-size) 701 rules recentf-arrange-rules
595 (while l 702 elts menus
596 (let* ((menu-element (car l)) 703 menu nil)
597 (file-path (recentf-menu-element-value menu-element)) 704 (while (and (not menu) rules)
598 (rules recentf-arrange-rules) 705 (when (recentf-match-rule-p (cdar rules) file)
599 (found nil)) 706 (setq menu (car elts))
600 (setq index 0) 707 (recentf-set-menu-element-value
601 (while (and (not found) rules) 708 menu (cons elt (recentf-menu-element-value menu))))
602 (if (recentf-match-rule-p (cdar rules) file-path) 709 (setq rules (cdr rules)
603 (let ((sub-menu (aref sub-menus index))) 710 elts (cdr elts)))
604 (setq found t) 711 (unless menu
605 (recentf-set-menu-element-value 712 (push elt others)))
606 sub-menu 713
607 (cons menu-element (recentf-menu-element-value sub-menu))) 714 (setq l nil
608 )) 715 min (if (natnump recentf-arrange-by-rules-min-items)
609 (setq index (1+ index)) 716 recentf-arrange-by-rules-min-items 0))
610 (setq rules (cdr rules))) 717 (dolist (menu menus)
611 (or found 718 (when (setq elts (recentf-menu-element-value menu))
612 (setq other-menu-elements 719 (setq count (length elts))
613 (cons menu-element other-menu-elements))) 720 (if (< count min)
614 (setq l (cdr l)))) 721 (setq others (nconc elts others))
615 (setq index 0) 722 (recentf-set-menu-element-item
616 (setq l nil) 723 menu (format (recentf-menu-element-item menu) count))
617 (setq min-size (if (integerp recentf-arrange-by-rules-min-items) 724 (recentf-set-menu-element-value
618 (max 0 recentf-arrange-by-rules-min-items) 725 menu (recentf-apply-menu-filter
619 0)) 726 recentf-arrange-by-rule-subfilter (nreverse elts)))
620 (while (< index sub-menus-number) 727 (push menu l))))
621 (let* ((sub-menu (aref sub-menus index)) 728
622 (sub-menu-title (recentf-menu-element-item sub-menu)) 729 (if (and (stringp recentf-arrange-by-rule-others) others)
623 (sub-menu-elements (recentf-menu-element-value sub-menu)) 730 (nreverse
624 (sub-menu-length (length sub-menu-elements))) 731 (cons
625 (if (> sub-menu-length 0) 732 (recentf-make-menu-element
626 (cond 733 (format recentf-arrange-by-rule-others (length others))
627 ((< sub-menu-length min-size) 734 (recentf-apply-menu-filter
628 (setq other-menu-elements 735 recentf-arrange-by-rule-subfilter (nreverse others)))
629 (nconc sub-menu-elements other-menu-elements))) 736 l))
630 ((>= sub-menu-length min-size) 737 (nconc
631 (recentf-set-menu-element-item 738 (nreverse l)
632 sub-menu 739 (recentf-apply-menu-filter
633 (format sub-menu-title sub-menu-length)) 740 recentf-arrange-by-rule-subfilter (nreverse others)))))
634 (recentf-set-menu-element-value 741 ))
635 sub-menu 742
636 (recentf-apply-menu-filter 743;;; Predefined rule based menu filters
637 recentf-arrange-by-rule-subfilter 744;;
638 (nreverse sub-menu-elements)))
639 (setq l (cons sub-menu l)))))
640 (setq index (1+ index))))
641 (if (and (stringp recentf-arrange-by-rule-others)
642 other-menu-elements)
643 (setq l
644 (nreverse
645 (cons (recentf-make-menu-element
646 (format recentf-arrange-by-rule-others
647 (length other-menu-elements))
648 (recentf-apply-menu-filter
649 recentf-arrange-by-rule-subfilter
650 (nreverse other-menu-elements)))
651 l)))
652 (setq l (nconc (nreverse l)
653 (recentf-apply-menu-filter
654 recentf-arrange-by-rule-subfilter
655 (nreverse other-menu-elements)))))))
656 l))
657
658(defun recentf-build-mode-rules () 745(defun recentf-build-mode-rules ()
659 "Convert `auto-mode-alist' to `recentf-arrange-rules' format." 746 "Convert `auto-mode-alist' to menu filter rules.
747Rules obey `recentf-arrange-rules' format."
660 (let ((case-fold-search recentf-case-fold-search) 748 (let ((case-fold-search recentf-case-fold-search)
661 (modes auto-mode-alist) 749 regexp rule-name rule rules)
662 regexp mode rule-name rule rules) 750 (dolist (mode auto-mode-alist)
663 (while modes 751 (setq regexp (car mode)
664 (setq regexp (caar modes)) 752 mode (cdr mode))
665 (setq mode (cdar modes))
666 (when (symbolp mode) 753 (when (symbolp mode)
667 (setq rule-name (symbol-name mode)) 754 (setq rule-name (symbol-name mode))
668 (if (string-match "\\(.*\\)-mode$" rule-name) 755 (if (string-match "\\(.*\\)-mode$" rule-name)
669 (setq rule-name (match-string 1 rule-name))) 756 (setq rule-name (match-string 1 rule-name)))
670 (setq rule-name (concat rule-name " (%d)")) 757 (setq rule-name (concat rule-name " (%d)")
671 (setq rule (assoc rule-name rules)) 758 rule (assoc rule-name rules))
672 (if rule 759 (if rule
673 (setcdr rule (cons regexp (cdr rule))) 760 (setcdr rule (cons regexp (cdr rule)))
674 (setq rules (cons (list rule-name regexp) rules)))) 761 (push (list rule-name regexp) rules))))
675 (setq modes (cdr modes)))
676 ;; It is important to preserve auto-mode-alist order 762 ;; It is important to preserve auto-mode-alist order
677 ;; to ensure the right file <-> mode association 763 ;; to ensure the right file <-> mode association
678 (nreverse rules))) 764 (nreverse rules)))
679 765
680(defun recentf-arrange-by-mode (l) 766(defun recentf-arrange-by-mode (l)
681 "Filter the list of menu-elements L to build sub-menus for each major mode." 767 "Split the list of menu-elements L into sub-menus by major mode."
682 (let ((recentf-arrange-rules (recentf-build-mode-rules)) 768 (let ((recentf-arrange-rules (recentf-build-mode-rules))
683 (recentf-arrange-by-rule-others "others (%d)")) 769 (recentf-arrange-by-rule-others "others (%d)"))
684 (recentf-arrange-by-rule l))) 770 (recentf-arrange-by-rule l)))
685 771
686(defun recentf-build-dir-rules (l) 772(defun recentf-build-dir-rules (l)
687 "Convert directories in menu-elements L to rules in `recentf-arrange-rules' format." 773 "Convert directories in menu-elements L to menu filter rules.
774Rules obey `recentf-arrange-rules' format."
688 (let (dirs) 775 (let (dirs)
689 (mapc (function 776 (mapcar #'(lambda (e)
690 (lambda (e) 777 (let ((dir (file-name-directory
691 (let ((dir (file-name-directory 778 (recentf-menu-element-value e))))
692 (recentf-menu-element-value e)))) 779 (or (recentf-string-member dir dirs)
693 (or (member dir dirs) 780 (push dir dirs))))
694 (setq dirs (cons dir dirs)))))) 781 l)
695 l) 782 (mapcar #'(lambda (d)
696 (mapcar (function 783 (cons (concat d " (%d)")
697 (lambda (d) 784 (concat "\\`" d)))
698 (cons (concat d " (%d)") 785 (nreverse (sort dirs 'recentf-string-lessp)))))
699 (concat "\\`" d))))
700 (nreverse (sort dirs 'string-lessp)))))
701 786
702(defun recentf-file-name-nondir (l) 787(defun recentf-file-name-nondir (l)
703 "Filter the list of menu-elements L to show only filenames. 788 "Filter the list of menu-elements L to show filenames sans directory.
704This simplified version of `recentf-show-basenames' does not handle 789This simplified version of `recentf-show-basenames' does not handle
705duplicates. It is used by `recentf-arrange-by-dir' as its 790duplicates. It is used by `recentf-arrange-by-dir' as its
706`recentf-arrange-by-rule-subfilter'." 791`recentf-arrange-by-rule-subfilter'."
707 (mapcar (function 792 (mapcar #'(lambda (e)
708 (lambda (e) 793 (recentf-make-menu-element
709 (recentf-make-menu-element 794 (file-name-nondirectory (recentf-menu-element-value e))
710 (file-name-nondirectory (recentf-menu-element-value e)) 795 (recentf-menu-element-value e)))
711 (recentf-menu-element-value e))))
712 l)) 796 l))
713 797
714(defun recentf-arrange-by-dir (l) 798(defun recentf-arrange-by-dir (l)
715 "Filter the list of menu-elements L to build sub-menus for each directory." 799 "Split the list of menu-elements L into sub-menus by directory."
716 (let ((recentf-arrange-rules (recentf-build-dir-rules l)) 800 (let ((recentf-arrange-rules (recentf-build-dir-rules l))
717 (recentf-arrange-by-rule-subfilter 'recentf-file-name-nondir) 801 (recentf-arrange-by-rule-subfilter 'recentf-file-name-nondir)
718 recentf-arrange-by-rule-others) 802 recentf-arrange-by-rule-others)
719 (nreverse (recentf-arrange-by-rule l)))) 803 (nreverse (recentf-arrange-by-rule l))))
720 804
805;;; Ring of menu filters
806;;
721(defvar recentf-filter-changer-state nil 807(defvar recentf-filter-changer-state nil
722 "Used by `recentf-filter-changer' to hold its state.") 808 "Used by `recentf-filter-changer' to hold its state.")
723 809
@@ -728,153 +814,140 @@ duplicates. It is used by `recentf-arrange-by-dir' as its
728 (recentf-arrange-by-rule . "*Files by User Rule*") 814 (recentf-arrange-by-rule . "*Files by User Rule*")
729 ) 815 )
730 "*List of filters managed by `recentf-filter-changer'. 816 "*List of filters managed by `recentf-filter-changer'.
731Each filter is defined by a pair (FILTER-FUN . FILTER-LBL) where: 817Each filter is defined by a pair (FUNCTION . LABEL), where FUNCTION is
732 818the filter function, and LABEL is the menu item displayed to select
733- - FILTER-FUN is the function that filters menu-elements 819that filter."
734- - FILTER-LBL is the menu item used to activate the filter"
735 :group 'recentf-filters 820 :group 'recentf-filters
736 :type '(repeat (cons function string)) 821 :type '(repeat (cons function string))
737 :set (lambda (sym val) 822 :set (lambda (variable value)
738 (setq recentf-filter-changer-state nil) 823 (setq recentf-filter-changer-state nil)
739 (recentf-menu-customization-changed sym val))) 824 (recentf-menu-customization-changed variable value)))
740 825
741(defun recentf-filter-changer-goto-next () 826(defun recentf-filter-changer-goto-next ()
742 "Go to the next filter available (see `recentf-filter-changer')." 827 "Go to the next filter available.
743 (and (consp recentf-filter-changer-state) 828See `recentf-filter-changer'."
744 (setq recentf-filter-changer-state 829 (setq recentf-filter-changer-state (cdr recentf-filter-changer-state))
745 (cdr recentf-filter-changer-state))) 830 (recentf-clear-data))
746 (setq recentf-update-menu-p t)) 831
747 832(defsubst recentf-filter-changer-get-current ()
748(defun recentf-filter-changer-get-current () 833 "Get the current filter available.
749 "Get the current filter available (see `recentf-filter-changer')." 834See `recentf-filter-changer'."
750 (if (null recentf-filter-changer-state) 835 (unless recentf-filter-changer-state
751 (setq recentf-filter-changer-state recentf-filter-changer-alist)) 836 (setq recentf-filter-changer-state recentf-filter-changer-alist))
752 (and (consp recentf-filter-changer-state) 837 (car recentf-filter-changer-state))
753 (car recentf-filter-changer-state))) 838
754 839(defsubst recentf-filter-changer-get-next ()
755(defun recentf-filter-changer-get-next () 840 "Get the next filter available.
756 "Get the next filter available (see `recentf-filter-changer')." 841See `recentf-filter-changer'."
757 (let ((filters recentf-filter-changer-state)) 842 ;; At this point the current filter is the first element of
758 (cond ((consp filters) 843 ;; `recentf-filter-changer-state'.
759 (setq filters (cdr filters)) 844 (car (or (cdr recentf-filter-changer-state)
760 (if (null filters) 845 ;; There is no next element in
761 (setq filters recentf-filter-changer-alist))) 846 ;; `recentf-filter-changer-state', so loop back to the
762 (t 847 ;; first element of `recentf-filter-changer-alist'.
763 (setq filters recentf-filter-changer-alist) 848 recentf-filter-changer-alist)))
764 (if (consp filters)
765 (setq filters (cdr filters)))))
766 (if (consp filters)
767 (car filters))))
768 849
769(defun recentf-filter-changer (l) 850(defun recentf-filter-changer (l)
770 "Manage a ring of filters. 851 "Manage a ring of menu filters.
771`recentf-filter-changer-alist' defines the filters in the ring. 852`recentf-filter-changer-alist' defines the filters in the ring.
772Actual filtering of L is delegated to the current filter in the 853Filtering of L is delegated to the current filter in the ring. A
773ring. A filter menu item is displayed allowing to dynamically activate 854filter menu item is displayed allowing to dynamically activate the
774the next filter in the ring. If the filter ring is empty L is left 855next filter in the ring. If the filter ring is empty, L is left
775unchanged." 856unchanged."
776 (let ((current-filter-item (recentf-filter-changer-get-current)) 857 (let ((filter (recentf-filter-changer-get-current)))
777 (next-filter-item (recentf-filter-changer-get-next))) 858 (when filter
778 (when current-filter-item 859 (setq l (recentf-apply-menu-filter (car filter) l)
779 (setq l (recentf-apply-menu-filter (car current-filter-item) l)) 860 filter (recentf-filter-changer-get-next))
780 (if next-filter-item 861 (when filter
781 (setq recentf-menu-filter-commands 862 (setq recentf-menu-filter-commands
782 (list (vector (cdr next-filter-item) 863 (list (vector (cdr filter)
783 '(recentf-filter-changer-goto-next) 864 '(recentf-filter-changer-goto-next)
784 :active t))))) 865 t)))))
785 l)) 866 l))
786 867
787;;;; 868;;; Common dialog stuff
788;;;; Dialogs stuff 869;;
789;;;;
790
791(defun recentf-cancel-dialog (&rest ignore) 870(defun recentf-cancel-dialog (&rest ignore)
792 "Cancel the current dialog. 871 "Cancel the current dialog.
793Used by `recentf-edit-list' and `recentf-open-files' dialogs." 872Used internally by recentf dialogs.
873IGNORE arguments."
794 (interactive) 874 (interactive)
795 (kill-buffer (current-buffer)) 875 (kill-buffer (current-buffer))
796 (message "Dialog canceled")) 876 (message "Dialog canceled"))
797 877
798(defvar recentf-dialog-mode-map nil 878(defvar recentf-dialog-mode-map
799 "`recentf-dialog-mode' keymap.") 879 (let ((km (make-sparse-keymap)))
800 880 (define-key km "q" 'recentf-cancel-dialog)
801(if recentf-dialog-mode-map 881 (define-key km [down-mouse-1] 'widget-button-click)
802 () 882 (set-keymap-parent km widget-keymap)
803 (setq recentf-dialog-mode-map (make-sparse-keymap)) 883 km)
804 (define-key recentf-dialog-mode-map "q" 'recentf-cancel-dialog) 884 "Keymap used in recentf dialogs.")
805 (define-key recentf-dialog-mode-map [down-mouse-1] 'widget-button-click)
806 (set-keymap-parent recentf-dialog-mode-map widget-keymap))
807 885
808(defun recentf-dialog-mode () 886(defun recentf-dialog-mode ()
809 "Major mode used in recentf dialogs. 887 "Major mode of recentf dialogs.
810 888
811These are the special commands of `recentf-dialog-mode' mode: 889\\{recentf-dialog-mode-map}"
812 q -- cancel this dialog."
813 (interactive) 890 (interactive)
814 (setq major-mode 'recentf-dialog-mode) 891 (setq major-mode 'recentf-dialog-mode)
815 (setq mode-name "recentf-dialog") 892 (setq mode-name "recentf-dialog")
816 (use-local-map recentf-dialog-mode-map)) 893 (use-local-map recentf-dialog-mode-map))
817 894
818;;;; 895;;; Hooks
819;;;; Hooks and Commands 896;;
820;;;; 897(defun recentf-track-opened-file ()
821 898 "Insert the name of the file just opened or written into the recent list."
822(defun recentf-add-file-hook () 899 (and buffer-file-name
823 "Insert the name of the file just opened or written into `recentf-list'." 900 (recentf-add-file buffer-file-name))
824 (and buffer-file-name (recentf-add-file buffer-file-name)) 901 ;; Must return nil because it is run from `write-file-functions'.
825 nil)
826
827(defun recentf-remove-file-hook ()
828 "When a buffer is killed remove a non readable file from `recentf-list'."
829 (and buffer-file-name (recentf-remove-if-non-readable buffer-file-name))
830 nil) 902 nil)
831 903
832(defun recentf-update-menu-hook () 904(defun recentf-track-closed-file ()
833 "Update the recentf menu from the current `recentf-list'." 905 "Update the recent list when a buffer is killed.
834 (when recentf-update-menu-p 906That is, remove a non readable file from the recent list, if
835 (condition-case nil 907`recentf-keep-non-readable-files-flag' is nil."
836 (progn 908 (and buffer-file-name
837 (setq recentf-update-menu-p nil) 909 (not recentf-keep-non-readable-files-flag)
910 (recentf-remove-if-non-readable buffer-file-name)))
911
912(defun recentf-update-menu ()
913 "Update the recentf menu from the current recent list."
914 (let ((cache (cons default-directory recentf-list)))
915 ;; Does nothing, if nothing has changed.
916 (unless (equal recentf-data-cache cache)
917 (setq recentf-data-cache cache)
918 (condition-case err
838 (easy-menu-change recentf-menu-path 919 (easy-menu-change recentf-menu-path
839 recentf-menu-title 920 recentf-menu-title
840 (recentf-make-menu-items) 921 (recentf-make-menu-items)
841 recentf-menu-before)) 922 recentf-menu-before)
842 (error nil)))) 923 (error
843 924 (message "recentf update menu failed: %s"
844(defun recentf-dump-variable (variable &optional limit) 925 (error-message-string err)))))))
845 "Insert a \"(setq VARIABLE value)\" in the current buffer.
846Optional argument LIMIT specifies a maximum length when VARIABLE value
847is a list (default to the full list)."
848 (let ((value (symbol-value variable)))
849 (if (listp value)
850 (progn
851 (when (and (integerp limit) (> limit 0))
852 (setq value (recentf-trunc-list value limit)))
853 (insert (format "(setq %S '(" variable))
854 (mapc (lambda (e) (insert (format "\n%S" e))) value)
855 (insert "))\n"))
856 (insert (format "(setq %S %S)\n" variable value)))))
857 926
858;;;###autoload 927(defconst recentf-used-hooks
859(defun recentf-save-list () 928 '(
860 "Save the current `recentf-list' to the file `recentf-save-file'." 929 (find-file-hook recentf-track-opened-file)
861 (interactive) 930 (write-file-functions recentf-track-opened-file)
862 (with-temp-buffer 931 (kill-buffer-hook recentf-track-closed-file)
863 (erase-buffer) 932 (menu-bar-update-hook recentf-update-menu)
864 (insert (format recentf-save-file-header (current-time-string))) 933 (kill-emacs-hook recentf-save-list)
865 (recentf-dump-variable 'recentf-list recentf-max-saved-items) 934 )
866 (recentf-dump-variable 'recentf-filter-changer-state) 935 "Hooks used by recentf.")
867 (if (file-writable-p recentf-save-file)
868 (write-region (point-min) (point-max) recentf-save-file))
869 (kill-buffer (current-buffer)))
870 nil)
871 936
937(defsubst recentf-enabled-p ()
938 "Return non-nil if recentf mode is currently enabled."
939 (memq 'recentf-update-menu menu-bar-update-hook))
940
941;;; Commands
942;;
872(defvar recentf-edit-selected-items nil 943(defvar recentf-edit-selected-items nil
873 "Used by `recentf-edit-list'. 944 "List of files to be deleted from the recent list.
874Holds list of files to be deleted from `recentf-list'.") 945Used internally by `recentf-edit-list'.")
875 946
876(defun recentf-edit-list-action (widget &rest ignore) 947(defun recentf-edit-list-action (widget &rest ignore)
877 "Checkbox WIDGET action used by `recentf-edit-list' to select/unselect a file." 948 "Checkbox WIDGET action that toogles a file selection.
949Used internally by `recentf-edit-list'.
950IGNORE other arguments."
878 (let ((value (widget-get widget ':tag))) 951 (let ((value (widget-get widget ':tag)))
879 ;; if value is already in the selected items 952 ;; if value is already in the selected items
880 (if (memq value recentf-edit-selected-items) 953 (if (memq value recentf-edit-selected-items)
@@ -882,136 +955,124 @@ Holds list of files to be deleted from `recentf-list'.")
882 (progn 955 (progn
883 (setq recentf-edit-selected-items 956 (setq recentf-edit-selected-items
884 (delq value recentf-edit-selected-items)) 957 (delq value recentf-edit-selected-items))
885 (message "%s removed from selection." value)) 958 (message "%s removed from selection" value))
886 ;; else add it 959 ;; else add it
887 (progn 960 (push value recentf-edit-selected-items)
888 (setq recentf-edit-selected-items 961 (message "%s added to selection" value))))
889 (nconc (list value) recentf-edit-selected-items))
890 (message "%s added to selection." value)))))
891 962
892;;;###autoload
893(defun recentf-edit-list () 963(defun recentf-edit-list ()
894 "Allow the user to edit the files that are kept in the recent list." 964 "Show a dialog buffer to edit the recent list.
965That is to select files to be deleted from the recent list."
895 (interactive) 966 (interactive)
896 (with-current-buffer (get-buffer-create (concat "*" recentf-menu-title " - Edit list*")) 967 (with-current-buffer
968 (get-buffer-create (format "*%s - Edit list*" recentf-menu-title))
897 (switch-to-buffer (current-buffer)) 969 (switch-to-buffer (current-buffer))
970 ;; Cleanup buffer
898 (kill-all-local-variables) 971 (kill-all-local-variables)
899 (let ((inhibit-read-only t)) 972 (let ((inhibit-read-only t)
900 (erase-buffer)) 973 (ol (overlay-lists)))
901 (let ((all (overlay-lists))) 974 (erase-buffer)
902 ;; Delete all the overlays. 975 ;; Delete all the overlays.
903 (mapc 'delete-overlay (car all)) 976 (mapc 'delete-overlay (car ol))
904 (mapc 'delete-overlay (cdr all))) 977 (mapc 'delete-overlay (cdr ol)))
905 (setq recentf-edit-selected-items nil) 978 (setq recentf-edit-selected-items nil)
906 ;; Insert the dialog header 979 ;; Insert the dialog header
907 (widget-insert "Select the files to be deleted from the 'recentf-list'.\n\n") 980 (widget-insert
908 (widget-insert "Click on Ok to update the list. ") 981 "\
909 (widget-insert "Click on Cancel or type \"q\" to quit.\n") 982Select the files to be deleted from the recent list.\n\n\
983Click on Ok to update the list. \
984Click on Cancel or type \"q\" to quit.\n")
910 ;; Insert the list of files as checkboxes 985 ;; Insert the list of files as checkboxes
911 (mapc (function 986 (dolist (item recentf-list)
912 (lambda (item) 987 (widget-create
913 (widget-create 'checkbox 988 'checkbox
914 :value nil ; unselected checkbox 989 :value nil ; unselected checkbox
915 :format "\n %[%v%] %t" 990 :format "\n %[%v%] %t"
916 :tag item 991 :tag item
917 :notify 'recentf-edit-list-action))) 992 :notify 'recentf-edit-list-action))
918 recentf-list)
919 (widget-insert "\n\n") 993 (widget-insert "\n\n")
920 ;; Insert the Ok button 994 ;; Insert the Ok button
921 (widget-create 'push-button 995 (widget-create
922 :notify (lambda (&rest ignore) 996 'push-button
923 (if recentf-edit-selected-items 997 :notify (lambda (&rest ignore)
924 (progn (kill-buffer (current-buffer)) 998 (if recentf-edit-selected-items
925 (mapc (function 999 (let ((i 0))
926 (lambda (item) 1000 (kill-buffer (current-buffer))
927 (setq recentf-list 1001 (dolist (e recentf-edit-selected-items)
928 (delq item recentf-list)))) 1002 (setq recentf-list (delq e recentf-list)
929 recentf-edit-selected-items) 1003 i (1+ i)))
930 (message "%S file(s) removed from the list" 1004 (message "%S file(s) removed from the list" i))
931 (length recentf-edit-selected-items)) 1005 (message "No file selected")))
932 (setq recentf-update-menu-p t)) 1006 "Ok")
933 (message "No file selected.")))
934 "Ok")
935 (widget-insert " ") 1007 (widget-insert " ")
936 ;; Insert the Cancel button 1008 ;; Insert the Cancel button
937 (widget-create 'push-button 1009 (widget-create
938 :notify 'recentf-cancel-dialog 1010 'push-button
939 "Cancel") 1011 :notify 'recentf-cancel-dialog
1012 "Cancel")
940 (recentf-dialog-mode) 1013 (recentf-dialog-mode)
941 (widget-setup) 1014 (widget-setup)
942 (goto-char (point-min)))) 1015 (goto-char (point-min))))
943 1016
944;;;###autoload
945(defun recentf-cleanup ()
946 "Remove all non-readable and excluded files from `recentf-list'."
947 (interactive)
948 (let ((count (length recentf-list)))
949 (setq recentf-list
950 (delq nil
951 (mapcar (function
952 (lambda (filename)
953 (and (file-readable-p filename)
954 (recentf-include-p filename)
955 filename)))
956 recentf-list)))
957 (setq count (- count (length recentf-list)))
958 (message "%s removed from the list"
959 (cond ((= count 0) "No file")
960 ((= count 1) "One file")
961 (t (format "%d files" count)))))
962 (setq recentf-update-menu-p t))
963
964(defun recentf-open-files-action (widget &rest ignore) 1017(defun recentf-open-files-action (widget &rest ignore)
965 "Button WIDGET action used by `recentf-open-files' to open a file." 1018 "Button WIDGET action that open a file.
1019Used internally by `recentf-open-files'.
1020IGNORE other arguments."
966 (kill-buffer (current-buffer)) 1021 (kill-buffer (current-buffer))
967 (funcall recentf-menu-action (widget-value widget))) 1022 (funcall recentf-menu-action (widget-value widget)))
968 1023
969(defvar recentf-open-files-item-shift "" 1024(defvar recentf-open-files-item-shift ""
970 "String used by `recentf-open-files' to shift right sub-menu items.") 1025 "Amount of space to shift right sub-menu items.
1026Used internally by `recentf-open-files'.")
971 1027
972(defun recentf-open-files-item (menu-element) 1028(defun recentf-open-files-item (menu-element)
973 "Insert MENU-ELEMENT item in the current interaction buffer." 1029 "Insert an item widget for MENU-ELEMENT in the current dialog buffer.
974 (let ((menu-item (car menu-element)) 1030Used internally by `recentf-open-files'."
975 (file-path (cdr menu-element))) 1031 (let ((item (car menu-element))
976 (if (consp file-path) ; This is a sub-menu 1032 (file (cdr menu-element)))
1033 (if (consp file) ; This is a sub-menu
977 (let* ((shift recentf-open-files-item-shift) 1034 (let* ((shift recentf-open-files-item-shift)
978 (recentf-open-files-item-shift (concat shift " "))) 1035 (recentf-open-files-item-shift (concat shift " ")))
979 (widget-create 'item 1036 (widget-create
980 :tag menu-item 1037 'item
981 :sample-face 'bold 1038 :tag item
982 :format (concat shift "%{%t%}:\n")) 1039 :sample-face 'bold
983 (mapc 'recentf-open-files-item 1040 :format (concat shift "%{%t%}:\n"))
984 file-path) 1041 (mapc 'recentf-open-files-item file)
985 (widget-insert "\n")) 1042 (widget-insert "\n"))
986 (widget-create 'push-button 1043 (widget-create
987 :button-face 'default 1044 'push-button
988 :tag menu-item 1045 :button-face 'default
989 :help-echo (concat "Open " file-path) 1046 :tag item
990 :format (concat recentf-open-files-item-shift "%[%t%]") 1047 :help-echo (concat "Open " file)
991 :notify 'recentf-open-files-action 1048 :format (concat recentf-open-files-item-shift "%[%t%]")
992 file-path) 1049 :notify 'recentf-open-files-action
1050 file)
993 (widget-insert "\n")))) 1051 (widget-insert "\n"))))
994 1052
995;;;###autoload
996(defun recentf-open-files (&optional files buffer-name) 1053(defun recentf-open-files (&optional files buffer-name)
997 "Display buffer allowing user to choose a file from recently-opened list. 1054 "Show a dialog buffer to open a recent file.
998The optional argument FILES may be used to specify the list, otherwise 1055If optional argument FILES is non-nil, it specifies the list of
999`recentf-list' is used. The optional argument BUFFER-NAME specifies 1056recently-opened files to choose from. It is the whole recent list
1000which buffer to use for the interaction." 1057otherwise.
1058If optional argument BUFFER-NAME is non-nil, it specifies which buffer
1059name to use for the interaction. It is \"*`recentf-menu-title'*\" by
1060default."
1001 (interactive) 1061 (interactive)
1002 (if (null files) 1062 (unless files
1003 (setq files recentf-list)) 1063 (setq files recentf-list))
1004 (if (null buffer-name) 1064 (unless buffer-name
1005 (setq buffer-name (concat "*" recentf-menu-title "*"))) 1065 (setq buffer-name (format "*%s*" recentf-menu-title)))
1006 (with-current-buffer (get-buffer-create buffer-name) 1066 (with-current-buffer (get-buffer-create buffer-name)
1007 (switch-to-buffer (current-buffer)) 1067 (switch-to-buffer (current-buffer))
1068 ;; Cleanup buffer
1008 (kill-all-local-variables) 1069 (kill-all-local-variables)
1009 (let ((inhibit-read-only t)) 1070 (let ((inhibit-read-only t)
1010 (erase-buffer)) 1071 (ol (overlay-lists)))
1011 (let ((all (overlay-lists))) 1072 (erase-buffer)
1012 ;; Delete all the overlays. 1073 ;; Delete all the overlays.
1013 (mapc 'delete-overlay (car all)) 1074 (mapc 'delete-overlay (car ol))
1014 (mapc 'delete-overlay (cdr all))) 1075 (mapc 'delete-overlay (cdr ol)))
1015 ;; Insert the dialog header 1076 ;; Insert the dialog header
1016 (widget-insert "Click on a file to open it. ") 1077 (widget-insert "Click on a file to open it. ")
1017 (widget-insert "Click on Cancel or type \"q\" to quit.\n\n" ) 1078 (widget-insert "Click on Cancel or type \"q\" to quit.\n\n" )
@@ -1023,54 +1084,78 @@ which buffer to use for the interaction."
1023 (mapcar 'recentf-make-default-menu-element files)))) 1084 (mapcar 'recentf-make-default-menu-element files))))
1024 (widget-insert "\n") 1085 (widget-insert "\n")
1025 ;; Insert the Cancel button 1086 ;; Insert the Cancel button
1026 (widget-create 'push-button 1087 (widget-create
1027 :notify 'recentf-cancel-dialog 1088 'push-button
1028 "Cancel") 1089 :notify 'recentf-cancel-dialog
1090 "Cancel")
1029 (recentf-dialog-mode) 1091 (recentf-dialog-mode)
1030 (widget-setup) 1092 (widget-setup)
1031 (goto-char (point-min)))) 1093 (goto-char (point-min))))
1032 1094
1033;;;###autoload
1034(defun recentf-open-more-files () 1095(defun recentf-open-more-files ()
1035 "Allow the user to open files that are not in the menu." 1096 "Show a dialog buffer to open a recent file that is not in the menu."
1036 (interactive) 1097 (interactive)
1037 (recentf-open-files (nthcdr recentf-max-menu-items recentf-list) 1098 (recentf-open-files (nthcdr recentf-max-menu-items recentf-list)
1038 (concat "*" recentf-menu-title " - More*"))) 1099 (format "*%s - More*" recentf-menu-title)))
1039 1100
1101(defconst recentf-save-file-header
1102 ";;; Automatically generated by `recentf' on %s.\n"
1103 "Header to be written into the `recentf-save-file'.")
1104
1105(defun recentf-save-list ()
1106 "Save the recent list.
1107Write data into the file specified by `recentf-save-file'."
1108 (interactive)
1109 (with-temp-file (expand-file-name recentf-save-file)
1110 (erase-buffer)
1111 (insert (format recentf-save-file-header (current-time-string)))
1112 (recentf-dump-variable 'recentf-list recentf-max-saved-items)
1113 (recentf-dump-variable 'recentf-filter-changer-state)
1114 nil))
1115
1116(defun recentf-load-list ()
1117 "Load a previously saved recent list.
1118Read data from the file specified by `recentf-save-file'."
1119 (interactive)
1120 (let ((file (expand-file-name recentf-save-file)))
1121 (when (file-readable-p file)
1122 (load-file file))))
1123
1124(defun recentf-cleanup ()
1125 "Remove all non-readable and excluded files from the recent list."
1126 (interactive)
1127 (message "Cleaning up the recentf list...")
1128 (let (newlist)
1129 (dolist (f recentf-list)
1130 (if (and (file-readable-p f) (recentf-include-p f))
1131 (push f newlist)
1132 (message "File %s removed from the recentf list" f)))
1133 (setq recentf-list (nreverse newlist))
1134 (message "Cleaning up the recentf list...done")))
1040 1135
1041;;; Note this definition must be at the end of the file, because
1042;;; `define-minor-mode' actually calls the mode-function if the
1043;;; associated variable is non-nil, which requires that all needed
1044;;; functions be already defined. [This is arguably a bug in d-m-m]
1045;;;###autoload 1136;;;###autoload
1046(define-minor-mode recentf-mode 1137(define-minor-mode recentf-mode
1047 "Toggle recentf mode. 1138 "Toggle recentf mode.
1048With prefix argument ARG, turn on if positive, otherwise off. 1139With prefix argument ARG, turn on if positive, otherwise off.
1049Returns non-nil if the new state is enabled. 1140Returns non-nil if the new state is enabled.
1050 1141
1051When recentf mode is enabled, it maintains a menu for visiting files that 1142When recentf mode is enabled, it maintains a menu for visiting files
1052were operated on recently." 1143that were operated on recently."
1053 :global t 1144 :global t
1054 :group 'recentf 1145 :group 'recentf
1055 (if recentf-mode 1146 (unless (and recentf-mode (recentf-enabled-p))
1056 (unless recentf-initialized-p 1147 (if recentf-mode
1057 (setq recentf-initialized-p t) 1148 (recentf-load-list)
1058 (if (file-readable-p recentf-save-file) 1149 (recentf-save-list))
1059 (load-file recentf-save-file)) 1150 (recentf-auto-cleanup)
1060 (setq recentf-update-menu-p t) 1151 (recentf-clear-data)
1061 (add-hook 'find-file-hooks 'recentf-add-file-hook) 1152 (let ((hook-setup (if recentf-mode 'add-hook 'remove-hook)))
1062 (add-hook 'write-file-hooks 'recentf-add-file-hook) 1153 (dolist (hook recentf-used-hooks)
1063 (add-hook 'menu-bar-update-hook 'recentf-update-menu-hook) 1154 (apply hook-setup hook)))
1064 (add-hook 'kill-emacs-hook 'recentf-save-list)) 1155 (run-hooks 'recentf-mode-hook)
1065 (when recentf-initialized-p 1156 (when (interactive-p)
1066 (setq recentf-initialized-p nil) 1157 (message "Recentf mode %sabled" (if recentf-mode "en" "dis"))))
1067 (recentf-save-list) 1158 recentf-mode)
1068 (easy-menu-remove-item nil recentf-menu-path recentf-menu-title)
1069 (remove-hook 'find-file-hooks 'recentf-add-file-hook)
1070 (remove-hook 'write-file-hooks 'recentf-add-file-hook)
1071 (remove-hook 'menu-bar-update-hook 'recentf-update-menu-hook)
1072 (remove-hook 'kill-emacs-hook 'recentf-save-list))))
1073
1074 1159
1075(provide 'recentf) 1160(provide 'recentf)
1076 1161