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