diff options
author | Carsten Dominik <carsten.dominik@gmail.com> | 2019-09-09 07:36:06 +0200 |
---|---|---|
committer | Carsten Dominik <carsten.dominik@gmail.com> | 2019-09-09 07:36:06 +0200 |
commit | 6f32e7af88805441c1261e16f729172639d8b3fa (patch) | |
tree | 0d277a66966636a2b416fbcb5a5482ea1a5b5dfd | |
parent | 8919181c127089c4f1aa2f1d1470d4317ad1ebb2 (diff) | |
parent | 7ac2707658b722eed5fe5d54c7389faad49e0586 (diff) | |
download | org-mode-6f32e7af88805441c1261e16f729172639d8b3fa.tar.gz |
Merge branch 'improve-consistency-in-filter-commands'
-rw-r--r-- | doc/org-manual.org | 82 | ||||
-rw-r--r-- | lisp/org-agenda.el | 200 |
2 files changed, 149 insertions, 133 deletions
diff --git a/doc/org-manual.org b/doc/org-manual.org index 6abf204..f2f059e 100644 --- a/doc/org-manual.org +++ b/doc/org-manual.org @@ -8928,12 +8928,10 @@ If you would like to have a special category for a single entry or a (sub)tree, give the entry a =CATEGORY= property with the special category you want to apply as the value. -The display in the agenda buffer looks best if the category is not -longer than 10 characters. - #+vindex: org-agenda-category-icon-alist -You can set up icons for category by customizing the -~org-agenda-category-icon-alist~ variable. +The display in the agenda buffer looks best if the category is not +longer than 10 characters. You can set up icons for category by +customizing the ~org-agenda-category-icon-alist~ variable. *** Time-of-day specifications :PROPERTIES: @@ -9039,20 +9037,16 @@ the estimated effort of an entry (see [[*Effort Estimates]]). #+vindex: org-agenda-tag-filter-preset #+vindex: org-agenda-effort-filter-preset #+vindex: org-agenda-regexp-filter-preset -Agenda built-in or customized commands are statically defined. Agenda -filters and limits provide two ways of narrowing down the list of -agenda entries. +Agenda built-in or custom commands are statically defined. Agenda +filters and limits allow to flexibly narrow down the list of agenda +entries. -Filters only change the visibility of items, are very fast and are +/Filters/ only change the visibility of items, are very fast and are mostly used interactively[fn:96]. You can switch quickly between -different filters without having to recreate the agenda. If creating -the agenda seems slow, one solution would be to create a view that -contains everything you might want to work on for a while, and then -use filtering to drill down. - -Limits on the other hand take effect before the agenda buffer is -populated, so they are mostly useful when defined as local variables -within custom agenda commands. +different filters without having to recreate the agenda. /Limits/ on +the other hand take effect before the agenda buffer is populated, so +they are mostly useful when defined as local variables within custom +agenda commands. **** Filtering in the agenda :PROPERTIES: @@ -9068,7 +9062,11 @@ within custom agenda commands. The general filtering command is ~org-agenda-filter~, bound to {{{kbd(/)}}}. Before we introduce it, we describe commands for -individual filter types. +individual filter types. All filtering commands handle prefix +arguments in the same way: A single {{{kbd(C-u)}}} prefix negates the +filter, so it removes lines selected by the filter. A double prefix +adds the new filter condition to the one(s) already in place, so +filter elements are accumulated. - {{{kbd(\)}}} (~org-agenda-filter-by-tag~) :: @@ -9078,37 +9076,32 @@ individual filter types. Pressing {{{kbd(TAB)}}} at that prompt offers completion to select a tag, including any tags that do not have a selection character. The command then hides all entries that do not contain or inherit this - tag. Call the command repeatedly to add several tags to the - filter. When called with prefix argument, remove the entries that - /do/ have the tag. Pressing {{{kbd(+)}}} or {{{kbd(-)}}} at the - prompt also switches between filtering for and against the next tag. - {{{kbd(\)}}} at the prompt turns off the filter and shows any hidden - entries. - + tag. Pressing {{{kbd(+)}}} or {{{kbd(-)}}} at the prompt switches + between filtering for and against the next tag. To clear the + filter, press {{{kbd(\)}}} twice (once to call the command again, + and once at the prompt). - {{{kbd(<)}}} (~org-agenda-filter-by-category~) :: #+findex: org-agenda-filter-by-category Filter by category of the line at point, and show only entries with - this category. Pressing {{{kbd(<)}}} again removes this filter. - When called with a prefix argument exclude the category of the item - at point from the agenda. + this category. When called with a prefix argument, hide all entries + with the category at point. To clear the filter, call this command + again by pressing {{{kbd(<)}}}. - {{{kbd(=)}}} (~org-agenda-filter-by-regexp~) :: #+findex: org-agenda-filter-by-regexp Filter the agenda view by a regular expression: only show agenda - entries matching the regular expression the user entered. When - called with a prefix argument, it filters /out/ entries matching the - regexp. Called in a regexp-filtered agenda view, remove the filter, - unless there are two universal prefix arguments, in which case - filters are accumulated. + entries matching the regular expression the user entered. To clear + the filter, call the command again by pressing {{{kbd(=)}}}. - {{{kbd(_)}}} (~org-agenda-filter-by-effort~) :: #+findex: org-agenda-filter-by-effort - Filter the agenda view with respect to effort estimates. You first - need to set up allowed efforts globally, for example + Filter the agenda view with respect to effort estimates, so select + tasks that take the right amount of time. You first need to set up + a list of efforts globally, for example #+begin_src emacs-lisp (setq org-global-properties @@ -9123,18 +9116,16 @@ individual filter types. restricts to entries with effort smaller-or-equal, equal, or larger-or-equal than the selected value. For application of the operator, entries without a defined effort are treated according to - the value of ~org-sort-agenda-noeffort-is-high~. - - When called with a prefix argument, it removes entries matching the - condition. With two universal prefix arguments, it clears effort - filters, which can be accumulated. + the value of ~org-sort-agenda-noeffort-is-high~. To clear the + filter, press {{{kbd(_)}}} twice (once to call the command again, + and once at the first prompt). - {{{kbd(^)}}} (~org-agenda-filter-by-top-headline~) :: #+findex: org-agenda-filter-by-top-headline Filter the current agenda view and only display items that fall - under the same top-level headline as the current entry. Press - {{{kbd(^)}}} again to turn this filter off. + under the same top-level headline as the current entry. To clear + the filter, call this command again by pressing {{{kbd(^)}}}. - {{{kbd(/)}}} (~org-agenda-filter~) :: @@ -9155,9 +9146,10 @@ individual filter types. (tags will take priority). If you reply to the prompt with the empty string, all filtering is removed. If a filter is specified, it replaces all current filters. But if you call the command with a - prefix argument, or if you add an additional `+' (e.g. `++work') to - the front of the string, the new filter elements are added to the - active ones. + double prefix argument, or if you add an additional `+' + (e.g. `++work') to the front of the string, the new filter elements + are added to the active ones. A single prefix argument applies the + entire filter in a negative sense. - {{{kbd(|)}}} (~org-agenda-filter-remove-all~) :: diff --git a/lisp/org-agenda.el b/lisp/org-agenda.el index d539dbb..a4ad649 100644 --- a/lisp/org-agenda.el +++ b/lisp/org-agenda.el @@ -7468,8 +7468,8 @@ With a prefix argument, do so in all agenda buffers." (defun org-agenda-filter-by-category (strip) "Filter lines in the agenda buffer that have a specific category. The category is that of the current line. -Without prefix argument, keep only the lines of that category. -With a prefix argument, exclude the lines of that category." +With a `\\[universal-argument]' prefix argument, exclude the lines of that category. +When there is already a category filter in place, this command removes the filter." (interactive "P") (if (and org-agenda-filtered-by-category org-agenda-category-filter) @@ -7499,7 +7499,8 @@ search from." (defvar org-agenda-filtered-by-top-headline nil) (defun org-agenda-filter-by-top-headline (strip) "Keep only those lines that are descendants from the same top headline. -The top headline is that of the current line." +The top headline is that of the current line. With prefix arg STRIP, hide +all lines of the category at point." (interactive "P") (if org-agenda-filtered-by-top-headline (progn @@ -7511,77 +7512,85 @@ The top headline is that of the current line." (error "No top-level headline at point"))))) (defvar org-agenda-regexp-filter nil) -(defun org-agenda-filter-by-regexp (strip) - "Filter agenda entries by regular expressions. - -With one prefix argument, filter out entries matching the regexp. -If there is already a regexp filter, remove it unless called with -two prefix arguments." +(defun org-agenda-filter-by-regexp (strip-or-accumulate) + "Filter agenda entries by a regular expressions. +You will be prompted for the regular expresssion, and the agenda +view will only show entires that are matched by that expression. + +With one `\\[universal-argument]' prefix argument, hide entries matching the regexp. +When there is already a regexp filter active, this command removed the +filter. However, with two `\\[universal-argument]' prefix arguments, add a new condition to +an already existing regexp filter." (interactive "P") - (cond - ((and org-agenda-regexp-filter (not (equal strip '(16)))) - (org-agenda-filter-show-all-re) - (message "Regexp filter removed")) - (t (let ((flt (concat (if (equal strip '(4)) "-" "+") - (read-from-minibuffer - (if (equal strip '(4)) - "Filter out entries matching regexp: " - "Narrow to entries matching regexp: "))))) - (push flt org-agenda-regexp-filter) - (org-agenda-filter-apply org-agenda-regexp-filter 'regexp))))) - + (let* ((strip (equal strip-or-accumulate '(4))) + (accumulate (equal strip-or-accumulate '(16)))) + (cond + ((and org-agenda-regexp-filter (not accumulate)) + (org-agenda-filter-show-all-re) + (message "Regexp filter removed")) + (t (let ((flt (concat (if strip "-" "+") + (read-from-minibuffer + (if strip + "Hide entries matching regexp: " + "Narrow to entries matching regexp: "))))) + (push flt org-agenda-regexp-filter) + (org-agenda-filter-apply org-agenda-regexp-filter 'regexp)))))) + (defvar org-agenda-effort-filter nil) -(defun org-agenda-filter-by-effort (strip) +(defun org-agenda-filter-by-effort (strip-or-accumulate) "Filter agenda entries by effort. -With no prefix argument, keep entries matching the effort condition. -With one prefix argument, filter out entries matching the condition. -With two prefix arguments, remove the effort filters." +With no `\\[universal-argument]' prefix argument, keep entries matching the effort condition. +With one `\\[universal-argument]' prefix argument, filter out entries matching the condition. +With two `\\[universal-argument]' prefix arguments, add a second condition to the existing filter. +This last option is in practice not very useful, but it is available for +consistency with the other filter commands." (interactive "P") - (cond - ((member strip '(nil 4)) - (let* ((efforts (split-string - (or (cdr (assoc (concat org-effort-property "_ALL") - org-global-properties)) - "0 0:10 0:30 1:00 2:00 3:00 4:00 5:00 6:00 7:00"))) - ;; XXX: the following handles only up to 10 different - ;; effort values. - (allowed-keys (if (null efforts) nil - (mapcar (lambda (n) (mod n 10)) ;turn 10 into 0 - (number-sequence 1 (length efforts))))) - (op nil)) - (while (not (memq op '(?< ?> ?= ?_))) - (setq op (read-char-exclusive "Effort operator? (> = or <) or press `_' again to remove filter"))) - ;; Select appropriate duration. Ignore non-digit characters. - (if (eq op ?_) - (progn - (org-agenda-filter-show-all-effort) - (message "Effort filter removed")) - (let ((prompt - (apply #'format - (concat "Effort %c " - (mapconcat (lambda (s) (concat "[%d]" s)) - efforts - " ")) - op allowed-keys)) - (eff -1)) - (while (not (memq eff allowed-keys)) - (message prompt) - (setq eff (- (read-char-exclusive) 48))) - (setq org-agenda-effort-filter - (list (concat (if strip "-" "+") - (char-to-string op) - ;; Numbering is 1 2 3 ... 9 0, but we want - ;; 0 1 2 ... 8 9. - (nth (mod (1- eff) 10) efforts))))) - (org-agenda-filter-apply org-agenda-effort-filter 'effort)))) - (t (org-agenda-filter-show-all-effort) - (message "Effort filter removed")))) - - -(defun org-agenda-filter (&optional keep) + (let* ((efforts (split-string + (or (cdr (assoc (concat org-effort-property "_ALL") + org-global-properties)) + "0 0:10 0:30 1:00 2:00 3:00 4:00 5:00 6:00 7:00"))) + ;; XXX: the following handles only up to 10 different + ;; effort values. + (allowed-keys (if (null efforts) nil + (mapcar (lambda (n) (mod n 10)) ;turn 10 into 0 + (number-sequence 1 (length efforts))))) + (keep (equal strip-or-accumulate '(16))) + (negative (equal strip-or-accumulate '(4))) + (current org-agenda-effort-filter) + (op nil)) + (while (not (memq op '(?< ?> ?= ?_))) + (setq op (read-char-exclusive + "Effort operator? (> = or <) or press `_' again to remove filter"))) + ;; Select appropriate duration. Ignore non-digit characters. + (if (eq op ?_) + (progn + (org-agenda-filter-show-all-effort) + (message "Effort filter removed")) + (let ((prompt + (apply #'format + (concat "Effort %c " + (mapconcat (lambda (s) (concat "[%d]" s)) + efforts + " ")) + op allowed-keys)) + (eff -1)) + (while (not (memq eff allowed-keys)) + (message prompt) + (setq eff (- (read-char-exclusive) 48))) + (org-agenda-filter-show-all-effort) + (setq org-agenda-effort-filter + (append + (list (concat (if negative "-" "+") + (char-to-string op) + ;; Numbering is 1 2 3 ... 9 0, but we want + ;; 0 1 2 ... 8 9. + (nth (mod (1- eff) 10) efforts))) + (if keep current nil))) + (org-agenda-filter-apply org-agenda-effort-filter 'effort))))) + + +(defun org-agenda-filter (&optional strip-or-accumulate) "Prompt for a general filter string and apply it to the agenda. -The new filter replaces all existing elements. When called with a -prefix arg KEEP, add the new elements to the existing filter. The string may contain filter elements like @@ -7604,14 +7613,17 @@ values is offered. Since the syntax for categories and tags is identical there should be no overlap between categoroes and tags. If there is, tags get priority. -Instead of using the prefix argument to add to the current filter -set, you can also add an additional leading `+' to filter string, -like `+-John'. +A single `\\[universal-argument]' prefix arg STRIP-OR-ACCUMULATE will negate the +entire filter, which can be useful in connection with the prompt history. -With a double prefix argument, execute the computed filtering defined in +A double `\\[universal-argument] \\[universal-argument]' prefix arg will add the new filter elements to the +existing ones. A shortcut for this is to add an additional `+' at the +beginning of the string, like `+-John'. + +With a triple prefix argument, execute the computed filtering defined in the variable `org-agenda-auto-exclude-function'." (interactive "P") - (if (equal keep '(16)) + (if (equal strip-or-accumulate '(64)) ;; Execute the auto-exclude action (if (not org-agenda-auto-exclude-function) (user-error "`org-agenda-auto-exclude-function' is undefined") @@ -7626,11 +7638,15 @@ the variable `org-agenda-auto-exclude-function'." ;; Prompt for a filter and act (let* ((tag-list (org-agenda-get-represented-tags)) (category-list (org-agenda-get-represented-categories)) - (f-string (completing-read "Filter [+cat-tag<0:10-/regexp/]: " - 'org-agenda-filter-completion-function)) + (negate (equal strip-or-accumulate '(4))) + (f-string (completing-read + (concat + (if negate "Negative filter" "Filter") + " [+cat-tag<0:10-/regexp/]: ") + 'org-agenda-filter-completion-function)) (keep (or (if (string-match "^+[-+]" f-string) (progn (setq f-string (substring f-string 1)) t)) - keep)) + (equal strip-or-accumulate '(16)))) (fc (if keep org-agenda-category-filter)) (ft (if keep org-agenda-tag-filter)) (fe (if keep org-agenda-effort-filter)) @@ -7638,6 +7654,8 @@ the variable `org-agenda-auto-exclude-function'." pm s) (while (string-match "^[ \t]*\\([-+]\\)?\\(\\([^-+<>=/ \t]+\\)\\|\\([<>=][0-9:]+\\)\\|\\(/\\([^/]+\\)/?\\)\\)" f-string) (setq pm (if (match-beginning 1) (match-string 1 f-string) "+")) + (when negate + (setq pm (if (equal pm "+") "-" "+"))) (cond ((match-beginning 3) ;; category or tag @@ -7648,7 +7666,7 @@ the variable `org-agenda-auto-exclude-function'." ((member s category-list) (add-to-list 'fc (concat pm s) 'append 'equal)) (t (message - "`%s%s' filter ignored tag/category is not represented" + "`%s%s' filter ignored because tag/category is not represented" pm s)))) ((match-beginning 4) ;; effort @@ -7718,14 +7736,17 @@ which see." (org-agenda-filter-show-all-effort)) (org-agenda-finalize)) -(defun org-agenda-filter-by-tag (arg &optional char exclude) +(defun org-agenda-filter-by-tag (strip-or-accumulate &optional char exclude) "Keep only those lines in the agenda buffer that have a specific tag. The tag is selected with its fast selection letter, as configured. -With a `\\[universal-argument]' prefix, exclude the agenda search. +With a `\\[universal-argument]' prefix, apply the filter negatively, stripping all matches. + +With a `\\[universal-argument] \\[universal-argument]' prefix, add the new tag to the existing filter +instead of replacing it. -With a `\\[universal-argument] \\[universal-argument]' prefix, filter the literal tag, \ +With a `\\[universal-argument] \\[universal-argument] \\[universal-argument]' prefix, filter the literal tag, \ i.e. don't filter on all its group members. @@ -7746,8 +7767,9 @@ also press `-' or `+' to switch between filtering and excluding." org-tag-alist-for-agenda "")) (valid-char-list (append '(?\t ?\r ?\\ ?. ?\s ?q) (string-to-list tag-chars))) - (exclude (or exclude (equal arg '(4)))) - (expand (not (equal arg '(16)))) + (exclude (or exclude (equal strip-or-accumulate '(4)))) + (accumulate (equal strip-or-accumulate '(16))) + (expand (not (equal strip-or-accumulate '(64)))) (inhibit-read-only t) (current org-agenda-tag-filter) a n tag) @@ -7764,7 +7786,7 @@ also press `-' or `+' to switch between filtering and excluding." (cond ((eq char ?-) (setq exclude t)) ((eq char ?+) (setq exclude nil))))) (when (eq char ?\t) - (unless (local-variable-p 'org-global-tags-completion-table (current-buffer)) + (unless (local-variable-p 'org-global-tags-completion-table) (setq-local org-global-tags-completion-table (org-global-tags-completion-table))) (let ((completion-ignore-case t)) @@ -7798,7 +7820,7 @@ also press `-' or `+' to switch between filtering and excluding." (setq tag (car a)) (setq org-agenda-tag-filter (cons (concat (if exclude "-" "+") tag) - current)) + (if accumulate current nil))) (org-agenda-filter-apply org-agenda-tag-filter 'tag expand)) (t (error "Invalid tag selection character %c" char))))) @@ -7890,7 +7912,7 @@ function to set the right switches in the returned form." (dolist (x tags (cons (if (eq op ?-) 'and 'or) form)) (let* ((tag (substring x 1)) (f (cond - ((string= "" tag) '(not tags)) + ((string= "" tag) 'tags) ((and (string-match-p "\\`{" tag) (string-match-p "}\\'" tag)) ;; TAG is a regexp. (list 'org-match-any-p (substring tag 1 -1) 'tags)) @@ -7945,7 +7967,8 @@ tags in the FILTER if any of the tags in FILTER are grouptags." ;; Deactivate `org-agenda-entry-text-mode' when filtering (when org-agenda-entry-text-mode (org-agenda-entry-text-mode)) (let (tags cat txt) - (setq org-agenda-filter-form (org-agenda-filter-make-matcher filter type expand)) + (setq org-agenda-filter-form (org-agenda-filter-make-matcher + filter type expand)) ;; Only set `org-agenda-filtered-by-category' to t when a unique ;; category is used as the filter: (setq org-agenda-filtered-by-category @@ -7998,7 +8021,8 @@ tags in the FILTER if any of the tags in FILTER are grouptags." (save-excursion (goto-char (point-min)) (let ((inhibit-read-only t) pos) - (while (setq pos (text-property-any (point) (point-max) 'org-filter-type type)) + (while (setq pos (text-property-any (point) (point-max) + 'org-filter-type type)) (goto-char pos) (remove-text-properties (point) (next-single-property-change (point) 'org-filter-type) |