Mercurial > emacs
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 |