GNU bug report logs - #22829
25.1.50; Display number of marked files

Previous Next

Package: emacs;

Reported by: Tino Calancha <f92capac <at> gmail.com>

Date: Sat, 27 Feb 2016 14:27:02 UTC

Severity: wishlist

Tags: fixed, patch

Found in version 25.1.50

Fixed in version 27.1

Done: Lars Ingebrigtsen <larsi <at> gnus.org>

Bug is archived. No further changes may be made.

Full log


View this message in rfc822 format

From: Tino Calancha <f92capac <at> gmail.com>
To: 22829 <at> debbugs.gnu.org
Subject: bug#22829: Acknowledgement (25.1.50; Display number of marked files)
Date: Thu, 3 Mar 2016 18:58:40 +0900 (JST)
[Message part 1 (text/plain, inline)]

>I confirm this is very useful.  I'm using the same for a long time,
>but additionally also displaying a total sum of sizes on every mark
Thank you Juri,
I like very much your idea about showing the file sizes!

If we modify `dired-get-marked-files' as in the Bug#22892
we can get in a  straightforward way the file sizes from the file system.
I include the modified version of your code after these comments.

>What about the size of directories are marked?  I assume that it is to
>costly to include the recursively computed size...?
Good idea.
>This is beginning to sound more useful.  :-)
Sure, now is much better.

>the proposed key 'N' is already bound to dired-man in dired-x.el.
>Another good mnemonic key would be '#', but it's taken as well.
>Than maybe put it on the marking prefix key '*'.
I do like '*#', but i like 'N' more.
We can also reconsider a bit about the keybinding 'N'
for dired-man. Honestly i don't see the needs for this, having `man'
and `woman' working nicely. Indeed, the funtion is not working as it should:
(dired "/bin")
j bash RET N bash RET
Can’t find the /bin/bash manpage
;; Notice how i need to introduce 'bash' by hand, because the
;; interactive specification of this func. is just (interactive).
;; I don't expect anyone will complaint if we change 'N' binding here.

>Yes, doing `du -sh` often takes soo long...  But we could include sizes
>of subdirs only when called with a prefix arg.

>I think the default should be to compute the sizes of files in the
>directories, too.  Perhaps a prefix to not do that...

I have included the subdir sizes as an option: currently the subdir sizes are
not computed by default. We can change this behaviour to the opposite,
with subdir sizes computed by default, after the code being well tested.

There are two functions doing the same thing:
* my-dired-count-sizes-opt1:
* my-dired-count-sizes-opt2: This looks nicer but it requires cl-lib.

Current implementation just works for local subdirectories.
If the Dired buffer is visiting a remote directory we need to introduce
some tramp handlers. I need support from tramp experts.


In GNU Emacs 25.1.50.1 (x86_64-pc-linux-gnu, GTK+ Version 2.24.29)
 of 2016-03-03 built on calancha-pc
Repository revision: 887f6126c5ce9084f93083765ac026ca6b28175c


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Following code requires the patch on Bug#22892

(defcustom my-dired-used-space-program "du"
  "Program to get recursively the total size of a directory
We assume the output has the format of `du'.
The value of this variable must be just a command name or file name;
if you want to specify options, use `my-dired-used-space-args'.

A value of nil disables this feature."
  :type '(choice (string :tag "Program") (const :tag "None" nil))
  :group 'dired)

;; this var is not a defcustom because current implementation
;; of `my-dired-count-sizes-opt1' and `my-dired-count-sizes-opt2' expect
;; sizes in bytes; then, depending of the value `my-dired-human-readable'
;; those sizes and shown in human readable units or keep in byts.
(defvar my-dired-used-space-args "-sk"
  "Options to use when running `my-dired-used-space-program'.")

(defvar my-dired-human-readable nil
  "When Non-nil, `my-dired-count-sizes' display file sizes
using `file-size-human-readable'.")

(defun my-dired-toggle-human-readable ()
  (interactive)
  (setq my-dired-human-readable (not my-dired-human-readable))
  (let ((string (if my-dired-human-readable
                    "Using human-readable units in `my-dired-count-sizes'"
                  "Using comma separator in `my-dired-count-sizes'")))
    (message string)))

(defun my-dired-use-comma-separator (num)
  "Return number NUM as an string using comma separator"
  (replace-regexp-in-string
   "^," ""
   (apply 'string
          (reverse
           (string-to-list
            (replace-regexp-in-string
             "\\([0-9]\\{3\\}\\)" "\\1,"
             (apply 'string
                    (reverse
                     (string-to-list
                      (replace-regexp-in-string
                       "\\.0$" ""
                       (number-to-string
                        num)))))))))))

