From 6f2a5b73aaf4d2610935d8bb476fc470475a1098 Mon Sep 17 00:00:00 2001 From: JSDurand Date: Sat, 25 Feb 2023 15:47:33 +0800 Subject: eshell: improve the method to group directories first * eshell-conf.el (em-term, dir-literal, show-recursive, sort-method) (show-all, show-almost-all, listing-style, human-readable) (numeric-uid-gid, block-size, reverse-list, show-size, insert-func) (flush-func, error-func): Just variables that are needed by Eshell implementation of ls. (eshell/l): Equivalent with calling `eshell/dl` with "-glah" in front of arguments. (durand-eshell-group-entries-by-directory): Obsolete function. (durand-eshell-ls-dirs-first): A variable that controls whether to group directories first. (durand-eshell-ls-sort-entries, eshell-ls-sort-entries): An advice around the original sorting function, which groups directories into a separate list, to be sorted before the rest. This approach respects the original sorting method, and performs no extra sorting, as opposed to the previous approach. --- eshell-conf.el | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 149 insertions(+), 19 deletions(-) diff --git a/eshell-conf.el b/eshell-conf.el index 866d4bf..25ffffb 100644 --- a/eshell-conf.el +++ b/eshell-conf.el @@ -31,6 +31,7 @@ (require 'eshell)) (require 'esh-util) (require 'em-term) +(require 'em-ls) (require 'ring) (require 'type-break) @@ -371,31 +372,160 @@ candidates." ;;; convenient ls -;;;###autoload +(defvar dir-literal nil) +(defvar show-recursive nil) +(defvar sort-method nil) +(defvar show-all nil) +(defvar show-almost-all nil) +(defvar listing-style nil) +(defvar human-readable nil) +(defvar numeric-uid-gid nil) +(defvar block-size nil) +(defvar reverse-list nil) +(defvar show-size nil) +(defvar insert-func nil) +(defvar flush-func nil) +(defvar error-func nil) + (defun eshell/l (&rest args) - "Equivalent with ls -ahl ARGS. -If called without ARGS, then use ./ instead." - (eshell/ls "-hal" (or args "./"))) + "Equivalent with \"dl -alhg ARGS\"." + (eshell/dl "-alhg" (or args "."))) + +(defun eshell/dl (&rest args) + "My enhanced implementation of \"ls\" in Emacs Lisp, with ARGS. +See `eshell/l` for the original version. + +I added the option of \"g\" for grouping directories first." + (let ((insert-func 'eshell-buffered-print) + (error-func 'eshell-error) + (flush-func 'eshell-flush)) + (funcall flush-func -1) + ;; Process the command arguments, and begin listing files. + (eshell-eval-using-options + "ls" (if eshell-ls-initial-args + (list eshell-ls-initial-args args) + args) + '((?a "all" nil show-all + "do not ignore entries starting with .") + (?A "almost-all" nil show-almost-all + "do not list implied . and ..") + (?c nil by-ctime sort-method + "sort by last status change time") + (?d "directory" nil dir-literal + "list directory entries instead of contents") + (?k "kilobytes" 1024 block-size + "using 1024 as the block size") + (?h "human-readable" 1024 human-readable + "print sizes in human readable format") + (?H "si" 1000 human-readable + "likewise, but use powers of 1000 not 1024") + (?I "ignore" t ignore-pattern + "do not list implied entries matching pattern") + (?l nil long-listing listing-style + "use a long listing format") + (?n "numeric-uid-gid" nil numeric-uid-gid + "list numeric UIDs and GIDs instead of names") + (?r "reverse" nil reverse-list + "reverse order while sorting") + (?s "size" nil show-size + "print size of each file, in blocks") + (?t nil by-mtime sort-method + "sort by modification time") + (?u nil by-atime sort-method + "sort by last access time") + (?x nil by-lines listing-style + "list entries by lines instead of by columns") + (?C nil by-columns listing-style + "list entries by columns") + (?L "dereference" nil dereference-links + "list entries pointed to by symbolic links") + (?R "recursive" nil show-recursive + "list subdirectories recursively") + (?g "group-directories-first" nil durand-eshell-ls-dirs-first + "group directories first") + (?S nil by-size sort-method + "sort by file size") + (?U nil unsorted sort-method + "do not sort; list entries in directory order") + (?X nil by-extension sort-method + "sort alphabetically by entry extension") + (?1 nil single-column listing-style + "list one file per line") + (nil "dired" nil dired-flag + "Here for compatibility with GNU ls.") + (nil "help" nil nil + "show this usage display") + :external "ls" + :usage "[OPTION]... [FILE]... +List information about the FILEs (the current directory by default). +Sort entries alphabetically across.") + ;; setup some defaults, based on what the user selected + (unless block-size + (setq block-size eshell-ls-default-blocksize)) + (unless listing-style + (setq listing-style 'by-columns)) + (unless args + (setq args (list "."))) + (let ((eshell-ls-exclude-regexp eshell-ls-exclude-regexp)) + (when ignore-pattern + (unless (eshell-using-module 'eshell-glob) + (error (concat "-I option requires that `eshell-glob'" + " be a member of `eshell-modules-list'"))) + (set-text-properties 0 (length ignore-pattern) nil ignore-pattern) + (setq eshell-ls-exclude-regexp + (if eshell-ls-exclude-regexp + (concat "\\(" eshell-ls-exclude-regexp "\\|" + (eshell-glob-regexp ignore-pattern) "\\)") + (eshell-glob-regexp ignore-pattern)))) + ;; list the files! + (eshell-ls-entries + (mapcar (lambda (arg) + (cons (if (and (eshell-under-windows-p) + (file-name-absolute-p arg)) + (expand-file-name arg) + arg) + (eshell-file-attributes + arg (if numeric-uid-gid 'integer 'string)))) + args) + t (expand-file-name default-directory))) + (funcall flush-func)))) ;;; Group directories first in `eshell/ls' -(defun durand-eshell-group-entries-by-directory (entries) - "Sort ENTRIES so that directories come before non-directories." - (sort entries - (lambda (l r) - (cond - ((string= (car l) ".")) - ((string= (car r) ".") nil) - ((string= (car l) "..")) - ((string= (car r) "..") nil) - ((and (file-directory-p (car l)) - (file-directory-p (car r))) - (string-lessp (car l) (car r))) - ((file-directory-p (car l))) - ((file-directory-p (car r)) nil))))) +(defvar durand-eshell-ls-dirs-first nil + "The variable that controls whether +`durand-eshell-ls-sort-entries' puts directories first.") + +(defun durand-eshell-ls-sort-entries (old-fun entries) + "An advice around `eshell-ls-sort-entries'. +OLD-FUN should be `eshell-ls-sort-entries'. + +ENTRIES will be split into two lists, one for directories, and +the other for non-directory files. Then each list is passed to +OLD-FUN separately. The two results are then concatenated. + +This can preserve the original sorting method." + (cond + (durand-eshell-ls-dirs-first + (let (durand-dir-files durand-other-files) + (mapc + (lambda (file) + (cond + ((cond + ((stringp file) (file-directory-p file)) + ((consp file) (eshell-ls-filetype-p (cdr file) ?d)) + ((user-error "Invalid file: %S" file))) + (setq durand-dir-files (cons file durand-dir-files))) + ((setq durand-other-files (cons file durand-other-files))))) + entries) + (append + (funcall old-fun durand-dir-files) + (funcall old-fun durand-other-files)))) + ((funcall old-fun entries)))) (advice-add #'eshell-ls-sort-entries - :after #'durand-eshell-group-entries-by-directory) + :around #'durand-eshell-ls-sort-entries) + ;;; Integration with Tramp -- cgit v1.2.3-18-g5258