Article View: gnu.emacs.sources
Article #285tree-dired-aho.el
From: Anders Holst
Date: Thu, 17 Dec 1992 20:21
Date: Thu, 17 Dec 1992 20:21
376 lines
16064 bytes
16064 bytes
This is my modifications of tree-dired. See the discussion in the
previous article, or the documentation below for details.
;;
;; File: aho-tree-dired.el
;;
;; Author: Anders Holst (aho@sans.kth.se)
;;
;; Last change: 12 December 1992
;;
;; Copyright (C) Anders Holst
;;
;; ----------------------------------------------------------------------
;; 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 1, 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
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with your copy of Emacs; if not, write to the Free Software
;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
;; ----------------------------------------------------------------------
;;
;;
;; INSTALLATION
;;
;; The easiest way to install this, is to put the file in your load-path,
;; and add the following to 'dired-load-hook':
;;
;; (load "aho-tree-dired")
;; (define-key dired-mode-map "q" 'dired-back)
;; (define-key dired-mode-map "Q" 'dired-quit)
;;
;;
;; DESCRIPTION
;;
;; This file implements the following modifications to tree dired:
;;
;; * More extensive "window" defaulting in Copy/Rename/Link (searches
;; all windows for a dired-buffer), together with defaulting to
;; last directory used (if "window" defaulting fails).
;; * Sorting in alphabetically of new files/links (if the current sort
;; order is alphabetical).
;; * More emphasized tree-structure between the different dired-buffers.
;; 'f' and 'v' climbs (as before) down to a sub-directory, 'q' climbs
;; back up again. (All dired-buffers in the "dired-buffer-tree" are
;; kept buried, except the current.) 'Q' jumps out of the entire
;; "dired-buffer-tree".
;;
;; Comments on the modifications:
;;
;; * Regarding "window" defaulting: I don't know how the changes will
;; work with for example Epoch, or Emacs 19, since I have never
;; used Epoch myself. But if every buffer gets a window of it's own
;; (or have I missunderstood something ?) that might mean that all
;; dired-buffers have windows, and the defaulting will be
;; completely useless (i.e. always come up with something, and
;; probably wrong).
;; * The defaulting to last used directory works as a second
;; alternative, when there are no other dired-window to default to.
;; It is currently activated only together with this window
;; defaulting, i.e. when the variable 'dired-dwim-target' is non-nil.
;; Perhaps it should have a variable of its own ?
;; * Sorting in of new files are done only when sort order is
;; alphabetical, since that was most easy to do. Perhaps it should
;; be done also for other sort orders. On the other hand, it might
;; slow things down a bit to sort (but I havn't noticed anything
;; such yet). Also on some places there is a local language
;; sort order which string-lessp follows, instead of the normal
;; ascii-order which ls uses. I had to work around this in a way
;; that may make sorting more slow.
;; * 'q' is not the same as '^'. For example when following a link
;; '^' goes up one directory on "the other side", but 'q' returns
;; to the dired-buffer you came from. Also, when in the first
;; dired-buffer of such a dired-buffer-tree, 'q' quits the tree,
;; whereas '^' climbs further up ('q' will after such use of '^'
;; climb back down instead).
;;
;; NOTE: The modifications in this file has only been tested with
;; version 6.0 of tree-dired. I don't know how much tree-dired will
;; change, but at least some of my modifications deals with small
;; inconveniences in tree-dired, which hopefully might dissapear in
;; later versions.
;; These modifications should also work reasonably together with
;; dired-nstd.el, some strange things of which I have tried to
;; mitigate.
;;
(defvar father-buffer nil)
(make-variable-buffer-local 'father-buffer)
(defun dired-up-directory ()
"Run dired on parent directory of current directory.
Find the parent directory either in this buffer or another buffer.
Creates a buffer if necessary."
(interactive)
(let* ((dir (dired-current-directory))
(up (file-name-directory (directory-file-name dir))))
(or (dired-goto-file (directory-file-name dir))
(dired-goto-subdir up)
(let ((buf (current-buffer))
(father father-buffer))
(bury-buffer)
(dired up)
(dired-goto-file dir)
(if (not (equal father (current-buffer)))
(setq father-buffer buf))))))
(defun dired-view-file ()
"In dired, examine a file in view mode, returning to dired when done.
When file is a directory, show it in this buffer if it is inserted;
otherwise, display it in another buffer."
(interactive)
(let ((file (dired-get-filename)))
(if (file-directory-p file)
(or (dired-goto-subdir file)
(let ((buf (current-buffer))
(father father-buffer))
(bury-buffer)
(dired file)
(if (not (equal father (current-buffer)))
(setq father-buffer buf))))
(view-file file))))
(defun dired-find-file ()
"In dired, visit the file or directory named on this line."
(interactive)
(let ((file (dired-get-filename)))
(if (file-directory-p file)
(or (dired-goto-subdir file)
(let ((buf (current-buffer))
(father father-buffer))
(bury-buffer)
(dired file)
(if (not (equal father (current-buffer)))
(setq father-buffer buf))))
(find-file file))))
(defun dired-back ()
"Return to the dired-buffer this buffer was invoked from.
If none, just bury this buffer"
(interactive)
(let ((buf father-buffer))
(bury-buffer)
(if (and (bufferp buf)
(buffer-name buf))
(if (get-buffer-window buf)
(select-window (get-buffer-window buf))
(switch-to-buffer buf)))))
(defun dired-quit ()
"Bury the current dired buffer."
(interactive)
(bury-buffer))
(defvar dired-last-used-directory ()
"Default directory in copy/rename/link file")
(make-variable-buffer-local 'dired-last-used-directory)
(defun dired-default-target-directory ()
;; Try to guess which target directory the user may want.
;; If there is a dired buffer displayed in the next window, use
;; its current subdir, else last used diredctory if any, else
;; use current subdir of this dired buffer.
(let ((this-dir (and (eq major-mode 'dired-mode)
(dired-current-directory))))
(if dired-dwim-target
(let* ((this-window (selected-window))
(other-window (next-window this-window))
other-dir)
(while (not (or (equal this-window other-window)
other-dir))
(set-buffer (window-buffer other-window))
(setq other-dir (and (eq major-mode 'dired-mode)
(dired-current-directory)))
(setq other-window (next-window other-window)))
(set-buffer (window-buffer this-window))
(or other-dir dired-last-used-directory this-dir))
this-dir)))
(defun dired-do-create-files (op-symbol file-creator operation arg
&optional marker-char op1
how-to)
;; Create a new file for each marked file.
;; Prompts user for target, which is a directory in which to create
;; the new files. Target may be a plain file if only one marked
;; file exists.
;; OP-SYMBOL is the symbol for the operation. Function `dired-mark-pop-up'
;; will determine wether pop-ups are appropriate for this OP-SYMBOL.
;; FILE-CREATOR and OPERATION as in dired-create-files.
;; ARG as in dired-mark-get-files.
;; Optional arg OP1 is an alternate form for OPERATION if there is
;; only one file.
;; Optional arg MARKER-CHAR as in dired-create-files.
;; Optional arg HOW-TO determines how to treat target:
;; If HOW-TO is not given (or nil), and target is a directory, the
;; file(s) are created inside the target directory. If target
;; is not a directory, there must be exactly one marked file,
;; else error.
;; If HOW-TO is t, then target is not modified. There must be
;; exactly one marked file, else error.
;; Else HOW-TO is assumed to be a function of one argument, target,
;; that looks at target and returns a value for the into-dir
;; variable. The function dired-into-dir-with-symlinks is provided
;; for the case (common when creating symlinks) that symbolic
;; links to directories are not to be considered as directories
;; (as file-directory-p would if HOW-TO had been nil).
(or op1 (setq op1 operation))
(let* ((fn-list (dired-mark-get-files nil arg))
(fn-count (length fn-list))
(target (expand-file-name
(dired-mark-read-file-name
(concat (if (= 1 fn-count) op1 operation) " %s to: ")
(dired-default-target-directory)
op-symbol arg (mapcar (function dired-make-relative) fn-list))))
(into-dir (cond ((null how-to) (file-directory-p target))
((eq how-to t) nil)
(t (funcall how-to target)))))
(if (and (> fn-count 1)
(not into-dir))
(error "Marked %s: target must be a directory: %s" operation target))
;; rename-file bombs when moving directories unless we do this:
(or into-dir (setq target (directory-file-name target)))
(setq dired-last-used-directory (if into-dir
(file-name-as-directory target)
(file-name-directory target)))
(dired-create-files
file-creator operation fn-list
(if into-dir ; target is a directory
;; This function uses fluid vars into-dir and target when called
;; inside dired-create-files:
(function (lambda (from)
(expand-file-name (file-name-nondirectory from) target)))
(function (lambda (from) target)))
marker-char)))
(defun dired-create-directory (directory)
"Create a directory called DIRECTORY."
(interactive
(list (read-file-name "Create directory: " (dired-current-directory))))
(let ((expanded (directory-file-name (expand-file-name directory))))
(make-directory expanded)
(setq dired-last-used-directory (file-name-as-directory expanded))
(dired-add-file expanded)
(dired-move-to-filename)))
(defun dired-add-entry (filename &optional marker-char)
;; Add a new entry for FILENAME, optionally marking it
;; with MARKER-CHAR (a character, else dired-marker-char is used).
;; Note that this adds the entry `out of order' if files sorted by
;; time, etc.
;; At least this version inserts in the right subdirectory (if present).
;; And it skips "." or ".." (see `dired-trivial-filenames').
;; Hidden subdirs are exposed if a file is added there.
(setq filename (directory-file-name filename))
;; Entry is always for files, even if they happen to also be directories
(let ((opoint (point))
(cur-dir (dired-current-directory))
(directory (file-name-directory filename))
reason)
(setq filename (file-name-nondirectory filename)
reason
(catch 'not-found
(if (string= directory cur-dir)
(progn
(if (dired-subdir-hidden-p cur-dir)
(dired-unhide-subdir))
;; We are already where we should be, except when
;; point is before the subdir line or its total line.
(dired-goto-the-right-place filename))
;; else try to find correct place to insert
(if (dired-goto-subdir directory)
(progn;; unhide if necessary
(if (looking-at "\r");; point is at end of subdir line
(dired-unhide-subdir))
;; found - skip subdir and `total' line
;; and uninteresting files like . and ..
;; This better not moves into the next subdir!
(dired-goto-the-right-place filename))
;; not found
(throw 'not-found "Subdir not found")))
;; found and point is at The Right Place:
(let ((buffer-read-only ())
(at-beg (bolp)))
(beginning-of-line)
(if at-beg
(progn
(backward-char 1)
(insert-char 10 1)))
(dired-add-entry-do-indentation marker-char)
(dired-ls (dired-make-absolute filename directory)
;; don't expand `.' !
(concat dired-actual-switches "d"))
(if at-beg
(delete-char 1))
(forward-line -1)
;; We want to have the non-directory part, only:
(let* ((beg (dired-move-to-filename t)) ; error for strange output
(end (dired-move-to-end-of-filename)))
(setq filename (buffer-substring beg end))
(delete-region beg end)
(insert (file-name-nondirectory filename)))
(if dired-after-readin-hook;; the subdir-alist is not affected...
(save-excursion;; ...so we can run it right now:
(save-restriction
(beginning-of-line)
(narrow-to-region (point) (save-excursion
(forward-line 1) (point)))
(run-hooks 'dired-after-readin-hook))))
(dired-move-to-filename))
;; return nil if all went well
nil))
(if reason ; don't move away on failure
(goto-char opoint))
(not reason))) ; return t on succes, nil else
;; Ridiculous that this should be needed just because someone tries to be
;; smart and makes string-lessp sort according to LOCALE, which is wrong
;; here since ls doesn't care about that. I dont want to think about how
;; much extra time this might take:
(if (or (getenv "LANG") (getenv "LC_LOCALE"))
(defun my-string-lessp (str1 str2)
(let ((maxlen (min (length str1) (length str2)))
(i 0))
(while (and (< i maxlen)
(= (string-to-char (substring str1 i))
(string-to-char (substring str2 i))))
(setq i (1+ i)))
(< (string-to-char (substring str1 i))
(string-to-char (substring str2 i)))))
(fset 'my-string-lessp 'string-lessp))
(defun dired-goto-the-right-place (filename)
(if (string-match dired-sort-by-name-regexp dired-actual-switches)
;;sort in alphabetically
(let ((max (dired-subdir-max)))
(goto-char (dired-after-subdir-garbage (dired-current-directory)))
(while (and (< (point) max)
(my-string-lessp (file-name-nondirectory
(or (dired-get-filename nil t) "\377"))
filename))
(dired-next-file-same-dir)))
(dired-goto-next-nontrivial-file)))
(defun dired-goto-next-nontrivial-file ()
;; Position point on first nontrivial file after point.
(dired-goto-next-file);; so there is a file to compare with
(if (stringp dired-trivial-filenames)
(while (and (not (eobp))
(string-match dired-trivial-filenames
(file-name-nondirectory
(or (dired-get-filename nil t) ""))))
(dired-next-file-same-dir))))
;; Actually, all the interface to how the buffer looks, and where
;; to move, should be done through functions like this, to make the
;; change to alternative formats, as e.g. dired-nstd.el, more
;; transparent. In that case this could also be done more efficient.
(defun dired-next-file-same-dir ()
(let ((dir (dired-current-directory))
(max (dired-subdir-max)))
(dired-next-line 1)
(while (and (< (point) max)
(not (equal (dired-current-directory) dir)))
(dired-next-line 1))
(if (not (equal (dired-current-directory) dir))
(beginning-of-line))))
Technical Details
Message-ID:
<AHO.92Dec17212118@thalamus.sans.kth.se>Path:
arc1-ca.pugleaf.net!sparky!uunet!mcsun!sunic!kth.se!News.kth.se!ahoShow Full Headers
Complete Article Headers
Newsgroups: gnu.emacs.sources Path: arc1-ca.pugleaf.net!sparky!uunet!mcsun!sunic!kth.se!News.kth.se!aho From: aho@thalamus.sans.kth.se (Anders Holst) Subject: tree-dired-aho.el Message-ID: <AHO.92Dec17212118@thalamus.sans.kth.se> Sender: usenet@kth.se (Usenet) Nntp-Posting-Host: thalamus.sans.kth.se Organization: /home/aho/.organization Date: Thu, 17 Dec 1992 20:21:18 GMT Lines: 376