Merge branch 'make-new-filter-interface-the-default'

This commit is contained in:
Carsten Dominik 2019-09-03 15:50:34 +02:00
commit 9962df2b48
2 changed files with 177 additions and 131 deletions

View File

@ -9030,13 +9030,24 @@ the estimated effort of an entry (see [[*Effort Estimates]]).
:DESCRIPTION: Dynamically narrow the agenda.
:END:
#+vindex: org-agenda-category-filter-preset
#+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 dynamically narrowing down the
list of agenda entries: /filters/ and /limits/. Filters only act on
the display of the items, while limits take effect before the list of
agenda entries is built. Filters are more often used interactively,
while limits are mostly useful when defined as local variables within
custom agenda commands.
filters and limits provide two ways of narrowing down the list of
agenda entries.
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.
**** Filtering in the agenda
:PROPERTIES:
@ -9050,64 +9061,34 @@ custom agenda commands.
#+cindex: effort filtering, in agenda
#+cindex: query editing, in agenda
- {{{kbd(/)}}} (~org-agenda-filter-by-tag~) ::
The general filtering command that gives access to the full
functionality is ~org-agenda-filter~, bound to {{{kbd(/)}}}. But
before we introduce it, we first describe commands for individual
filter types.
- {{{kbd(\)}}} (~org-agenda-filter-by-tag~) ::
#+findex: org-agenda-filter-by-tag
#+vindex: org-agenda-tag-filter-preset
Filter the agenda view with respect to a tag and/or effort
estimates. The difference between this and a custom agenda command
is that filtering is very fast, so that you can switch quickly
between different filters without having to recreate the
agenda.[fn:96]
Filter the agenda view with respect to a tag. You are prompted for
a tag selection letter; {{{kbd(SPC)}}} means any tag at all.
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.
You are prompted for a tag selection letter; {{{kbd(SPC)}}} means
any tag at all. 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. When called with prefix argument,
remove the entries that /do/ have the tag. A second {{{kbd(/)}}} at
the prompt turns off the filter and shows any hidden entries.
Pressing {{{kbd(+)}}} or {{{kbd(-)}}} switches between filtering and
excluding the next tag.
#+vindex: org-agenda-auto-exclude-function
Org also supports automatic, context-aware tag filtering. If the
variable ~org-agenda-auto-exclude-function~ is set to a user-defined
function, that function can decide which tags should be excluded
from the agenda automatically. Once this is set, the {{{kbd(/)}}}
command then accepts {{{kbd(RET)}}} as a sub-option key and runs the
auto exclusion logic. For example, let's say you use a =Net= tag to
identify tasks which need network access, an =Errand= tag for
errands in town, and a =Call= tag for making phone calls. You could
auto-exclude these tags based on the availability of the Internet,
and outside of business hours, with something like this:
#+begin_src emacs-lisp
(defun org-my-auto-exclude-function (tag)
(and (cond
((string= tag "Net")
(/= 0 (call-process "/sbin/ping" nil nil nil
"-c1" "-q" "-t1" "mail.gnu.org")))
((or (string= tag "Errand") (string= tag "Call"))
(let ((hour (nth 2 (decode-time))))
(or (< hour 8) (> hour 21)))))
(concat "-" tag)))
(setq org-agenda-auto-exclude-function 'org-my-auto-exclude-function)
#+end_src
- {{{kbd(<)}}} (~org-agenda-filter-by-category~) ::
#+findex: org-agenda-filter-by-category
Filter the current agenda view with respect to the category of the
item at point. Pressing {{{kbd(<)}}} another time removes this
filter. When called with a prefix argument exclude the category of
the item at point from the agenda.
#+vindex: org-agenda-category-filter-preset
You can add a filter preset in custom agenda commands through the
option ~org-agenda-category-filter-preset~. See [[*Setting options
for custom commands]].
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.
- {{{kbd(=)}}} (~org-agenda-filter-by-regexp~) ::
@ -9117,12 +9098,7 @@ custom agenda commands.
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 cumulated.
#+vindex: org-agenda-regexp-filter-preset
You can add a filter preset in custom agenda commands through the
option ~org-agenda-regexp-filter-preset~. See [[*Setting options
for custom commands]].
filters are accumulated.
- {{{kbd(_)}}} (~org-agenda-filter-by-effort~) ::
@ -9149,21 +9125,23 @@ custom agenda commands.
condition. With two universal prefix arguments, it clears effort
filters, which can be accumulated.
#+vindex: org-agenda-effort-filter-preset
You can add a filter preset in custom agenda commands through the
option ~org-agenda-effort-filter-preset~. See [[*Setting options for
custom commands]].
- {{{kbd(^)}}} (~org-agenda-filter-by-top-headline~) ::
- {{{kbd(\)}}} (~org-agenda-filter~) ::
#+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. So this
simulated the effect of restricting the agenda creation to this
tree.
- {{{kbd(/)}}} (~org-agenda-filter~) ::
#+findex: org-agenda-filter
This is an alternative interface to all four filter methods
described above. At the prompt, one would specify different filter
elements in a single string, with full completion support. For
example,
This a the unified interface to four of the five filter methods
described above. At the prompt, specify different filter elements
in a single string, with full completion support. For example,
#+begin_example
+work-John<0:10-/plot/
+work-John+<0:10-/plot/
#+end_example
selects entries with category `work' and effort estimates below 10
@ -9174,20 +9152,47 @@ custom agenda commands.
(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. `+-John') to
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.
- {{{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.
- {{{kbd(|)}}} (~org-agenda-filter-remove-all~) ::
Remove all filters in the current agenda view.
**** Computed exclusion filtering
:PROPERTIES:
:UNNUMBERED: notoc
:END:
#+vindex: org-agenda-auto-exclude-function
If the variable ~org-agenda-auto-exclude-function~ is set to a
user-defined function, that function can select tags that should be
excluded from the agenda when requested. The function will be called
with lower-case versions of all tags. For example, let's say you use
a =Net= tag to identify tasks which need network access, an =Errand=
tag for errands in town, and a =Call= tag for making phone calls. You
could auto-exclude these tags based on the availability of the
Internet, and outside of business hours, with something like this:
#+begin_src emacs-lisp
(defun org-my-auto-exclude-fn (tag)
(and (cond
((string= tag "net")
(/= 0 (call-process "/sbin/ping" nil nil nil
"-c1" "-q" "-t1" "mail.gnu.org")))
((or (member tag '("errand" "call")))
(let ((hr (nth 2 (decode-time))))
(or (< hr 8) (> hr 21)))))
(concat "-" tag)))
(setq org-agenda-auto-exclude-function 'org-my-auto-exclude-fn)
#+end_src
You can apply this self-adapting filter by using a double prefix
argument to ~org-agenda-filter~, i.e. press {{{kbd(C-u C-u /)}}}, or
by pressing {{{kbd(RET)}}} in ~org-agenda-filter-by-tag~.
**** Setting limits for the agenda
:PROPERTIES:
:UNNUMBERED: notoc
@ -21407,12 +21412,15 @@ to ISO and therefore independent of the value of
[fn:95] You can, however, disable this by setting
~org-agenda-search-headline-for-time~ variable to a ~nil~ value.
[fn:96] Custom commands can preset a filter by binding the variable
~org-agenda-tag-filter-preset~ as an option. This filter is then
applied to the view and persists as a basic filter through refreshes
and more secondary filtering. The filter is a global property of the
entire agenda view---in a block agenda, you should only set this in
the global options section, not in the section of an individual block.
[fn:96] Custom agenda commands can preset a filter by binding one of
the variables ~org-agenda-tag-filter-preset~,
~org-agenda-category-filter-preset~, ~org-agenda-effort-filter-preset~
or ~org-agenda-regexp-filter-preset~ as an option. This filter is
then applied to the view and persists as a basic filter through
refreshes and more secondary filtering. The filter is a global
property of the entire agenda view---in a block agenda, you should
only set this in the global options section, not in the section of an
individual block.
[fn:97] Only tags filtering is respected here, effort filtering is
ignored.

View File

@ -2399,10 +2399,10 @@ The following commands are available:
(org-defkey org-agenda-mode-map "]" 'org-agenda-manipulate-query-subtract)
(org-defkey org-agenda-mode-map "{" 'org-agenda-manipulate-query-add-re)
(org-defkey org-agenda-mode-map "}" 'org-agenda-manipulate-query-subtract-re)
(org-defkey org-agenda-mode-map "/" 'org-agenda-filter-by-tag)
(org-defkey org-agenda-mode-map "\\" 'org-agenda-filter-by-tag)
(org-defkey org-agenda-mode-map "_" 'org-agenda-filter-by-effort)
(org-defkey org-agenda-mode-map "=" 'org-agenda-filter-by-regexp)
(org-defkey org-agenda-mode-map "\\" 'org-agenda-filter)
(org-defkey org-agenda-mode-map "/" 'org-agenda-filter)
(org-defkey org-agenda-mode-map "|" 'org-agenda-filter-remove-all)
(org-defkey org-agenda-mode-map "~" 'org-agenda-limit-interactively)
(org-defkey org-agenda-mode-map "<" 'org-agenda-filter-by-category)
@ -7464,7 +7464,26 @@ With a prefix argument, do so in all agenda buffers."
"Return the category of the agenda line."
(org-get-at-bol 'org-category))
(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."
(interactive "P")
(if (and org-agenda-filtered-by-category
org-agenda-category-filter)
(org-agenda-filter-show-all-cat)
(let ((cat (org-no-properties (org-get-at-eol 'org-category 1))))
(cond
((and cat strip)
(org-agenda-filter-apply
(push (concat "-" cat) org-agenda-category-filter) 'category))
(cat
(org-agenda-filter-apply
(setq org-agenda-category-filter
(list (concat "+" cat))) 'category))
(t (error "No category at point"))))))
(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.
@ -7613,48 +7632,67 @@ 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'."
like `+-John'.
With a double prefix argument, execute the computed filtering defined in
the variable `org-agenda-auto-exclude-function'."
(interactive "P")
(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))
(keep (or (if (string-match "^+[-+]" f-string)
(progn (setq f-string (substring f-string 1)) t))
keep))
(fc (if keep org-agenda-category-filter))
(ft (if keep org-agenda-tag-filter))
(fe (if keep org-agenda-effort-filter))
(fr (if keep org-agenda-regexp-filter))
log s)
(while (string-match "^[ \t]*\\([-+]\\)?\\(\\([^-+<>=/ \t]+\\)\\|\\([<>=][0-9:]+\\)\\|\\(/\\([^/]+\\)/?\\)\\)"
f-string)
(setq log (if (match-beginning 1) (match-string 1 f-string) "+"))
(cond
((match-beginning 3)
;; category or tag
(setq s (match-string 3 f-string))
(cond ((member s tag-list)
(add-to-list 'ft (concat log s) 'append 'equal))
((member s category-list)
(add-to-list 'fc (concat log s) 'append 'equal))
(t (message "`%s%s' filter ignored because it is not represented as tag or category" log s))))
((match-beginning 4)
;; effort
(add-to-list 'fe (concat log (match-string 4 f-string)) 'append 'equal))
((match-beginning 5)
;; regexp
(add-to-list 'fr (concat log (match-string 6 f-string)) 'append 'equal)))
(setq f-string (substring f-string (match-end 0))))
(org-agenda-filter-remove-all)
(and fc (org-agenda-filter-apply
(setq org-agenda-category-filter fc) 'category))
(and ft (org-agenda-filter-apply
(setq org-agenda-tag-filter ft) 'tag))
(and fe (org-agenda-filter-apply
(setq org-agenda-effort-filter fe) 'effort))
(and fr (org-agenda-filter-apply
(setq org-agenda-regexp-filter fr) 'regexp))
))
(if (equal keep '(16))
;; Execute the auto-exclude action
(if (not org-agenda-auto-exclude-function)
(user-error "`org-agenda-auto-exclude-function' is undefined")
(org-agenda-filter-show-all-tag)
(setq org-agenda-tag-filter nil)
(dolist (tag (org-agenda-get-represented-tags))
(let ((modifier (funcall org-agenda-auto-exclude-function tag)))
(when modifier
(push modifier org-agenda-tag-filter))))
(unless (null org-agenda-tag-filter)
(org-agenda-filter-apply org-agenda-tag-filter 'tag expand)))
;; 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))
(keep (or (if (string-match "^+[-+]" f-string)
(progn (setq f-string (substring f-string 1)) t))
keep))
(fc (if keep org-agenda-category-filter))
(ft (if keep org-agenda-tag-filter))
(fe (if keep org-agenda-effort-filter))
(fr (if keep org-agenda-regexp-filter))
pm s)
(while (string-match "^[ \t]*\\([-+]\\)?\\(\\([^-+<>=/ \t]+\\)\\|\\([<>=][0-9:]+\\)\\|\\(/\\([^/]+\\)/?\\)\\)" f-string)
(setq pm (if (match-beginning 1) (match-string 1 f-string) "+"))
(cond
((match-beginning 3)
;; category or tag
(setq s (match-string 3 f-string))
(cond
((member s tag-list)
(add-to-list 'ft (concat pm s) 'append 'equal))
((member s category-list)
(add-to-list 'fc (concat pm s) 'append 'equal))
(t (message
"`%s%s' filter ignored tag/category is not represented"
pm s))))
((match-beginning 4)
;; effort
(add-to-list 'fe (concat pm (match-string 4 f-string)) t 'equal))
((match-beginning 5)
;; regexp
(add-to-list 'fr (concat pm (match-string 6 f-string)) t 'equal)))
(setq f-string (substring f-string (match-end 0))))
(org-agenda-filter-remove-all)
(and fc (org-agenda-filter-apply
(setq org-agenda-category-filter fc) 'category))
(and ft (org-agenda-filter-apply
(setq org-agenda-tag-filter ft) 'tag))
(and fe (org-agenda-filter-apply
(setq org-agenda-effort-filter fe) 'effort))
(and fr (org-agenda-filter-apply
(setq org-agenda-regexp-filter fr) 'regexp))
)))
(defun org-agenda-filter-completion-function (string _predicate &optional flag)
"Complete a complex filter string
@ -7732,7 +7770,7 @@ also press `-' or `+' to switch between filtering and excluding."
(char-to-string (cdr x)))
""))
org-tag-alist-for-agenda ""))
(valid-char-list (append '(?\t ?\r ?/ ?. ?\s ?q)
(valid-char-list (append '(?\t ?\r ?\\ ?. ?\s ?q)
(string-to-list tag-chars)))
(exclude (or exclude (equal arg '(4))))
(expand (not (equal arg '(16))))