RecentFiles

Recentf is a minor mode that builds a list of recently opened files. This list is automatically saved across sessions on exiting Emacs - you can then access this list through a command or the menu. Recentf has been part of GNU Emacs since version 21.

Usage

To enable ‘recentf-mode’, put this in your InitFile:

(recentf-mode 1)
(setq recentf-max-menu-items 25)
(setq recentf-max-saved-items 25)
(global-set-key "\C-x\ \C-r" 'recentf-open-files)

The command ‘recentf-open-files’ shows a dialog to open a file from the recent list.

Integrations with completion frameworks

Ido

Here is another version of recent-files completion, which use InteractivelyDoThings (ido). Not perfect, but seems to work ok. You first choose among filenames and if more than one filename matches, you are prompted to choose again among the matching files, but the path is added to the list.

(defun recentf-interactive-complete ()
  "find a file in the recently open file using ido for completion"
  (interactive)
  (let* ((all-files recentf-list)
	 (file-assoc-list (mapcar (lambda (x) (cons (file-name-nondirectory x) x)) all-files))
	 (filename-list (remove-duplicates (mapcar 'car file-assoc-list) :test 'string=))
	 (ido-make-buffer-list-hook
	  (lambda ()
	    (setq ido-temp-list filename-list)))
	 (filename (ido-read-buffer "Find Recent File: "))
	 (result-list (delq nil (mapcar (lambda (x) (if (string= (car x) filename) (cdr x))) file-assoc-list)))
	 (result-length (length result-list)))
         (find-file
	  (cond
	   ((= result-length 0) filename)
	   ((= result-length 1) (car result-list))
	   ( t
	     (let ( (ido-make-buffer-list-hook
		     (lambda ()
		       (setq ido-temp-list result-list))))
	       (ido-read-buffer (format "%d matches:" result-length))))
	   ))))

Or this somewhat simpler version:

(defun recentf-ido-find-file ()
  "Find a recent file using Ido."
  (interactive)
  (let ((file (ido-completing-read "Choose recent file: " recentf-list nil t)))
    (when file
      (find-file file))))

where file-name-nondirectory could be applied to recentf-list if preferred.

And this is a version with file-name-nondirectory.

(defun recentf-ido-find-file ()
  "Find a recent file using Ido."
  (interactive)
  (let* ((file-assoc-list
	  (mapcar (lambda (x)
		    (cons (file-name-nondirectory x)
			  x))
		  recentf-list))
	 (filename-list
	  (remove-duplicates (mapcar #'car file-assoc-list)
			     :test #'string=))
	 (filename (ido-completing-read "Choose recent file: "
					filename-list
					nil
					t)))
    (when filename
      (find-file (cdr (assoc filename
			     file-assoc-list))))))

Icicles

Icicles lets you take advantage of library recentf.el in two ways.

Multi-Command `icicle-recent-file'

‘recentf-mode’ of vanilla Emacs forces you to open a file using a menu interface. Icicles command ‘icicle-recent-file’ lets you use the usual ‘find-file’ minibuffer interface, with completion and cycling among your recent files.

‘icicle-recent-file’ is also a multi-command, so you can use it to open several recently used files at once. And the completion candidates are two-part multi-completions: (1) the (absolute) file name and (2) the file’s last modification date. You can match your minibuffer input against either or both parts. This means that you can easily find those notes you took sometime last week…

Including Recent Files for Buffer Completion

In Icicle mode, ‘C-x b’ (command ‘icicle-buffer’) can optionally include the names of recently accessed files as completion candidates (there need not currently be any buffers for the files). The [option] for this is `icicle-buffer-include-recent-files-nflag’. You can toggle the option value during buffer-name completion using ‘C-x R’ (command ‘icicle-toggle-include-recent-files’). A prefix argument sets the option value to the numeric prefix value.

The option value is a non-zero integer. If positive then recent file names are included; if negative they are not. The absolute value is the maximum number of recent-file candidates to include when turned on. So for example, if the option value is 20 then the names of only the twenty most recently visited files are candidates.

Helm

‘helm-recentf’ allows opening a file from the recent list using a Helm interface.

Basic extensions and integrations

Periodically saving the list of files

By default, Recentf saves the list of recent files on exiting Emacs (specifically, `recentf-save-list` is called on `kill-emacs-hook`). If Emacs exits abruptly for some reason the recent file list will be lost - therefore you may wish to call `recentf-save-list` periodically, e.g. every 5 minutes:

    (run-at-time nil (* 5 60) 'recentf-save-list)

`recentf-minibuffer-dialog'

2006-03-15, SteveBerman posted some code to gnu-emacs-sources@gnu.org that takes recentf filters into account in another way. Here is Steve’s commentary, followed by his code. This requires enabling recursive minibuffer editing: (setq enable-recursive-minibuffers t). Steve: Why don’t you upload this as an Emacs-Lisp library? – DrewAdams

One of the features of recentf.el that I find most useful is the possibility of filtering ‘recentf-list’ and structuring the recent file menu in accordance with the filter. So I wrote some code that makes the filtering and structuring of the recent file menu accessible from the minibuffer, with standard tabbed completion. Here it is. (I find it convenient to globally bind ‘C-c r’ to the command ‘recentf-minibuffer-dialog’.) – SteveBerman
(defun recentf-filtered-list (arg)
  "Return a filtered list of ARG recentf items."
    (recentf-apply-menu-filter
     recentf-menu-filter
     (mapcar 'recentf-make-default-menu-element
	     (butlast recentf-list (- (length recentf-list) arg)))))

(defun recentf-list-submenus (arg)
  "Return a list of the recentf submenu names."
  (if (listp (cdar (recentf-filtered-list arg))) ; submenues exist
      (delq nil (mapcar 'car (recentf-filtered-list arg)))))

(defmacro recentf-list-entries (fn arg)
  "Return a list of ARG recentf menu entries as determined by FN.
When FN is `'car' return the menu entry names, when FN is `'cdr'
return the absolute file names."
  `(mapcar (lambda (x) (mapcar ,fn x))
	   (if (recentf-list-submenus ,arg)
	       (mapcar 'cdr (recentf-filtered-list ,arg))
	     (list (recentf-filtered-list ,arg)))))

;; This function is not specific to recentf mode but is needed by
;; `recentf-minibuffer-dialog'.  I've also made enough use of it in
;; other contexts that I'm surprised it's not part of Emacs, and the
;; fact that it isn't makes me wonder if there's a preferred way of
;; doing what I use this function for.
(defun recentf-memindex (mem l)
  "Return the index of MEM in list L."
  (let ((mempos -1) ret)
    (while (eq ret nil)
      (setq mempos (1+ mempos))
      (when (equal (car l) mem) (setq ret mempos))
      (setq l (cdr l)))
    ret))

(defun recentf-minibuffer-dialog (arg)
  "Open the recentf menu via the minubuffer, with completion.
With positive prefix ARG, show the ARG most recent items.
Otherwise, show the default maximum number of recent items."
  (interactive "P")
  (let* ((num (prog1 (if (and (not (null arg))
			      (> arg 0))
			 (min arg (length recentf-list))
		       recentf-max-menu-items)
		(and (not (null arg))
		     (> arg (length recentf-list))
		     (message "There are only %d recent items."
			      (length recentf-list))
		     (sit-for 2))))
	 (menu (if (recentf-list-submenus num)
		   (completing-read "Open recent: "
				    (recentf-list-submenus num))))
	 (i (recentf-memindex menu (recentf-list-submenus num)))
	 (items (nth i (recentf-list-entries 'car num)))
	 (files (nth i (recentf-list-entries 'cdr num)))
	 (item (completing-read "Open recent: " items))
	 (j (recentf-memindex item items))
	 (file (nth j files)))
    (funcall recentf-menu-action file))) ; find-file by default

Buffer with the list of recently open files.

Lisp:recentf-buffer.el – creates a buffer with the list of recently open files. It’s an addition to recentf.el. To install:

(load "recentf-buffer")
(global-set-key [?\C-c ?r ?f] 'recentf-open-files-in-simply-buffer)

EugeneMarkov

Icicles gives you the list when you use ‘icicle-recent-file’ or ‘icicle-recent-file-other-window’ (see RecentFilesWithCompletion above). Just hit ‘TAB’ or ‘S-TAB’. If you type a regexp, ‘S-TAB’ will give you the list of matching recent files. – DrewAdams

recent-open-files-compl

This command provides completion for your recent files, based on code written by Jerome B. and posted in an article for http://www.emacsfr.org/:

(defun recentf-open-files-compl ()
  (interactive)
  (let* ((tocpl (mapcar (lambda (x) (cons (file-name-nondirectory x) x))
                        recentf-list))
         (fname (completing-read "File name: " tocpl nil nil)))
    (when fname
      (find-file (cdr (assoc-string fname tocpl))))))

recentf and dired buffers

After evaluating the following code the directories visited through dired buffers will also be put to recentf. – vibrys.

(eval-after-load "recentf"
'(progn
(defun recentf-track-opened-file ()
  "Insert the name of the dired or file just opened or written into the recent list."
  (let ((buff-name (or buffer-file-name (and (derived-mode-p 'dired-mode) default-directory))))
    (and buff-name
         (recentf-add-file buff-name)))
  ;; Must return nil because it is run from `write-file-functions'.
  nil)

(defun recentf-track-closed-file ()
  "Update the recent list when a file or dired buffer is killed.
That is, remove a non kept file from the recent list."
  (let ((buff-name (or buffer-file-name (and (derived-mode-p 'dired-mode) default-directory))))
    (and buff-name
         (recentf-remove-if-non-kept buff-name))))

(add-hook 'dired-after-readin-hook 'recentf-track-opened-file)))

Updating recentf-list when renaming files in dired

If you move a source directory with dired from A to B, by default there’s no integration to make sure that an entry of A/xxx gets updated to B/xxx. This snippet of code makes this work properly (it uses the new Emacs advice functions; if you have an Emacs older than 24.4, you’ll have to port it).

;; Magic advice to rename entries in recentf when moving files in
;; dired.
(defun rjs/recentf-rename-notify (oldname newname &rest args)
  (if (file-directory-p newname)
      (rjs/recentf-rename-directory oldname newname)
    (rjs/recentf-rename-file oldname newname)))

(defun rjs/recentf-rename-file (oldname newname)
  (setq recentf-list
        (mapcar (lambda (name)
                  (if (string-equal name oldname)
                      newname
                    name))
                recentf-list)))

(defun rjs/recentf-rename-directory (oldname newname)
  ;; oldname, newname and all entries of recentf-list should already
  ;; be absolute and normalised so I think this can just test whether
  ;; oldname is a prefix of the element.
  (setq recentf-list
        (mapcar (lambda (name)
                  (if (string-prefix-p oldname name)
                      (concat newname (substring name (length oldname)))
                    name))
                recentf-list)))

(advice-add 'dired-rename-file :after #'rjs/recentf-rename-notify)

Misc extensions and integrations

BufferMenuPlus: Visited Files Sorted by Access Date/Time

This concerns buffers visiting files, so it is related to some of the topics on this page. But this does not concern a persistent visited-files list.

See BufferMenuPlus for a description of the functionality of library Lisp:buff-menu+.el. With this library, you can sort visited files by date/time in two different ways (each either ascending or descending):

This is especially useful if you have a large number of visited files, and you want to choose one of the recently used file buffers (time of last access) or one of the recently opened files (time of first visit, reversed). Reminder: You can use ‘T’ (command ‘Buffer-menu-toggle-files-only’) to display only buffers visiting files.

Note that these two kinds of sort are not inverses. Example: if you opened a file yesterday that you are still visiting, and all other currently visited files were opened today, then that file is at the top (or bottom) when you sort by column CRM. When you sort by column Time, its position depends on the last time it was accessed. – DrewAdams

Gnus

I use Gnus in one process, and everything related to plain file editing in another emacs process. My initial load starts recentf-mode but for gnus, I really needed the recentf to use a different history file.

;; use a different set of recent files for gnus
;; cmg@dok.org
(setq recentf-save-file (recentf-expand-file-name "~/.recentf.gnus"))

;; prevent double initialization of recentf
(if (not (boundp 'cmg:prevent-gnus-recentf-double-init))
    (setq cmg:prevent-gnus-recentf-double-init nil))

(if (not cmg:prevent-gnus-recentf-double-init)
    (progn
      (load-file recentf-save-file)
      (setq cmg:prevent-gnus-recentf-double-init t)))

LaCarte

You can take advantage of recentf filters (that is, structured menus) from the keyboard (with completion and so on) by using library LaCarte.

Assuming that you have ‘ESC ESC x’ (that is, ‘ESC M-x’) bound to ‘lacarte-execute-menu-command’ (which you should ;-)), just type part of a recent file name (any part, or a regexp), then hit ‘S-TAB’ to complete to matching recent files.

If you use, say, the recentf filter ‘recentf-arrange-by-dir’, then the File > Open Recent menu is organized by directory, and this is reflected in the completion candidates used by ‘lacarte-execute-menu-command’.

Merge recent lists from several Emacs sessions

I wrote to the maintainer of recentf.el but received no feedback til now, so I might as well share my code here.

I have the habit to start one Emacs dedicated to Gnus first thing in the morning and close it last thing in the evening. With the standard recentf storage habit this Emacs will always overwrite the list of recent files, thus making it more or less constant. There are several ways to solve this, one would be to read the list before saving it and merging the list from the file with that in the current instance of Emacs before saving it back. My code for this, which I have in daily usage for a while now, is listed below. Somehow I can’t just overwrite the functions from recentf but I need to find the right time for that, that’s why I put it after the requiring and befor the activation. – StefanKamphausen

(defun try-require (feature)
  (condition-case nil
      (require feature)
    (error (progn
             (message "could not require %s" feature)
             nil))))

(when (try-require 'recentf)
  (setq recentf-exclude '("~$"))
  (setq recentf-max-saved-items 99)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;; Here I override some functions of recentf to get a merged list
  ;; This seems to be stable (used it for approx two weeks at the time
  ;; of this writing)
  (defun recentf-save-list ()
    "Save the recent list.
Load the list from the file specified by `recentf-save-file',
merge the changes of your current session, and save it back to the
file."
    (interactive)
    (let ((instance-list (copy-list recentf-list)))
      (recentf-load-list)
      (recentf-merge-with-default-list instance-list)
      (recentf-write-list-to-file)))

  (defun recentf-merge-with-default-list (other-list)
    "Add all items from `other-list' to `recentf-list'."
    (dolist (oitem other-list)
      ;; add-to-list already checks for equal'ity
      (add-to-list 'recentf-list oitem)))

  (defun recentf-write-list-to-file ()
    "Write the recent files list to file.
Uses `recentf-list' as the list and `recentf-save-file' as the
file to write to."
    (condition-case error
        (with-temp-buffer
          (erase-buffer)
          (set-buffer-file-coding-system recentf-save-file-coding-system)
          (insert (format recentf-save-file-header (current-time-string)))
          (recentf-dump-variable 'recentf-list recentf-max-saved-items)
          (recentf-dump-variable 'recentf-filter-changer-current)
          (insert "\n \n;;; Local Variables:\n"
                  (format ";;; coding: %s\n" recentf-save-file-coding-system)
                  ";;; End:\n")
          (write-file (expand-file-name recentf-save-file))
          (when recentf-save-file-modes
            (set-file-modes recentf-save-file recentf-save-file-modes))
          nil)
      (error
       (warn "recentf mode: %s" (error-message-string error)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  (recentf-mode 1))

I wrote an alternative implementation that uses dash.el and supports recentf-cleanups – ThomasFrössman

    (defvar recentfs-list-on-last-sync nil
      "List of recent files reference point.")

    (defun recentfs-update-sync ()
      "Load saved projects from `recentf-list'."
      (setq recentfs-list-on-last-sync
            (and (sequencep recentf-list)
               (copy-sequence recentf-list))))

    (defadvice recentf-load-list (after recentfs-loaded-sync activate)
      (recentfs-update-sync))

    (defadvice recentf-save-list (around recentfs activate)
      (recentfs-merge-lists)
      ad-do-it
      (recentfs-update-sync))

    (defun recentfs-load-list ()
      "Load a previously saved recent list and return it as a value
instead of setting it."
      (let ((file (expand-file-name recentf-save-file))
            (recentf-filter-changer-current nil) ;; ignored atm
            (recentf-list nil))
        (when (file-readable-p file)
          (load-file file))
        recentf-list))

    (defun recentfs-merge-lists ()
      "Merge any change from `recentf-list'.

This enables multiple Emacs processes to make changes without
overwriting each other's changes."
      (let* ((known-now recentf-list)
             (known-on-last-sync recentfs-list-on-last-sync)
             (known-on-file (recentfs-load-list))
             (removed-after-sync (-difference known-on-last-sync known-now))
             (removed-in-other-process
              (-difference known-on-last-sync known-on-file))
             (new-in-other-process
              (-difference
               known-on-file
               (-concat removed-after-sync removed-in-other-process known-now)))
             (result (-distinct
                      (-difference
                       (-concat new-in-other-process known-now)
                       (-concat removed-after-sync removed-in-other-process)))))
        (setq recentf-list result)))

`recentf-ext.el'

Lisp:recentf-ext.el extends recentf package.

rubikitch

TrampMode

When using TrampMode with recentf.el, it’s advisable to turn off the cleanup feature of recentf that attempts to stat all the files and remove them from the recently accessed list if they are readable. Tramp means that this requires recentf to open up a remote site which will block your emacs process at the most inopportune times.

(require 'recentf)
(setq recentf-auto-cleanup 'never) ;; disable before we start recentf!
(recentf-mode 1)

zsh recent directories

(defun slurp (f)
  (with-temp-buffer
    (insert-file-contents f)
    (buffer-substring-no-properties
       (point-min)
       (point-max))))

(defun visit-zsh-recent-dirs ()
  "Open the list of recent directories from zsh in a special buffer"
  (interactive)
  (recentf-open-files (mapcar '(lambda (str) (substring str 2 -1))
                              (split-string
                               (slurp "~/.chpwd-recent-dirs") "\n" t))))


CategoryCommands CategoryDotEmacs CategoryPersistence CategoryCompletion