unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Stephen Berman <stephen.berman@gmx.net>
To: "Basil L. Contovounesios" <contovob@tcd.ie>
Cc: 18475@debbugs.gnu.org
Subject: bug#18475: 24.4.50; Wdired: cannot use C-k to delete a dir name if -F switch used
Date: Tue, 30 Apr 2019 23:50:27 +0200	[thread overview]
Message-ID: <87muk740yk.fsf@gmx.net> (raw)
In-Reply-To: <56994c97-c501-4233-b029-dcb12c796441@default>

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

On Sat, 27 Apr 2019 01:09:16 +0100 "Basil L. Contovounesios" <contovob@tcd.ie> wrote:

> Drew Adams <drew.adams@oracle.com> writes:
>
>> The `wdired-mode' doc string says: "If you delete the filename of a
>> file, it is flagged for deletion in the Dired buffer."  And (emacs)
>> `Wdired' says: "To mark a file for deletion, delete the entire file
>> name."
>>
>> And this applies to directory names also.
>>
>> No problem, except if you use `ls' switch `-F', which appends `/' to
>> directory names.  In that case, if you try to use `C-k' anywhere on the
>> dir name text, you get the error "Text is read only".
>>
>> (Yes, you can use other deletion keys besides `C-k' to delete the dir
>> name but not delete the `/'.  That looks weird and is not easily
>> guessable by users, but it works.  `C-k' should work also, as one would
>> expect.)
>>
>> In GNU Emacs 24.4.50.1 (i686-pc-mingw32)
>>  of 2014-08-15 on LEG570
>> Bzr revision: 117706 rgm@gnu.org-20140815043406-p5hbu97cbm7pulcn
>> Windowing system distributor `Microsoft Corp.', version 6.1.7601
>> Configured using:
>>  `configure --enable-checking 'CFLAGS=-O0 -g3' CPPFLAGS=-DGLYPH_DEBUG=1'
>
> I can still reproduce this on Emacs 26 and latest master.

This happens not only with `/' but also with the other ls file indicator
characters appended when using the -F switch.  Making C-k work as
expected for these cases is a small fix.  However, the same issue also
arises with symlinks, whether or not -F is used, and it does not seem as
straightforward to deal with this case.  The attached patch (against
master) tries to ensure the following behavior:

- Typing `C-k' with point just before the first character of a file name
  ending with an indicator character (using -F), or of link name,
  deletes the file name/symlink in WDired, but it is restored but marked
  for deletion on returning to Dired.  The same also happens when just
  deleting the link name in WDired.