(defun my-dired-file-marked-with-char-p (&optional mark)
  "Return t if file at current line is marked with MARK
When MARK nil, `dired-marked-char' is used.
If MARK equals any, any marker is accepted."
  (let ((marker (cond ((null mark) (dired-marker-regexp))
                      ((characterp mark) (concat "^" (regexp-quote (char-to-string mark))))
                      (t (dired-marker-regexp 'all-marks)))))
    (save-excursion
      (forward-line 0)
      (looking-at-p marker))))
;;;
;;; Doesn't require cl-lib
;;;
(defun my-dired-count-sizes-opt1 (&optional mark ask include-dirs)
  "Count sizes of files marked by MARK mark.
When ASK non-nil user is prompted for MARK. Otherwise `dired-marker-char'
is used.
Optional arg INCLUDE-DIRS, if non-nil, run `my-dired-used-space-program'
on the markd directories. Otherwise the size of the directories is
not included."
  ;; TODO: add this info to mode-line and file count too, e.g.: F32 S64k
  ;; and make minor mode
  ;; see `dired-change-marks'
  (interactive
   (let* ((cursor-in-echo-area t)
          (default current-prefix-arg)
          (mark (or (and default (progn (message "Count files marked with mark: ")
                                                   (read-char)))
                    dired-marker-char))
          (dirs (and default my-dired-used-space-program (y-or-n-p "Include directories? "))))
     (list mark t dirs)))
  (unless mark (setq mark dired-marker-char))
  ;; If `my-dired-used-space-program' not available signal an error.
  (when (and include-dirs
             (not (equal 0 (condition-case nil
                               (process-file my-dired-used-space-program nil nil nil null-device)
                             (error nil)))))
    (error "Program `my-dired-used-space-program' not found"))
  (if (eq mark ?\r)
      (progn
        (message "Mark cannot be \\r")
        (sit-for 1)
        (ding))
    (let* ((files          (dired-get-marked-files nil nil nil t mark))
           (ndirectories 0)
           (nreg-files 0)
           (total-size     (or (and (not (cdr files)) 0)
                               (apply '+ (mapcar (lambda(fname-attrb)
                                                   (let ((fname (car fname-attrb))
                                                         (attrb (cdr fname-attrb)))
                                                     (if (eq (elt attrb 0) t)
                                                         (progn
                                                           (setq ndirectories (1+ ndirectories))
                                                           (if (not include-dirs)
                                                               0
                                                             (with-temp-buffer
                                                               (call-process my-dired-used-space-program
                                                                             nil t nil
                                                                             my-dired-used-space-args fname)
                                                               (goto-char 1)
                                                               (search-forward-regexp "^[[:alnum:]]+" nil t)
                                                               (* 1024.0 (string-to-number (match-string 0))))))
                                                       (setq nreg-files (1+ nreg-files))
                                                       (elt attrb 7))))
                                                 (mapcar (lambda(x) (cons x (file-attributes x))) (delq t files))))))
           (total-size-str (if my-dired-human-readable
                               (file-size-human-readable total-size)
                             (my-dired-use-comma-separator total-size)))
           (total-count    (if (eq (car files) t) 1 (length files))))
        (if (null (cdr files))
            (message "No marked files with mark '%s'" (char-to-string mark))
          (message "Marked %s %s (%d non-dirs/%d dirs) with '%s' and total size %s%s%s"
                   total-count
                   (or (and (eq (car files) t) "file") "files")
                   nreg-files
                   ndirectories
                   (char-to-string mark)
                   total-size-str
                   (or (and my-dired-human-readable "") " bytes")
                   (or (and (not include-dirs) " (dirs size excluded)") ""))))))

