(provide 'recent-files) ;; See http://standards.freedesktop.org/recent-file-spec/recent-file-spec-0.2.html#storage (defconst recent-files-encoding "UTF-8") (defconst recent-files-name "~/.recently-used") ;; (defconst recent-files-limit 500) (require 'url-util) (require 'mailcap) (defvar update-recent-file t) (defun update-recent-file (file &optional mime-type timestamp private groups) "Add or update FILE's entry in `recent-files-name'." (when update-recent-file (update-recent-file-item (concat "file://" (mapconcat 'url-hexify-string (split-string (expand-file-name file) "/") "/")) (or mime-type (cdr (assoc (file-name-extension file t) mailcap-mime-extensions)) "application/octet-stream") (or timestamp (format "%.f" (float-time))) private groups))) (defun update-recent-file-item (uri mime-type timestamp &optional private groups) ;; Replace "<", "&", and ">" by "<", "&", and ">" resp. ;; in all string arguments (PRIVATE is boolean): (setq uri (url-insert-entities-in-string uri) mime-type (url-insert-entities-in-string mime-type) timestamp (url-insert-entities-in-string timestamp) groups (mapcar 'url-insert-entities-in-string groups)) ;; Make sure recent-files-name is opened and saved in the correct encoding: (let* ((coding-system-for-read (intern (downcase recent-files-encoding))) (coding-system-for-write coding-system-for-read) (coding-system-require-warning t) (recent-buffer (get-file-buffer recent-files-name))) (save-excursion (set-buffer (or recent-buffer ;; Avoid adding `recent-files-name' to itself: (let ((update-recent-file nil)) (find-file-noselect recent-files-name)))) (save-restriction (widen) (when (= (buffer-size) 0) (goto-char (point-min)) (insert "") (insert "\n" "") (insert "\n" "")) (goto-char (point-min)) (if (search-forward-regexp (concat "" (regexp-quote uri) "") nil t) ;; Per the spec, only update the timestamp and add new groups: (progn (search-forward-regexp "\\(.*\\)") (replace-match timestamp t t nil 1) (goto-char (match-end 0)) ; end-of-line (when groups (let (group-start group-end) (if (looking-at "\n*") (progn (setq group-start (match-end 0)) (setq group-end (search-forward ""))) (insert "\n" "") (setq group-start (point)) (insert "\n" "") (setq group-end (point-marker))) (goto-char group-start) (mapc (lambda (group) (unless (save-excursion (search-forward-regexp (concat "" (regexp-quote group) "") nil group-end)) (insert "\n" "" group ""))) groups)))) ;; Else insert the new item: (search-forward "") (goto-char (match-beginning 0)) (insert "") (insert "\n" "" uri "" "\n" "" mime-type "" "\n" "" timestamp "") (when groups (insert "\n" "") (mapc (lambda (group) (insert "\n" "" group "")) groups) (insert "\n" "")) (when private (insert "\n" "")) (insert "\n" "" "\n")) (save-buffer)) (or recent-buffer (kill-buffer (current-buffer)))))) (defvar find-file-recent-files-private t "If set, specify the \"\" tag when adding visited files to \ `recent-files-name'.") (defvar find-file-recent-files-groups '("Emacs") "List of group names to add when updating visited files in \ `recent-files-name'.") (defun find-file-update-recent-file () "Add the visited file to `recent-files-name'. This function is designed to be added to `find-file-hook' and/or `find-file-not-found-functions'." (unless (or (null buffer-file-name) (backup-file-name-p buffer-file-name) (auto-save-file-name-p (file-name-nondirectory buffer-file-name))) (update-recent-file buffer-file-name nil nil find-file-recent-files-private find-file-recent-files-groups))) ;; (add-hook 'find-file-hook 'find-file-update-recent-file) ;; (add-hook 'find-file-not-found-functions 'find-file-update-recent-file)