(provide 'file-tree)
(defvar file-tree-indent
2
"Amount to indent for each directory.")
(defvar file-tree-ignore
'("^\.$" "^\.\.$" "^\.svn$" "^_MTN$" "^CVS$" "^#.*?#$" "^\.#" "~$")
"List of regular expressions for filenames to ignore.")
(defun file-tree-make-tree (path)
"Erase the current buffer and replace its contents with a recursively
generated display the list of files from a given root path."
(interactive "DFile tree from: ")
(setq buffer-read-only nil)
(erase-buffer)
(insert "Directory: " (file-truename path) "\n")
(let* ((matches (lambda (name lst)
(if lst
(if (string-match (car lst) name)
t
(funcall matches name (cdr lst))))))
(recurse (lambda (dir depth)
(let ((files (directory-files dir))
(indent (make-string depth ?\ )))
(while files
(if (not (funcall matches (car files) file-tree-ignore))
(let ((fullpath (concat dir "/" (car files)))
(line (concat indent (car files) "\n")))
(put-text-property 0 (length line) 'path fullpath line)
(insert line)
(if (file-directory-p fullpath)
(funcall recurse fullpath (+ depth file-tree-indent)))))
(setq files (cdr files)))))))
(funcall recurse (file-truename path) 0))
(goto-char (point-min)))
(defun file-tree-find ()
"Load the file in the list named under point."
(interactive)
(let ((path (get-text-property (point) 'path)))
(if path
(find-file-other-window path))))
(defun file-tree-move-and-find (event)
"Move point in the buffer to clicked character and find the file."
(interactive "e")
(mouse-set-point event)
(file-tree-find))
(defvar file-tree-mode-map nil
"Key map used for the file-tree window.")
(unless file-tree-mode-map
(setq file-tree-mode-map (make-sparse-keymap))
(define-key file-tree-mode-map [return] 'file-tree-find)
(define-key file-tree-mode-map [?\r] 'file-tree-find)
(define-key file-tree-mode-map [mouse-1] 'file-tree-move-and-find)
(define-key file-tree-mode-map [?f] 'file-tree-find)
(define-key file-tree-mode-map [?q] 'delete-window)
(define-key file-tree-mode-map [?r] 'file-tree-make-tree))
(defun file-tree (path)
"Creates a buffer and window with a persistant display of the tree of the
files under a given path. Switching to the buffer and pressing enter or f
will find the file. Press r to refresh the tree, possibly with a new
top-level directory. Press q to close the window. This is meant to allow
quick navigation much like the speedbar file list but friendlier to use on
a TTY."
(interactive "DFile tree from: ")
(let* ((buf (get-buffer-create "*File Tree*"))
(win (get-buffer-window buf)))
(if win
(select-window win)
(split-window-horizontally 25)
(set-window-buffer (selected-window) buf))
(kill-all-local-variables)
(file-tree-make-tree path)
(setq buffer-read-only t)
(setq mode-name "File-Tree"
major-mode 'file-tree-mode)
(use-local-map file-tree-mode-map)))