Subject: bug#20739: 25.0.50; Dired switches have no effect when explicit list of files provided

From: Drew Adams <[email-masked]>
Date: Sat, 6 Jun 2015 11:43:20 -0700 (PDT)

> they are listed in the order in which you specified them in the list
> passed as the first argument to 'dired'.  That just happened to
> coincide with alphabetic order in your case.

Right.  I misspoke.

> You evidently expected 'dired' to apply the order-related options in
> switches to the entire list of files.  But that's not what 'dired'
> does when it is called with its 1st argument a list.  What it does
> is invoke 'insert-directory' with each of the files in the list, in
> order, passing it the value of switches.  So when calling 'dired' in
> this manner, the order-related switches have no effect whatsoever.

I think you are describing what it does, and not what it should
do or perhaps could do, and which would be more in line with user
expectations.  Yes, that is what the behavior is now.

A priori, a user can reasonably expect switches to have their usual
effect.  Can we at least keep this expectation/request open as an
enhancement request?

In any case, the problem wrt `ls' switches is not total.  Some parts
of this bug/enhancement can be taken care of (fixed) more easily.

`i', for instance, shows inodes, and `h' shows file sizes in
human-friendly units.  But other switches are not reflected in the
Dired behavior when you provide an explicit list of files and dirs.

The behavior is limited, I'm guessing, wrt any parts of `ls' that
depend on the whole list of files and subdirs.  It seems that parts
of the `ls' behavior that depend only on the info about a given
file are retained.  That makes sense, to me anyway.

We should be able to easily make Dired at least report correctly
about the switches behavior that it gets right, and make it tell
users not to expect other switches to have any effect in this
use case.

> I've updated the doc string to mention this peculiarity.
> 
> > Hitting `s' any number of times has no effect on the order of the
> > files.
> 
> For the same reason.

First, the doc should specify what I said above (if it is in fact
the case): `ls' behavior that depends on the entire list is not
available for this use case - the only switches that affect the
display are those that depend only on the info for an individual
file or dir; other switches are ignored.

And either the list of those that are useful (not ignored) should
be given explicitly (but that can be platform-dependent), or at
least a couple examples that are typically relevant can be given.

Second, it's not just about the doc string.  If no improvement
in the behavior is to be expected (I would prefer that it be
improved to respect the switches generally, to the extent that is
possible), then I think a minimum bug fix, beyond the doc (see
above), would be to change the mode-line lighter.  At a bare
minimum, the misleading lighter indications "by name|date" need
to be removed.

Whe DIRNAME is a cons, the lighter should not show anything like
"by name" or "by date".  Instead, it should either have just
"Dired" or (better) include some indication that the listing is
from an explicit list and not necessarily a directory listing.
In the latter case, it could also show the (relevant) switches.

Here is one possibility for the mode-line lighter: `Dired/*-hiF'.
Here, I'm using:

* "*" to indicate listing selected files instead of a directory.

* "-hiF" are the relevant `ls' switches.  (Irrelevant switches
  given by the user are ignored - this indicates which are used.)

* "/" instead of "by ".  So `Dired/name' and `Dired/date'.

  FWIW: I do this /-for-by replacement anyway in my code, to save
  space and because I add more info to the lighter than just the
  sort order.  When files are marked or flagged I add that info
  to the lighter.  E.g., `Dired/name 3* 2D', with the `3*' and
  the `2D' highlighted using,by default, the same faces as marks
  `*' and `D'.

  If the current line is marked/flagged, then the lighter shows
  how many are, both through that line and total.  For example,
  `Dired/date 6* 2/9D' says this: There are 6 marked lines
  and 9 flagged lines; the file on the current line is flagged;
  there is one flagged above it and there are 8 flagged below it.

That's just one possibility, for discussion.  `*' is maybe not
the best indicator of an explicit listing; dunno.  And maybe
it won't be easy to always correctly get the list of relevant
switches; dunno.

FYI - The code for the mode-line indicator that I use now (e.g.,
showing number of marks and flags) is in function
`diredp-nb-marked-in-mode-name', in `dired+.el'.  It could be
added to vanilla dired.el, if wanted.  In dired+.el, that function
is added to these hooks:

(add-hook 'dired-after-readin-hook 'diredp-nb-marked-in-mode-name)
;; `find-dired' does not call `dired-readin'.
(add-hook 'dired-mode-hook         'diredp-nb-marked-in-mode-name)

Here is the code for the function.

(defun diredp-nb-marked-in-mode-name ()
  "Add number of marked and flagged lines to mode name in the mode line.
\(Flagged means flagged for deletion.)
If the current line is marked/flagged and there are others
marked/flagged after it then show `N/M', where N is the number
marked/flagged through the current line and M is the total number
marked/flagged.

Also abbreviate `mode-name', using \"Dired/\" instead of \"Dired by\"."
  (let ((mname  (format-mode-line mode-name)))
    ;; Prop `dired+-mode-name' indicates whether `mode-name' was changed.
    (unless (get-text-property 0 'dired+-mode-name mname)
      (save-match-data
        (setq mode-name
              `(,(propertize
                  (if (string-match "^[dD]ired \\(by \\)?\\(.*\\)" mname)
                      (format "Dired/%s" (match-string 2 mname))
                    mname)
                  'dired+-mode-name t)
                 (:eval
                  (let* ((dired-marker-char
                          (if (eq ?D dired-marker-char)
                              ?*
                            dired-marker-char))
                         (marked-regexp (dired-marker-regexp))
                         (nb-marked
                          (count-matches
                           marked-regexp (point-min) (point-max))))
                    (if (not (> nb-marked 0))
                        ""
                      (propertize
                       (format
                        " %s%d%c"
                        (save-excursion
                          (forward-line 0)
                          (if (looking-at (concat marked-regexp ".*"))
                              (format "%d/" (1+ (count-matches
                                                 marked-regexp
                                                 (point-min) (point))))
                            ""))
                        nb-marked dired-marker-char)
                       'face 'diredp-mode-line-marked
                       'dired+-mode-name t))))
                 (:eval
                  (let* ((flagged-regexp
                          (let ((dired-marker-char  dired-del-marker))
                            (dired-marker-regexp)))
                         (nb-flagged
                          (count-matches
                           flagged-regexp (point-min) (point-max))))
                    (if (not (> nb-flagged 0))
                        ""
                      (propertize
                       (format
                        " %s%dD"
                        (save-excursion
                          (forward-line 0)
                          (if (looking-at (concat flagged-regexp ".*"))
                              (format "%d/" (1+ (count-matches
                                                 flagged-regexp
                                                 (point-min) (point))))
                            ""))
                        nb-flagged)
                       'face 'diredp-mode-line-flagged))))))))))