diff options
Diffstat (limited to 'lisp/recentf.el')
-rw-r--r-- | lisp/recentf.el | 196 |
1 files changed, 119 insertions, 77 deletions
diff --git a/lisp/recentf.el b/lisp/recentf.el index 7a4b589e457..b80ee3dd7d8 100644 --- a/lisp/recentf.el +++ b/lisp/recentf.el @@ -1,4 +1,4 @@ -;;; recentf.el --- setup a menu of recently opened files -*- lexical-binding: t -*- +;;; recentf.el --- keep track of recently opened files -*- lexical-binding: t -*- ;; Copyright (C) 1999-2022 Free Software Foundation, Inc. @@ -23,10 +23,19 @@ ;;; Commentary: -;; This package maintains a menu for visiting files that were operated -;; on recently. When enabled a new "Open Recent" submenu is -;; displayed in the "File" menu. The recent files list is -;; automatically saved across Emacs sessions. +;; This package maintains a list of recently opened files and makes it +;; easy to visit them. The recent files list is automatically saved +;; across Emacs sessions. + +;; There are three ways to access recent files: +;; +;; (1) `M-x recentf-open' prompts for a recently opened file. +;; +;; (2) When this mode is enabled, a new "Open Recent" submenu is +;; displayed in the "File" menu. +;; +;; (3) `M-x recentf-open-files' lists recently visited files in a +;; buffer. ;; You can customize the number of recent files displayed, the ;; location of the menu and other options. Type: @@ -45,16 +54,17 @@ ;;; Internal data ;; (defvar recentf-list nil - "List of recently opened files.") + "List of recently opened files for `recentf-mode'.") -(defsubst recentf-enabled-p () - "Return non-nil if recentf mode is currently enabled." +(defun recentf-enabled-p () + "Return non-nil if `recentf-mode' is currently enabled." (memq 'recentf-save-list kill-emacs-hook)) + ;;; Customization ;; (defgroup recentf nil - "Maintain a menu of recently opened files." + "Maintain a list of recently opened files." :version "21.1" :group 'files) @@ -159,7 +169,7 @@ If nil add it at end of menu (see also `easy-menu-add-item')." (const :tag "Last" nil)) :set 'recentf-menu-customization-changed) -(defcustom recentf-menu-action 'find-file +(defcustom recentf-menu-action #'find-file "Function to invoke with a filename item of the recentf menu. The default is to call `find-file' to edit the selected file." :group 'recentf @@ -168,7 +178,7 @@ The default is to call `find-file' to edit the selected file." (defcustom recentf-max-menu-items 10 "Maximum number of items in the recentf menu." :group 'recentf - :type 'integer) + :type 'natnum) (defcustom recentf-menu-filter nil "Function used to filter files displayed in the recentf menu. @@ -237,6 +247,8 @@ This item will replace the \"More...\" item." (defcustom recentf-auto-cleanup 'mode "Define when to automatically cleanup the recent list. +That is, remove duplicates, non-kept, and excluded files. + The following values can be set: - `mode' @@ -284,7 +296,7 @@ If `file-name-history' is not empty, do nothing." (make-obsolete-variable 'recentf-load-hook "use `with-eval-after-load' instead." "28.1") -(defcustom recentf-filename-handlers nil +(defcustom recentf-filename-handlers '(abbreviate-file-name) "Functions to post process recent file names. They are successively passed a file name to transform it." :group 'recentf @@ -294,7 +306,8 @@ They are successively passed a file name to transform it." (choice (const file-truename) (const abbreviate-file-name) - (function :tag "Other function"))))) + (function :tag "Other function")))) + :version "29.1") (defcustom recentf-show-file-shortcuts-flag t "Non-nil means to show \"[N]\" for the Nth item up to 10. @@ -309,14 +322,14 @@ used as shortcuts to open the Nth file." (memq system-type '(windows-nt cygwin)) "Non-nil if recentf searches and matches should ignore case.") -(defsubst recentf-string-equal (s1 s2) +(defun recentf-string-equal (s1 s2) "Return non-nil if strings S1 and S2 have identical contents. Ignore case if `recentf-case-fold-search' is non-nil." (if recentf-case-fold-search (string-equal (downcase s1) (downcase s2)) (string-equal s1 s2))) -(defsubst recentf-string-lessp (s1 s2) +(defun recentf-string-lessp (s1 s2) "Return non-nil if string S1 is less than S2 in lexicographic order. Ignore case if `recentf-case-fold-search' is non-nil." (if recentf-case-fold-search @@ -371,7 +384,7 @@ See also the option `recentf-auto-cleanup'.") ;;; File functions ;; -(defsubst recentf-push (filename) +(defun recentf-push (filename) "Push FILENAME into the recent list, if it isn't there yet. If it is there yet, move it at the beginning of the list. If `recentf-case-fold-search' is non-nil, ignore case when comparing @@ -394,7 +407,7 @@ returned nil." (error nil)) name)) -(defsubst recentf-expand-file-name (name) +(defun recentf-expand-file-name (name) "Convert file NAME to absolute, and canonicalize it. NAME is first passed to the function `expand-file-name', then to `recentf-filename-handlers' to post process it." @@ -435,7 +448,7 @@ That is, if it matches any of the `recentf-keep' checks." checks (cdr checks))) keepit)) -(defsubst recentf-add-file (filename) +(defun recentf-add-file (filename) "Add or move FILENAME at the beginning of the recent list. Does nothing if the name satisfies any of the `recentf-exclude' regexps or predicates." @@ -443,7 +456,7 @@ regexps or predicates." (when (recentf-include-p filename) (recentf-push filename))) -(defsubst recentf-remove-if-non-kept (filename) +(defun recentf-remove-if-non-kept (filename) "Remove FILENAME from the recent list, if file is not kept. Return non-nil if FILENAME has been removed." (unless (recentf-keep-p filename) @@ -461,10 +474,30 @@ Return non-nil if F1 is less than F2." (recentf-string-lessp (file-name-nondirectory f1) (file-name-nondirectory f2)) (recentf-string-lessp d1 d2)))) + + +;;; Open files +;; + +;;;###autoload +(defun recentf-open (file) + "Prompt for FILE in `recentf-list' and visit it. +Enable `recentf-mode' if it isn't already." + (interactive + (list + (progn (unless recentf-mode (recentf-mode 1)) + (completing-read (format-prompt "Open recent file" nil) + recentf-list nil t)))) + (when file + (funcall recentf-menu-action file))) + +;;;###autoload +(defalias 'recentf 'recentf-open) + ;;; Menu building ;; -(defsubst recentf-digit-shortcut-command-name (n) +(defun recentf-digit-shortcut-command-name (n) "Return a command name to open the Nth most recent file. See also the command `recentf-open-most-recent-file'." (intern (format "recentf-open-most-recent-file-%d" n))) @@ -476,10 +509,10 @@ See also the command `recentf-open-most-recent-file'." ;; Define a shortcut command. (defalias cmd `(lambda () - (interactive) + (interactive nil recentf-dialog-mode) (recentf-open-most-recent-file ,k))) ;; Bind it to a digit key. - (define-key km (vector (+ k ?0)) cmd))) + (keymap-set km (format "%d" k) cmd))) km) "Digit shortcuts keymap.") @@ -510,10 +543,6 @@ 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.") -(defsubst recentf-elements (n) - "Return a list of the first N elements of the recent list." - (seq-take recentf-list n)) - (defsubst recentf-make-menu-element (menu-item menu-value) "Create a new menu-element. A menu element is a pair (MENU-ITEM . MENU-VALUE), where MENU-ITEM is @@ -553,7 +582,7 @@ This a menu element (FILE . FILE)." "Return a list of the first N default menu elements from the recent list. See also `recentf-make-default-menu-element'." (mapcar #'recentf-make-default-menu-element - (recentf-elements n))) + (seq-take recentf-list n))) (defun recentf-apply-menu-filter (filter l) "Apply function FILTER to the list of menu-elements L. @@ -650,7 +679,7 @@ Return nil if file NAME is not one of the ten more recent." :help (concat "Open " value) :active t))))) -(defsubst recentf-menu-bar () +(defun recentf-menu-bar () "Return the keymap of the global menu bar." (lookup-key global-map [menu-bar])) @@ -670,59 +699,59 @@ Return nil if file NAME is not one of the ten more recent." ;;; Predefined menu filters ;; -(defsubst recentf-sort-ascending (l) +(defun recentf-sort-ascending (l) "Sort the list of menu elements L in ascending order. The MENU-ITEM part of each menu element is compared." (sort (copy-sequence l) - #'(lambda (e1 e2) - (recentf-string-lessp - (recentf-menu-element-item e1) - (recentf-menu-element-item e2))))) + (lambda (e1 e2) + (recentf-string-lessp + (recentf-menu-element-item e1) + (recentf-menu-element-item e2))))) -(defsubst recentf-sort-descending (l) +(defun recentf-sort-descending (l) "Sort the list of menu elements L in descending order. The MENU-ITEM part of each menu element is compared." (sort (copy-sequence l) - #'(lambda (e1 e2) - (recentf-string-lessp - (recentf-menu-element-item e2) - (recentf-menu-element-item e1))))) + (lambda (e1 e2) + (recentf-string-lessp + (recentf-menu-element-item e2) + (recentf-menu-element-item e1))))) -(defsubst recentf-sort-basenames-ascending (l) +(defun recentf-sort-basenames-ascending (l) "Sort the list of menu elements L in ascending order. Only filenames sans directory are compared." (sort (copy-sequence l) - #'(lambda (e1 e2) - (recentf-string-lessp - (file-name-nondirectory (recentf-menu-element-value e1)) - (file-name-nondirectory (recentf-menu-element-value e2)))))) + (lambda (e1 e2) + (recentf-string-lessp + (file-name-nondirectory (recentf-menu-element-value e1)) + (file-name-nondirectory (recentf-menu-element-value e2)))))) -(defsubst recentf-sort-basenames-descending (l) +(defun recentf-sort-basenames-descending (l) "Sort the list of menu elements L in descending order. Only filenames sans directory are compared." (sort (copy-sequence l) - #'(lambda (e1 e2) - (recentf-string-lessp - (file-name-nondirectory (recentf-menu-element-value e2)) - (file-name-nondirectory (recentf-menu-element-value e1)))))) + (lambda (e1 e2) + (recentf-string-lessp + (file-name-nondirectory (recentf-menu-element-value e2)) + (file-name-nondirectory (recentf-menu-element-value e1)))))) -(defsubst recentf-sort-directories-ascending (l) +(defun recentf-sort-directories-ascending (l) "Sort the list of menu elements L in ascending order. Compares directories then filenames to order the list." (sort (copy-sequence l) - #'(lambda (e1 e2) - (recentf-directory-compare - (recentf-menu-element-value e1) - (recentf-menu-element-value e2))))) + (lambda (e1 e2) + (recentf-directory-compare + (recentf-menu-element-value e1) + (recentf-menu-element-value e2))))) -(defsubst recentf-sort-directories-descending (l) +(defun recentf-sort-directories-descending (l) "Sort the list of menu elements L in descending order. Compares directories then filenames to order the list." (sort (copy-sequence l) - #'(lambda (e1 e2) - (recentf-directory-compare - (recentf-menu-element-value e2) - (recentf-menu-element-value e1))))) + (lambda (e1 e2) + (recentf-directory-compare + (recentf-menu-element-value e2) + (recentf-menu-element-value e1))))) (defun recentf-show-basenames (l &optional no-dir) "Filter the list of menu elements L to show filenames sans directory. @@ -743,14 +772,14 @@ optional argument NO-DIR is non-nil, or its directory otherwise." (setq name (format "%s(%s)" name sufx))) (push (recentf-make-menu-element name full) filtered-list)))) -(defsubst recentf-show-basenames-ascending (l) +(defun recentf-show-basenames-ascending (l) "Filter the list of menu elements L to show filenames sans directory. Filenames are sorted in ascending order. This filter combines the `recentf-sort-basenames-ascending' and `recentf-show-basenames' filters." (recentf-show-basenames (recentf-sort-basenames-ascending l))) -(defsubst recentf-show-basenames-descending (l) +(defun recentf-show-basenames-descending (l) "Filter the list of menu elements L to show filenames sans directory. Filenames are sorted in descending order. This filter combines the `recentf-sort-basenames-descending' and @@ -808,7 +837,7 @@ corresponding sub-menu items are displayed in the main recent files menu or in the `recentf-arrange-by-rule-others' sub-menu if defined." :group 'recentf-filters - :type 'number) + :type 'natnum) (defcustom recentf-arrange-by-rule-subfilter nil "Function called by a rule based filter to filter sub-menu elements. @@ -1037,7 +1066,7 @@ That is, remove a non kept file from the recent list." (defun recentf-cancel-dialog (&rest _ignore) "Cancel the current dialog. IGNORE arguments." - (interactive) + (interactive nil recentf-dialog-mode) (kill-buffer (current-buffer)) (message "Dialog canceled")) @@ -1055,19 +1084,20 @@ Go to the beginning of buffer if not found." (error (goto-char (point-min))))) -(defvar recentf-dialog-mode-map - (let ((km (copy-keymap recentf--shortcuts-keymap))) - (set-keymap-parent km widget-keymap) - (define-key km "q" #'recentf-cancel-dialog) - (define-key km "n" #'next-line) - (define-key km "p" #'previous-line) - km) - "Keymap used in recentf dialogs.") +(defvar-keymap recentf-dialog-mode-map + :doc "Keymap used in recentf dialogs." + :parent (make-composed-keymap recentf--shortcuts-keymap widget-keymap) + "q" #'recentf-cancel-dialog + "n" #'next-line + "p" #'previous-line + "C-c C-c" #'recentf-edit-list-validate + "C-c C-k" #'recentf-cancel-dialog) (define-derived-mode recentf-dialog-mode nil "recentf-dialog" "Major mode of recentf dialogs. \\{recentf-dialog-mode-map}" + :interactive nil :syntax-table nil :abbrev-table nil (setq truncate-lines t)) @@ -1104,6 +1134,7 @@ IGNORE other arguments." (defun recentf-edit-list-validate (&rest _ignore) "Process the recent list when the edit list dialog is committed. IGNORE arguments." + (interactive nil recentf-dialog-mode) (if recentf-edit-list (let ((i 0)) (dolist (e recentf-edit-list) @@ -1123,8 +1154,8 @@ IGNORE arguments." (widget-insert (format-message (substitute-command-keys - "Click on OK to delete selected files from the recent list. -Click on Cancel or type \\[recentf-cancel-dialog] to cancel.\n"))) + "Click on \"OK\" or type \\[recentf-edit-list-validate] to delete selected files from the recent list. +Click on \"Cancel\" or type \\[recentf-cancel-dialog] to cancel.\n"))) ;; Insert the list of files as checkboxes (dolist (item recentf-list) (widget-create 'checkbox @@ -1337,12 +1368,18 @@ That is, remove duplicates, non-kept, and excluded files." ;;; The minor mode ;; -(defvar recentf-mode-map (make-sparse-keymap) - "Keymap to use in recentf mode.") +(defvar-keymap recentf-mode-map + :doc "Keymap to use in `recentf-mode'.") ;;;###autoload (define-minor-mode recentf-mode - "Toggle \"Open Recent\" menu (Recentf mode). + "Toggle keeping track of opened files (Recentf mode). +This mode maintains a list of recently opened files and makes it +easy to visit them. The recent files list is automatically saved +across Emacs sessions. + +You can use `recentf-open' or `recentf-open-files' to visit +files. When Recentf mode is enabled, a \"Open Recent\" submenu is displayed in the \"File\" menu, containing a list of files that @@ -1353,7 +1390,7 @@ to a file, and killing a buffer is counted as \"operating\" on the file. If instead you want to prioritize files that appear in buffers you switch to a lot, you can say something like the following: - (add-hook 'buffer-list-update-hook 'recentf-track-opened-file)" + (add-hook \\='buffer-list-update-hook #\\='recentf-track-opened-file)" :global t :group 'recentf :keymap recentf-mode-map @@ -1379,8 +1416,13 @@ buffers you switch to a lot, you can say something like the following: (define-obsolete-function-alias 'recentf-trunc-list #'seq-take "28.1") +(defun recentf-elements (n) + "Return a list of the first N elements of the recent list." + (declare (obsolete "use `(seq-take recentf-list n)'." "29.1")) + (seq-take recentf-list n)) + (provide 'recentf) (run-hooks 'recentf-load-hook) - + ;;; recentf.el ends here |