- Typing `C-k' with point on such a file or link name, but after the
  first character, deletes the rest, resulting in renaming on returning
  to Dired.

- Deleting the indicator character is possible in WDired but a noop: the
  character is restored on returning to Dired.  (In the current code
  without the patch, the indicator characters are read-only, but I had
  to change that to make C-k work.)

With symlinks:

- The patch preserves the current behavior that an edit of the target
  name (possible when wdired-allow-to-redirect-links is non-nil, as it
  is by default) is saved on returning to Dired, and if the target name
  is deleted, then the new target on returning to Dired is "/dev/null".

- Editing (changing or deleting) the string " -> " between the link and
  target names is possible in WDired but a noop: the symlink is
  unaltered on returning to Dired.  (In the current code without the
  patch, " -> " is read-only, but I had to change that to make C-k work.)

I've tested these cases, but it is quite possible that I overlooked some
variants or other cases, so I'd appreciate testing and feedback from
others.  (Also, the code still needs more commenting and probably
cleaning up.)

Steve Berman


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: wdired patch --]
[-- Type: text/x-patch, Size: 9118 bytes --]

diff --git a/lisp/wdired.el b/lisp/wdired.el
index d2a298bd25..e44b619b46 100644
--- a/lisp/wdired.el
+++ b/lisp/wdired.el
@@ -255,7 +255,7 @@ wdired-change-to-wdired-mode
   (setq buffer-read-only nil)
   (dired-unadvertise default-directory)
   (add-hook 'kill-buffer-hook 'wdired-check-kill-buffer nil t)
-  (add-hook 'after-change-functions 'wdired--restore-dired-filename-prop nil t)
+  (add-hook 'after-change-functions 'wdired--restore-properties nil t)
   (setq major-mode 'wdired-mode)
   (setq mode-name "Editable Dired")
   (setq revert-buffer-function 'wdired-revert)
@@ -266,7 +266,7 @@ wdired-change-to-wdired-mode
   (wdired-preprocess-files)
   (if wdired-allow-to-change-permissions
       (wdired-preprocess-perms))
-  (if (and wdired-allow-to-redirect-links (fboundp 'make-symbolic-link))
+  (if (fboundp 'make-symbolic-link)
       (wdired-preprocess-symlinks))
   (buffer-enable-undo) ; Performance hack. See above.
   (set-buffer-modified-p nil)
@@ -288,6 +288,7 @@ wdired-preprocess-files
   (save-excursion
     (goto-char (point-min))
     (let ((b-protection (point))
+          (used-F (dired-check-switches dired-actual-switches "F" "classify"))
 	  filename)
       (while (not (eobp))
 	(setq filename (dired-get-filename nil t))
@@ -299,8 +300,16 @@ wdired-preprocess-files
 	  (add-text-properties
 	   (1- (point)) (point) `(old-name ,filename rear-nonsticky (read-only)))
 	  (put-text-property b-protection (point) 'read-only t)
-	  (setq b-protection (dired-move-to-end-of-filename t))
+          (dired-move-to-end-of-filename t)
 	  (put-text-property (point) (1+ (point)) 'end-name t))
+          (when (and used-F (looking-at "[*/@|=>]$")) (forward-char))
+          (when (save-excursion
+                  (and (re-search-backward
+                        dired-permission-flags-regexp nil t)
+                       (looking-at "l")
+                       (search-forward " -> " (line-end-position) t)))
+            (goto-char (line-end-position)))
+	  (setq b-protection (point))
         (forward-line))
       (put-text-property b-protection (point-max) 'read-only t))))

@@ -327,7 +336,8 @@ wdired-get-filename
 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)
+  (let ((used-F (dired-check-switches dired-actual-switches "F" "classify"))
+        beg end file)
     (save-excursion
       (setq end (line-end-position))
       (beginning-of-line)
@@ -339,7 +349,20 @@ wdired-get-filename
 	  ;; the filename end is found even when the filename is empty.
 	  ;; Fixes error and spurious newlines when marking files for
 	  ;; deletion.
-	  (setq end (next-single-property-change beg 'end-name))
+	  (setq end (next-single-property-change beg 'end-name nil end))
+          (when (save-excursion
+                  (and (re-search-forward
+                        dired-permission-flags-regexp nil t)
+                       (goto-char (match-beginning 0))
+                       (looking-at "l")
+                       (search-forward " -> " (line-end-position) t)))
+            (goto-char (match-beginning 0))
+            (setq end (point)))
+          (when (and used-F
+                     (save-excursion
+                       (goto-char end)
+                       (looking-back "[*/@|=>]$" (1- (point)))))
+              (setq end (1- end)))
 	  (setq file (buffer-substring-no-properties (1+ beg) end)))
 	;; Don't unquote the old name, it wasn't quoted in the first place
         (and file (setq file (wdired-normalize-filename file (not old)))))
@@ -364,7 +387,7 @@ wdired-change-to-dired-mode
   (setq mode-name "Dired")
   (dired-advertise)
   (remove-hook 'kill-buffer-hook 'wdired-check-kill-buffer t)
-  (remove-hook 'after-change-functions 'wdired--restore-dired-filename-prop t)
+  (remove-hook 'after-change-functions 'wdired--restore-properties t)
   (set (make-local-variable 'revert-buffer-function) 'dired-revert))


@@ -425,9 +448,9 @@ wdired-finish-edit
     (when files-renamed
       (setq errors (+ errors (wdired-do-renames files-renamed))))
     ;; We have to be in wdired-mode when wdired-do-renames is executed
-    ;; so that wdired--restore-dired-filename-prop runs, but we have
-    ;; to change back to dired-mode before reverting the buffer to
-    ;; avoid using wdired-revert, which changes back to wdired-mode.
+    ;; so that wdired--restore-properties runs, but we have to change
+    ;; back to dired-mode before reverting the buffer to avoid using
+    ;; wdired-revert, which changes back to wdired-mode.
     (wdired-change-to-dired-mode)
     (if changes
 	(progn
@@ -449,7 +472,11 @@ wdired-finish-edit
 				'(old-name nil end-name nil old-link nil
 					   end-link nil end-perm nil
 					   old-perm nil perm-changed nil))
-	(message "(No changes to be performed)")))
+	(message "(No changes to be performed)")
+        ;; Deleting file indicator characters or editing the symlink
+        ;; arrow in WDired are noops, so redisplay them immediately on
+        ;; returning to Dired.
+        (revert-buffer)))
     (when files-deleted
       (wdired-flag-for-deletion files-deleted))
     (when (> errors 0)
@@ -603,11 +630,21 @@ wdired-check-kill-buffer
 ;; dired-filename text property, which allows functions that look for
 ;; this property (e.g. dired-isearch-filenames) to work in wdired-mode
 ;; and also avoids an error with non-nil wdired-use-interactive-rename
-;; (bug#32173).
-(defun wdired--restore-dired-filename-prop (beg end _len)
+;; (bug#32173).  Also prevents editing the symlink arrow (which is a
+;; noop) from corrupting the link name (see bug#18475 for elaboration).
+(defun wdired--restore-properties (beg end _len)
   (save-match-data
     (save-excursion
       (let ((lep (line-end-position)))
+        ;; Deleting the space between the link name and the arrow (a
+        ;; noop) also deletes the end-name property, so restore it.
+        (when (and (save-excursion
+                     (re-search-backward dired-permission-flags-regexp nil t)
+                     (looking-at "l"))
+                   (get-text-property (1- (point)) 'dired-filename)
+                   (not (get-text-property (point) 'dired-filename))
+                   (not (get-text-property (point) 'end-name)))
+            (put-text-property (point) (1+ (point)) 'end-name t))
         (beginning-of-line)
         (when (re-search-forward
                directory-listing-before-filename-regexp lep t)
@@ -665,34 +702,37 @@ wdired-preprocess-symlinks
     (save-excursion
       (goto-char (point-min))
       (while (not (eobp))
-        (if (looking-at dired-re-sym)
-            (progn
-              (re-search-forward " -> \\(.*\\)$")
-	      (put-text-property (- (match-beginning 1) 2)
-				 (1- (match-beginning 1)) 'old-link
-				 (match-string-no-properties 1))
-              (put-text-property (match-end 1) (1+ (match-end 1)) 'end-link t)
-              (put-text-property (1- (match-beginning 1))
-				 (match-beginning 1)
-				 'rear-nonsticky '(read-only))
-	      (put-text-property (match-beginning 1)
-				 (match-end 1) 'read-only nil)))
+        (when (looking-at dired-re-sym)
+          (re-search-forward " -> \\(.*\\)$")
+	  (put-text-property (1- (match-beginning 1))
+			     (match-beginning 1) 'old-link
+			     (match-string-no-properties 1))
+          (put-text-property (match-end 1) (1+ (match-end 1)) 'end-link t)
+          (unless wdired-allow-to-redirect-links
+            (put-text-property (match-beginning 0)
+			       (match-end 1) 'read-only t)))
         (forward-line)
 	(beginning-of-line)))))

-
 (defun wdired-get-previous-link (&optional old move)
   "Return the next symlink target.
 If OLD, return the old target.  If MOVE, move point before it."
   (let (beg end target)
     (setq beg (previous-single-property-change (point) 'old-link nil))
-    (if beg
-	(progn
-	  (if old
-	      (setq target (get-text-property (1- beg) 'old-link))
-	    (setq end (next-single-property-change beg 'end-link))
-	    (setq target (buffer-substring-no-properties (1+ beg) end)))
-	  (if move (goto-char (1- beg)))))
+    (when beg
+      (when (save-excursion
+              (goto-char beg)
+              (and (looking-at " ")
+                   (looking-back " ->" (line-beginning-position))))
+        (setq beg (1+ beg)))
+      (if old
+          (setq target (get-text-property (1- beg) 'old-link))
+        (setq end (save-excursion
+                    (goto-char beg)
+                    (next-single-property-change beg 'end-link nil
+                                                 (line-end-position))))
+        (setq target (buffer-substring-no-properties beg end)))
+	  (if move (goto-char (1- beg))))
     (and target (wdired-normalize-filename target t))))

 (declare-function make-symbolic-link "fileio.c")

  parent reply	other threads:[~2019-04-30 21:50 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-09-14 18:01 bug#18475: 24.4.50; Wdired: cannot use C-k to delete a dir name if -F switch used Drew Adams
2019-04-27  0:09 ` Basil L. Contovounesios
2019-04-28  8:13   ` Stephen Berman
2019-04-28 13:07     ` Basil L. Contovounesios
2019-04-30 21:50   ` Stephen Berman [this message]
2019-05-12 12:36     ` Basil L. Contovounesios
2020-08-25 10:11       ` Lars Ingebrigtsen
2020-08-26 10:11         ` Stephen Berman
2020-10-11  4:06           ` Lars Ingebrigtsen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87muk740yk.fsf@gmx.net \
    --to=stephen.berman@gmx.net \
    --cc=18475@debbugs.gnu.org \
    --cc=contovob@tcd.ie \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).