Download
(defgroup file-journal '()
"Filejournal keeps a list of all the files you have visited, like
a persistent most-recently viewed list. You can control the number
of days to keep the journal for. Additionally you can set some
files to not be on the list.
Work with files as usual and use M-x fj-show to revisit them later
by date.
")
(eval-when-compile
(require 'cl))
(defcustom fj-journal-size 5
"Number of past days to keep in the journal."
:type 'integer
:group 'file-journal)
(defcustom fj-journal-file "~/.file-journal"
"File where journal info is stored."
:type 'file
:group 'file-journal)
(defcustom fj-exclude-files '()
"List of files to exclude from journal (each item a regex).
I use the regex .*\.muse$ to not store any muse files, otherwise planner
mode tends to pollute the list."
:type '(repeat regexp)
:group 'file-journal)
(defcustom fj-save-timer-interval 3600
"Autosave the journal after this many seconds."
:type 'integer
:group 'file-journal)
(defface fj-header-face
'((t (:inherit highlight)))
"Face for date headers."
:group 'file-journal)
(defvar fj-journal nil
"List of (DATE . FILES) pairs describing which files were visited when.")
(define-derived-mode fj-mode fundamental-mode "File Journal")
(suppress-keymap fj-mode-map)
(define-key fj-mode-map (kbd "<return>") 'fj-visit-file)
(defun fj-show ()
"Show the journal and allow the user to select a file."
(interactive)
(switch-to-buffer "*file-journal*")
(fj-mode)
(fj-update-fj-buffer)
(message (substitute-command-keys "Visit a file with \\[fj-visit-file]")))
(defun fj-update-fj-buffer ()
"Update the contents of the journal buffer"
(erase-buffer)
(dolist (entry fj-journal)
(unless (bobp)
(insert "\n"))
(let ((start (point)))
(insert (car entry) "\n")
(put-text-property start (point) 'face 'fj-header-face))
(dolist (file (cdr entry))
(insert " " file "\n")))
(goto-char (point-min)))
(defun fj-visit-file ()
"Visit file under the cursor."
(interactive)
(if (save-excursion
(beginning-of-line)
(looking-at " "))
(find-file (buffer-substring (1+ (line-beginning-position))
(line-end-position)))
(error "No file on this line.")))
(defun fj-visit-files ()
"Visit all the files in the region."
(interactive)
)
(defun fj-file-in-excluded (file)
"Test to see if FILE matches the exclusion regex."
(catch 'excluded
(dolist (exclusion fj-exclude-files)
(when (string-match exclusion file)
(throw 'excluded 't)))))
(when nil
(fj-file-in-excluded "somefoo")
(fj-file-in-excluded "somefoo.muse"))
(defun fj-record-file ()
"Record the file in the journal."
(when (and buffer-file-name (not (fj-file-in-excluded buffer-file-name)))
(let* ((date (format-time-string "%Y-%m-%d"))
(entry (assoc-default date fj-journal)))
(if entry
(setq entry (remove (buffer-file-name) entry))
(push (cons date nil) fj-journal)
(if (> (length fj-journal) fj-journal-size)
(setq fj-journal (nbutlast fj-journal (- (length fj-journal)
fj-journal-size)))))
(push (buffer-file-name) entry)
(setcdr (assoc date fj-journal) entry)
(when (get-buffer "*file-journal*")
(save-excursion
(set-buffer (get-buffer "*file-journal*"))
(fj-update-fj-buffer))))))
(defadvice switch-to-buffer (after fj-switch-to-buffer activate)
(fj-record-file))
(defun fj-save-journal ()
"Save journal to file."
(interactive)
(with-temp-buffer
(insert
";; -*- mode: emacs-lisp -*-\n"
";; Journal entries for visited files\n")
(prin1 `(setq fj-journal ',fj-journal) (current-buffer))
(insert ?\n)
(write-region (point-min) (point-max) fj-journal-file nil
(unless (interactive-p) 'quiet))))
(defvar fj-save-journal-timer (run-with-timer fj-save-timer-interval fj-save-timer-interval 'fj-save-journal))
(add-hook 'kill-emacs-hook 'fj-save-journal)
(if (file-readable-p fj-journal-file)
(load-file fj-journal-file))
(defvar fj--anything-candidates 'fj--anything-candidates)
(defun fj--anything-candidates ()
"returns a list of candidates for the anything package."
(reduce 'append (mapcar 'cdr fj-journal)))
(defvar fj--anything-source
'(((name . "File Journal")
(candidates . fj--anything-candidates)
(volatile)
(type . file))))
(defun fj--attach-with-anything ()
(setq anything-sources (append anything-sources fj--anything-source)))
(add-hook 'emacs-startup-hook
(lambda ()
(when (featurep 'anything)
(fj--attach-with-anything))))
(provide 'file-journal)