branch: elpa/magit commit 83d89ee5bb1c488544bae60d52f7ee1987b6449e Author: Jonas Bernoulli <jo...@bernoul.li> Commit: Jonas Bernoulli <jo...@bernoul.li>
Speed up and simplify listing files in status buffer --- CHANGELOG | 5 +- docs/magit.org | 59 +++++++++++----- docs/magit.texi | 61 +++++++++++----- lisp/magit-status.el | 192 ++++++++++++++++++++------------------------------- test/magit-tests.el | 1 + 5 files changed, 160 insertions(+), 158 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 979de2742db..340c0336110 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,8 +7,6 @@ offered some fallback methods. The presentation of those has been improved. #5253 a08b4dd513 -- [IN PROGRESS] Improve listing of untracked files. - - Added new hook options ~magit-revision-wash-message-hook~ and ~magit-log-wash-summary-hook~, and populate them with new and existing highlighting functions, making easier to remove default highlighting @@ -23,6 +21,9 @@ - Improved commit menu, documentation and implementation details. #5261 +- Speed up listing untracked files in the status buffer, simplify how + the list is configured, and give up on optionally using a tree. + Bug fixes: - When applying a stash, it was not always discovered when the trivial diff --git a/docs/magit.org b/docs/magit.org index 99487e150b1..f9752ea5238 100644 --- a/docs/magit.org +++ b/docs/magit.org @@ -2258,42 +2258,65 @@ hooks and how to customize them. *** Status File List Sections -Its possible to limit the diffs in the current buffer to a certain -directory using ~D = f <DIRECTORY> RET g~. If you do that, then that -that also affects these functions. The diff filter can be used to -limit to multiple files. In that case these functions only respect -the first of the files and only if it is a directory. +These functions honor the buffer's file filter, which can be set using +~D - -~. - Function: magit-insert-untracked-files :: - Maybe insert a list or tree of untracked files. + This function may insert a list of untracked files. Whether it + actually does so, depends on the option described next. - The option ~magit-status-show-untracked-files~ (which see), in - cooperation with the Git variable ~status.showUntrackedFiles~, - control whether and how that is done. +- User Option: magit-status-show-untracked-files :: - If the first element of ~magit-buffer-diff-files~ is a directory, then - limit the list to files below that. The value of that variable can - be set using ~D -- DIRECTORY RET g~. + This option controls whether the above function inserts a list of + untracked files in the status buffer. -While the above function is a member of ~magit-status-section-hook~, -the following functions by default are not. + To disable listing untracked files in a specific repository only, + add the following to ~.dir-locals.el~: + + #+begin_src emacs-lisp + ((magit-status-mode + (magit-status-show-untracked-files . \"no\"))) + #+end_src + + Alternatively (and mostly for historic reasons), it is possible to + use ~git config~ to disable listing files for a specific repository, + using: + + #+begin_src shell-script + git config set --local status.showUntrackedFiles no + #+end_src + + This does *not* override the (if any) local value of this Lisp variable. + It also is not possible to use the Git variable to enable listing files + (in case the global value of this variable is nil). + +- User Option: magit-status-file-list-limit :: + + This option controls many files are listed at most in each section + that lists files in the status buffer. For performance reasons, it + is recommended that you do not increase this limit. + +While the above function is a member of ~magit-status-section-hook~ by +default, the following functions have to be explicitly added by the +user. Because that negatively affects performance, it is recommended +that you don't do that. - Function: magit-insert-tracked-files :: - Insert a tree of tracked files. + Insert a list of tracked files. - Function: magit-insert-ignored-files :: - Insert a tree of ignored files. + Insert a list of ignored files. - Function: magit-insert-skip-worktree-files :: - Insert a tree of skip-worktree files. + Insert a list of skip-worktree files. - Function: magit-insert-assumed-unchanged-files :: - Insert a tree of files that are assumed to be unchanged. + Insert a list of files that are assumed to be unchanged. *** Status Log Sections diff --git a/docs/magit.texi b/docs/magit.texi index 75249101b52..c51b44e2da6 100644 --- a/docs/magit.texi +++ b/docs/magit.texi @@ -2641,41 +2641,64 @@ push-remote yet. @anchor{Status File List Sections} @subsection Status File List Sections -Its possible to limit the diffs in the current buffer to a certain -directory using @code{D = f <DIRECTORY> RET g}. If you do that, then that -that also affects these functions. The diff filter can be used to -limit to multiple files. In that case these functions only respect -the first of the files and only if it is a directory. +These functions honor the buffer's file filter, which can be set using +@code{D - -}. @defun magit-insert-untracked-files -Maybe insert a list or tree of untracked files. +This function may insert a list of untracked files. Whether it +actually does so, depends on the option described next. +@end defun -The option @code{magit-status-show-untracked-files} (which see), in -cooperation with the Git variable @code{status.showUntrackedFiles}, -control whether and how that is done. +@defopt magit-status-show-untracked-files +This option controls whether the above function inserts a list of +untracked files in the status buffer. -If the first element of @code{magit-buffer-diff-files} is a directory, then -limit the list to files below that. The value of that variable can -be set using @code{D -- DIRECTORY RET g}. -@end defun +To disable listing untracked files in a specific repository only, +add the following to @code{.dir-locals.el}: + +@lisp +((magit-status-mode + (magit-status-show-untracked-files . \"no\"))) +@end lisp + +Alternatively (and mostly for historic reasons), it is possible to +use @code{git config} to disable listing files for a specific repository, +using: + +@example +git config set --local status.showUntrackedFiles no +@end example + +This does @strong{not} override the (if any) local value of this Lisp variable. +It also is not possible to use the Git variable to enable listing files +(in case the global value of this variable is nil). +@end defopt + +@defopt magit-status-file-list-limit +This option controls many files are listed at most in each section +that lists files in the status buffer. For performance reasons, it +is recommended that you do not increase this limit. +@end defopt -While the above function is a member of @code{magit-status-section-hook}, -the following functions by default are not. +While the above function is a member of @code{magit-status-section-hook} by +default, the following functions have to be explicitly added by the +user. Because that negatively affects performance, it is recommended +that you don't do that. @defun magit-insert-tracked-files -Insert a tree of tracked files. +Insert a list of tracked files. @end defun @defun magit-insert-ignored-files -Insert a tree of ignored files. +Insert a list of ignored files. @end defun @defun magit-insert-skip-worktree-files -Insert a tree of skip-worktree files. +Insert a list of skip-worktree files. @end defun @defun magit-insert-assumed-unchanged-files -Insert a tree of files that are assumed to be unchanged. +Insert a list of files that are assumed to be unchanged. @end defun @anchor{Status Log Sections} diff --git a/lisp/magit-status.el b/lisp/magit-status.el index 78ed6250f22..d06a32264c1 100644 --- a/lisp/magit-status.el +++ b/lisp/magit-status.el @@ -142,57 +142,35 @@ The functions which respect this option are :group 'magit-status :type 'boolean) -(defcustom magit-status-show-untracked-files nil - "Whether and how to list untracked files in the status buffer. +(defcustom magit-status-show-untracked-files t + "Whether to list untracked files in the status buffer. -This controls the behavior of function `magit-insert-untracked-files'. -If that function is removed from `magit-status-sections-hook', then -untracked files are not shown, regardless of what this option says. +To disable listing untracked files in a specific repository only, add +the following to \".dir-locals.el\": -The behavior can be controled using this option and/or the Git variable -`status.showUntrackedFiles'. The following settings are used in order: + ((magit-status-mode + (magit-status-show-untracked-files . \"no\"))) -1. The buffer-local value of this option. +Alternatively (and mostly for historic reasons), it is possible to use +`git-config' to disable listing files for a specific repository, using: - This can be set in \".dir-locals-2.el\" like so: - ((magit-status-mode - (magit-status-show-untracked-files . \"no\"))) + git config set --local status.showUntrackedFiles no -2. The repository-local value of `status.showUntrackedFiles'. - - This can be set using: - git config --local status.showUntrackedFiles normal - -3. The global value of this option. - - This can be set using the Custom interface or `setq'. - -4. The global value of `status.showUntrackedFiles'. - - This can be set using: - git config --global status.showUntrackedFiles all - -5. The default is \"normal\" and is used if all of the above places - are unspecified or, in the case of the Lisp values, nil. - -The valid non-nil values are: - -- \"no\" - Show no untracked files. - -- \"normal\" - Show top-level untracked files and directories containing - untracked files. The directories can be expanded to reveal the - contained untracked files. +This does *not* override the (if any) local value of this Lisp variable. +It also is not possible to use the Git variable to enable listing files +\(in case the global value of this variable is nil)." + :package-version '(magit . "4.2.1") + :group 'magit-status + :type 'boolean + :safe 'booleanp) -- \"all\" - Immediately show all individual untracked files. This is - potentially expensive and/or overwhelming, and should be avoided." +(defcustom magit-status-file-list-limit 100 + "How many files to list in file list sections in the status buffer. +For performance reasons, it is recommended that you do not +increase this limit." :package-version '(magit . "4.2.1") :group 'magit-status - :type '(choice - (const :tag "Show no untracked files" "no") - (const :tag "Show directories containing untracked files" "normal") - (const :tag "Show individual untracked files" "all") - (const :tag "Use value of status.showUntrackedFiles" nil)) - :safe (lambda (value) (member value '("no" "normal" "all" nil)))) + :type 'natnum) (defcustom magit-status-margin (list nil @@ -755,100 +733,76 @@ remote in alphabetic order." magit-insert-assume-unchanged-files) (defun magit-insert-untracked-files () - "Maybe insert a list or tree of untracked files. + "Maybe insert list of untracked files. -The option `magit-status-show-untracked-files' (which see), in -cooperation with the Git variable `status.showUntrackedFiles', control -whether and how that is done. +List files if `magit-status-show-untracked-files' is non-nil, but also +take the local value of Git variable `status.showUntrackedFiles' into +account. If the Lisp variable has no local value and the local value +of the Git variable is \"no\" or \"false\", then do not insert files. In +all other cases only the Lisp variable matters. -If the first element of `magit-buffer-diff-files' is a directory, then -limit the list to files below that. The value of that variable can be -set using \"D -- DIRECTORY RET g\"." - (let ((show (or (and (local-variable-p 'magit-status-show-untracked-files) +Honor the buffer's file filter, which can be set using \"D - -\"." + (when-let + ((value (or (and (local-variable-p 'magit-status-show-untracked-files) magit-status-show-untracked-files) - (magit-get "--local" "status.showUntrackedFiles") - (default-value 'magit-status-show-untracked-files) - (magit-get "--global" "status.showUntrackedFiles") - "normal"))) - (unless (equal show "no") - (let* ((all (equal show "all")) - (base (car magit-buffer-diff-files)) - (base (and base (file-directory-p base) base))) + (and (not (member + (magit-get "--local" "status.showUntrackedFiles") + '("no" "false"))) + magit-status-show-untracked-files)))) + (if (eq value t) (magit-insert-files 'untracked - (lambda () (magit-untracked-files nil base)) - all))))) + (lambda (args) (magit-untracked-files nil (cdr args)))) + (display-warning 'magit-insert-untracked-files + (format "Value should be a lisp boolean, not %S." value) + :error)))) (defun magit-insert-tracked-files () - "Insert a tree of tracked files. - -If the first element of `magit-buffer-diff-files' is a -directory, then limit the list to files below that. The value -value of that variable can be set using \"D -- DIRECTORY RET g\"." + "Insert a list of tracked files. +Honor the buffer's file filter, which can be set using \"D - -\"." (magit-insert-files 'tracked #'magit-list-files)) (defun magit-insert-ignored-files () - "Insert a tree of ignored files. - -If the first element of `magit-buffer-diff-files' is a -directory, then limit the list to files below that. The value -of that variable can be set using \"D -- DIRECTORY RET g\"." - (magit-insert-files 'ignored (lambda () (magit-ignored-files "--directory")))) + "Insert a list of ignored files. +Honor the buffer's file filter, which can be set using \"D - -\"." + (magit-insert-files 'ignored + (lambda (args) (magit-ignored-files "--directory" args)))) (defun magit-insert-skip-worktree-files () - "Insert a tree of skip-worktree files. - -If the first element of `magit-buffer-diff-files' is a -directory, then limit the list to files below that. The value -of that variable can be set using \"D -- DIRECTORY RET g\"." + "Insert a list of skip-worktree files. +Honor the buffer's file filter, which can be set using \"D - -\"." (magit-insert-files 'skip-worktree #'magit-skip-worktree-files)) (defun magit-insert-assume-unchanged-files () - "Insert a tree of files that are assumed to be unchanged. - -If the first element of `magit-buffer-diff-files' is a -directory, then limit the list to files below that. The value -of that variable can be set using \"D -- DIRECTORY RET g\"." + "Insert a list of files that are assumed to be unchanged. +Honor the buffer's file filter, which can be set using \"D - -\"." (magit-insert-files 'assume-unchanged #'magit-assume-unchanged-files)) -(defvar magit-file-section-indent nil - "Indentation used for simple `file' sections. -If non-nil, this must be a string or character, and is used as -indentation of `file' sections inserted by `magit-insert-*-files'. -`file' sections that are part of diffs are not affected. This is mainly -intended as a workaround for https://github.com/magit/magit/issues/5176, -in which case ?\N{ZERO WIDTH SPACE} is a good value.") - -(defun magit-insert-files (type fn &optional nogroup) - (when-let ((files (funcall fn))) - (let* ((base (car magit-buffer-diff-files)) - (base (and base (file-directory-p base) base)) - (title (symbol-name type))) - (magit-insert-section ((eval type) nil t) - (magit-insert-heading (length files) +(defun magit-insert-files (type fn) + (when-let ((files (funcall fn + (and magit-buffer-diff-files + (cons "--" magit-buffer-diff-files))))) + (magit-insert-section section ((eval type) nil t) + (magit-insert-heading (length files) + (let ((title (symbol-name type))) (format "%c%s files" (capitalize (aref title 0)) - (substring title 1))) - (magit-insert-files-1 files base nogroup) - (insert ?\n))))) - -(defun magit-insert-files-1 (files directory &optional nogroup) - (while (and files (or nogroup - (not directory) - (string-prefix-p directory (car files)))) - (let ((dir (file-name-directory (car files)))) - (if (or nogroup (equal dir directory)) - (let ((file (pop files))) - (magit-insert-section (file file) - (when magit-file-section-indent - (insert magit-file-section-indent)) - (insert (propertize file 'font-lock-face 'magit-filename) ?\n))) - (magit-insert-section (file dir t) - (when magit-file-section-indent - (insert magit-file-section-indent)) - (insert (propertize dir 'file 'magit-filename) ?\n) - (magit-insert-heading) - (setq files (magit-insert-files-1 files dir)))))) - files) + (substring title 1)))) + (magit-insert-section-body + (let ((magit-section-insert-in-reverse t) + (limit magit-status-file-list-limit)) + (while (and files (> limit 0)) + (cl-decf limit) + (let ((file (pop files))) + (magit-insert-section (file file) + (insert (propertize file 'font-lock-face 'magit-filename)) + (insert ?\n)))) + (when files + (magit-insert-section (info) + (insert (propertize + (format "%s files not listed\n" (length files)) + 'face 'warning))))) + (insert ?\n) + (oset section children (nreverse (oref section children))))))) ;;; _ (provide 'magit-status) diff --git a/test/magit-tests.el b/test/magit-tests.el index 4df82d010fd..bb7f661541c 100644 --- a/test/magit-tests.el +++ b/test/magit-tests.el @@ -426,6 +426,7 @@ Enter passphrase for key '/home/user/.ssh/id_rsa': " (defun magit-test-get-section (list file) (magit-status-setup-buffer default-directory) + (magit-section-show-level-4-all) (--first (equal (oref it value) file) (oref (magit-get-section `(,list (status))) children)))