Monday, September 29, 2008

Emacs Hidden Save

One thing about emacs that annoys me is its default behavior for backing up files. I don't have anything against generally backing up files, but I am annoyed that any directory I work heavily in doubles in size because of the backup files.

I've seen a number of different solutions for this little annoyance, ranging from eliminating backups entirely to creating an entire shadow directory structure for backups. The closest thing I've seen to what I like, though, is passing an option to ls that excludes backup files.

Although excluding files ending in ~ works fairly well, if someone else is browsing through my filesystem that doesn't know to exclude these files will still be annoyed by them. A more general solution would be to prepend . to all of the file names so that they'd be hidden for everybody. Now, this isn't a perfect solution as backups of hidden files are still visible, but I feel this is a more acceptable degree of loss than visitors having problems. (Note: this program is covered under the GPL)

;;; Copyright (c) 2008, Colin Williams
;;; All rights reserved

;;; This program is free software: you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License as published by
;;; the Free Software Foundation, either version 3 of the License, or
;;; (at your option) any later version.

;;; This program is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; GNU General Public License for more details.

;;; You should have received a copy of the GNU General Public License
;;; along with this program.  If not, see .

(setq make-backup-file-name-function (lambda (file)
           (concat (file-name-directory file)
            (file-name-nondirectory file)

(defun backup-file-name-p (file)
  "Return non-nil if FILE is a backup file name (numeric or not).
This is a separate function so you can redefine it for customization.
You may need to redefine `file-name-sans-versions' as well."
  (string-match "^\\..*~$" (file-name-nondirectory file)))

;; Much of this function was based off of file-name-sans-version
;; in files.el, the source and credit for this package is located
;; at 
(defun file-name-sans-versions (name &optional keep-backup-version)
  "Return file NAME sans backup versions or strings.
This is a separate procedure so your site-init or startup file can
redefine it.
If the optional argument KEEP-BACKUP-VERSION is non-nil,
we do not remove backup version numbers, only true file version numbers."
  (let ((handler (find-file-name-handler name 'file-name-sans-versions)))
    (if handler
 (funcall handler 'file-name-sans-versions name keep-backup-version)
      (if (eq system-type 'vax-vms)
   ;; VMS version number is (a) semicolon, optional
   ;; sign, zero or more digits or (b) period, option
   ;; sign, zero or more digits, provided this is the
   ;; second period encountered outside of the
   ;; device/directory part of the file name.
   (substring name 0
       (or (string-match ";[-+]?[0-9]*\\'" name)
    (if (string-match "\\.[^]>:]*\\(\\.[-+]?[0-9]*\\)\\'"
        (match-beginning 1))
    (length name)))
 (if keep-backup-version
   (let ((file-name (file-name-nondirectory name))
  (directory-name (file-name-directory name)))
     (concat directory-name (let* ((file-name-end
        (or (string-match "\\.~[0-9.]+~\\'"
            (string-match "~\\'"
            (length file-name)))
        (if (= file-name-end
        (length file-name))
          (string-match "^\\." file-name))))

         (substring file-name
      (if (= file-name-start 0)
          (length file-name)

Ironically, the code for actually creating the filename was the easy part. The tricky bits were the support functions backup-file-name-p and file-name-sans-versions. I've been using this scheme on my desktop for a while without any problems, if anybody has suggestions for improving this scheme please let me know.