[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] master 728e791 005/187: Added dired-async
From: |
Michael Albinus |
Subject: |
[elpa] master 728e791 005/187: Added dired-async |
Date: |
Wed, 30 Dec 2015 11:49:20 +0000 |
branch: master
commit 728e791de259b941ab9801c2012443037fb7d8c9
Author: John Wiegley <address@hidden>
Commit: John Wiegley <address@hidden>
Added dired-async
---
dired-async.el | 304 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 304 insertions(+), 0 deletions(-)
diff --git a/dired-async.el b/dired-async.el
new file mode 100644
index 0000000..02ee42a
--- /dev/null
+++ b/dired-async.el
@@ -0,0 +1,304 @@
+;;; dired-async --- Copy/move/delete asynchronously in dired
+
+;; Copyright (C) 2012 John Wiegley
+
+;; Author: John Wiegley <address@hidden>
+;; Created: 14 Jun 2012
+;; Version: 1.0
+;; Keywords: dired async network
+;; X-URL: https://github.com/jwiegley/dired-async
+
+;; 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 2, 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 GNU Emacs; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; The function, which must be loaded *after* dired-aux.el, performs copies,
+;; moves and deletes in the background using a slave Emacs process, by means
+;; of the async.el module. To use it, put this in your .emacs:
+;;
+;; (eval-after-load "dired-aux"
+;; '(require 'dired-async))
+
+;;; Code:
+
+(require 'dired-aux)
+(require 'async)
+
+(defgroup dired-async nil
+ "Copy/move/delete asynchronously in dired"
+ :group 'dired)
+
+(defun dired-after-file-create (to actual-marker-char &optional overwrite)
+ (if overwrite
+ ;; If we get here, file-creator hasn't been aborted
+ ;; and the old entry (if any) has to be deleted
+ ;; before adding the new entry.
+ (dired-remove-file to))
+ (dired-add-file to actual-marker-char))
+
+(eval-when-compile
+ (defvar actual-marker-char)
+ (defvar overwrite)
+ (defvar async-callback))
+
+(defun dired-copy-file-recursive (from to ok-flag &optional
+ preserve-time top recursive)
+ (when (and (eq t (car (file-attributes from)))
+ (file-in-directory-p to from))
+ (error "Cannot copy `%s' into its subdirectory `%s'" from to))
+ (let ((attrs (file-attributes from)))
+ (if (and recursive
+ (eq t (car attrs))
+ (or (eq recursive 'always)
+ (yes-or-no-p (format "Recursive copies of %s? " from))))
+ ;; This is a directory.
+ (async-start
+ `(lambda ()
+ (copy-directory ,from ,to ,preserve-time))
+ `(lambda (&optional ignore)
+ (dired-after-file-create ,to ,actual-marker-char ,overwrite)))
+ ;; Not a directory.
+ (or top (dired-handle-overwrite to))
+ (condition-case err
+ (if (stringp (car attrs))
+ ;; It is a symlink
+ (make-symbolic-link (car attrs) to ok-flag)
+ (async-start
+ `(lambda ()
+ ;; jww (2012-06-18): Error gets absorbed if one happens at all
+ ;; right now. I need to pass back the exception from the
+ ;; child process.
+ (copy-file ,from ,to ,ok-flag ,preserve-time))
+ `(lambda (&optional ignore)
+ (dired-after-file-create ,to ,actual-marker-char ,overwrite))))
+ (file-date-error
+ (push (dired-make-relative from)
+ dired-create-files-failures)
+ (dired-log "Can't set date on %s:\n%s\n" from err))))))
+
+(defun dired-rename-file (file newname ok-if-already-exists)
+ (dired-handle-overwrite newname)
+ (async-start
+ `(lambda ()
+ ;; error is caught in -create-files
+ (rename-file ,file ,newname ,ok-if-already-exists))
+ `(lambda (&optional ignore)
+ ;; Silently rename the visited file of any buffer visiting this file.
+ (and (get-file-buffer ,file)
+ (with-current-buffer (get-file-buffer ,file)
+ (set-visited-file-name ,newname nil t)))
+ (dired-remove-file ,file)
+ ;; See if it's an inserted subdir, and rename that, too.
+ (dired-rename-subdir ,file ,newname)
+
+ (dired-after-file-create ,newname ,actual-marker-char ,overwrite))))
+
+(defun dired-delete-file (file &optional recursive trash) "\
+Delete FILE or directory (possibly recursively if optional RECURSIVE is true.)
+RECURSIVE determines what to do with a non-empty directory. If RECURSIVE is:
+nil, do not delete.
+`always', delete recursively without asking.
+`top', ask for each directory at top level.
+Anything else, ask for each sub-directory."
+ ;; This test is equivalent to
+ ;; (and (file-directory-p fn) (not (file-symlink-p fn)))
+ ;; but more efficient
+ (if (not (eq t (car (file-attributes file))))
+ (async-start
+ `(lambda ()
+ (delete-file ,file ,trash))
+ 'ignore)
+ (if (and recursive
+ (directory-files file t dired-re-no-dot) ; Not empty.
+ (or (eq recursive 'always)
+ (yes-or-no-p (format "Recursively %s %s? "
+ (if (and trash
+ delete-by-moving-to-trash)
+ "trash"
+ "delete")
+ (dired-make-relative file)))))
+ (if (eq recursive 'top) (setq recursive 'always)) ; Don't ask again.
+ (setq recursive nil))
+ (async-start
+ `(lambda ()
+ (delete-directory ,file ,recursive ,trash))
+ 'ignore)))
+
+(defun dired-create-files (file-creator operation fn-list name-constructor
+ &optional marker-char)
+ "Create one or more new files from a list of existing files FN-LIST.
+This function also handles querying the user, updating Dired
+buffers, and displaying a success or failure message.
+
+FILE-CREATOR should be a function. It is called once for each
+file in FN-LIST, and must create a new file, querying the user
+and updating Dired buffers as necessary. It should accept three
+arguments: the old file name, the new name, and an argument
+OK-IF-ALREADY-EXISTS with the same meaning as in `copy-file'.
+
+OPERATION should be a capitalized string describing the operation
+performed (e.g. `Copy'). It is used for error logging.
+
+FN-LIST is the list of files to copy (full absolute file names).
+
+NAME-CONSTRUCTOR should be a function accepting a single
+argument, the name of an old file, and returning either the
+corresponding new file name or nil to skip.
+
+Optional MARKER-CHAR is a character with which to mark every
+newfile's entry, or t to use the current marker character if the
+old file was marked."
+ (let (dired-create-files-failures failures
+ skipped (success-count 0) (total (length fn-list)))
+ (let (to overwrite-query
+ overwrite-backup-query) ; for dired-handle-overwrite
+ (dolist (from fn-list)
+ (setq to (funcall name-constructor from))
+ (if (equal to from)
+ (progn
+ (setq to nil)
+ (dired-log "Cannot %s to same file: %s\n"
+ (downcase operation) from)))
+ (if (not to)
+ (setq skipped (cons (dired-make-relative from) skipped))
+ (let* ((overwrite (file-exists-p to))
+ (dired-overwrite-confirmed ; for dired-handle-overwrite
+ (and overwrite
+ (let ((help-form '(format "\
+Type SPC or `y' to overwrite file `%s',
+DEL or `n' to skip to next,
+ESC or `q' to not overwrite any of the remaining files,
+`!' to overwrite all remaining files with no more questions." to)))
+ (dired-query 'overwrite-query
+ "Overwrite `%s'?" to))))
+ ;; must determine if FROM is marked before file-creator
+ ;; gets a chance to delete it (in case of a move).
+ (actual-marker-char
+ (cond ((integerp marker-char) marker-char)
+ (marker-char (dired-file-marker from)) ; slow
+ (t nil))))
+ ;; Handle the `dired-copy-file' file-creator specially
+ ;; When copying a directory to another directory or
+ ;; possibly to itself or one of its subdirectories.
+ ;; e.g "~/foo/" => "~/test/"
+ ;; or "~/foo/" =>"~/foo/"
+ ;; or "~/foo/ => ~/foo/bar/")
+ ;; In this case the 'name-constructor' have set the destination
+ ;; TO to "~/test/foo" because the old emacs23 behavior
+ ;; of `copy-directory' was to not create the subdirectory
+ ;; and instead copy the contents.
+ ;; With the new behavior of `copy-directory'
+ ;; (similar to the `cp' shell command) we don't
+ ;; need such a construction of the target directory,
+ ;; so modify the destination TO to "~/test/" instead of
"~/test/foo/".
+ (let ((destname (file-name-directory to)))
+ (when (and (file-directory-p from)
+ (file-directory-p to)
+ (eq file-creator 'dired-copy-file))
+ (setq to destname))
+ ;; If DESTNAME is a subdirectory of FROM, not a symlink,
+ ;; and the method in use is copying, signal an error.
+ (and (eq t (car (file-attributes destname)))
+ (eq file-creator 'dired-copy-file)
+ (file-in-directory-p destname from)
+ (error "Cannot copy `%s' into its subdirectory `%s'"
+ from to)))
+ (condition-case err
+ (funcall file-creator from to dired-overwrite-confirmed)
+ (file-error ; FILE-CREATOR aborted
+ (progn
+ (push (dired-make-relative from)
+ failures)
+ (dired-log "%s `%s' to `%s' failed:\n%s\n"
+ operation from to err))))))))
+ (cond
+ (dired-create-files-failures
+ (setq failures (nconc failures dired-create-files-failures))
+ (dired-log-summary
+ (format "%s failed for %d file%s in %d requests"
+ operation (length failures)
+ (dired-plural-s (length failures))
+ total)
+ failures))
+ (failures
+ (dired-log-summary
+ (format "%s failed for %d of %d file%s"
+ operation (length failures)
+ total (dired-plural-s total))
+ failures))
+ (skipped
+ (dired-log-summary
+ (format "%s: %d of %d file%s skipped"
+ operation (length skipped) total
+ (dired-plural-s total))
+ skipped))
+ (t
+ (message "%s proceeding asynchronously..." operation)))))
+
+(defun dired-internal-do-deletions (l arg &optional trash)
+ ;; L is an alist of files to delete, with their buffer positions.
+ ;; ARG is the prefix arg.
+ ;; Filenames are absolute.
+ ;; (car L) *must* be the *last* (bottommost) file in the dired buffer.
+ ;; That way as changes are made in the buffer they do not shift the
+ ;; lines still to be changed, so the (point) values in L stay valid.
+ ;; Also, for subdirs in natural order, a subdir's files are deleted
+ ;; before the subdir itself - the other way around would not work.
+ (let* ((files (mapcar (function car) l))
+ (count (length l))
+ (succ 0)
+ (trashing (and trash delete-by-moving-to-trash))
+ (progress-reporter
+ (make-progress-reporter
+ (if trashing "Trashing..." "Deleting...")
+ succ count)))
+ ;; canonicalize file list for pop up
+ (setq files (nreverse (mapcar (function dired-make-relative) files)))
+ (if (dired-mark-pop-up
+ " *Deletions*" 'delete files dired-deletion-confirmer
+ (format "%s %s "
+ (if trashing "Trash" "Delete")
+ (dired-mark-prompt arg files)))
+ (save-excursion
+ (let (failures);; files better be in reverse order for this loop!
+ (while l
+ (goto-char (cdr (car l)))
+ (let ((inhibit-read-only t))
+ (condition-case err
+ (let ((fn (car (car l))))
+ (dired-delete-file fn dired-recursive-deletes trash)
+ ;; if we get here, removing worked
+ (setq succ (1+ succ))
+ (progress-reporter-update progress-reporter succ)
+ (dired-fun-in-all-buffers
+ (file-name-directory fn) (file-name-nondirectory fn)
+ (function dired-delete-entry) fn))
+ (error;; catch errors from failed deletions
+ (dired-log "%s\n" err)
+ (setq failures (cons (car (car l)) failures)))))
+ (setq l (cdr l)))
+ (if (not failures)
+ (progress-reporter-done progress-reporter)
+ (dired-log-summary
+ (format "%d of %d deletion%s failed"
+ (length failures) count
+ (dired-plural-s count))
+ failures))))
+ (message "(No deletions performed)"))))
+
+(provide 'dired-async)
+
+;;; dired-async.el ends here
- [elpa] master updated (e053cef -> ef4a542), Michael Albinus, 2015/12/30
- [elpa] master 6647749 002/187: Small change, Michael Albinus, 2015/12/30
- [elpa] master 8d37c00 004/187: Added note to README, Michael Albinus, 2015/12/30
- [elpa] master 4733d1e 001/187: Initial revision, Michael Albinus, 2015/12/30
- [elpa] master 3d388c6 007/187: Added support for signal propagation, Michael Albinus, 2015/12/30
- [elpa] master 0a7f3dd 008/187: Added async-inject-environment, Michael Albinus, 2015/12/30
- [elpa] master 954c865 006/187: Updated README, Michael Albinus, 2015/12/30
- [elpa] master a8ac820 003/187: Renamed emacs-async.el to async.el, Michael Albinus, 2015/12/30
- [elpa] master 63fe7ee 010/187: Updated README, Michael Albinus, 2015/12/30
- [elpa] master c48d3db 009/187: Added smtpmail-async.el, Michael Albinus, 2015/12/30
- [elpa] master 728e791 005/187: Added dired-async,
Michael Albinus <=
- [elpa] master 628a295 014/187: Send data over a pipe, rather than in an argument, Michael Albinus, 2015/12/30
- [elpa] master 190a040 012/187: Fixed async-smtpmail-send-it, Michael Albinus, 2015/12/30
- [elpa] master 4fb51ce 013/187: Corrected a problem with async deletions, Michael Albinus, 2015/12/30
- [elpa] master 1ad2902 011/187: Quiet byte-compiler warnings, Michael Albinus, 2015/12/30
- [elpa] master 742c82e 020/187: Added message passing, but undocumented for now, Michael Albinus, 2015/12/30
- [elpa] master b80f1a5 018/187: Don't use pipes for communication just yet, Michael Albinus, 2015/12/30
- [elpa] master 497e4da 024/187: Fix to dired-async for wdired-mode, Michael Albinus, 2015/12/30
- [elpa] master e08c251 017/187: Added `dired-async-use-native-commands', Michael Albinus, 2015/12/30
- [elpa] master 7ba4f40 022/187: Always base64 encode, Michael Albinus, 2015/12/30
- [elpa] master 1028235 023/187: Don't use lexical-binding in dired-async.el, Michael Albinus, 2015/12/30