Browse Source

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

Carsten Dominik 6 years ago
parent
commit
9962df2b48
2 changed files with 177 additions and 131 deletions
  1. 94 86
      doc/org-manual.org
  2. 83 45
      lisp/org-agenda.el

+ 94 - 86
doc/org-manual.org

@@ -9030,13 +9030,24 @@ the estimated effort of an entry (see [[*Effort Estimates]]).
 :DESCRIPTION: Dynamically narrow the agenda.
 :DESCRIPTION: Dynamically narrow the agenda.
 :END:
 :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
 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
 **** Filtering in the agenda
 :PROPERTIES:
 :PROPERTIES:
@@ -9050,64 +9061,34 @@ custom agenda commands.
 #+cindex: effort filtering, in agenda
 #+cindex: effort filtering, in agenda
 #+cindex: query editing, 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
   #+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]
-
-  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:
+  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.
 
 
-  #+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~) ::
 - {{{kbd(<)}}} (~org-agenda-filter-by-category~) ::
 
 
   #+findex: 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~) ::
 - {{{kbd(=)}}} (~org-agenda-filter-by-regexp~) ::
 
 
@@ -9117,12 +9098,7 @@ custom agenda commands.
   called with a prefix argument, it filters /out/ entries matching the
   called with a prefix argument, it filters /out/ entries matching the
   regexp.  Called in a regexp-filtered agenda view, remove the filter,
   regexp.  Called in a regexp-filtered agenda view, remove the filter,
   unless there are two universal prefix arguments, in which case
   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~) ::
 - {{{kbd(_)}}} (~org-agenda-filter-by-effort~) ::
 
 
@@ -9149,21 +9125,23 @@ custom agenda commands.
   condition.  With two universal prefix arguments, it clears effort
   condition.  With two universal prefix arguments, it clears effort
   filters, which can be accumulated.
   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
   #+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
   #+begin_example
-     +work-John<0:10-/plot/
+     +work-John+<0:10-/plot/
   #+end_example
   #+end_example
 
 
   selects entries with category `work' and effort estimates below 10
   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
   (tags will take priority).  If you reply to the prompt with the
   empty string, all filtering is removed.  If a filter is specified,
   empty string, all filtering is removed.  If a filter is specified,
   it replaces all current filters.  But if you call the command with a
   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
   the front of the string, the new filter elements are added to the
   active ones.
   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~) ::
 - {{{kbd(|)}}} (~org-agenda-filter-remove-all~) ::
 
 
   Remove all filters in the current agenda view.
   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
 **** Setting limits for the agenda
 :PROPERTIES:
 :PROPERTIES:
 :UNNUMBERED: notoc
 :UNNUMBERED: notoc
@@ -21407,12 +21412,15 @@ to ISO and therefore independent of the value of
 [fn:95] You can, however, disable this by setting
 [fn:95] You can, however, disable this by setting
 ~org-agenda-search-headline-for-time~ variable to a ~nil~ value.
 ~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
 [fn:97] Only tags filtering is respected here, effort filtering is
 ignored.
 ignored.

+ 83 - 45
lisp/org-agenda.el

@@ -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-subtract)
 (org-defkey org-agenda-mode-map "{" 'org-agenda-manipulate-query-add-re)
 (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-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-effort)
 (org-defkey org-agenda-mode-map "=" 'org-agenda-filter-by-regexp)
 (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-filter-remove-all)
 (org-defkey org-agenda-mode-map "~" 'org-agenda-limit-interactively)
 (org-defkey org-agenda-mode-map "~" 'org-agenda-limit-interactively)
 (org-defkey org-agenda-mode-map "<" 'org-agenda-filter-by-category)
 (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."
   "Return the category of the agenda line."
   (org-get-at-bol 'org-category))
   (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)
 (defun org-agenda-filter-by-category (strip)
   "Filter lines in the agenda buffer that have a specific category.
   "Filter lines in the agenda buffer that have a specific category.
 The category is that of the current line.
 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
 Instead of using the prefix argument to add to the current filter
 set, you can also add an additional leading `+' to filter string,
 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")
   (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)
 (defun org-agenda-filter-completion-function (string _predicate &optional flag)
   "Complete a complex filter string
   "Complete a complex filter string
@@ -7732,7 +7770,7 @@ also press `-' or `+' to switch between filtering and excluding."
 				       (char-to-string (cdr x)))
 				       (char-to-string (cdr x)))
 				   ""))
 				   ""))
 		     org-tag-alist-for-agenda ""))
 		     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)))
 				  (string-to-list tag-chars)))
 	 (exclude (or exclude (equal arg '(4))))
 	 (exclude (or exclude (equal arg '(4))))
 	 (expand (not (equal arg '(16))))
 	 (expand (not (equal arg '(16))))