diff options
| author | Gerd Moellmann | 2000-07-18 12:59:01 +0000 |
|---|---|---|
| committer | Gerd Moellmann | 2000-07-18 12:59:01 +0000 |
| commit | 21ab62bdf2e0dfbadf63d7f56042e238446f5589 (patch) | |
| tree | 6e50ef4180034bbd2001c37e197eb5c6f6a377b9 | |
| parent | 12c74386d92efbff9ead54c15959556e59c72f15 (diff) | |
| download | emacs-21ab62bdf2e0dfbadf63d7f56042e238446f5589.tar.gz emacs-21ab62bdf2e0dfbadf63d7f56042e238446f5589.zip | |
Update from author.
| -rw-r--r-- | lisp/recentf.el | 1612 |
1 files changed, 1060 insertions, 552 deletions
diff --git a/lisp/recentf.el b/lisp/recentf.el index 375c8c890cc..dc040be910b 100644 --- a/lisp/recentf.el +++ b/lisp/recentf.el | |||
| @@ -1,552 +1,1060 @@ | |||
| 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 Free Software Foundation, Inc. | 3 | ;; Copyright (C) 1999, 2000 Free Software Foundation, Inc. |
| 4 | 4 | ||
| 5 | ;; Author: David Ponce <david.ponce@wanadoo.fr> | 5 | ;; Author: David Ponce <david@dponce.com> |
| 6 | ;; Created: July 19 1999 | 6 | ;; Created: July 19 1999 |
| 7 | ;; Keywords: customization | 7 | ;; Keywords: customization |
| 8 | 8 | ||
| 9 | ;; This file is part of GNU Emacs. | 9 | ;; This file is part of GNU Emacs. |
| 10 | 10 | ||
| 11 | ;; GNU Emacs is free software; you can redistribute it and/or modify | 11 | ;; 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 | 12 | ;; it under the terms of the GNU General Public License as published by |
| 13 | ;; the Free Software Foundation; either version 2, or (at your option) | 13 | ;; the Free Software Foundation; either version 2, or (at your option) |
| 14 | ;; any later version. | 14 | ;; any later version. |
| 15 | 15 | ||
| 16 | ;; GNU Emacs is distributed in the hope that it will be useful, | 16 | ;; GNU Emacs is distributed in the hope that it will be useful, |
| 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 19 | ;; GNU General Public License for more details. | 19 | ;; GNU General Public License for more details. |
| 20 | 20 | ||
| 21 | ;; You should have received a copy of the GNU General Public License | 21 | ;; You should have received a copy of the GNU General Public License |
| 22 | ;; along with GNU Emacs; see the file COPYING. If not, write to the | 22 | ;; along with GNU Emacs; see the file COPYING. If not, write to the |
| 23 | ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, | 23 | ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| 24 | ;; Boston, MA 02111-1307, USA. | 24 | ;; Boston, MA 02111-1307, USA. |
| 25 | 25 | ||
| 26 | ;;; Commentary: | 26 | ;;; Commentary: |
| 27 | 27 | ||
| 28 | ;; This package maintains a menu for visiting files that were operated | 28 | ;; This package maintains a menu for visiting files that were operated |
| 29 | ;; on recently. When enabled a new "Open Recent" submenu is displayed | 29 | ;; on recently. When enabled a new "Open Recent" submenu is displayed |
| 30 | ;; in the "Files" menu. The recent files list is automatically saved | 30 | ;; in the "Files" menu. The recent files list is automatically saved |
| 31 | ;; across Emacs sessions. You can customize the number of recent | 31 | ;; across Emacs sessions. You can customize the number of recent |
| 32 | ;; files displayed, the location of the menu and others options (see | 32 | ;; 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 | 33 | ;; the source code for details). To install and use, put the file on |
| 34 | ;; your Emacs-Lisp load path and add the following into your ~/.emacs | 34 | ;; your Emacs-Lisp load path and add the following into your ~/.emacs |
| 35 | ;; startup file: | 35 | ;; startup file: |
| 36 | ;; | 36 | ;; |
| 37 | ;; (require 'recentf) | 37 | ;; (require 'recentf) |
| 38 | ;; (recentf-mode 1) | 38 | ;; (recentf-mode 1) |
| 39 | 39 | ||
| 40 | ;;; Code: | 40 | ;;; Code: |
| 41 | 41 | ||
| 42 | (require 'easymenu) | 42 | (require 'easymenu) |
| 43 | (require 'wid-edit) | 43 | (require 'wid-edit) |
| 44 | 44 | ||
| 45 | (defconst recentf-save-file-header | 45 | (defconst recentf-save-file-header |
| 46 | ";;; Automatically generated by `recentf' on %s.\n" | 46 | ";;; Automatically generated by `recentf' on %s.\n" |
| 47 | "Header to be written into the `recentf-save-file'.") | 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-update-menu-p t |
| 53 | "Non-nil if the recentf menu must be updated.") | 53 | "Non-nil if the recentf menu must be updated.") |
| 54 | 54 | ||
| 55 | (defvar recentf-initialized-p nil | 55 | (defvar recentf-initialized-p nil |
| 56 | "Non-nil if recentf already initialized.") | 56 | "Non-nil if recentf already initialized.") |
| 57 | 57 | ||
| 58 | ;; IMPORTANT: This function must be defined before the following defcustoms | 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 | 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' | 60 | ;; the `symbol-value' function is used to access the `recentf-menu-path' |
| 61 | ;; and `recentf-menu-title' values. | 61 | ;; and `recentf-menu-title' values. |
| 62 | (defun recentf-menu-customization-changed (sym val) | 62 | (defun recentf-menu-customization-changed (sym val) |
| 63 | "Function called when menu customization has changed. | 63 | "Function called when menu customization has changed. |
| 64 | It removes the recentf menu and forces its complete redrawing." | 64 | It removes the recentf menu and forces its complete redrawing." |
| 65 | (when recentf-initialized-p | 65 | (when recentf-initialized-p |
| 66 | (easy-menu-remove-item nil | 66 | (easy-menu-remove-item nil |
| 67 | (symbol-value 'recentf-menu-path) | 67 | (symbol-value 'recentf-menu-path) |
| 68 | (symbol-value 'recentf-menu-title)) | 68 | (symbol-value 'recentf-menu-title)) |
| 69 | (setq recentf-update-menu-p t)) | 69 | (setq recentf-update-menu-p t)) |
| 70 | (custom-set-default sym val)) | 70 | (custom-set-default sym val)) |
| 71 | 71 | ||
| 72 | (defgroup recentf nil | 72 | (defgroup recentf nil |
| 73 | "Maintain a menu of recently opened files." | 73 | "Maintain a menu of recently opened files." |
| 74 | :version "21.1" | 74 | :version "21.1" |
| 75 | :group 'files) | 75 | :group 'files) |
| 76 | 76 | ||
| 77 | (defcustom recentf-max-saved-items 20 | 77 | (defgroup recentf-filters nil |
| 78 | "*Maximum number of items saved to `recentf-save-file'." | 78 | "Group to customize recentf menu filters. |
| 79 | :group 'recentf | 79 | You should define the options of your own filters in this group." |
| 80 | :type 'integer) | 80 | :group 'recentf) |
| 81 | 81 | ||
| 82 | (defcustom recentf-save-file (expand-file-name "~/.recentf") | 82 | (defcustom recentf-max-saved-items 20 |
| 83 | "*File to save `recentf-list' into." | 83 | "*Maximum number of items saved to `recentf-save-file'." |
| 84 | :group 'recentf | 84 | :group 'recentf |
| 85 | :type 'file) | 85 | :type 'integer) |
| 86 | 86 | ||
| 87 | (defcustom recentf-exclude nil | 87 | (defcustom recentf-save-file (expand-file-name "~/.recentf") |
| 88 | "*List of regexps for filenames excluded from `recentf-list'." | 88 | "*File to save `recentf-list' into." |
| 89 | :group 'recentf | 89 | :group 'recentf |
| 90 | :type '(repeat regexp)) | 90 | :type 'file) |
| 91 | 91 | ||
| 92 | (defcustom recentf-menu-title "Open Recent" | 92 | (defcustom recentf-exclude nil |
| 93 | "*Name of the recentf menu." | 93 | "*List of regexps for filenames excluded from `recentf-list'." |
| 94 | :group 'recentf | 94 | :group 'recentf |
| 95 | :type 'string | 95 | :type '(repeat regexp)) |
| 96 | :set 'recentf-menu-customization-changed) | 96 | |
| 97 | 97 | (defcustom recentf-menu-title "Open Recent" | |
| 98 | (defcustom recentf-menu-path '("files") | 98 | "*Name of the recentf menu." |
| 99 | "*Path where to add the recentf menu. | 99 | :group 'recentf |
| 100 | If nil add it at top-level (see also `easy-menu-change')." | 100 | :type 'string |
| 101 | :group 'recentf | 101 | :set 'recentf-menu-customization-changed) |
| 102 | :type '(choice (const :tag "Top Level" nil) | 102 | |
| 103 | (sexp :tag "Menu Path")) | 103 | (defcustom recentf-menu-path '("files") |
| 104 | :set 'recentf-menu-customization-changed) | 104 | "*Path where to add the recentf menu. |
| 105 | 105 | If nil add it at top-level (see also `easy-menu-change')." | |
| 106 | (defcustom recentf-menu-before "open-file" | 106 | :group 'recentf |
| 107 | "*Name of the menu before which the recentf menu will be added. | 107 | :type '(choice (const :tag "Top Level" nil) |
| 108 | If nil add it at end of menu (see also `easy-menu-change')." | 108 | (sexp :tag "Menu Path")) |
| 109 | :group 'recentf | 109 | :set 'recentf-menu-customization-changed) |
| 110 | :type '(choice (string :tag "Name") | 110 | |
| 111 | (const :tag "Last" nil)) | 111 | (defcustom recentf-menu-before "open-file" |
| 112 | :set 'recentf-menu-customization-changed) | 112 | "*Name of the menu before which the recentf menu will be added. |
| 113 | 113 | If nil add it at end of menu (see also `easy-menu-change')." | |
| 114 | (defcustom recentf-menu-action 'recentf-find-file | 114 | :group 'recentf |
| 115 | "*Function to invoke with a filename item of the recentf menu. | 115 | :type '(choice (string :tag "Name") |
| 116 | The default action `recentf-find-file' calls `find-file' to edit an | 116 | (const :tag "Last" nil)) |
| 117 | existing file. If the file does not exist or is not readable, it is | 117 | :set 'recentf-menu-customization-changed) |
| 118 | not edited and its name is removed from `recentf-list'. You can use | 118 | |
| 119 | `find-file' instead to open non-existing files and keep them in the | 119 | (defcustom recentf-menu-action 'recentf-find-file |
| 120 | list of recently opened files." | 120 | "*Function to invoke with a filename item of the recentf menu. |
| 121 | :group 'recentf | 121 | The default action `recentf-find-file' calls `find-file' to edit an |
| 122 | :type 'function | 122 | existing file. If the file does not exist or is not readable, it is |
| 123 | :set 'recentf-menu-customization-changed) | 123 | not edited and its name is removed from `recentf-list'. You can use |
| 124 | 124 | `find-file' instead to open non-existing files and keep them in the | |
| 125 | (defcustom recentf-max-menu-items 10 | 125 | list of recently opened files." |
| 126 | "*Maximum number of items in the recentf menu." | 126 | :group 'recentf |
| 127 | :group 'recentf | 127 | :type 'function |
| 128 | :type 'integer | 128 | :set 'recentf-menu-customization-changed) |
| 129 | :set 'recentf-menu-customization-changed) | 129 | |
| 130 | 130 | (defcustom recentf-max-menu-items 10 | |
| 131 | (defcustom recentf-menu-filter nil | 131 | "*Maximum number of items in the recentf menu." |
| 132 | "*Function used to filter files displayed in the recentf menu. | 132 | :group 'recentf |
| 133 | Nil means no filter. The following functions are predefined: | 133 | :type 'integer |
| 134 | 134 | :set 'recentf-menu-customization-changed) | |
| 135 | - - `recentf-sort-ascending' to sort menu items in ascending order. | 135 | |
| 136 | - - `recentf-sort-descending' to sort menu items in descending order. | 136 | (defcustom recentf-menu-filter nil |
| 137 | - - `recentf-sort-basenames-ascending' to sort file names in descending order. | 137 | "*Function used to filter files displayed in the recentf menu. |
| 138 | - - `recentf-sort-basenames-descending' to sort file names in descending order. | 138 | Nil means no filter. The following functions are predefined: |
| 139 | - - `recentf-show-basenames' to show file names (no directories) in menu items. | 139 | |
| 140 | - - `recentf-show-basenames-ascending' to show file names in ascending order. | 140 | - - `recentf-sort-ascending' to sort menu items in ascending order. |
| 141 | - - `recentf-show-basenames-descending' to show file names in descending order. | 141 | - - `recentf-sort-descending' to sort menu items in descending order. |
| 142 | - - `recentf-relative-filter' to show file names relative to `default-directory'. | 142 | - - `recentf-sort-basenames-ascending' to sort file names in descending order. |
| 143 | 143 | - - `recentf-sort-basenames-descending' to sort file names in descending order. | |
| 144 | The filter function is called with one argument, the list of menu elements | 144 | - - `recentf-sort-directories-ascending' to sort directories in ascending order. |
| 145 | used to build the menu and must return a new list of menu elements (see | 145 | - - `recentf-sort-directories-descending' to sort directories in descending order. |
| 146 | `recentf-menu-elements' for menu element form)." | 146 | - - `recentf-show-basenames' to show file names (no directories) in menu items. |
| 147 | :group 'recentf | 147 | - - `recentf-show-basenames-ascending' to show file names in ascending order. |
| 148 | :type 'function | 148 | - - `recentf-show-basenames-descending' to show file names in descending order. |
| 149 | :set 'recentf-menu-customization-changed) | 149 | - - `recentf-relative-filter' to show file names relative to `default-directory'. |
| 150 | 150 | - - `recentf-arrange-by-rule' to show sub-menus following user defined rules. | |
| 151 | (defcustom recentf-menu-append-commands-p t | 151 | - - `recentf-arrange-by-mode' to show a sub-menu for each major mode. |
| 152 | "*If not-nil command items are appended to the menu." | 152 | - - `recentf-arrange-by-dir' to show a sub-menu for each directory. |
| 153 | :group 'recentf | 153 | - - `recentf-filter-changer' to manage a ring of filters. |
| 154 | :type 'boolean | 154 | |
| 155 | :set 'recentf-menu-customization-changed) | 155 | The filter function is called with one argument, the list of menu elements |
| 156 | 156 | used to build the menu and must return a new list of menu elements (see | |
| 157 | (defcustom recentf-keep-non-readable-files-p nil | 157 | `recentf-make-menu-element' for menu element form)." |
| 158 | "*If nil (default), non-readable files are not kept in `recentf-list'." | 158 | :group 'recentf |
| 159 | :group 'recentf | 159 | :type 'function |
| 160 | :type 'boolean | 160 | :set 'recentf-menu-customization-changed) |
| 161 | :require 'recentf | 161 | |
| 162 | :initialize 'custom-initialize-default | 162 | (defcustom recentf-menu-append-commands-p t |
| 163 | :set (lambda (sym val) | 163 | "*If not-nil command items are appended to the menu." |
| 164 | (if val | 164 | :group 'recentf |
| 165 | (remove-hook 'kill-buffer-hook 'recentf-remove-file-hook) | 165 | :type 'boolean |
| 166 | (add-hook 'kill-buffer-hook 'recentf-remove-file-hook)) | 166 | :set 'recentf-menu-customization-changed) |
| 167 | (custom-set-default sym val))) | 167 | |
| 168 | 168 | (defcustom recentf-keep-non-readable-files-p nil | |
| 169 | (defcustom recentf-mode nil | 169 | "*If nil (default), non-readable files are not kept in `recentf-list'." |
| 170 | "Toggle recentf mode. | 170 | :group 'recentf |
| 171 | When recentf mode is enabled, it maintains a menu for visiting files that | 171 | :type 'boolean |
| 172 | were operated on recently. | 172 | :require 'recentf |
| 173 | Setting this variable directly does not take effect; | 173 | :initialize 'custom-initialize-default |
| 174 | use either \\[customize] or the function `recentf-mode'." | 174 | :set (lambda (sym val) |
| 175 | :set (lambda (symbol value) | 175 | (if val |
| 176 | (recentf-mode (or value 0))) | 176 | (remove-hook 'kill-buffer-hook 'recentf-remove-file-hook) |
| 177 | :initialize 'custom-initialize-default | 177 | (add-hook 'kill-buffer-hook 'recentf-remove-file-hook)) |
| 178 | :type 'boolean | 178 | (custom-set-default sym val))) |
| 179 | :group 'recentf | 179 | |
| 180 | :require 'recentf) | 180 | (defcustom recentf-mode nil |
| 181 | 181 | "Toggle recentf mode. | |
| 182 | (defcustom recentf-load-hook nil | 182 | When recentf mode is enabled, it maintains a menu for visiting files that |
| 183 | "*Normal hook run at end of loading the `recentf' package." | 183 | were operated on recently. |
| 184 | :group 'recentf | 184 | Setting this variable directly does not take effect; |
| 185 | :type 'hook) | 185 | use either \\[customize] or the function `recentf-mode'." |
| 186 | 186 | :set (lambda (symbol value) | |
| 187 | ;;;###autoload | 187 | (recentf-mode (or value 0))) |
| 188 | (defun recentf-mode (&optional arg) | 188 | :initialize 'custom-initialize-default |
| 189 | "Toggle recentf mode. | 189 | :type 'boolean |
| 190 | With prefix ARG, turn recentf mode on if and only if ARG is positive. | 190 | :group 'recentf |
| 191 | Returns the new status of recentf mode (non-nil means on). | 191 | :require 'recentf) |
| 192 | 192 | ||
| 193 | When recentf mode is enabled, it maintains a menu for visiting files that | 193 | (defcustom recentf-load-hook nil |
| 194 | were operated on recently." | 194 | "*Normal hook run at end of loading the `recentf' package." |
| 195 | (interactive "P") | 195 | :group 'recentf |
| 196 | (let ((on-p (if arg | 196 | :type 'hook) |
| 197 | (> (prefix-numeric-value arg) 0) | 197 | |
| 198 | (not recentf-mode)))) | 198 | ;;; |
| 199 | (if on-p | 199 | ;;; Common functions |
| 200 | (unless recentf-initialized-p | 200 | ;;; |
| 201 | (setq recentf-initialized-p t) | 201 | (defconst recentf-case-fold-search |
| 202 | (if (file-readable-p recentf-save-file) | 202 | (memq system-type '(vax-vms windows-nt)) |
| 203 | (load-file recentf-save-file)) | 203 | "Non-nil if recentf searches and matches should ignore case.") |
| 204 | (setq recentf-update-menu-p t) | 204 | |
| 205 | (add-hook 'find-file-hooks 'recentf-add-file-hook) | 205 | (defun recentf-include-p (filename) |
| 206 | (add-hook 'write-file-hooks 'recentf-add-file-hook) | 206 | "Return t if FILENAME matches none of the `recentf-exclude' regexps." |
| 207 | ;; (add-hook 'activate-menubar-hook 'recentf-update-menu-hook) | 207 | (let ((case-fold-search recentf-case-fold-search) |
| 208 | (add-hook 'menu-bar-update-hook 'recentf-update-menu-hook) | 208 | (rl recentf-exclude)) |
| 209 | (add-hook 'kill-emacs-hook 'recentf-save-list)) | 209 | (while (and rl (not (string-match (car rl) filename))) |
| 210 | (when recentf-initialized-p | 210 | (setq rl (cdr rl))) |
| 211 | (setq recentf-initialized-p nil) | 211 | (null rl))) |
| 212 | (recentf-save-list) | 212 | |
| 213 | (easy-menu-remove-item nil recentf-menu-path recentf-menu-title) | 213 | (defun recentf-add-file (filename) |
| 214 | (remove-hook 'find-file-hooks 'recentf-add-file-hook) | 214 | "Add or move FILENAME at the beginning of `recentf-list'. |
| 215 | (remove-hook 'write-file-hooks 'recentf-add-file-hook) | 215 | Does nothing if FILENAME matches one of the `recentf-exclude' regexps." |
| 216 | ;; (remove-hook 'activate-menubar-hook 'recentf-update-menu-hook) | 216 | (let ((filename (expand-file-name filename))) |
| 217 | (remove-hook 'menu-bar-update-hook 'recentf-update-menu-hook) | 217 | (when (recentf-include-p filename) |
| 218 | (remove-hook 'kill-emacs-hook 'recentf-save-list))) | 218 | (setq recentf-list (cons filename (delete filename recentf-list))) |
| 219 | (setq recentf-mode on-p))) | 219 | (setq recentf-update-menu-p t)))) |
| 220 | 220 | ||
| 221 | (defun recentf-add-file-hook () | 221 | (defun recentf-remove-if-non-readable (filename) |
| 222 | "Insert the name of the file just opened or written into `recentf-list'." | 222 | "Remove FILENAME from `recentf-list' if not readable." |
| 223 | (and buffer-file-name (recentf-add-file buffer-file-name)) | 223 | (unless (file-readable-p filename) |
| 224 | nil) | 224 | (setq recentf-list (delete filename recentf-list)) |
| 225 | 225 | (setq recentf-update-menu-p t))) | |
| 226 | (defun recentf-remove-file-hook () | 226 | |
| 227 | "When a buffer is killed remove a non readable file from `recentf-list'." | 227 | (defun recentf-find-file (filename) |
| 228 | (and buffer-file-name (recentf-remove-if-non-readable buffer-file-name)) | 228 | "Edit file FILENAME using `find-file'. |
| 229 | nil) | 229 | If FILENAME is not readable it is removed from `recentf-list'." |
| 230 | 230 | (if (file-readable-p filename) | |
| 231 | (defun recentf-update-menu-hook () | 231 | (find-file filename) |
| 232 | "Update the recentf menu from the current `recentf-list'." | 232 | (progn |
| 233 | (when recentf-update-menu-p | 233 | (message "File `%s' not found." filename) |
| 234 | (condition-case nil | 234 | (setq recentf-list (delete filename recentf-list)) |
| 235 | (progn | 235 | (setq recentf-update-menu-p t)))) |
| 236 | (setq recentf-update-menu-p nil) | 236 | |
| 237 | (easy-menu-change recentf-menu-path | 237 | (defun recentf-trunc-list (l n) |
| 238 | recentf-menu-title | 238 | "Return a list of the first N elements of L." |
| 239 | (recentf-make-menu-items) | 239 | (let ((lh nil)) |
| 240 | recentf-menu-before)) | 240 | (while (and l (> n 0)) |
| 241 | (error nil)))) | 241 | (setq lh (cons (car l) lh)) |
| 242 | 242 | (setq n (1- n)) | |
| 243 | ;;;###autoload | 243 | (setq l (cdr l))) |
| 244 | (defun recentf-save-list () | 244 | (nreverse lh))) |
| 245 | "Save the current `recentf-list' to the file `recentf-save-file'." | 245 | |
| 246 | (interactive) | 246 | (defun recentf-elements (n) |
| 247 | (let ((saved-list (recentf-elements recentf-max-saved-items))) | 247 | "Return a list of the first N elements of `recentf-list'." |
| 248 | (with-temp-buffer | 248 | (recentf-trunc-list recentf-list n)) |
| 249 | (erase-buffer) | 249 | |
| 250 | (insert (format recentf-save-file-header (current-time-string))) | 250 | (defun recentf-make-menu-element (menu-item menu-value) |
| 251 | (insert "(setq recentf-list\n '(\n") | 251 | "Create a new menu-element. |
| 252 | (mapcar '(lambda (e) | 252 | |
| 253 | (insert (format " %S\n" e))) | 253 | A menu element is a pair (MENU-ITEM . MENU-VALUE) where: |
| 254 | saved-list) | 254 | |
| 255 | (insert " ))") | 255 | - - MENU-ITEM is the menu item string displayed. |
| 256 | (if (file-writable-p recentf-save-file) | 256 | - - MENU-VALUE is the path used to open the file when the |
| 257 | (write-region (point-min) (point-max) recentf-save-file)) | 257 | corresponding MENU-ITEM is selected. Or it is |
| 258 | (kill-buffer (current-buffer)))) | 258 | a pair (SUB-MENU-TITLE . MENU-ELEMENTS) where |
| 259 | nil) | 259 | SUB-MENU-TITLE is a sub-menu title and |
| 260 | 260 | MENU-ELEMENTS is the list of menu elements in | |
| 261 | (defvar recentf-edit-selected-items nil | 261 | the sub-menu." |
| 262 | "Used by `recentf-edit-list' to hold the list of files to be deleted | 262 | (cons menu-item menu-value)) |
| 263 | from `recentf-list'.") | 263 | |
| 264 | 264 | (defun recentf-menu-element-item (e) | |
| 265 | (defun recentf-edit-list-action (widget &rest ignore) | 265 | "Return the item part of the menu-element E." |
| 266 | "Checkbox widget action used by `recentf-edit-list' to select/unselect a file." | 266 | (car e)) |
| 267 | (let ((value (widget-get widget ':tag))) | 267 | |
| 268 | ;; if value is already in the selected items | 268 | (defun recentf-menu-element-value (e) |
| 269 | (if (memq value recentf-edit-selected-items) | 269 | "Return the value part of the menu-element E." |
| 270 | ;; then remove it | 270 | (cdr e)) |
| 271 | (progn | 271 | |
| 272 | (setq recentf-edit-selected-items | 272 | (defun recentf-set-menu-element-item (e item) |
| 273 | (delq value recentf-edit-selected-items)) | 273 | "Change the item part of menu-element E to ITEM." |
| 274 | (message "%s removed from selection." value)) | 274 | (setcar e item)) |
| 275 | ;; else add it | 275 | |
| 276 | (progn | 276 | (defun recentf-set-menu-element-value (e value) |
| 277 | (setq recentf-edit-selected-items | 277 | "Change the value part of menu-element E to VALUE." |
| 278 | (nconc (list value) recentf-edit-selected-items)) | 278 | (setcdr e value)) |
| 279 | (message "%s added to selection." value))))) | 279 | |
| 280 | 280 | (defun recentf-sub-menu-element-p (e) | |
| 281 | ;;;###autoload | 281 | "Return non-nil if menu-element E defines a sub-menu." |
| 282 | (defun recentf-edit-list () | 282 | (consp (recentf-menu-element-value e))) |
| 283 | "Allow the user to edit the files that are kept in the recent list." | 283 | |
| 284 | (interactive) | 284 | (defun recentf-make-default-menu-element (file-path) |
| 285 | (with-current-buffer (get-buffer-create (concat "*" recentf-menu-title " - Edit list*")) | 285 | "Make a new default menu element (MENU-ITEM . MENU-VALUE) for the |
| 286 | (switch-to-buffer (current-buffer)) | 286 | given recent file path FILE-PATH. MENU-ITEM and MENU-VALUE are set to |
| 287 | (kill-all-local-variables) | 287 | FILE-PATH. See also `recentf-make-menu-element'." |
| 288 | (let ((inhibit-read-only t)) | 288 | (recentf-make-menu-element file-path file-path)) |
| 289 | (erase-buffer)) | 289 | |
| 290 | (let ((all (overlay-lists))) | 290 | (defun recentf-menu-elements (n) |
| 291 | ;; Delete all the overlays. | 291 | "Return a list of the first N default menu elements from |
| 292 | (mapcar 'delete-overlay (car all)) | 292 | `recentf-list'. See also `recentf-make-default-menu-element'." |
| 293 | (mapcar 'delete-overlay (cdr all))) | 293 | (mapcar 'recentf-make-default-menu-element |
| 294 | (setq recentf-edit-selected-items nil) | 294 | (recentf-elements n))) |
| 295 | ;; Insert the dialog header | 295 | |
| 296 | (widget-insert "Select the files to be deleted from the 'recentf-list'.\n\n") | 296 | (defun recentf-apply-menu-filter (filter l) |
| 297 | (widget-insert "Click on Ok to update the list or on Cancel to quit.\n" ) | 297 | "Convenient funtion to apply the function FILTER to the list of |
| 298 | ;; Insert the list of files as checkboxes | 298 | menu-elements L. It takes care of sub-menu elements in L and |
| 299 | (mapcar '(lambda (item) | 299 | recursively apply FILTER to them. It is guarantee than FILTER receives |
| 300 | (widget-create 'checkbox | 300 | only a list of single menu-elements (no sub-menu)." |
| 301 | :value nil ; unselected checkbox | 301 | (if (and (functionp filter) l) |
| 302 | :format "\n %[%v%] %t" | 302 | (let ((case-fold-search recentf-case-fold-search) |
| 303 | :tag item | 303 | menu-element sub-menu-elements single-elements) |
| 304 | :notify 'recentf-edit-list-action)) | 304 | ;; split L in two sub-listes: |
| 305 | recentf-list) | 305 | ;; one of sub-menus elements and |
| 306 | (widget-insert "\n\n") | 306 | ;; one of single menu elements |
| 307 | ;; Insert the Ok button | 307 | (while l |
| 308 | (widget-create 'push-button | 308 | (setq menu-element (car l)) |
| 309 | :notify (lambda (&rest ignore) | 309 | (if (recentf-sub-menu-element-p menu-element) |
| 310 | (if recentf-edit-selected-items | 310 | (setq sub-menu-elements |
| 311 | (progn (kill-buffer (current-buffer)) | 311 | (cons menu-element sub-menu-elements)) |
| 312 | (mapcar '(lambda (item) | 312 | (setq single-elements |
| 313 | (setq recentf-list | 313 | (cons menu-element single-elements))) |
| 314 | (delq item recentf-list))) | 314 | (setq l (cdr l))) |
| 315 | recentf-edit-selected-items) | 315 | ;; apply FILTER to the list of single menu elements |
| 316 | (message "%S file(s) removed from the list" | 316 | (if single-elements |
| 317 | (length recentf-edit-selected-items)) | 317 | (setq single-elements (funcall filter |
| 318 | (setq recentf-update-menu-p t)) | 318 | (nreverse single-elements)))) |
| 319 | (message "No file selected."))) | 319 | ;; apply FILTER to sub-menu menu element list |
| 320 | "Ok") | 320 | (setq l sub-menu-elements) |
| 321 | (widget-insert " ") | 321 | (setq sub-menu-elements nil) |
| 322 | ;; Insert the Cancel button | 322 | (while l |
| 323 | (widget-create 'push-button | 323 | (setq menu-element (car l)) |
| 324 | :notify (lambda (&rest ignore) | 324 | (recentf-set-menu-element-value |
| 325 | (kill-buffer (current-buffer)) | 325 | menu-element |
| 326 | (message "Command canceled.")) | 326 | (recentf-apply-menu-filter |
| 327 | "Cancel") | 327 | filter |
| 328 | (use-local-map widget-keymap) | 328 | (recentf-menu-element-value menu-element))) |
| 329 | (widget-setup) | 329 | (setq sub-menu-elements (cons menu-element sub-menu-elements)) |
| 330 | (goto-char (point-min)))) | 330 | (setq l (cdr l))) |
| 331 | 331 | ;; build and return the new filtered menu element list | |
| 332 | ;;;###autoload | 332 | (nconc sub-menu-elements single-elements)) |
| 333 | (defun recentf-cleanup () | 333 | l)) |
| 334 | "Remove all non-readable and excluded files from `recentf-list'." | 334 | |
| 335 | (interactive) | 335 | (defvar recentf-menu-items-for-commands |
| 336 | (let ((count (length recentf-list))) | 336 | (list ["Cleanup list" recentf-cleanup t] |
| 337 | (setq recentf-list | 337 | ["Edit list..." recentf-edit-list t] |
| 338 | (delq nil | 338 | ["Save list now" recentf-save-list t] |
| 339 | (mapcar '(lambda (filename) | 339 | (vector "Recentf Options..." '(customize-group "recentf") t)) |
| 340 | (and (file-readable-p filename) | 340 | "List of menu items for recentf commands.") |
| 341 | (recentf-include-p filename) | 341 | |
| 342 | filename)) | 342 | (defvar recentf-menu-filter-commands nil |
| 343 | recentf-list))) | 343 | "This variable can be used by menu filters to setup their own command menu. |
| 344 | (setq count (- count (length recentf-list))) | 344 | |
| 345 | (message "%s removed from the list" | 345 | If non-nil it must contain a list of valid menu-items to be appended |
| 346 | (cond ((= count 0) "No file") | 346 | to the recent file list part of the menu. Before calling a menu |
| 347 | ((= count 1) "One file") | 347 | filter function this variable is reset to nil.") |
| 348 | (t (format "%d files" count))))) | 348 | |
| 349 | (setq recentf-update-menu-p t)) | 349 | (defun recentf-make-menu-items () |
| 350 | 350 | "Make menu items from `recentf-list'." | |
| 351 | (defun recentf-open-more-files-action (widget &rest ignore) | 351 | (setq recentf-menu-filter-commands nil) |
| 352 | "Button widget action used by `recentf-open-more-files' to open a file." | 352 | (let ((file-items |
| 353 | (kill-buffer (current-buffer)) | 353 | (mapcar 'recentf-make-menu-item |
| 354 | (funcall recentf-menu-action (widget-value widget))) | 354 | (recentf-apply-menu-filter |
| 355 | 355 | recentf-menu-filter | |
| 356 | ;;;###autoload | 356 | (recentf-menu-elements recentf-max-menu-items))))) |
| 357 | (defun recentf-open-more-files () | 357 | (append (or file-items (list ["No files" t nil])) |
| 358 | "Allow the user to open files that are not in the menu." | 358 | (and (< recentf-max-menu-items (length recentf-list)) |
| 359 | (interactive) | 359 | (list ["More..." recentf-open-more-files t])) |
| 360 | (with-current-buffer (get-buffer-create (concat "*" recentf-menu-title " - More*")) | 360 | (and recentf-menu-filter-commands |
| 361 | (switch-to-buffer (current-buffer)) | 361 | (cons "---" |
| 362 | (kill-all-local-variables) | 362 | recentf-menu-filter-commands)) |
| 363 | (let ((inhibit-read-only t)) | 363 | (and recentf-menu-append-commands-p |
| 364 | (erase-buffer)) | 364 | (cons "---" |
| 365 | (let ((all (overlay-lists))) | 365 | recentf-menu-items-for-commands))))) |
| 366 | ;; Delete all the overlays. | 366 | |
| 367 | (mapcar 'delete-overlay (car all)) | 367 | (defun recentf-make-menu-item (menu-element) |
| 368 | (mapcar 'delete-overlay (cdr all))) | 368 | "Make a menu item from a menu element (see `recentf-make-menu-element')." |
| 369 | ;; Insert the dialog header | 369 | (let ((menu-item (recentf-menu-element-item menu-element)) |
| 370 | (widget-insert "Click on a file to open it or on Cancel to quit.\n\n") | 370 | (menu-value (recentf-menu-element-value menu-element))) |
| 371 | ;; Insert the list of files as buttons | 371 | (if (recentf-sub-menu-element-p menu-element) |
| 372 | (mapcar '(lambda (menu-element) | 372 | (cons menu-item (mapcar 'recentf-make-menu-item menu-value)) |
| 373 | (let ((menu-item (car menu-element)) | 373 | (vector menu-item |
| 374 | (file-path (cdr menu-element))) | 374 | (list recentf-menu-action menu-value) |
| 375 | (widget-create 'push-button | 375 | t)))) |
| 376 | :button-face 'default | 376 | |
| 377 | :tag menu-item | 377 | ;;; |
| 378 | :help-echo (concat "Open " file-path) | 378 | ;;; Predefined menu filter functions |
| 379 | :format "%[%t%]" | 379 | ;;; |
| 380 | :notify 'recentf-open-more-files-action | 380 | |
| 381 | file-path) | 381 | (defun recentf-sort-ascending (l) |
| 382 | (widget-insert "\n"))) | 382 | "Sort the list of menu elements L in ascending order. |
| 383 | (funcall (or recentf-menu-filter 'identity) | 383 | The MENU-ITEM part of each menu element is compared." |
| 384 | (mapcar '(lambda (item) (cons item item)) | 384 | (sort (copy-sequence l) |
| 385 | (nthcdr recentf-max-menu-items recentf-list)))) | 385 | (function |
| 386 | (widget-insert "\n") | 386 | (lambda (e1 e2) |
| 387 | ;; Insert the Cancel button | 387 | (string-lessp (recentf-menu-element-item e1) |
| 388 | (widget-create 'push-button | 388 | (recentf-menu-element-item e2)))))) |
| 389 | :notify (lambda (&rest ignore) | 389 | |
| 390 | (kill-buffer (current-buffer)) | 390 | (defun recentf-sort-descending (l) |
| 391 | (message "Command canceled.")) | 391 | "Sort the list of menu elements L in descending order. |
| 392 | "Cancel") | 392 | The MENU-ITEM part of each menu element is compared." |
| 393 | (use-local-map widget-keymap) | 393 | (sort (copy-sequence l) |
| 394 | (widget-setup) | 394 | (function |
| 395 | (goto-char (point-min)))) | 395 | (lambda (e1 e2) |
| 396 | 396 | (string-lessp (recentf-menu-element-item e2) | |
| 397 | (defvar recentf-menu-items-for-commands | 397 | (recentf-menu-element-item e1)))))) |
| 398 | (list ["Cleanup list" recentf-cleanup t] | 398 | |
| 399 | ["Edit list..." recentf-edit-list t] | 399 | (defun recentf-sort-basenames-ascending (l) |
| 400 | ["Save list now" recentf-save-list t] | 400 | "Sort the list of menu elements L in ascending order. |
| 401 | (vector "Recentf Options..." '(customize-group "recentf") t)) | 401 | Only file names (without directories) are compared." |
| 402 | "List of menu items for recentf commands.") | 402 | (sort (copy-sequence l) |
| 403 | 403 | (function | |
| 404 | (defun recentf-make-menu-items () | 404 | (lambda (e1 e2) |
| 405 | "Make menu items from `recentf-list'." | 405 | (string-lessp |
| 406 | (let ((file-items | 406 | (file-name-nondirectory (recentf-menu-element-value e1)) |
| 407 | (mapcar 'recentf-make-menu-item | 407 | (file-name-nondirectory (recentf-menu-element-value e2))))))) |
| 408 | (funcall (or recentf-menu-filter 'identity) | 408 | |
| 409 | (recentf-menu-elements recentf-max-menu-items))))) | 409 | (defun recentf-sort-basenames-descending (l) |
| 410 | (append (or file-items (list ["No files" t nil])) | 410 | "Sort the list of menu elements L in descending order. |
| 411 | (and (< recentf-max-menu-items (length recentf-list)) | 411 | Only file names (without directories) are compared." |
| 412 | (list ["More..." recentf-open-more-files t])) | 412 | (sort (copy-sequence l) |
| 413 | (and recentf-menu-append-commands-p | 413 | (function |
| 414 | (cons ["---" nil nil] | 414 | (lambda (e1 e2) |
| 415 | recentf-menu-items-for-commands))))) | 415 | (string-lessp |
| 416 | 416 | (file-name-nondirectory (recentf-menu-element-value e2)) | |
| 417 | (defun recentf-make-menu-item (menu-element) | 417 | (file-name-nondirectory (recentf-menu-element-value e1))))))) |
| 418 | "Make a menu item from a menu element (see `recentf-menu-elements')." | 418 | |
| 419 | (vector (car menu-element) (list recentf-menu-action (cdr menu-element)) t)) | 419 | (defun recentf-directory-compare (p1 p2) |
| 420 | 420 | "Compare directories then filenames in pathes P1 and P2 and return | |
| 421 | (defun recentf-add-file (filename) | 421 | non-nil if P1 is less than P2." |
| 422 | "Add or move FILENAME at the beginning of `recentf-list'. | 422 | (let ((d1 (file-name-directory p1)) |
| 423 | Does nothing if FILENAME matches one of the `recentf-exclude' regexps." | 423 | (f1 (file-name-nondirectory p1)) |
| 424 | (when (recentf-include-p filename) | 424 | (d2 (file-name-directory p2)) |
| 425 | (setq recentf-list (cons filename (delete filename recentf-list))) | 425 | (f2 (file-name-nondirectory p2))) |
| 426 | (setq recentf-update-menu-p t))) | 426 | (if (string= d1 d2) |
| 427 | 427 | (string-lessp f1 f2) | |
| 428 | (defun recentf-remove-if-non-readable (filename) | 428 | (string-lessp d1 d2)))) |
| 429 | "Remove FILENAME from `recentf-list' if not readable." | 429 | |
| 430 | (unless (file-readable-p filename) | 430 | (defun recentf-sort-directories-ascending (l) |
| 431 | (setq recentf-list (delete filename recentf-list)) | 431 | "Sort the list of menu elements L in ascending order. |
| 432 | (setq recentf-update-menu-p t))) | 432 | Compares directories then filenames to order the list." |
| 433 | 433 | (sort (copy-sequence l) | |
| 434 | (defun recentf-find-file (filename) | 434 | (function |
| 435 | "Edit file FILENAME using `find-file'. | 435 | (lambda (e1 e2) |
| 436 | If FILENAME is not readable it is removed from `recentf-list'." | 436 | (recentf-directory-compare (recentf-menu-element-value e1) |
| 437 | (if (file-readable-p filename) | 437 | (recentf-menu-element-value e2)))))) |
| 438 | (find-file filename) | 438 | |
| 439 | (progn | 439 | (defun recentf-sort-directories-descending (l) |
| 440 | (message "File `%s' not found." filename) | 440 | "Sort the list of menu elements L in descending order. |
| 441 | (setq recentf-list (delete filename recentf-list)) | 441 | Compares directories then filenames to order the list." |
| 442 | (setq recentf-update-menu-p t)))) | 442 | (sort (copy-sequence l) |
| 443 | 443 | (function | |
| 444 | (defun recentf-include-p (filename) | 444 | (lambda (e1 e2) |
| 445 | "Return t if FILENAME matches none of the `recentf-exclude' regexps." | 445 | (recentf-directory-compare (recentf-menu-element-value e2) |
| 446 | (let ((rl recentf-exclude)) | 446 | (recentf-menu-element-value e1)))))) |
| 447 | (while (and rl (not (string-match (car rl) filename))) | 447 | |
| 448 | (setq rl (cdr rl))) | 448 | (defun recentf-show-basenames (l) |
| 449 | (null rl))) | 449 | "Filter the list of menu elements L to show only file names (no directories) |
| 450 | 450 | in the menu. When file names are duplicated their directory component is added." | |
| 451 | (defun recentf-elements (n) | 451 | (let ((names (mapcar (function |
| 452 | "Return a list of the first N elements of `recentf-list'." | 452 | (lambda (item) |
| 453 | (let ((lh nil) (l recentf-list)) | 453 | (file-name-nondirectory |
| 454 | (while (and l (> n 0)) | 454 | (recentf-menu-element-value item)))) |
| 455 | (setq lh (cons (car l) lh)) | 455 | l)) |
| 456 | (setq n (1- n)) | 456 | (dirs (mapcar (function |
| 457 | (setq l (cdr l))) | 457 | (lambda (item) |
| 458 | (nreverse lh))) | 458 | (file-name-directory |
| 459 | 459 | (recentf-menu-element-value item)))) | |
| 460 | (defun recentf-menu-elements (n) | 460 | l)) |
| 461 | "Return a list of the first N menu elements from `recentf-list'. | 461 | (pathes (mapcar 'recentf-menu-element-value l)) |
| 462 | Each menu element has this form: | 462 | (pos -1) |
| 463 | 463 | item filtered-items filtered-list) | |
| 464 | (MENU-ITEM . FILE-PATH) | 464 | (while names |
| 465 | 465 | (setq item (car names)) | |
| 466 | MENU-ITEM is the menu item string displayed. | 466 | (setq names (cdr names)) |
| 467 | 467 | (setq pos (1+ pos)) | |
| 468 | FILE-PATH is the path used to open the file when the corresponding MENU-ITEM | 468 | (setq filtered-list |
| 469 | is selected. | 469 | (cons (recentf-make-menu-element |
| 470 | 470 | (if (or (member item names) (member item filtered-items)) | |
| 471 | At the start each MENU-ITEM is set to its corresponding FILE-PATH." | 471 | (concat item " (" (nth pos dirs) ")") |
| 472 | (mapcar '(lambda (item) (cons item item)) (recentf-elements n))) | 472 | item) |
| 473 | 473 | (nth pos pathes)) | |
| 474 | 474 | filtered-list)) | |
| 475 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 475 | (setq filtered-items (cons item filtered-items))) |
| 476 | ;; Predefined menu filter functions ;; | 476 | (nreverse filtered-list))) |
| 477 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 477 | |
| 478 | 478 | (defun recentf-show-basenames-ascending (l) | |
| 479 | (defun recentf-sort-ascending (l) | 479 | "Filter the list of menu elements L to show only file names in the menu, |
| 480 | "Sort the list of menu elements L in ascending order. | 480 | sorted in ascending order. This filter combines the `recentf-sort-basenames-ascending' |
| 481 | The MENU-ITEM part of each menu element is compared." | 481 | and `recentf-show-basenames' filters." |
| 482 | (sort l '(lambda (e1 e2) (string-lessp (car e1) (car e2))))) | 482 | (recentf-show-basenames (recentf-sort-basenames-ascending l))) |
| 483 | 483 | ||
| 484 | (defun recentf-sort-descending (l) | 484 | (defun recentf-show-basenames-descending (l) |
| 485 | "Sort the list of menu elements L in descending order. | 485 | "Filter the list of menu elements L to show only file names in the menu, |
| 486 | The MENU-ITEM part of each menu element is compared." | 486 | sorted in descending order. This filter combines the `recentf-sort-basenames-descending' |
| 487 | (sort l '(lambda (e1 e2) (string-lessp (car e2) (car e1))))) | 487 | and `recentf-show-basenames' filters." |
| 488 | 488 | (recentf-show-basenames (recentf-sort-basenames-descending l))) | |
| 489 | (defun recentf-sort-basenames-ascending (l) | 489 | |
| 490 | "Sort the list of menu elements L in ascending order. | 490 | (defun recentf-relative-filter (l) |
| 491 | Only file names (without directories) are compared." | 491 | "Filter the list of `recentf-menu-elements' L to show filenames |
| 492 | (sort l '(lambda (e1 e2) (string-lessp | 492 | relative to `default-directory'." |
| 493 | (file-name-nondirectory (cdr e1)) | 493 | (setq recentf-update-menu-p t) ; force menu update |
| 494 | (file-name-nondirectory (cdr e2)))))) | 494 | (mapcar (function |
| 495 | 495 | (lambda (menu-element) | |
| 496 | (defun recentf-sort-basenames-descending (l) | 496 | (let* ((ful-path (recentf-menu-element-value menu-element)) |
| 497 | "Sort the list of menu elements L in descending order. | 497 | (rel-path (file-relative-name ful-path))) |
| 498 | Only file names (without directories) are compared." | 498 | (if (string-match "^\\.\\." rel-path) |
| 499 | (sort l '(lambda (e1 e2) (string-lessp | 499 | menu-element |
| 500 | (file-name-nondirectory (cdr e2)) | 500 | (recentf-make-menu-element rel-path ful-path))))) |
| 501 | (file-name-nondirectory (cdr e1)))))) | 501 | l)) |
| 502 | 502 | ||
| 503 | (defun recentf-show-basenames (l) | 503 | (defcustom recentf-arrange-rules |
| 504 | "Filter the list of menu elements L to show only file names (no directories) | 504 | '( |
| 505 | in the menu. When file names are duplicated their directory component is added." | 505 | ("Elisp files (%d)" ".\\.el$") |
| 506 | (let ((names (mapcar '(lambda (item) (file-name-nondirectory (cdr item))) l)) | 506 | ("Java files (%d)" ".\\.java$") |
| 507 | (dirs (mapcar '(lambda (item) (file-name-directory (cdr item))) l)) | 507 | ("C/C++ files (%d)" "c\\(pp\\)?$") |
| 508 | (pathes (mapcar 'cdr l)) | 508 | ) |
| 509 | (pos -1) | 509 | "*List of rules used by `recentf-arrange-by-rule' to build sub-menus. |
| 510 | item filtered-items filtered-list) | 510 | A rule is a pair (SUB-MENU-TITLE . MATCHER). SUB-MENU-TITLE is the |
| 511 | (while names | 511 | displayed title of the sub-menu where a '%d' `format' pattern is |
| 512 | (setq item (car names)) | 512 | replaced by the number of items in the sub-menu. MATCHER is a regexp |
| 513 | (setq names (cdr names)) | 513 | or a list of regexps. Items matching one of the regular expressions in |
| 514 | (setq pos (1+ pos)) | 514 | MATCHER are added to the corresponding sub-menu." |
| 515 | (setq filtered-list | 515 | :group 'recentf-filters |
| 516 | (cons (cons (if (or (member item names) (member item filtered-items)) | 516 | :type '(repeat (cons string (repeat regexp))) |
| 517 | (concat item " (" (nth pos dirs) ")") | 517 | :set 'recentf-menu-customization-changed) |
| 518 | item) | 518 | |
| 519 | (nth pos pathes)) | 519 | (defcustom recentf-arrange-by-rule-others "Other files (%d)" |
| 520 | filtered-list)) | 520 | "*Title of the `recentf-arrange-by-rule' sub-menu where items that |
| 521 | (setq filtered-items (cons item filtered-items))) | 521 | don't match any `recentf-arrange-rules' are displayed. If nil |
| 522 | (nreverse filtered-list))) | 522 | these items are displayed in the main recent files menu. A '%d' |
| 523 | 523 | `format' pattern in the title is replaced by the number of items in | |
| 524 | (defun recentf-show-basenames-ascending (l) | 524 | the sub-menu." |
| 525 | "Filter the list of menu elements L to show only file names in the menu, | 525 | :group 'recentf-filters |
| 526 | sorted in ascending order. This filter combines the `recentf-sort-basenames-ascending' | 526 | :type '(choice (const :tag "Main menu" nil) |
| 527 | and `recentf-show-basenames' filters." | 527 | (string :tag "Title")) |
| 528 | (recentf-show-basenames (recentf-sort-basenames-ascending l))) | 528 | :set 'recentf-menu-customization-changed) |
| 529 | 529 | ||
| 530 | (defun recentf-show-basenames-descending (l) | 530 | (defcustom recentf-arrange-by-rules-min-items 0 |
| 531 | "Filter the list of menu elements L to show only file names in the menu, | 531 | "*Minimum number of items in a `recentf-arrange-by-rule' sub-menu. |
| 532 | sorted in descending order. This filter combines the `recentf-sort-basenames-descending' | 532 | If the number of items in a sub-menu is less than this value the |
| 533 | and `recentf-show-basenames' filters." | 533 | corresponding sub-menu items are displayed in the main recent files |
| 534 | (recentf-show-basenames (recentf-sort-basenames-descending l))) | 534 | menu or in the `recentf-arrange-by-rule-others' sub-menu if |
| 535 | 535 | defined." | |
| 536 | (defun recentf-relative-filter (l) | 536 | :group 'recentf-filters |
| 537 | "Filter the list of `recentf-menu-elements' L to show filenames | 537 | :type 'number |
| 538 | relative to `default-directory'." | 538 | :set 'recentf-menu-customization-changed) |
| 539 | (setq recentf-update-menu-p t) ; force menu update | 539 | |
| 540 | (mapcar '(lambda (menu-element) | 540 | (defcustom recentf-arrange-by-rule-subfilter nil |
| 541 | (let* ((ful-path (cdr menu-element)) | 541 | "*Function used by `recentf-arrange-by-rule' to filter sub-menu elements. |
| 542 | (rel-path (file-relative-name ful-path))) | 542 | Nil means no filter. See also `recentf-menu-filter'. You can't use |
| 543 | (if (string-match "^\\.\\." rel-path) | 543 | `recentf-arrange-by-rule' itself here!" |
| 544 | menu-element | 544 | :group 'recentf-filters |
| 545 | (cons rel-path ful-path)))) | 545 | :type 'function |
| 546 | l)) | 546 | :set (lambda (sym val) |
| 547 | 547 | (if (eq val 'recentf-arrange-by-rule) | |
| 548 | (provide 'recentf) | 548 | (error "Can't use `recentf-arrange-by-rule' itself here!") |
| 549 | 549 | (recentf-menu-customization-changed sym val)))) | |
| 550 | (run-hooks 'recentf-load-hook) | 550 | |
| 551 | 551 | (defun recentf-match-rule-p (matcher file-path) | |
| 552 | ;;; recentf.el ends here. | 552 | "Return non-nil if FILE-PATH match the rule specified by MATCHER. |
| 553 | See `recentf-arrange-rules' for details on MATCHER." | ||
| 554 | (if (stringp matcher) | ||
| 555 | (string-match matcher file-path) | ||
| 556 | (while (and (consp matcher) | ||
| 557 | (not (string-match (car matcher) file-path))) | ||
| 558 | (setq matcher (cdr matcher))) | ||
| 559 | matcher)) | ||
| 560 | |||
| 561 | (defun recentf-arrange-by-rule (l) | ||
| 562 | "Filter the list of menu-elements L to arrange them in sub-menus | ||
| 563 | following rules in `recentf-arrange-rules'." | ||
| 564 | (let ((sub-menus-number (length recentf-arrange-rules))) | ||
| 565 | (if (> sub-menus-number 0) | ||
| 566 | (let ((sub-menus (apply 'vector | ||
| 567 | (mapcar (function | ||
| 568 | (lambda (pair) | ||
| 569 | (list (car pair)))) | ||
| 570 | recentf-arrange-rules))) | ||
| 571 | other-menu-elements index min-size) | ||
| 572 | (while l | ||
| 573 | (let* ((menu-element (car l)) | ||
| 574 | (file-path (recentf-menu-element-value menu-element)) | ||
| 575 | (rules recentf-arrange-rules) | ||
| 576 | (found nil)) | ||
| 577 | (setq index 0) | ||
| 578 | (while (and (not found) rules) | ||
| 579 | (if (recentf-match-rule-p (cdar rules) file-path) | ||
| 580 | (let ((sub-menu (aref sub-menus index))) | ||
| 581 | (setq found t) | ||
| 582 | (recentf-set-menu-element-value | ||
| 583 | sub-menu | ||
| 584 | (cons menu-element (recentf-menu-element-value sub-menu))) | ||
| 585 | )) | ||
| 586 | (setq index (1+ index)) | ||
| 587 | (setq rules (cdr rules))) | ||
| 588 | (or found | ||
| 589 | (setq other-menu-elements | ||
| 590 | (cons menu-element other-menu-elements))) | ||
| 591 | (setq l (cdr l)))) | ||
| 592 | (setq index 0) | ||
| 593 | (setq l nil) | ||
| 594 | (setq min-size (if (integerp recentf-arrange-by-rules-min-items) | ||
| 595 | (max 0 recentf-arrange-by-rules-min-items) | ||
| 596 | 0)) | ||
| 597 | (while (< index sub-menus-number) | ||
| 598 | (let* ((sub-menu (aref sub-menus index)) | ||
| 599 | (sub-menu-title (recentf-menu-element-item sub-menu)) | ||
| 600 | (sub-menu-elements (recentf-menu-element-value sub-menu)) | ||
| 601 | (sub-menu-length (length sub-menu-elements))) | ||
| 602 | (if (> sub-menu-length 0) | ||
| 603 | (cond | ||
| 604 | ((< sub-menu-length min-size) | ||
| 605 | (setq other-menu-elements | ||
| 606 | (nconc sub-menu-elements other-menu-elements))) | ||
| 607 | ((>= sub-menu-length min-size) | ||
| 608 | (recentf-set-menu-element-item | ||
| 609 | sub-menu | ||
| 610 | (format sub-menu-title sub-menu-length)) | ||
| 611 | (recentf-set-menu-element-value | ||
| 612 | sub-menu | ||
| 613 | (recentf-apply-menu-filter | ||
| 614 | recentf-arrange-by-rule-subfilter | ||
| 615 | (nreverse sub-menu-elements))) | ||
| 616 | (setq l (cons sub-menu l))))) | ||
| 617 | (setq index (1+ index)))) | ||
| 618 | (if (and (stringp recentf-arrange-by-rule-others) | ||
| 619 | other-menu-elements) | ||
| 620 | (setq l | ||
| 621 | (nreverse | ||
| 622 | (cons (recentf-make-menu-element | ||
| 623 | (format recentf-arrange-by-rule-others | ||
| 624 | (length other-menu-elements)) | ||
| 625 | (recentf-apply-menu-filter | ||
| 626 | recentf-arrange-by-rule-subfilter | ||
| 627 | (nreverse other-menu-elements))) | ||
| 628 | l))) | ||
| 629 | (setq l (nconc (nreverse l) | ||
| 630 | (recentf-apply-menu-filter | ||
| 631 | recentf-arrange-by-rule-subfilter | ||
| 632 | (nreverse other-menu-elements))))))) | ||
| 633 | l)) | ||
| 634 | |||
| 635 | (defun recentf-build-mode-rules () | ||
| 636 | "Convert `auto-mode-alist' to `recentf-arrange-rules' format." | ||
| 637 | (let ((case-fold-search recentf-case-fold-search) | ||
| 638 | (modes auto-mode-alist) | ||
| 639 | regexp mode rule-name rule rules) | ||
| 640 | (while modes | ||
| 641 | (setq regexp (caar modes)) | ||
| 642 | (setq mode (cdar modes)) | ||
| 643 | (when (symbolp mode) | ||
| 644 | (setq rule-name (symbol-name mode)) | ||
| 645 | (if (string-match "\\(.*\\)-mode$" rule-name) | ||
| 646 | (setq rule-name (match-string 1 rule-name))) | ||
| 647 | (setq rule-name (concat rule-name " (%d)")) | ||
| 648 | (setq rule (assoc rule-name rules)) | ||
| 649 | (if rule | ||
| 650 | (setcdr rule (cons regexp (cdr rule))) | ||
| 651 | (setq rules (cons (list rule-name regexp) rules)))) | ||
| 652 | (setq modes (cdr modes))) | ||
| 653 | ;; It is important to preserve auto-mode-alist order | ||
| 654 | ;; to ensure the right file <-> mode association | ||
| 655 | (nreverse rules))) | ||
| 656 | |||
| 657 | (defun recentf-arrange-by-mode (l) | ||
| 658 | "Filter the list of menu-elements L to build sub-menus for each | ||
| 659 | major mode." | ||
| 660 | (let ((recentf-arrange-rules (recentf-build-mode-rules)) | ||
| 661 | (recentf-arrange-by-rule-others "others (%d)")) | ||
| 662 | (recentf-arrange-by-rule l))) | ||
| 663 | |||
| 664 | (defun recentf-build-dir-rules (l) | ||
| 665 | "Convert directories in the list of menu-elements L to rules in | ||
| 666 | `recentf-arrange-rules' format." | ||
| 667 | (let (dirs) | ||
| 668 | (mapcar (function | ||
| 669 | (lambda (e) | ||
| 670 | (let ((dir (file-name-directory | ||
| 671 | (recentf-menu-element-value e)))) | ||
| 672 | (or (member dir dirs) | ||
| 673 | (setq dirs (cons dir dirs)))))) | ||
| 674 | l) | ||
| 675 | (mapcar (function | ||
| 676 | (lambda (d) | ||
| 677 | (cons (concat d " (%d)") | ||
| 678 | (concat "\\`" d)))) | ||
| 679 | (nreverse (sort dirs 'string-lessp))))) | ||
| 680 | |||
| 681 | (defun recentf-file-name-nondir (l) | ||
| 682 | "Filter the list of menu-elements L to show only filenames. This | ||
| 683 | simplified version of `recentf-show-basenames' do not handle | ||
| 684 | duplicates. It is used by `recentf-arrange-by-dir' as its | ||
| 685 | `recentf-arrange-by-rule-subfilter'." | ||
| 686 | (mapcar (function | ||
| 687 | (lambda (e) | ||
| 688 | (recentf-make-menu-element | ||
| 689 | (file-name-nondirectory (recentf-menu-element-value e)) | ||
| 690 | (recentf-menu-element-value e)))) | ||
| 691 | l)) | ||
| 692 | |||
| 693 | (defun recentf-arrange-by-dir (l) | ||
| 694 | "Filter the list of menu-elements L to build sub-menus for each | ||
| 695 | directory." | ||
| 696 | (let ((recentf-arrange-rules (recentf-build-dir-rules l)) | ||
| 697 | (recentf-arrange-by-rule-subfilter 'recentf-file-name-nondir) | ||
| 698 | recentf-arrange-by-rule-others) | ||
| 699 | (nreverse (recentf-arrange-by-rule l)))) | ||
| 700 | |||
| 701 | (defvar recentf-filter-changer-state nil | ||
| 702 | "Used by `recentf-filter-changer' to hold its state.") | ||
| 703 | |||
| 704 | (defcustom recentf-filter-changer-alist | ||
| 705 | '( | ||
| 706 | (recentf-arrange-by-mode . "*Files by Mode*") | ||
| 707 | (recentf-arrange-by-dir . "*Files by Directory*") | ||
| 708 | (recentf-arrange-by-rule . "*Files by User Rule*") | ||
| 709 | ) | ||
| 710 | "*List of filters managed by `recentf-filter-changer'. | ||
| 711 | Each filter is defined by a pair (FILTER-FUN . FILTER-LBL) where: | ||
| 712 | |||
| 713 | - - FILTER-FUN is the function that filters menu-elements | ||
| 714 | - - FILTER-LBL is the menu item used to activate the filter" | ||
| 715 | :group 'recentf-filters | ||
| 716 | :type '(repeat (cons function string)) | ||
| 717 | :set (lambda (sym val) | ||
| 718 | (setq recentf-filter-changer-state nil) | ||
| 719 | (recentf-menu-customization-changed sym val))) | ||
| 720 | |||
| 721 | (defun recentf-filter-changer-goto-next () | ||
| 722 | "Go to the next filter available (see `recentf-filter-changer')." | ||
| 723 | (and (consp recentf-filter-changer-state) | ||
| 724 | (setq recentf-filter-changer-state | ||
| 725 | (cdr recentf-filter-changer-state))) | ||
| 726 | (setq recentf-update-menu-p t)) | ||
| 727 | |||
| 728 | (defun recentf-filter-changer-get-current () | ||
| 729 | "Get the current filter available (see `recentf-filter-changer')." | ||
| 730 | (if (null recentf-filter-changer-state) | ||
| 731 | (setq recentf-filter-changer-state recentf-filter-changer-alist)) | ||
| 732 | (and (consp recentf-filter-changer-state) | ||
| 733 | (car recentf-filter-changer-state))) | ||
| 734 | |||
| 735 | (defun recentf-filter-changer-get-next () | ||
| 736 | "Get the next filter available (see `recentf-filter-changer')." | ||
| 737 | (let ((filters recentf-filter-changer-state)) | ||
| 738 | (cond ((consp filters) | ||
| 739 | (setq filters (cdr filters)) | ||
| 740 | (if (null filters) | ||
| 741 | (setq filters recentf-filter-changer-alist))) | ||
| 742 | (t | ||
| 743 | (setq filters recentf-filter-changer-alist) | ||
| 744 | (if (consp filters) | ||
| 745 | (setq filters (cdr filters))))) | ||
| 746 | (if (consp filters) | ||
| 747 | (car filters)))) | ||
| 748 | |||
| 749 | (defun recentf-filter-changer (l) | ||
| 750 | "Manage a ring of filters. `recentf-filter-changer-alist' defines | ||
| 751 | the filters in the ring. Actual filtering of L is delegated to the | ||
| 752 | current filter in the ring. A filter menu item is displayed allowing | ||
| 753 | to dynamically activate the next filter in the ring. If the filter | ||
| 754 | ring is empty L is left unchanged." | ||
| 755 | (let ((current-filter-item (recentf-filter-changer-get-current)) | ||
| 756 | (next-filter-item (recentf-filter-changer-get-next))) | ||
| 757 | (when current-filter-item | ||
| 758 | (setq l (recentf-apply-menu-filter (car current-filter-item) l)) | ||
| 759 | (if next-filter-item | ||
| 760 | (setq recentf-menu-filter-commands | ||
| 761 | (list (vector (cdr next-filter-item) | ||
| 762 | '(recentf-filter-changer-goto-next) | ||
| 763 | t))))) | ||
| 764 | l)) | ||
| 765 | |||
| 766 | ;;; | ||
| 767 | ;;; Dialogs stuff | ||
| 768 | ;;; | ||
| 769 | |||
| 770 | (defun recentf-cancel-dialog (&rest ignore) | ||
| 771 | "Cancel the current dialog. Used by `recentf-edit-list' and | ||
| 772 | `recentf-open-files' dialogs." | ||
| 773 | (interactive) | ||
| 774 | (kill-buffer (current-buffer)) | ||
| 775 | (message "Dialog canceled.")) | ||
| 776 | |||
| 777 | (defvar recentf-dialog-mode-map nil | ||
| 778 | "`recentf-dialog-mode' keymap.") | ||
| 779 | |||
| 780 | (if recentf-dialog-mode-map | ||
| 781 | () | ||
| 782 | (setq recentf-dialog-mode-map (make-sparse-keymap)) | ||
| 783 | (define-key recentf-dialog-mode-map "q" 'recentf-cancel-dialog) | ||
| 784 | (set-keymap-parent recentf-dialog-mode-map widget-keymap)) | ||
| 785 | |||
| 786 | (defun recentf-dialog-mode () | ||
| 787 | "Major mode used in recentf dialogs. | ||
| 788 | |||
| 789 | These are the special commands of recentf-dialog-mode mode: | ||
| 790 | q -- cancel this dialog." | ||
| 791 | (interactive) | ||
| 792 | (setq major-mode 'recentf-dialog-mode) | ||
| 793 | (setq mode-name "recentf-dialog") | ||
| 794 | (use-local-map recentf-dialog-mode-map)) | ||
| 795 | |||
| 796 | ;;; | ||
| 797 | ;;; Hooks and Commands | ||
| 798 | ;;; | ||
| 799 | |||
| 800 | (defun recentf-add-file-hook () | ||
| 801 | "Insert the name of the file just opened or written into `recentf-list'." | ||
| 802 | (and buffer-file-name (recentf-add-file buffer-file-name)) | ||
| 803 | nil) | ||
| 804 | |||
| 805 | (defun recentf-remove-file-hook () | ||
| 806 | "When a buffer is killed remove a non readable file from `recentf-list'." | ||
| 807 | (and buffer-file-name (recentf-remove-if-non-readable buffer-file-name)) | ||
| 808 | nil) | ||
| 809 | |||
| 810 | (defun recentf-update-menu-hook () | ||
| 811 | "Update the recentf menu from the current `recentf-list'." | ||
| 812 | (when recentf-update-menu-p | ||
| 813 | (condition-case nil | ||
| 814 | (progn | ||
| 815 | (setq recentf-update-menu-p nil) | ||
| 816 | (easy-menu-change recentf-menu-path | ||
| 817 | recentf-menu-title | ||
| 818 | (recentf-make-menu-items) | ||
| 819 | recentf-menu-before)) | ||
| 820 | (error nil)))) | ||
| 821 | |||
| 822 | (defun recentf-dump-variable (variable &optional limit) | ||
| 823 | "Insert a \"(setq VARIABLE value)\" in the current buffer. Optional | ||
| 824 | argument LIMIT specifies a maximum length when VARIABLE value is a | ||
| 825 | list (default to the full list)." | ||
| 826 | (let ((value (symbol-value variable))) | ||
| 827 | (insert (format "(setq %S\n '(\n" variable)) | ||
| 828 | (cond ((consp value) | ||
| 829 | (if (and (integerp limit) (> limit 0)) | ||
| 830 | (setq value (recentf-trunc-list value limit))) | ||
| 831 | (mapcar (function | ||
| 832 | (lambda (e) | ||
| 833 | (insert (format " %S\n" e)))) | ||
| 834 | value)) | ||
| 835 | (t | ||
| 836 | (insert (format " %S\n" value)))) | ||
| 837 | (insert " ))\n") | ||
| 838 | )) | ||
| 839 | |||
| 840 | ;;;###autoload | ||
| 841 | (defun recentf-save-list () | ||
| 842 | "Save the current `recentf-list' to the file `recentf-save-file'." | ||
| 843 | (interactive) | ||
| 844 | (with-temp-buffer | ||
| 845 | (erase-buffer) | ||
| 846 | (insert (format recentf-save-file-header (current-time-string))) | ||
| 847 | (recentf-dump-variable 'recentf-list recentf-max-saved-items) | ||
| 848 | (recentf-dump-variable 'recentf-filter-changer-state) | ||
| 849 | (if (file-writable-p recentf-save-file) | ||
| 850 | (write-region (point-min) (point-max) recentf-save-file)) | ||
| 851 | (kill-buffer (current-buffer))) | ||
| 852 | nil) | ||
| 853 | |||
| 854 | (defvar recentf-edit-selected-items nil | ||
| 855 | "Used by `recentf-edit-list' to hold the list of files to be deleted | ||
| 856 | from `recentf-list'.") | ||
| 857 | |||
| 858 | (defun recentf-edit-list-action (widget &rest ignore) | ||
| 859 | "Checkbox widget action used by `recentf-edit-list' to select/unselect a file." | ||
| 860 | (let ((value (widget-get widget ':tag))) | ||
| 861 | ;; if value is already in the selected items | ||
| 862 | (if (memq value recentf-edit-selected-items) | ||
| 863 | ;; then remove it | ||
| 864 | (progn | ||
| 865 | (setq recentf-edit-selected-items | ||
| 866 | (delq value recentf-edit-selected-items)) | ||
| 867 | (message "%s removed from selection." value)) | ||
| 868 | ;; else add it | ||
| 869 | (progn | ||
| 870 | (setq recentf-edit-selected-items | ||
| 871 | (nconc (list value) recentf-edit-selected-items)) | ||
| 872 | (message "%s added to selection." value))))) | ||
| 873 | |||
| 874 | ;;;###autoload | ||
| 875 | (defun recentf-edit-list () | ||
| 876 | "Allow the user to edit the files that are kept in the recent list." | ||
| 877 | (interactive) | ||
| 878 | (with-current-buffer (get-buffer-create (concat "*" recentf-menu-title " - Edit list*")) | ||
| 879 | (switch-to-buffer (current-buffer)) | ||
| 880 | (kill-all-local-variables) | ||
| 881 | (let ((inhibit-read-only t)) | ||
| 882 | (erase-buffer)) | ||
| 883 | (let ((all (overlay-lists))) | ||
| 884 | ;; Delete all the overlays. | ||
| 885 | (mapcar 'delete-overlay (car all)) | ||
| 886 | (mapcar 'delete-overlay (cdr all))) | ||
| 887 | (setq recentf-edit-selected-items nil) | ||
| 888 | ;; Insert the dialog header | ||
| 889 | (widget-insert "Select the files to be deleted from the 'recentf-list'.\n\n") | ||
| 890 | (widget-insert "Click on Ok to update the list. ") | ||
| 891 | (widget-insert "Click on Cancel or type \"q\" to quit.\n") | ||
| 892 | ;; Insert the list of files as checkboxes | ||
| 893 | (mapcar (function | ||
| 894 | (lambda (item) | ||
| 895 | (widget-create 'checkbox | ||
| 896 | :value nil ; unselected checkbox | ||
| 897 | :format "\n %[%v%] %t" | ||
| 898 | :tag item | ||
| 899 | :notify 'recentf-edit-list-action))) | ||
| 900 | recentf-list) | ||
| 901 | (widget-insert "\n\n") | ||
| 902 | ;; Insert the Ok button | ||
| 903 | (widget-create 'push-button | ||
| 904 | :notify (lambda (&rest ignore) | ||
| 905 | (if recentf-edit-selected-items | ||
| 906 | (progn (kill-buffer (current-buffer)) | ||
| 907 | (mapcar (function | ||
| 908 | (lambda (item) | ||
| 909 | (setq recentf-list | ||
| 910 | (delq item recentf-list)))) | ||
| 911 | recentf-edit-selected-items) | ||
| 912 | (message "%S file(s) removed from the list" | ||
| 913 | (length recentf-edit-selected-items)) | ||
| 914 | (setq recentf-update-menu-p t)) | ||
| 915 | (message "No file selected."))) | ||
| 916 | "Ok") | ||
| 917 | (widget-insert " ") | ||
| 918 | ;; Insert the Cancel button | ||
| 919 | (widget-create 'push-button | ||
| 920 | :notify 'recentf-cancel-dialog | ||
| 921 | "Cancel") | ||
| 922 | (recentf-dialog-mode) | ||
| 923 | (widget-setup) | ||
| 924 | (goto-char (point-min)))) | ||
| 925 | |||
| 926 | ;;;###autoload | ||
| 927 | (defun recentf-cleanup () | ||
| 928 | "Remove all non-readable and excluded files from `recentf-list'." | ||
| 929 | (interactive) | ||
| 930 | (let ((count (length recentf-list))) | ||
| 931 | (setq recentf-list | ||
| 932 | (delq nil | ||
| 933 | (mapcar (function | ||
| 934 | (lambda (filename) | ||
| 935 | (and (file-readable-p filename) | ||
| 936 | (recentf-include-p filename) | ||
| 937 | filename))) | ||
| 938 | recentf-list))) | ||
| 939 | (setq count (- count (length recentf-list))) | ||
| 940 | (message "%s removed from the list" | ||
| 941 | (cond ((= count 0) "No file") | ||
| 942 | ((= count 1) "One file") | ||
| 943 | (t (format "%d files" count))))) | ||
| 944 | (setq recentf-update-menu-p t)) | ||
| 945 | |||
| 946 | (defun recentf-open-files-action (widget &rest ignore) | ||
| 947 | "Button widget action used by `recentf-open-files' to open a file." | ||
| 948 | (kill-buffer (current-buffer)) | ||
| 949 | (funcall recentf-menu-action (widget-value widget))) | ||
| 950 | |||
| 951 | (defvar recentf-open-files-item-shift "" | ||
| 952 | "String used by `recentf-open-files' to shift right sub-menu | ||
| 953 | items.") | ||
| 954 | |||
| 955 | (defun recentf-open-files-item (menu-element) | ||
| 956 | "Function called by `recentf-open-files' to insert a menu-element | ||
| 957 | item in the current interaction buffer." | ||
| 958 | (let ((menu-item (car menu-element)) | ||
| 959 | (file-path (cdr menu-element))) | ||
| 960 | (if (consp file-path) ; This is a sub-menu | ||
| 961 | (let* ((shift recentf-open-files-item-shift) | ||
| 962 | (recentf-open-files-item-shift (concat shift " "))) | ||
| 963 | (widget-create 'item | ||
| 964 | :tag menu-item | ||
| 965 | :sample-face 'bold | ||
| 966 | :format (concat shift "%{%t%}:\n")) | ||
| 967 | (mapcar 'recentf-open-files-item | ||
| 968 | file-path) | ||
| 969 | (widget-insert "\n")) | ||
| 970 | (widget-create 'push-button | ||
| 971 | :button-face 'default | ||
| 972 | :tag menu-item | ||
| 973 | :help-echo (concat "Open " file-path) | ||
| 974 | :format (concat recentf-open-files-item-shift "%[%t%]") | ||
| 975 | :notify 'recentf-open-files-action | ||
| 976 | file-path) | ||
| 977 | (widget-insert "\n")))) | ||
| 978 | |||
| 979 | ;;;###autoload | ||
| 980 | (defun recentf-open-files (&optional files buffer-name) | ||
| 981 | "Open a buffer that allows the user to choose a file to open from | ||
| 982 | the list of recently opened files. The optional argument FILES may be | ||
| 983 | used to specify the list, otherwise recentf-list is used. The optional | ||
| 984 | argument BUFFER-NAME specifies which buffer to use for the interaction." | ||
| 985 | (interactive) | ||
| 986 | (if (null files) | ||
| 987 | (setq files recentf-list)) | ||
| 988 | (if (null buffer-name) | ||
| 989 | (setq buffer-name (concat "*" recentf-menu-title "*"))) | ||
| 990 | (with-current-buffer (get-buffer-create buffer-name) | ||
| 991 | (switch-to-buffer (current-buffer)) | ||
| 992 | (kill-all-local-variables) | ||
| 993 | (let ((inhibit-read-only t)) | ||
| 994 | (erase-buffer)) | ||
| 995 | (let ((all (overlay-lists))) | ||
| 996 | ;; Delete all the overlays. | ||
| 997 | (mapcar 'delete-overlay (car all)) | ||
| 998 | (mapcar 'delete-overlay (cdr all))) | ||
| 999 | ;; Insert the dialog header | ||
| 1000 | (widget-insert "Click on a file to open it. ") | ||
| 1001 | (widget-insert "Click on Cancel or type \"q\" to quit.\n\n" ) | ||
| 1002 | ;; Insert the list of files as buttons | ||
| 1003 | (let ((recentf-open-files-item-shift "")) | ||
| 1004 | (mapcar 'recentf-open-files-item | ||
| 1005 | (recentf-apply-menu-filter | ||
| 1006 | recentf-menu-filter | ||
| 1007 | (mapcar 'recentf-make-default-menu-element files)))) | ||
| 1008 | (widget-insert "\n") | ||
| 1009 | ;; Insert the Cancel button | ||
| 1010 | (widget-create 'push-button | ||
| 1011 | :notify 'recentf-cancel-dialog | ||
| 1012 | "Cancel") | ||
| 1013 | (recentf-dialog-mode) | ||
| 1014 | (widget-setup) | ||
| 1015 | (goto-char (point-min)))) | ||
| 1016 | |||
| 1017 | ;;;###autoload | ||
| 1018 | (defun recentf-open-more-files () | ||
| 1019 | "Allow the user to open files that are not in the menu." | ||
| 1020 | (interactive) | ||
| 1021 | (recentf-open-files (nthcdr recentf-max-menu-items recentf-list) | ||
| 1022 | (concat "*" recentf-menu-title " - More*"))) | ||
| 1023 | |||
| 1024 | ;;;###autoload | ||
| 1025 | (defun recentf-mode (&optional arg) | ||
| 1026 | "Toggle recentf mode. | ||
| 1027 | With prefix ARG, turn recentf mode on if and only if ARG is positive. | ||
| 1028 | Returns the new status of recentf mode (non-nil means on). | ||
| 1029 | |||
| 1030 | When recentf mode is enabled, it maintains a menu for visiting files that | ||
| 1031 | were operated on recently." | ||
| 1032 | (interactive "P") | ||
| 1033 | (let ((on-p (if arg | ||
| 1034 | (> (prefix-numeric-value arg) 0) | ||
| 1035 | (not recentf-mode)))) | ||
| 1036 | (if on-p | ||
| 1037 | (unless recentf-initialized-p | ||
| 1038 | (setq recentf-initialized-p t) | ||
| 1039 | (if (file-readable-p recentf-save-file) | ||
| 1040 | (load-file recentf-save-file)) | ||
| 1041 | (setq recentf-update-menu-p t) | ||
| 1042 | (add-hook 'find-file-hooks 'recentf-add-file-hook) | ||
| 1043 | (add-hook 'write-file-hooks 'recentf-add-file-hook) | ||
| 1044 | (add-hook 'menu-bar-update-hook 'recentf-update-menu-hook) | ||
| 1045 | (add-hook 'kill-emacs-hook 'recentf-save-list)) | ||
| 1046 | (when recentf-initialized-p | ||
| 1047 | (setq recentf-initialized-p nil) | ||
| 1048 | (recentf-save-list) | ||
| 1049 | (easy-menu-remove-item nil recentf-menu-path recentf-menu-title) | ||
| 1050 | (remove-hook 'find-file-hooks 'recentf-add-file-hook) | ||
| 1051 | (remove-hook 'write-file-hooks 'recentf-add-file-hook) | ||
| 1052 | (remove-hook 'menu-bar-update-hook 'recentf-update-menu-hook) | ||
| 1053 | (remove-hook 'kill-emacs-hook 'recentf-save-list))) | ||
| 1054 | (setq recentf-mode on-p))) | ||
| 1055 | |||
| 1056 | (provide 'recentf) | ||
| 1057 | |||
| 1058 | (run-hooks 'recentf-load-hook) | ||
| 1059 | |||
| 1060 | ;;; recentf.el ends here. | ||