(define-key dired-mode-map [(shift f5)] 'my-dired-count-sizes-opt1)
;(define-key dired-mode-map (kbd "N") 'my-dired-count-sizes-opt1); dired-man
(define-key dired-mode-map (kbd "*#") 'my-dired-count-sizes-opt1)

;;;
;;; Following function requires cl-lib
;;;
(defun my-dired-count-sizes-opt2 (&optional mark ask include-dirs)
  "Count sizes of files marked by MARK mark.
When ASK non-nil user is prompted for MARK. Otherwise `dired-marker-char'
is used.
Optional arg INCLUDE-DIRS, if non-nil, run `my-dired-used-space-program'
on the markd directories. Otherwise the size of the directories is
not included."
  ;; TODO: add this info to mode-line and file count too, e.g.: F32 S64k
  ;; and make minor mode
  ;; see `dired-change-marks'
  (interactive
   (let* ((cursor-in-echo-area t)
          (default current-prefix-arg)
          (mark (or (and default (progn (message "Count files marked with mark: ")
                                                   (read-char)))
                    dired-marker-char))
          (dirs (and default my-dired-used-space-program (y-or-n-p "Include directories? "))))
     (list mark t dirs)))
  (unless mark (setq mark dired-marker-char))
  ;; If `my-dired-used-space-program' not available signal an error.
  (when (and include-dirs
             (not (equal 0 (condition-case nil
                               (process-file my-dired-used-space-program nil nil nil null-device)
                             (error nil)))))
    (error "Program `my-dired-used-space-program' not found"))
  (require 'cl-lib) ; for cl-remove-if and cl-set-difference
  (if (eq mark ?\r)
      (progn
        (message "Mark cannot be \\r")
        (sit-for 1)
        (ding))
    (let* ((files          (dired-get-marked-files nil nil nil t mark))
           (non-dirs       (cl-remove-if (lambda(x) (eq (car (file-attributes x)) t)) files))
           (dirs           (cl-set-difference files non-dirs :test 'equal))
           (ndirectories   (length dirs))
           (nreg-files     (length non-dirs))
           (total-size     (apply '+ (mapcar (lambda(x) (elt x 7))
                                             (mapcar 'file-attributes (delq t non-dirs)))))
           total-size-str total-count)

      (when (and include-dirs (not (= ndirectories 0)))
        (let ((size 0))
          (with-temp-buffer
            (apply 'call-process my-dired-used-space-program
                   nil t nil
                   my-dired-used-space-args dirs)
            (goto-char 1)
            (while (search-forward-regexp "^[[:alnum:]]+" nil t)
              (setq size (+ size (string-to-number (match-string 0))))))
          (setq total-size (+ total-size (* 1024.0 size)))))

      (setq total-size-str (if my-dired-human-readable
                               (file-size-human-readable total-size)
                             (my-dired-use-comma-separator total-size))
            total-count    (if (eq (car files) t) 1 (length files)))
      (if (null (cdr files))
          (message "No marked files with mark '%s'" (char-to-string mark))
        (message "Marked %s %s (%d non-dirs/%d dirs) with '%s' and total size %s%s%s"
                 total-count
                 (or (and (eq (car files) t) "file") "files")
                 nreg-files
                 ndirectories
                 (char-to-string mark)
                 total-size-str
                 (or (and my-dired-human-readable "") " bytes")
                 (or (and (not include-dirs) " (dirs size excluded)") ""))))))


;; (define-key dired-mode-map [(shift f5)] 'my-dired-count-sizes-opt2)
;; ;;(define-key dired-mode-map (kbd "N") 'my-dired-count-sizes-opt2); dired-man
;; (define-key dired-mode-map (kbd "*#") 'my-dired-count-sizes-opt2)
;; Bound for comparison with `my-dired-count-sizes-opt1' (bound to '*#')
(define-key dired-mode-map (kbd "N") 'my-dired-count-sizes-opt2)

(defun my-dired-mark (&optional arg mark)
  "Mark ARG files and print the total size of marked files
In interactive calls, if ARG equals to '(4),
the user is asked for arg and marker-char."
  (interactive (let* ((default current-prefix-arg)
                      (mark   (cond ((equal default '(4))
                                     (progn (message "Mark files with mark: ") (read-char)))
                                    (t dired-marker-char)))
                      (nfiles (and default (string-to-number (read-string "Number of files to mark: " nil nil "1")))))
                 (list (list nfiles) mark)))
  (let ((dired-marker-char (or mark dired-marker-char)))
    (dired-mark arg))
  (my-dired-count-sizes mark))
(define-key dired-mode-map [insert] 'my-dired-mark)

(defun my-dired-unmark-backward (&optional arg)
  "Move ARG up lines, remove deletion flag there and print size of marked files
In interactive calls, if `current-prefix-arg' equals '(4), the user is
prompt for ARG."
  (interactive (let* ((default current-prefix-arg)
                      (nfiles (and (equal default '(4))
                                   (string-to-number (read-string "Number of files to unmark: " nil nil "1")))))
                 (list (or nfiles 1))))
  (let (files)
    (save-excursion
      (cond ((null arg)
             (dired-next-line -1)
             (push (dired-get-filename nil 'noerror) files))

            ((or (numberp arg) (and (consp arg) (numberp (car arg))))
             (let ((times (or (and (atom arg) arg) (car arg))))
               (while (not (= times 0))
                 (dired-next-line -1)
                 (when (my-dired-file-marked-with-char-p ?a)
                   (push (dired-get-filename nil 'noerror) files))
                 (setq times (1- times)))
               (setq files (delq nil files))))))

    (dired-unmark-backward arg)
    (let* ((total-size  (apply '+ (mapcar (lambda(x) (elt x 7))
                                          (mapcar 'file-attributes files))))
           (total-size-str (if my-dired-human-readable
                               (file-size-human-readable total-size)
                             (my-dired-use-comma-separator total-size))))
      (if (null files)
          (message "No marked files")
        (message "Unmarked %s %s with %s%s"
                 (length files)
                 (or (and (cdr files) "files") "file")
                 total-size-str
                 (or (and my-dired-human-readable "") " bytes"))))))

(define-key dired-mode-map [backspace] 'my-dired-unmark-backward)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

This bug report was last modified 6 years and 23 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.