Skip to content

Commit d16eb0c

Browse files
committed
Merge branch 'feature/collect-tags'
2 parents 4f3211e + 08ef973 commit d16eb0c

File tree

3 files changed

+137
-47
lines changed

3 files changed

+137
-47
lines changed

README.org

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# -*- mode: org; mode: org-make-toc -*-
21
* :tags keyword for use-package
32
This library adds =:tags= keyword to =use-package=, which can be used
43
to enable only packages that belong to certain groups.
@@ -63,6 +62,11 @@ You can get a list of all packages in =use-package= forms in a file, or select p
6362
(use-package-tags-select '(programming blog)
6463
:from (expand-file-name "init.el" user-emacs-directory))
6564

65+
;; The :from argument also accepts t, which is evaluated to
66+
;; `use-package-tags-init-files' (default: user-init-file)
67+
(use-package-tags-select '(programming blog)
68+
:from t)
69+
6670
;; You can select all packages by passing t as the query
6771
(use-package-tags-select t)
6872

@@ -89,3 +93,5 @@ The function handles some built-in keywords of =use-package= to exclude packages
8993
- Having an =:if= property that evaluates to nil.
9094

9195
Not all keywords are supported right now, but it can be extended to support more keywords.
96+
97+
This package also provides =use-package-tags-collect-tags= function, which returns a list of all tags in a source.

use-package-tags-test.el

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636
(describe "use-package-tags-select"
3737
(let ((result (use-package-tags-select '(active foo)
38-
:from "./tests/init.el")))
38+
:from "./tests/init.el")))
3939
(it "selects packages without :tags keyword"
4040
(expect (member 'a result) :to-be-truthy))
4141
(it "selects packages with an active keyword"
@@ -61,4 +61,18 @@
6161
:as 'lines)
6262
:to-equal "a\nb\nc\ntest")))
6363

64+
(describe "use-package-tags-collect"
65+
(it "collects tags from a source"
66+
(expect (use-package-tags-collect-tags "./tests/init.el" :sort t)
67+
:to-equal '(active bar foo inactive))))
68+
69+
(describe "use-package-tags--source-buffer-list (private function)"
70+
(describe "When t is given"
71+
(if (file-exists-p (expand-file-name "init.el" user-emacs-directory))
72+
(it "returns use-package-tags-init-files"
73+
(expect (mapcar #'buffer-file-name
74+
(use-package-tags--source-buffer-list t))
75+
:to-equal use-package-tags-init-files))
76+
(xit "returns use-package-tags-init-files (the file does not exist)"))))
77+
6478
(provide 'use-package-tag-test)

use-package-tags.el

Lines changed: 115 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,64 @@ the conventions of use-package."
7979

8080
;;;; Extract package names from an init file
8181

82+
(defcustom use-package-tags-init-files
83+
(list (or user-init-file
84+
(expand-file-name "init.el" user-emacs-directory)))
85+
"List of files to look for package declarations."
86+
:type '(repeat file))
87+
88+
(defun use-package-tags--source-buffer-list (source)
89+
"Return a list of buffers for SOURCE.
90+
91+
If the argument is nil, it returns a list containing the current
92+
buffer.
93+
94+
Alternatively, the argument can be one of the following:
95+
96+
* t for `use-package-tags-init-files'.
97+
* File name.
98+
* Buffer.
99+
* List of files.
100+
* List of buffers."
101+
(cl-labels
102+
((to-buffer
103+
(item)
104+
(cl-etypecase item
105+
(buffer item)
106+
;; (symbol (to-buffer (eval symbol)))
107+
(file-exists (or (find-buffer-visiting item)
108+
(find-file-noselect item)))
109+
(string (error "File does not exist: %s" item)))))
110+
(cl-typecase source
111+
(null (list (current-buffer)))
112+
(symbol (if (eq t source)
113+
(mapcar #'to-buffer use-package-tags-init-files)
114+
;; TODO: Return the variable value when a symbol is given
115+
;; (list (to-buffer source))
116+
(error "Symbol is not accepted: %s" source)))
117+
(list (mapcar #'to-buffer source))
118+
(otherwise (list (to-buffer source))))))
119+
120+
(defsubst use-package-tags--normalize-query (query)
121+
"Normalize QUERY into a list or t."
122+
(cl-etypecase query
123+
(listp query)
124+
(symbolp (or (eq t query)
125+
(list query)))))
126+
127+
(defmacro use-package-tags--with-package-forms (buffers &rest progn)
128+
"In BUFFERS, evaluate PROGN at every `use-package' form."
129+
(declare (indent 1))
130+
`(dolist (buf ,buffers)
131+
(with-current-buffer buf
132+
(save-excursion
133+
(goto-char (point-min))
134+
(while (re-search-forward (rx "(use-package" space) nil t)
135+
(beginning-of-defun-raw)
136+
,@progn
137+
(end-of-defun))))))
138+
139+
;;;###autoload
82140
(cl-defun use-package-tags-select (query &key from installable
83141
(as 'symbols))
84142
"Get a list of packages declared in `use-package' forms.
@@ -91,8 +149,8 @@ at least one tag in the query or have no tags. Alternatively, you
91149
can select all packages declared in the source by specifying t as
92150
the query.
93151
94-
By default, the source is the current buffer.
95-
You can specify a file as the source by setting FROM to its file path.
152+
FROM specifies the source.
153+
See `use-package-tags--source-buffer-list'.
96154
97155
When INSTALLABLE is set to non-nil, it returns a list of packages
98156
available on the Emacs-Mirror. You will need epkg.el for this feature.
@@ -107,52 +165,40 @@ It accepts the following values (the default: symbols):
107165
* lines: a single string joined by newlines."
108166
(declare (indent 1))
109167
(let (alist
110-
(bufs (cl-etypecase from
111-
(null (list (current-buffer)))
112-
(file-exists-p (list (or (find-buffer-visiting from)
113-
(find-file-noselect from))))))
114-
(query (cl-etypecase query
115-
(listp query)
116-
(symbolp (or (eq t query)
117-
(list query))))))
168+
(query (use-package-tags--normalize-query query)))
118169
(cl-labels
119170
((get-keyword (prop rest) (-some->> (member prop rest)
120171
(nth 1))))
121-
(dolist (buf bufs)
122-
(with-current-buffer buf
123-
(save-excursion
124-
(goto-char (point-min))
125-
(while (re-search-forward (rx "(use-package" space) nil t)
126-
(beginning-of-defun-raw)
127-
(let* ((exp (read (current-buffer)))
128-
(name (nth 1 exp))
129-
(disabled (get-keyword :disabled exp))
130-
;; TODO: Handle dependencies
131-
;; (after (get-keyword :after))
132-
;; (requires (get-keyword :requires))
133-
;; TODO: Add support for :when and :unless
134-
(if-expr (get-keyword :if exp))
135-
(tags (get-keyword :tags exp)))
136-
(when (and (not disabled)
137-
(not (and if-expr
138-
(not (eval if-expr))))
139-
;; TODO: :requires keyword
140-
;; (or (not requires)
141-
;; (-all-p (lambda (feature)
142-
;; (assoc feature alist))
143-
;; (cl-etypecase requires
144-
;; (list requires)
145-
;; (symbol (list requires)))))
146-
(or (eq t query)
147-
(not tags)
148-
(cl-intersection tags query)))
149-
(push (list name)
150-
;; TODO: Handle dependencies
151-
;; (list name
152-
;; :after after
153-
;; :requires requires)
154-
alist)))
155-
(end-of-defun))))))
172+
(use-package-tags--with-package-forms
173+
(use-package-tags--source-buffer-list from)
174+
(let* ((exp (read (current-buffer)))
175+
(name (nth 1 exp))
176+
(disabled (get-keyword :disabled exp))
177+
;; TODO: Handle dependencies
178+
;; (after (get-keyword :after))
179+
;; (requires (get-keyword :requires))
180+
;; TODO: Add support for :when and :unless
181+
(if-expr (get-keyword :if exp))
182+
(tags (get-keyword :tags exp)))
183+
(when (and (not disabled)
184+
(not (and if-expr
185+
(not (eval if-expr))))
186+
;; TODO: :requires keyword
187+
;; (or (not requires)
188+
;; (-all-p (lambda (feature)
189+
;; (assoc feature alist))
190+
;; (cl-etypecase requires
191+
;; (list requires)
192+
;; (symbol (list requires)))))
193+
(or (eq t query)
194+
(not tags)
195+
(cl-intersection tags query)))
196+
(push (list name)
197+
;; TODO: Handle dependencies
198+
;; (list name
199+
;; :after after
200+
;; :requires requires)
201+
alist)))))
156202
(cl-labels
157203
((enabled-p
158204
;; The dependency handle is work in progress.
@@ -220,5 +266,29 @@ It accepts the following values (the default: symbols):
220266
(delq nil)
221267
(-uniq))))
222268

269+
;;;###autoload
270+
(cl-defun use-package-tags-collect-tags (source &key sort)
271+
"Collect package tags from a source.
272+
273+
For SOURCE, see `use-package-tags--source-buffer-list'.
274+
275+
If SORT is non-nil, the result will be lexicographically sorted."
276+
(cl-labels
277+
((get-keyword (prop rest) (-some->> (member prop rest)
278+
(nth 1)))
279+
(order (items) (if sort
280+
(cl-sort items #'string< :key #'symbol-name)
281+
items)))
282+
(let (result)
283+
(use-package-tags--with-package-forms
284+
(use-package-tags--source-buffer-list source)
285+
(let* ((exp (read (current-buffer)))
286+
(tags (get-keyword :tags exp)))
287+
(when tags
288+
(push tags result))))
289+
(->> (-flatten-n 1 result)
290+
(cl-remove-duplicates)
291+
(order)))))
292+
223293
(provide 'use-package-tags)
224294
;;; use-package-tags.el ends here

0 commit comments

Comments
 (0)