branch: externals/denote-search commit 988ac9d7a8525b520832d98c764a18b6d53d8122 Author: Lucas Quintana <lm...@protonmail.com> Commit: Lucas Quintana <lm...@protonmail.com>
Allow filtering by multiple keywords at once --- README.org | 14 +++++----- denote-search.el | 80 ++++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 59 insertions(+), 35 deletions(-) diff --git a/README.org b/README.org index f066572f9d..32ed713f4b 100644 --- a/README.org +++ b/README.org @@ -261,17 +261,19 @@ It's possible that you don't want to start a new search, but rather to search something on the curated file list you got. See [[*focused search][focused search]]. -#+findex: denote-search-exclude-files-with-keyword -#+findex: denote-search-only-include-files-with-keyword -Filtering by keyword is such a common operation that two special +#+findex: denote-search-exclude-files-with-keywords +#+findex: denote-search-only-include-files-with-keywords +Filtering by keywords is such a common operation that two special commands exist just for that: ~X~ -(~denote-search-exclude-files-with-keyword~) and ~I~ -(~denote-search-only-include-files-with-keyword~). They are +(~denote-search-exclude-files-with-keywords~) and ~I~ +(~denote-search-only-include-files-with-keywords~). They are equivalent to calling its regular counterparts and issuing a word with a leading underscore; however, they also offer completion for available keywords (using ~denote-keywords~, so its actual behaviour is governed by the variables ~denote-infer-keywords~ and -~denote-known-keywords~). +~denote-known-keywords~). But the main advantage is that they allow +issuing multiple keywords at once, separated by commas (or whatever +the value of ~crm-separator~ is, which should be a comma). History is available when filtering. Press ~M-p~ (~previous-history-element~) to view past queries. This history is diff --git a/denote-search.el b/denote-search.el index 30126d97ea..a85a2d95c4 100644 --- a/denote-search.el +++ b/denote-search.el @@ -136,16 +136,20 @@ non-nil." "Only include file names matching: ") nil 'denote-search-file-regexp-history))) -(defun denote-search-keyword-prompt (&optional include) - "Prompt for a keyword in the minibuffer, with completion. +(defun denote-search-keywords-prompt (&optional include) + "Prompt for keywords in the minibuffer, with completion. -The prompt assumes the user wants to exclude the keyword, unless INCLUDE -is non-nil." - (list (completing-read - (if (not include) - "Exclude files with keyword: " - "Only include files with keyword: ") - (denote-keywords) nil t nil 'denote-keyword-history))) +Keywords are read using `completing-read-multiple'. + +The prompt assumes the user wants to exclude the keywords, unless +INCLUDE is non-nil." + (list + (delete-dups + (completing-read-multiple + (if (not include) + "Exclude files with keywords: " + "Only include files with keywords: ") + (denote-keywords) nil t nil 'denote-keyword-history)))) (defun denote-search-query-prompt (&optional type) "Prompt for a search query in the minibuffer. @@ -355,11 +359,22 @@ RET’. Internally, this works by generating a new call to `denote-search' with the same QUERY as the last one, but with a restricted SET gotten from -checking REGEXP against last matched files." +checking REGEXP against last matched files. + +When called from Lisp, REGEXP can be a list; in that case, it should be +a list of fixed strings (NOT regexps) to check against last matched +files. Files that match any of the strings get excluded. Internally, +the list is processed using `regexp-opt', which see. For an example of +this usage, see `denote-search-exclude-files-with-keywords'." (interactive (denote-search-file-regexp-prompt)) (let (final-files) (dolist (file denote-search--last-files) - (unless (string-match regexp file) + (unless (string-match + ;; Support list of strings as REGEXP + (if (listp regexp) + (regexp-opt regexp) + regexp) + file) (push file final-files))) (if final-files (denote-search denote-search--last-query final-files) @@ -368,33 +383,40 @@ checking REGEXP against last matched files." (defun denote-search-only-include-files (regexp) "Exclude file names not matching REGEXP from current `denote-search' buffer. -See also `denote-search-exlude-files'." +See `denote-search-exlude-files' for details, including the behaviour +when REGEXP is a list." (interactive (denote-search-file-regexp-prompt :include)) (let (final-files) (dolist (file denote-search--last-files) - (when (string-match regexp file) + (when (string-match + ;; Support list of strings as REGEXP + (if (listp regexp) + (regexp-opt regexp) + regexp) + file) (push file final-files))) (if final-files (denote-search denote-search--last-query final-files) (user-error "No remaining files when applying that filter")))) -(defun denote-search-exclude-files-with-keyword (keyword) - "Exclude files with KEYWORD from current `denote-search' buffer. +(defun denote-search-exclude-files-with-keywords (keywords) + "Exclude files with KEYWORDS from current `denote-search' buffer. + +KEYWORDS should be a list of keywords (without underscore). -This is equivalent to passing the argument \"_KEYWORD\" to -`denote-search-exclude-files'. This command, however, offers completion -for available keywords." - (interactive (denote-search-keyword-prompt)) - (denote-search-exclude-files (concat "_" keyword))) +Interactively, KEYWORDS are read from the minibuffer using +`completing-read-multiple', which see." + (interactive (denote-search-keywords-prompt)) + (denote-search-exclude-files + (mapcar (lambda (kw) (concat "_" kw)) keywords))) -(defun denote-search-only-include-files-with-keyword (keyword) - "Exclude files without KEYWORD from current `denote-search' buffer. +(defun denote-search-only-include-files-with-keywords (keywords) + "Exclude files without KEYWORDS from current `denote-search' buffer. -This is equivalent to passing the argument \"_KEYWORD\" to -`denote-search-only-include-files'. This command, however, offers -completion for available keywords." - (interactive (denote-search-keyword-prompt :include)) - (denote-search-only-include-files (concat "_" keyword))) +See `denote-search-exclude-files-with-keywords' for details." + (interactive (denote-search-keywords-prompt :include)) + (denote-search-only-include-files + (mapcar (lambda (kw) (concat "_" kw)) keywords))) ;;;; Keymap and mode definition: @@ -409,8 +431,8 @@ completion for available keywords." "v" #'outline-cycle "x" #'denote-search-exclude-files "i" #'denote-search-only-include-files - "X" #'denote-search-exclude-files-with-keyword - "I" #'denote-search-only-include-files-with-keyword) + "X" #'denote-search-exclude-files-with-keywords + "I" #'denote-search-only-include-files-with-keywords) (define-minor-mode denote-search-mode "Minor mode enabled in the buffer generated by `denote-search'.