unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#31601: Dired/Wdired: Play nicely with recursive list of files and directories.
@ 2018-05-26  4:36 Keith David Bershatsky
  2018-05-26  9:02 ` Tino Calancha
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Keith David Bershatsky @ 2018-05-26  4:36 UTC (permalink / raw)
  To: 31601

I occasionally have the need to rename files and directories that are recursively located.  I create a dired-mode buffer as follows:

    (dired (directory-files-recursively "/path/to/directory" "" 'include-directories))

Wdired:  The first problem is that `wdired-get-filename' needs to be modified so that it handles absolute paths differently than relative filenames.  Here is an example that works -- using `file-name-absolute-p`:

(defun wdired-get-filename (&optional no-dir old)
  "Return the filename at line.
Similar to `dired-get-filename' but it doesn't rely on regexps.  It
relies on WDired buffer's properties.  Optional arg NO-DIR with value
non-nil means don't include directory.  Optional arg OLD with value
non-nil means return old filename."
  ;; FIXME: Use dired-get-filename's new properties.
  (let (beg end file)
    (save-excursion
      (setq end (line-end-position))
      (beginning-of-line)
      (setq beg (next-single-property-change (point) 'old-name nil end))
      (unless (eq beg end)
  (if old
      (setq file (get-text-property beg 'old-name))
    ;; In the following form changed `(1+ beg)' to `beg' so that
    ;; the filename end is found even when the filename is empty.
    ;; Fixes error and spurious newlines when marking files for
    ;; deletion.
    (if (= (point-at-eol) (point-max))
      (setq end (point-max)) ;; fix for @lawlist eliminating final new line at eob.
      (setq end (next-single-property-change beg 'end-name)))
    (setq file (buffer-substring-no-properties (1+ beg) end)))
  (and file (setq file (wdired-normalize-filename file))))
      (if (or no-dir old)
        file
        (cond
          ;;; When FILE is relative, concatenate default-directory to beginning.
          ((and file
                (> (length file) 0)
                (not (file-name-absolute-p file))
                (concat (dired-current-directory) file)))
          ;;; When FILE is absolute, no need to concatenate the default-directory.
          ((and file
                (> (length file) 0)
                (file-name-absolute-p file)
                file)))))))

Dired:  The second problem is a dired-mode problem in that the `dired-directory' variable is not updated when renaming a file.  I haven't tested deleting a file, but that probably suffers the same problem.  Absent updating the `dired-directory` with the renamed filename, the `revert-buffer` will encounter errors because `ls` or `gls` will try to find files that no longer exist, and that errors will be inserted into the dired- buffer.  A quick solution for renaming the file is to modify `dired-rename-file` as follows:

;;; (setq mylist '("apple" "pear" "peach" "nectarine" "watermelon"))
;;; (ar-replace--in-list "apple" "cherry" mylist)
;;; Written by @Andreas Röhler:  https://emacs.stackexchange.com/a/41631/2287
(defun ar-replace--in-list (elem replacement list)
  "Expects a LIST of strings.
ELEM: element to replace by arg REPLACEMENT"
  (let (newlist)
    (dolist (ele list)
      (if (string= ele elem)
      (push replacement newlist)
    (push ele newlist)))
    (nreverse newlist)))

(defun dired-rename-file (file newname ok-if-already-exists)
  (dired-handle-overwrite newname)
  (rename-file file newname ok-if-already-exists) ; error is caught in -create-files
  ;;; Update the `dired-directory'
  (when (and (listp dired-directory)
             (member file dired-directory))
    (setq dired-directory
      (ar-replace--in-list file newname dired-directory)))
  ;; 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))

However, this is understandably slow if there are a lot of files.  Perhaps a hash-table system would be better suited to keep track of files and directories instead of just a plain old list with file/directory names.

I haven't yet figured out the best approach to modify a wdired-mode buffer that has a combination of directories and files.  Presumably some portion of the absolute filename/directory-name will need read-only attributes ....  But, that is as far as my thinking has gone on this issue.

There may be many other situations that I haven't thought of because I've never used all of the features of dired-mode and/or wdired-mode.

Thanks,

Keith





^ permalink raw reply	[flat|nested] 4+ messages in thread

* bug#31601: Dired/Wdired: Play nicely with recursive list of files and directories.
  2018-05-26  4:36 bug#31601: Dired/Wdired: Play nicely with recursive list of files and directories Keith David Bershatsky
