@@ -2679,15 +2679,18 @@ either a full name or nil, and EMAIL is a valid email address."
2679
2679
(define-key map " i" 'package-menu-mark-install )
2680
2680
(define-key map " U" 'package-menu-mark-upgrades )
2681
2681
(define-key map " r" 'revert-buffer )
2682
- (define-key map (kbd " / k" ) 'package-menu-filter-by-keyword )
2683
- (define-key map (kbd " / n" ) 'package-menu-filter-by-name )
2684
- (define-key map (kbd " / /" ) 'package-menu-clear-filter )
2685
2682
(define-key map " ~" 'package-menu-mark-obsolete-for-deletion )
2686
2683
(define-key map " x" 'package-menu-execute )
2687
2684
(define-key map " h" 'package-menu-quick-help )
2688
2685
(define-key map " H" #'package-menu-hide-package )
2689
2686
(define-key map " ?" 'package-menu-describe-package )
2690
2687
(define-key map " (" #'package-menu-toggle-hiding )
2688
+ (define-key map (kbd " / /" ) 'package-menu-clear-filter )
2689
+ (define-key map (kbd " / a" ) 'package-menu-filter-by-archive )
2690
+ (define-key map (kbd " / k" ) 'package-menu-filter-by-keyword )
2691
+ (define-key map (kbd " / n" ) 'package-menu-filter-by-name )
2692
+ (define-key map (kbd " / s" ) 'package-menu-filter-by-status )
2693
+ (define-key map (kbd " / v" ) 'package-menu-filter-by-version )
2691
2694
map)
2692
2695
" Local keymap for `package-menu-mode' buffers." )
2693
2696
@@ -2714,8 +2717,11 @@ either a full name or nil, and EMAIL is a valid email address."
2714
2717
2715
2718
" --"
2716
2719
(" Filter Packages"
2720
+ [" Filter by Archive" package-menu-filter-by-archive :help " Filter packages by archive" ]
2717
2721
[" Filter by Keyword" package-menu-filter-by-keyword :help " Filter packages by keyword" ]
2718
2722
[" Filter by Name" package-menu-filter-by-name :help " Filter packages by name" ]
2723
+ [" Filter by Status" package-menu-filter-by-status :help " Filter packages by status" ]
2724
+ [" Filter by Version" package-menu-filter-by-version :help " Filter packages by version" ]
2719
2725
[" Clear Filter" package-menu-clear-filter :help " Clear package list filter" ])
2720
2726
2721
2727
[" Hide by Regexp" package-menu-hide-package :help " Permanently hide all packages matching a regexp" ]
@@ -3021,22 +3027,31 @@ When none are given, the package matches."
3021
3027
found)
3022
3028
t ))
3023
3029
3024
- (defun package-menu--generate (remember-pos packages &optional keywords )
3025
- " Populate the Package Menu.
3030
+ (defun package-menu--display (remember-pos suffix )
3031
+ " Display the Package Menu.
3026
3032
If REMEMBER-POS is non-nil, keep point on the same entry.
3033
+
3034
+ If SUFFIX is non-nil, append that to \" Package\" for the first
3035
+ column in the header line."
3036
+ (setf (car (aref tabulated-list-format 0 ))
3037
+ (if suffix
3038
+ (concat " Package[" suffix " ]" )
3039
+ " Package" ))
3040
+ (tabulated-list-init-header )
3041
+ (tabulated-list-print remember-pos))
3042
+
3043
+ (defun package-menu--generate (remember-pos &optional packages keywords )
3044
+ " Populate and display the Package Menu.
3027
3045
PACKAGES should be t, which means to display all known packages,
3028
3046
or a list of package names (symbols) to display.
3029
3047
3030
3048
With KEYWORDS given, only packages with those keywords are
3031
3049
shown."
3032
3050
(package-menu--refresh packages keywords)
3033
- (setf (car (aref tabulated-list-format 0 ))
3034
- (if keywords
3035
- (let ((filters (mapconcat #'identity keywords " ," )))
3036
- (concat " Package[" filters " ]" ))
3037
- " Package" ))
3038
- (tabulated-list-init-header )
3039
- (tabulated-list-print remember-pos))
3051
+ (package-menu--display remember-pos
3052
+ (when keywords
3053
+ (let ((filters (mapconcat #'identity keywords " ," )))
3054
+ (concat " Package[" filters " ]" )))))
3040
3055
3041
3056
(defun package-menu--print-info (pkg )
3042
3057
" Return a package entry suitable for `tabulated-list-entries' .
@@ -3673,45 +3688,160 @@ shown."
3673
3688
(select-window win)
3674
3689
(switch-to-buffer buf))))
3675
3690
3691
+ (defun package-menu--filter-by (predicate suffix )
3692
+ " Filter \" *Packages*\" buffer by PREDICATE and add SUFFIX to header.
3693
+ PREDICATE is a function which will be called with one argument, a
3694
+ `package-desc' object, and returns t if that object should be
3695
+ listed in the Package Menu.
3696
+
3697
+ SUFFIX is passed on to `package-menu--display' and is added to
3698
+ the header line of the first column."
3699
+ ; ; Update `tabulated-list-entries' so that it contains all
3700
+ ; ; packages before searching.
3701
+ (package-menu--refresh t nil )
3702
+ (let (found-entries)
3703
+ (dolist (entry tabulated-list-entries)
3704
+ (when (funcall predicate (car entry))
3705
+ (push entry found-entries)))
3706
+ (if found-entries
3707
+ (progn
3708
+ (setq tabulated-list-entries found-entries)
3709
+ (package-menu--display t suffix))
3710
+ (user-error " No packages found" ))))
3711
+
3712
+ (defun package-menu-filter-by-archive (archive )
3713
+ " Filter the \" *Packages*\" buffer by ARCHIVE.
3714
+ Display only packages from package archive ARCHIVE.
3715
+
3716
+ When called interactively, prompt for ARCHIVE, which can be a
3717
+ comma-separated string. If ARCHIVE is empty, show all packages.
3718
+
3719
+ When called from Lisp, ARCHIVE can be a string or a list of
3720
+ strings. If ARCHIVE is nil or the empty string, show all
3721
+ packages."
3722
+ (interactive (list (completing-read-multiple
3723
+ " Filter by archive (comma separated): "
3724
+ (mapcar #'car package-archives))))
3725
+ (package--ensure-package-menu-mode)
3726
+ (let ((re (if (listp archive)
3727
+ (regexp-opt archive)
3728
+ archive)))
3729
+ (package-menu--filter-by (lambda (pkg-desc )
3730
+ (let ((pkg-archive (package-desc-archive pkg-desc)))
3731
+ (and pkg-archive
3732
+ (string-match-p re pkg-archive))))
3733
+ (concat " archive:" (if (listp archive)
3734
+ (string-join archive " ," )
3735
+ archive)))))
3736
+
3676
3737
(defun package-menu-filter-by-keyword (keyword )
3677
3738
" Filter the \" *Packages*\" buffer by KEYWORD.
3678
- Show only those items that relate to the specified KEYWORD.
3679
-
3680
- KEYWORD can be a string or a list of strings. If it is a list, a
3681
- package will be displayed if it matches any of the keywords .
3682
- Interactively, it is a list of strings separated by commas.
3683
-
3684
- KEYWORD can also be used to filter by status or archive name by
3685
- using keywords like \" arc:gnu \" and \" status:available \" .
3686
- Statuses available include \" incompat \" , \" available \" ,
3687
- \" built-in \" and \" installed \" . "
3688
- ( interactive
3689
- ( list ( completing-read-multiple
3690
- " Keywords (comma separated): " ( package-all-keywords ) )))
3739
+ Display only packages with specified KEYWORD.
3740
+
3741
+ When called interactively, prompt for KEYWORD, which can be a
3742
+ comma-separated string. If KEYWORD is empty, show all packages .
3743
+
3744
+ When called from Lisp, KEYWORD can be a string or a list of
3745
+ strings. If KEYWORD is nil or the empty string, show all
3746
+ packages. "
3747
+ ( interactive ( list ( completing-read-multiple
3748
+ " Keywords (comma separated): "
3749
+ ( package-all-keywords ))))
3750
+ ( when ( stringp keyword)
3751
+ ( setq keyword ( list keyword )))
3691
3752
(package--ensure-package-menu-mode)
3692
- (package-show-package-list t (if (stringp keyword)
3693
- (list keyword)
3694
- keyword)))
3753
+ (if (not keyword)
3754
+ (package-menu--generate t t )
3755
+ (package-menu--filter-by (lambda (pkg-desc )
3756
+ (package--has-keyword-p pkg-desc keyword))
3757
+ (concat " keyword:" (string-join keyword " ," )))))
3695
3758
3696
3759
(defun package-menu-filter-by-name (name )
3697
- " Filter the \" *Packages*\" buffer by NAME.
3698
- Show only those items whose name matches the regular expression
3699
- NAME. If NAME is nil or the empty string, show all packages."
3700
- (interactive (list (read-from-minibuffer " Filter by name (regexp): " )))
3760
+ " Filter the \" *Packages*\" buffer by NAME regexp.
3761
+ Display only packages with name that matches regexp NAME.
3762
+
3763
+ When called interactively, prompt for NAME.
3764
+
3765
+ If NAME is nil or the empty string, show all packages."
3766
+ (interactive (list (read-regexp " Filter by name (regexp)" )))
3701
3767
(package--ensure-package-menu-mode)
3702
3768
(if (or (not name) (string-empty-p name))
3703
- (package-show-package-list t nil )
3704
- ; ; Update `tabulated-list-entries' so that it contains all
3705
- ; ; packages before searching.
3706
- (package-menu--refresh t nil )
3707
- (let (matched)
3708
- (dolist (entry tabulated-list-entries)
3709
- (let* ((pkg-name (package-desc-name (car entry))))
3710
- (when (string-match name (symbol-name pkg-name))
3711
- (push pkg-name matched))))
3712
- (if matched
3713
- (package-show-package-list matched nil )
3714
- (user-error " No packages found" )))))
3769
+ (package-menu--generate t t )
3770
+ (package-menu--filter-by (lambda (pkg-desc )
3771
+ (string-match-p name (symbol-name
3772
+ (package-desc-name pkg-desc))))
3773
+ (format " name:%s " name))))
3774
+
3775
+ (defun package-menu-filter-by-status (status )
3776
+ " Filter the \" *Packages*\" buffer by STATUS.
3777
+ Display only packages with specified STATUS.
3778
+
3779
+ When called interactively, prompt for STATUS, which can be a
3780
+ comma-separated string. If STATUS is empty, show all packages.
3781
+
3782
+ When called from Lisp, STATUS can be a string or a list of
3783
+ strings. If STATUS is nil or the empty string, show all
3784
+ packages."
3785
+ (interactive (list (completing-read " Filter by status: "
3786
+ '(" avail-obso"
3787
+ " available"
3788
+ " built-in"
3789
+ " dependency"
3790
+ " disabled"
3791
+ " external"
3792
+ " held"
3793
+ " incompat"
3794
+ " installed"
3795
+ " new"
3796
+ " unsigned" ))))
3797
+ (package--ensure-package-menu-mode)
3798
+ (if (or (not status) (string-empty-p status))
3799
+ (package-menu--generate t t )
3800
+ (package-menu--filter-by (lambda (pkg-desc )
3801
+ (string-match-p status (package-desc-status pkg-desc)))
3802
+ (format " status:%s " status))))
3803
+
3804
+ (defun package-menu-filter-by-version (version predicate )
3805
+ " Filter the \" *Packages*\" buffer by VERSION and PREDICATE.
3806
+ Display only packages with a matching version.
3807
+
3808
+ When called interactively, prompt for one of the qualifiers `<' ,
3809
+ `>' or `=' , and a package version. Show only packages that has a
3810
+ lower (`<' ), equal (`=' ) or higher (`>' ) version than the
3811
+ specified one.
3812
+
3813
+ When called from Lisp, VERSION should be a version string and
3814
+ PREDICATE should be the symbol `=' , `<' or `>' .
3815
+
3816
+ If VERSION is nil or the empty string, show all packages."
3817
+ (interactive (let ((choice (intern
3818
+ (char-to-string
3819
+ (read-char-choice
3820
+ " Filter by version? [Type =, <, > or q] "
3821
+ '(?< ?> ?= ?q ))))))
3822
+ (if (eq choice 'q )
3823
+ '(quit nil )
3824
+ (list (read-from-minibuffer
3825
+ (concat " Filter by version ("
3826
+ (pcase choice
3827
+ ('= " = equal to" )
3828
+ ('< " < less than" )
3829
+ ('> " > greater than" ))
3830
+ " ): " ))
3831
+ choice))))
3832
+ (unless (equal predicate 'quit )
3833
+ (if (or (not version) (string-empty-p version))
3834
+ (package-menu--generate t t )
3835
+ (package-menu--filter-by
3836
+ (let ((fun (pcase predicate
3837
+ ('= 'version-list-= )
3838
+ ('< 'version-list-< )
3839
+ ('> '(lambda (a b) (not (version-list-<= a b))))
3840
+ (_ (error " Unknown predicate: %s " predicate))))
3841
+ (ver (version-to-list version)))
3842
+ (lambda (pkg-desc )
3843
+ (funcall fun (package-desc-version pkg-desc) ver)))
3844
+ (format " versions:%s%s " predicate version)))))
3715
3845
3716
3846
(defun package-menu-clear-filter ()
3717
3847
" Clear any filter currently applied to the \" *Packages*\" buffer."
@@ -3760,6 +3890,7 @@ The return value is a string (or nil in case we can't find it)."
3760
3890
(or (lm-header " package-version" )
3761
3891
(lm-header " version" )))))))))
3762
3892
3893
+
3763
3894
; ;;; Quickstart: precompute activation actions for faster start up.
3764
3895
3765
3896
; ; Activating packages via `package-initialize' is costly: for N installed
0 commit comments