comparison lisp/recentf.el @ 50715:46229d46cbeb

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.
author Juanma Barranquero <lekktu@gmail.com>
date Sat, 26 Apr 2003 23:41:59 +0000
parents 99be3a1e2589
children cfbb46a2ee9c
comparison
equal deleted inserted replaced
50714:6cd3d0fd5cf1 50715:46229d46cbeb
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
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details. 23 ;; GNU General Public License for more details.
24 ;; Boston, MA 02111-1307, USA. 28 ;; Boston, MA 02111-1307, USA.
25 29
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)
44 45 (require 'timer)
45 (defconst recentf-save-file-header 46
46 ";;; Automatically generated by `recentf' on %s.\n" 47 ;;; Internal data
47 "Header to be written into the `recentf-save-file'.") 48 ;;
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 54 The 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.
64 It 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"
75 :group 'files) 61 :group 'files)
76 62
78 "Group to customize recentf menu filters. 64 "Group to customize recentf menu filters.
79 You should define the options of your own filters in this group." 65 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.
70 nil means to save the whole list.
71 See 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))
84
85 (defun recentf-menu-customization-changed (variable value)
86 "Function called when the recentf menu customization has changed.
87 Set 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))
96 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
100 :type 'string 96 :type 'string
116 (const :tag "Last" nil)) 112 (const :tag "Last" nil))
117 :set 'recentf-menu-customization-changed) 113 :set 'recentf-menu-customization-changed)
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.
121 The default action `recentf-find-file' calls `find-file' to edit an 117 The default is to call `recentf-find-file' to edit the selected file."
122 existing file. If the file does not exist or is not readable, it is
123 not 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
125 list 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)
129 121
130 (defcustom recentf-max-menu-items 10 122 (defcustom recentf-max-menu-items 10
135 127
136 (defcustom recentf-menu-filter nil 128 (defcustom recentf-menu-filter nil
137 "*Function used to filter files displayed in the recentf menu. 129 "*Function used to filter files displayed in the recentf menu.
138 nil means no filter. The following functions are predefined: 130 nil 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'
155 The filter function is called with one argument, the list of menu elements 147 Show filenames sans directory in ascending order.
156 used 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
161 The filter function is called with one argument, the list of menu
162 elements used to build the menu and must return a new list of menu
163 elements (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)
185 :group 'recentf 191 (make-obsolete-variable 'recentf-menu-append-commands-p
186 :type 'boolean 192 'recentf-menu-append-commands-flag
187 :require 'recentf 193 "21.4")
188 :initialize 'custom-initialize-default 194
189 :set (lambda (sym val) 195 (defcustom recentf-keep-non-readable-files-flag nil
190 (if val 196 "*non-nil means to keep non readable files in the recent list."
191 (remove-hook 'kill-buffer-hook 'recentf-remove-file-hook) 197 :group 'recentf
192 (add-hook 'kill-buffer-hook 'recentf-remove-file-hook)) 198 :type 'boolean)
193 (custom-set-default sym val))) 199
200 (defvaralias 'recentf-keep-non-readable-files-p
201 'recentf-keep-non-readable-files-flag)
202 (make-obsolete-variable 'recentf-keep-non-readable-files-p
203 'recentf-keep-non-readable-files-flag
204 "21.4")
205
206 (defcustom recentf-auto-cleanup 'mode
207 "*Define when to automatically cleanup the recent list.
208 The 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
219 Setting this variable directly does not take effect;
220 use \\[customize].
221
222 See also the command `recentf-cleanup', that can be used to manually
223 cleanup 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 ;;;; 246 It is passed a filename to give a chance to transform it.
247 If 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.")
256
257 (defsubst recentf-string-equal (s1 s2)
258 "Return non-nil if strings S1 and S2 have identical contents.
259 Ignore 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.
266 Ignore 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.
273 The value is actually the tail of LIST whose car is ELT.
274 ELT must be a string and LIST a list of strings.
275 Ignore 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.
291 When the value of VARIABLE is a list, optional argument LIMIT
292 specifies a maximum number of elements to insert. By default insert
293 the 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.
306 See 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.
329 If it is there yet, move it at the beginning of the list.
330 If `recentf-case-fold-search' is non-nil, ignore case when comparing
331 filenames."
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.
338 See also the function `expand-file-name'.
339 If defined, call the function `recentf-filename-handler' to post
340 process the canonical name."
341 (let* ((filename (expand-file-name name)))
342 (or (and recentf-filename-handler
343 (funcall recentf-filename-handler filename))
344 filename)))
206 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)
210 (rl recentf-exclude)) 349 (rl recentf-exclude))
211 (while (and rl (not (string-match (car rl) filename))) 350 (while (and rl (not (string-match (car rl) filename)))
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.
217 Does nothing if FILENAME matches one of the `recentf-exclude' regexps." 356 Does 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)))) 360
222 361 (defsubst recentf-remove-if-non-readable (filename)
223 (defun recentf-remove-if-non-readable (filename) 362 "Remove FILENAME from the recent list, if file is not readable.
224 "Remove FILENAME from `recentf-list' if not readable." 363 Return 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'.
231 If FILENAME is not readable it is removed from `recentf-list'." 371 If 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) 373 its 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)) 381 First compare directories, then filenames sans directory.
242 (while (and l (> n 0)) 382 Return 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)
247 387 (file-name-nondirectory f2))
248 (defun recentf-elements (n) 388 (recentf-string-lessp d1 d2))))
249 "Return a list of the first N elements of `recentf-list'." 389
250 (recentf-trunc-list recentf-list n)) 390 ;;; Menu building
251 391 ;;
252 (defun recentf-make-menu-element (menu-item menu-value)
253 "Create a new menu-element.
254
255 A menu element is a pair (MENU-ITEM . MENU-VALUE) where:
256
257 - - MENU-ITEM is the menu item string displayed.
258 - - MENU-VALUE is the path used to open the file when the
259 corresponding MENU-ITEM is selected. Or it is
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))
265
266 (defun recentf-menu-element-item (e)
267 "Return the item part of the menu-element E."
268 (car e))
269
270 (defun recentf-menu-element-value (e)
271 "Return the value part of the menu-element E."
272 (cdr e))
273
274 (defun recentf-set-menu-element-item (e item)
275 "Change the item part of menu-element E to ITEM."
276 (setcar e item))
277
278 (defun recentf-set-menu-element-value (e value)
279 "Change the value part of menu-element E to VALUE."
280 (setcdr e value))
281
282 (defun recentf-sub-menu-element-p (e)
283 "Return non-nil if menu-element E defines a sub-menu."
284 (consp (recentf-menu-element-value e)))
285
286 (defun recentf-make-default-menu-element (file-path)
287 "Make a new default menu element (MENU-ITEM . MENU-VALUE).
288 Do so for the given recent file path FILE-PATH. MENU-ITEM and
289 MENU-VALUE are set to FILE-PATH. See also
290 `recentf-make-menu-element'."
291 (recentf-make-menu-element file-path file-path))
292
293 (defun recentf-menu-elements (n)
294 "Return a list of the first N default menu elements from `recentf-list'.
295 See also `recentf-make-default-menu-element'."
296 (mapcar 'recentf-make-default-menu-element
297 (recentf-elements n)))
298
299 (defun recentf-apply-menu-filter (filter l)
300 "Apply function FILTER to the list of menu-elements L.
301 It takes care of sub-menu elements in L and recursively apply FILTER
302 to them. It is guaranteed that FILTER receives only a list of single
303 menu-elements (no sub-menu)."
304 (if (and (functionp filter) l)
305 (let ((case-fold-search recentf-case-fold-search)
306 menu-element sub-menu-elements single-elements)
307 ;; split L in two sub-listes:
308 ;; one of sub-menus elements and
309 ;; one of single menu elements
310 (while l
311 (setq menu-element (car l))
312 (if (recentf-sub-menu-element-p menu-element)
313 (setq sub-menu-elements
314 (cons menu-element sub-menu-elements))
315 (setq single-elements
316 (cons menu-element single-elements)))
317 (setq l (cdr l)))
318 ;; apply FILTER to the list of single menu elements
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
328 menu-element
329 (recentf-apply-menu-filter
330 filter
331 (recentf-menu-element-value menu-element)))
332 (setq sub-menu-elements (cons menu-element sub-menu-elements))
333 (setq l (cdr l)))
334 ;; build and return the new filtered menu element list
335 (nconc sub-menu-elements single-elements))
336 l))
337
338 (defvar recentf-menu-items-for-commands 392 (defvar recentf-menu-items-for-commands
339 (list ["Cleanup list" 393 (list ["Cleanup list"
340 recentf-cleanup 394 recentf-cleanup
341 :help "Remove all non-readable and excluded files from the recent list" 395 :help "Remove all non-readable and excluded files from the recent list"
342 :active t] 396 :active t]
355 ) 409 )
356 "List of menu items for recentf commands.") 410 "List of menu items for recentf commands.")
357 411
358 (defvar recentf-menu-filter-commands nil 412 (defvar recentf-menu-filter-commands nil
359 "This variable can be used by menu filters to setup their own command menu. 413 "This variable can be used by menu filters to setup their own command menu.
360
361 If non-nil it must contain a list of valid menu-items to be appended 414 If non-nil it must contain a list of valid menu-items to be appended
362 to the recent file list part of the menu. Before calling a menu 415 to the recent file list part of the menu. Before calling a menu
363 filter function this variable is reset to nil.") 416 filter function this variable is reset to nil.")
364 417
418 (defsubst recentf-elements (n)
419 "Return a list of the first N elements of the recent list."
420 (recentf-trunc-list recentf-list n))
421
422 (defsubst recentf-make-menu-element (menu-item menu-value)
423 "Create a new menu-element.
424 A menu element is a pair (MENU-ITEM . MENU-VALUE), where MENU-ITEM is
425 the menu item string displayed. MENU-VALUE is the file to be open
426 when the corresponding MENU-ITEM is selected. Or it is a
427 pair (SUB-MENU-TITLE . MENU-ELEMENTS) where SUB-MENU-TITLE is a
428 sub-menu title and MENU-ELEMENTS is the list of menu elements in the
429 sub-menu."
430 (cons menu-item menu-value))
431
432 (defsubst recentf-menu-element-item (e)
433 "Return the item part of the menu-element E."
434 (car e))
435
436 (defsubst recentf-menu-element-value (e)
437 "Return the value part of the menu-element E."
438 (cdr e))
439
440 (defsubst recentf-set-menu-element-item (e item)
441 "Change the item part of menu-element E to ITEM."
442 (setcar e item))
443
444 (defsubst recentf-set-menu-element-value (e value)
445 "Change the value part of menu-element E to VALUE."
446 (setcdr e value))
447
448 (defsubst recentf-sub-menu-element-p (e)
449 "Return non-nil if menu-element E defines a sub-menu."
450 (consp (recentf-menu-element-value e)))
451
452 (defsubst recentf-make-default-menu-element (file)
453 "Make a new default menu element with FILE.
454 This a menu element (FILE . FILE)."
455 (recentf-make-menu-element file file))
456
457 (defsubst recentf-menu-elements (n)
458 "Return a list of the first N default menu elements from the recent list.
459 See also `recentf-make-default-menu-element'."
460 (mapcar 'recentf-make-default-menu-element
461 (recentf-elements n)))
462
463 (defun recentf-apply-menu-filter (filter l)
464 "Apply function FILTER to the list of menu-elements L.
465 It takes care of sub-menu elements in L and recursively apply FILTER
466 to them. It is guaranteed that FILTER receives only a list of single
467 menu-elements (no sub-menu)."
468 (if (and l (functionp filter))
469 (let ((case-fold-search recentf-case-fold-search)
470 elts others)
471 ;; split L into two sub-listes, one of sub-menus elements and
472 ;; another of single menu elements.
473 (dolist (elt l)
474 (if (recentf-sub-menu-element-p elt)
475 (push elt elts)
476 (push elt others)))
477 ;; Apply FILTER to single elements.
478 (when others
479 (setq others (funcall filter (nreverse others))))
480 ;; Apply FILTER to sub-menu elements.
481 (setq l nil)
482 (dolist (elt elts)
483 (recentf-set-menu-element-value
484 elt (recentf-apply-menu-filter
485 filter (recentf-menu-element-value elt)))
486 (push elt l))
487 ;; Return the new filtered menu element list.
488 (nconc l others))
489 l))
490
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
370 (recentf-apply-menu-filter 496 (recentf-apply-menu-filter
371 recentf-menu-filter 497 recentf-menu-filter
378 :help "Open files that are not in the menu" 504 :help "Open files that are not in the menu"
379 :active t])) 505 :active t]))
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 ;;;; 525 This 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.
404 The MENU-ITEM part of each menu element is compared." 533 The 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.
413 The MENU-ITEM part of each menu element is compared." 542 The 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.
422 Only file names (without directories) are compared." 551 Only 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))))))) 557
429 558 (defsubst recentf-sort-basenames-descending (l)
430 (defun 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.
432 Only file names (without directories) are compared." 560 Only 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.
442 Return 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.
453 Compares directories then filenames to order the list." 569 Compares 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.
462 Compares directories then filenames to order the list." 578 Compares 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.
471 in the menu. When file names are duplicated their directory component is added." 587 When a filename is duplicated, it is appended a sequence number if
472 (let ((names (mapcar (function 588 optional 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 606 Filenames are sorted in ascending order.
491 (if (or (member item names) (member item filtered-items)) 607 This 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.
501 Show only file names in the menu, sorted in ascending order. This
502 filter 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.
508 Show only file names in the menu, sorted in descending order. This 613 Filenames are sorted in descending order.
509 filter combines the `recentf-sort-basenames-descending' and 614 This 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.
515 Show filenames relative to `default-directory'." 620 Filenames 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$")
529 ("Java files (%d)" ".\\.java$") 634 ("Java files (%d)" ".\\.java$")
530 ("C/C++ files (%d)" "c\\(pp\\)?$") 635 ("C/C++ files (%d)" "c\\(pp\\)?$")
559 :group 'recentf-filters 664 :group 'recentf-filters
560 :type 'number 665 :type 'number
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.
565 nil means no filter. See also `recentf-menu-filter'. You can't use 670 nil means no filter. See also `recentf-menu-filter'.
566 `recentf-arrange-by-rule' itself here!" 671 You 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.
576 See `recentf-arrange-rules' for details on MATCHER." 683 See `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.
586 Arrange them in sub-menus following rules in `recentf-arrange-rules'." 693 Arrange 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.
747 Rules 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.
774 Rules 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.
704 This simplified version of `recentf-show-basenames' does not handle 789 This simplified version of `recentf-show-basenames' does not handle
705 duplicates. It is used by `recentf-arrange-by-dir' as its 790 duplicates. 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
724 (defcustom recentf-filter-changer-alist 810 (defcustom recentf-filter-changer-alist
725 '( 811 '(
726 (recentf-arrange-by-mode . "*Files by Mode*") 812 (recentf-arrange-by-mode . "*Files by Mode*")
727 (recentf-arrange-by-dir . "*Files by Directory*") 813 (recentf-arrange-by-dir . "*Files by Directory*")
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'.
731 Each filter is defined by a pair (FILTER-FUN . FILTER-LBL) where: 817 Each filter is defined by a pair (FUNCTION . LABEL), where FUNCTION is
732 818 the filter function, and LABEL is the menu item displayed to select
733 - - FILTER-FUN is the function that filters menu-elements 819 that 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) 828 See `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')." 834 See `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')." 841 See `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.
772 Actual filtering of L is delegated to the current filter in the 853 Filtering of L is delegated to the current filter in the ring. A
773 ring. A filter menu item is displayed allowing to dynamically activate 854 filter menu item is displayed allowing to dynamically activate the
774 the next filter in the ring. If the filter ring is empty L is left 855 next filter in the ring. If the filter ring is empty, L is left
775 unchanged." 856 unchanged."
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.
793 Used by `recentf-edit-list' and `recentf-open-files' dialogs." 872 Used internally by recentf dialogs.
873 IGNORE 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
811 These 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) 902 nil)
826 903
827 (defun recentf-remove-file-hook () 904 (defun recentf-track-closed-file ()
828 "When a buffer is killed remove a non readable file from `recentf-list'." 905 "Update the recent list when a buffer is killed.
829 (and buffer-file-name (recentf-remove-if-non-readable buffer-file-name)) 906 That is, remove a non readable file from the recent list, if
830 nil) 907 `recentf-keep-non-readable-files-flag' is nil."
831 908 (and buffer-file-name
832 (defun recentf-update-menu-hook () 909 (not recentf-keep-non-readable-files-flag)
833 "Update the recentf menu from the current `recentf-list'." 910 (recentf-remove-if-non-readable buffer-file-name)))
834 (when recentf-update-menu-p 911
835 (condition-case nil 912 (defun recentf-update-menu ()
836 (progn 913 "Update the recentf menu from the current recent list."
837 (setq recentf-update-menu-p nil) 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. 926
846 Optional argument LIMIT specifies a maximum length when VARIABLE value 927 (defconst recentf-used-hooks
847 is a list (default to the full list)." 928 '(
848 (let ((value (symbol-value variable))) 929 (find-file-hook recentf-track-opened-file)
849 (if (listp value) 930 (write-file-functions recentf-track-opened-file)
850 (progn 931 (kill-buffer-hook recentf-track-closed-file)
851 (when (and (integerp limit) (> limit 0)) 932 (menu-bar-update-hook recentf-update-menu)
852 (setq value (recentf-trunc-list value limit))) 933 (kill-emacs-hook recentf-save-list)
853 (insert (format "(setq %S '(" variable)) 934 )
854 (mapc (lambda (e) (insert (format "\n%S" e))) value) 935 "Hooks used by recentf.")
855 (insert "))\n")) 936
856 (insert (format "(setq %S %S)\n" variable value))))) 937 (defsubst recentf-enabled-p ()
857 938 "Return non-nil if recentf mode is currently enabled."
858 ;;;###autoload 939 (memq 'recentf-update-menu menu-bar-update-hook))
859 (defun recentf-save-list () 940
860 "Save the current `recentf-list' to the file `recentf-save-file'." 941 ;;; Commands
861 (interactive) 942 ;;
862 (with-temp-buffer
863 (erase-buffer)
864 (insert (format recentf-save-file-header (current-time-string)))
865 (recentf-dump-variable 'recentf-list recentf-max-saved-items)
866 (recentf-dump-variable 'recentf-filter-changer-state)
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
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.
874 Holds list of files to be deleted from `recentf-list'.") 945 Used 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.
949 Used internally by `recentf-edit-list'.
950 IGNORE 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)
881 ;; then remove it 954 ;; then remove it
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)) 962
890 (message "%s added to selection." value)))))
891
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.
965 That 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") 982 Select the files to be deleted from the recent list.\n\n\
983 Click on Ok to update the list. \
984 Click 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.
1019 Used internally by `recentf-open-files'.
1020 IGNORE 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.
1026 Used 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)) 1030 Used 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.
998 The optional argument FILES may be used to specify the list, otherwise 1055 If optional argument FILES is non-nil, it specifies the list of
999 `recentf-list' is used. The optional argument BUFFER-NAME specifies 1056 recently-opened files to choose from. It is the whole recent list
1000 which buffer to use for the interaction." 1057 otherwise.
1058 If optional argument BUFFER-NAME is non-nil, it specifies which buffer
1059 name to use for the interaction. It is \"*`recentf-menu-title'*\" by
1060 default."
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" )
1018 ;; Insert the list of files as buttons 1079 ;; Insert the list of files as buttons
1019 (let ((recentf-open-files-item-shift "")) 1080 (let ((recentf-open-files-item-shift ""))
1021 (recentf-apply-menu-filter 1082 (recentf-apply-menu-filter
1022 recentf-menu-filter 1083 recentf-menu-filter
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
1040 1101 (defconst recentf-save-file-header
1041 ;;; Note this definition must be at the end of the file, because 1102 ";;; Automatically generated by `recentf' on %s.\n"
1042 ;;; `define-minor-mode' actually calls the mode-function if the 1103 "Header to be written into the `recentf-save-file'.")
1043 ;;; associated variable is non-nil, which requires that all needed 1104
1044 ;;; functions be already defined. [This is arguably a bug in d-m-m] 1105 (defun recentf-save-list ()
1106 "Save the recent list.
1107 Write 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.
1118 Read 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")))
1135
1045 ;;;###autoload 1136 ;;;###autoload
1046 (define-minor-mode recentf-mode 1137 (define-minor-mode recentf-mode
1047 "Toggle recentf mode. 1138 "Toggle recentf mode.
1048 With prefix argument ARG, turn on if positive, otherwise off. 1139 With prefix argument ARG, turn on if positive, otherwise off.
1049 Returns non-nil if the new state is enabled. 1140 Returns non-nil if the new state is enabled.
1050 1141
1051 When recentf mode is enabled, it maintains a menu for visiting files that 1142 When recentf mode is enabled, it maintains a menu for visiting files
1052 were operated on recently." 1143 that 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
1077 (run-hooks 'recentf-load-hook) 1162 (run-hooks 'recentf-load-hook)
1078 1163