@ 2018-05-26  9:02 ` Tino Calancha
  2018-05-26 18:51 ` Keith David Bershatsky
  2018-05-28 23:11 ` Keith David Bershatsky
  2 siblings, 0 replies; 4+ messages in thread
From: Tino Calancha @ 2018-05-26  9:02 UTC (permalink / raw)
  To: Keith David Bershatsky; +Cc: 31601, tino.calancha

Keith David Bershatsky <esq@lawlist.com> writes:

> I occasionally have the need to rename files and directories that are recursively located.  I create a dired-mode buffer as follows:
>
>     (dired (directory-files-recursively "/path/to/directory" "" 'include-directories))
>
> Wdired:  The first problem is that `wdired-get-filename' needs to be modified so that it handles absolute paths differently than relative filenames.

> Dired:  The second problem is a dired-mode problem in that the `dired-directory' variable is not updated when renaming a file

Thank you for your report Keith!
You are right, it looks annoying.

Though I haven't arrived with an ideal solution, following
are two ways how I would try to perform a similar task.

I)
If you are lucky and "sh" points to a shell that
supports globstar (zsh, fish), or if you are using dired
via `em-ls', then you can do:
M-x dired /path/to/directory/**/* RET
;; Now both, wdired and `dired-do-rename' works fine.

[If you are using bash or ksh, that support globstar but
disable it by default, then you might be interestd in my
proposal in Bug#31495: it gives the chance to enable such
feature on dired via an user option.]

II)
Another way could be to use a command of the `find-dired' family:
M-x find-name-dired /path/to/directory/ RET * RET
;; I just noticed that wdired resets `revert-buffer-function' also for
;; these dired buffers.  IMO it shouldn't, so I would propose
;; something like this:
--8<-----------------------------cut here---------------start------------->8---
commit 58842a20f570d32f5d3f90aced8f0e8c1b1535a7
Author: Tino Calancha <tino.calancha@gmail.com>
Date:   Sat May 26 17:39:29 2018 +0900

    Preserve revert-buffer-function on find-dired buffers
    
    * lisp/wdired.el (wdired-find-dired-buffer-p): New predicate.
    (wdired-change-to-wdired-mode, wdired-change-to-dired-mode):
    Preserve `revert-buffer-function' on dired buffers created
    with `find-dired' commands.

diff --git a/lisp/wdired.el b/lisp/wdired.el
index bb60e77776..de5b327d75 100644
--- a/lisp/wdired.el
+++ b/lisp/wdired.el
@@ -228,6 +228,12 @@ wdired-mode
   (error "This mode can be enabled only by `wdired-change-to-wdired-mode'"))
 (put 'wdired-mode 'mode-class 'special)
 
+(defun wdired-find-dired-buffer-p ()
+  "Return non-nil if the dired buffer comes from a `find-dired' command."
+  (save-excursion
+    (goto-char (point-min))
+    (forward-line 1)
+    (re-search-forward (format "^\\s-*%s " find-program) nil t)))
 
 ;;;###autoload
 (defun wdired-change-to-wdired-mode ()
@@ -257,7 +263,8 @@ wdired-change-to-wdired-mode
   (add-hook 'kill-buffer-hook 'wdired-check-kill-buffer nil t)
   (setq major-mode 'wdired-mode)
   (setq mode-name "Editable Dired")
-  (setq revert-buffer-function 'wdired-revert)
+  (unless (wdired-find-dired-buffer-p)
+    (setq revert-buffer-function 'wdired-revert))
   ;; I temp disable undo for performance: since I'm going to clear the
   ;; undo list, it can save more than a 9% of time with big
   ;; directories because setting properties modify the undo-list.
@@ -363,7 +370,8 @@ wdired-change-to-dired-mode
   (setq mode-name "Dired")
   (dired-advertise)
   (remove-hook 'kill-buffer-hook 'wdired-check-kill-buffer t)
-  (set (make-local-variable 'revert-buffer-function) 'dired-revert))
+  (unless (wdired-find-dired-buffer-p)
+    (set (make-local-variable 'revert-buffer-function) 'dired-revert)))
 
 
 (defun wdired-abort-changes ()
--8<-----------------------------cut here---------------end--------------->8---
In GNU Emacs 27.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.22.11)
 of 2018-05-26 built on calancha-pc
Repository revision: 66c9ab90d5f8b566467549bf1d48c936bc6d296b






^ permalink raw reply related	[flat|nested] 4+ messages in thread

* bug#31601: Dired/Wdired: Play nicely with recursive list of files and directories.
  2018-05-26  4:36 bug#31601: Dired/Wdired: Play nicely with recursive list of files and directories Keith David Bershatsky
  2018-05-26  9:02 ` Tino Calancha
@ 2018-05-26 18:51 ` Keith David Bershatsky
  2018-05-28 23:11 ` Keith David Bershatsky
  2 siblings, 0 replies; 4+ messages in thread
From: Keith David Bershatsky @ 2018-05-26 18:51 UTC (permalink / raw)
  To: Tino Calancha; +Cc: 31601

Thank you for the alternative approaches.  I applied the new diff and was able to successfully use the second option with `find-dired`.

As to the first option, however, I would need to spend time learning about zsh, fish and em-ls.  So, that exploration would need to wait for a rainy day.

Oh, by the way, please consider adding a `nil` to the end of `(defvar wdired-old-marks)` so that the user does not experience this:

funcall-interactively: Symbol’s value as variable is void: wdired-old-marks

In my spare time, I will most likely continue implementing functionality for wdired playing nice with:

(dired (directory-files-recursively "/path/to/directory" "" 'include-directories))

That will entail:  (1) placing read-only text properties over areas that cannot be changed; (2) when I modify a directory, I will need to propagate that change throughout the applicable entries of `dired-directory'; (3) I will consider implementing a hash-table approach with `dired-directory` entries for increased speed.

Keith

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

DATE:  [05-26-2018 02:02:26] <26 May 2018 18:02:26 +0900>
FROM:  Tino Calancha <tino.calancha@gmail.com>
> 
> * * *

> I)
> If you are lucky and "sh" points to a shell that
> supports globstar (zsh, fish), or if you are using dired
> via `em-ls', then you can do:
> M-x dired /path/to/directory/**/* RET
> ;; Now both, wdired and `dired-do-rename' works fine.
> 
> [If you are using bash or ksh, that support globstar but
> disable it by default, then you might be interestd in my
> proposal in Bug#31495: it gives the chance to enable such
> feature on dired via an user option.]
> 
> II)
> Another way could be to use a command of the `find-dired' family:
> M-x find-name-dired /path/to/directory/ RET * RET

* * *





^ permalink raw reply	[flat|nested] 4+ messages in thread

* bug#31601: Dired/Wdired: Play nicely with recursive list of files and directories.
  2018-05-26  4:36 bug#31601: Dired/Wdired: Play nicely with recursive list of files and directories Keith David Bershatsky
  2018-05-26  9:02 ` Tino Calancha
  2018-05-26 18:51 ` Keith David Bershatsky
@ 2018-05-28 23:11 ` Keith David Bershatsky
  2 siblings, 0 replies; 4+ messages in thread
From: Keith David Bershatsky @ 2018-05-28 23:11 UTC (permalink / raw)
  To: Tino Calancha; +Cc: 31601

[-- Attachment #1: Type: text/plain, Size: 1424 bytes --]

Attached is a draft/proof-concept patch.diff of my ideas regarding how to make dired/wdired play nicely with an arbitrary list of files and folders, including a recursive list.  I asked for some help on emacs.stackexchange.com to implement these new approaches, and used the answers by @Drew, @Tobias and @Andreas Röhler.  Citations to their answers are included in the draft/proof-concept patch.  I would need some help regarding how to update the dired-directory variable when deleting a file in a regular dired-mode buffer, and I have added a FIXME notation in the likely spot of `dired.el` -- the approach would be similar to renaming a file (already implemented in the attached draft patch), but is a bit more complicated due to potential recursive deletions.

The file-name-nondirectory components in wdired-mode now have read-only attributes so that a user no longer erroneously believes that he/she can modify them, and only latter see an error message when trying to commit the changes:  file-error Renaming No such file or directory ....

I have only done some basic renaming of files and directories in wdired mode using both `find-name-dired` and `dired-list-files-folders' to populate the dired- buffer.  I did not spend any time implementing hash-tables to speed up the modification of the dired-directory variable, as that would likely involve modifying a few or more aspects of dired-mode.

Thanks,

Keith


[-- Attachment #2: patch.diff --]
[-- Type: application/diff, Size: 15536 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2018-05-28 23:11 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-05-26  4:36 bug#31601: Dired/Wdired: Play nicely with recursive list of files and directories Keith David Bershatsky
2018-05-26  9:02 ` Tino Calancha
2018-05-26 18:51 ` Keith David Bershatsky
2018-05-28 23:11 ` Keith David Bershatsky

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).