diff options
Diffstat (limited to 'lisp/org/org-agenda.el')
-rw-r--r-- | lisp/org/org-agenda.el | 1869 |
1 files changed, 1098 insertions, 771 deletions
diff --git a/lisp/org/org-agenda.el b/lisp/org/org-agenda.el index 6870b780fa2..7cb5cca34cd 100644 --- a/lisp/org/org-agenda.el +++ b/lisp/org/org-agenda.el @@ -46,6 +46,7 @@ ;;; Code: (require 'cl-lib) +(require 'ol) (require 'org) (require 'org-macs) @@ -90,6 +91,7 @@ (defvar org-habit-show-habits) (defvar org-habit-show-habits-only-for-today) (defvar org-habit-show-all-today) +(defvar org-habit-scheduled-past-days) ;; Defined somewhere in this file, but used before definition. (defvar org-agenda-buffer-name "*Org Agenda*") @@ -381,6 +383,9 @@ the daily/weekly agenda, see `org-agenda-skip-function'.") (const :scheduled*) (const :timestamp) (const :sexp)))) + (list :tag "Columns format" + (const org-overriding-columns-format) + (string :tag "Format")) (list :tag "Standard skipping condition" :value (org-agenda-skip-function '(org-agenda-skip-entry-if)) (const org-agenda-skip-function) @@ -461,7 +466,7 @@ type The command type, any of the following symbols: ... A user-defined function. match What to search for: - a single keyword for TODO keyword searches - - a tags match expression for tags searches + - a tags/property/todo match expression for searches - a word search expression for text searches. - a regular expression for occur searches For all other commands, this should be the empty string. @@ -548,11 +553,11 @@ should provide a description for the prefix, like (const :format "" stuck) (const :tag "" :format "" "") ,org-agenda-custom-commands-local-options) - (list :tag "Tags search" + (list :tag "Tags/Property match (all agenda files)" (const :format "" tags) (string :tag "Match") ,org-agenda-custom-commands-local-options) - (list :tag "Tags search, TODO entries only" + (list :tag "Tags/Property match of TODO entries (all agenda files)" (const :format "" tags-todo) (string :tag "Match") ,org-agenda-custom-commands-local-options) @@ -1706,6 +1711,13 @@ Custom commands can set this variable in the options section." :version "26.1" :package-version '(Org . "9.1")) +(defcustom org-agenda-breadcrumbs-separator "->" + "The separator of breadcrumbs in agenda lines." + :group 'org-agenda-line-format + :package-version '(Org . "9.3") + :type 'string + :safe #'stringp) + (defvar org-prefix-format-compiled nil "The compiled prefix format and associated variables. This is a list where first element is a list of variable bindings, and second @@ -2021,7 +2033,8 @@ estimate." The sole argument to the function, which is called once for each possible tag, is a string giving the name of the tag. The function should return either nil if the tag should be included -as normal, or \"-<TAG>\" to exclude the tag. +as normal, \"-<TAG>\" to exclude the tag, or \"+<TAG>\" to exclude +lines not carrying this tag. Note that for the purpose of tag filtering, only the lower-case version of all tags will be considered, so that this function will only ever see the lower-case version of all tags." @@ -2065,6 +2078,23 @@ works you probably want to add it to `org-agenda-custom-commands' for good." (setcdr ass (cdr entry)) (push entry org-agenda-custom-commands)))) +(defmacro org-agenda--insert-overriding-header (default) + "Insert header into agenda view. +The inserted header depends on `org-agenda-overriding-header'. +If the empty string, don't insert a header. If any other string, +insert it as a header. If nil, insert DEFAULT, which should +evaluate to a string." + (declare (debug (form)) (indent defun)) + `(cond + ((not org-agenda-overriding-header) (insert ,default)) + ((equal org-agenda-overriding-header "") nil) + ((stringp org-agenda-overriding-header) + (insert (propertize org-agenda-overriding-header + 'face 'org-agenda-structure) + "\n")) + (t (user-error "Invalid value for `org-agenda-overriding-header': %S" + org-agenda-overriding-header)))) + ;;; Define the org-agenda-mode (defvaralias 'org-agenda-keymap 'org-agenda-mode-map) @@ -2167,29 +2197,36 @@ The following commands are available: \\{org-agenda-mode-map}" (interactive) - (cond (org-agenda-doing-sticky-redo - ;; Refreshing sticky agenda-buffer - ;; - ;; Preserve the value of `org-agenda-local-vars' variables, - ;; while letting `kill-all-local-variables' kill the rest - (let ((save (buffer-local-variables))) - (kill-all-local-variables) - (mapc #'make-local-variable org-agenda-local-vars) - (dolist (elem save) - (pcase elem - (`(,var . ,val) ;ignore unbound variables - (when (and val (memq var org-agenda-local-vars)) - (set var val)))))) - (setq-local org-agenda-this-buffer-is-sticky t)) - (org-agenda-sticky - ;; Creating a sticky Agenda buffer for the first time - (kill-all-local-variables) - (mapc 'make-local-variable org-agenda-local-vars) - (setq-local org-agenda-this-buffer-is-sticky t)) - (t - ;; Creating a non-sticky agenda buffer - (kill-all-local-variables) - (setq-local org-agenda-this-buffer-is-sticky nil))) + (let ((agenda-local-vars-to-keep + '(text-scale-mode-amount + text-scale-mode + text-scale-mode-lighter + face-remapping-alist)) + (save (buffer-local-variables))) + (kill-all-local-variables) + (cl-flet ((reset-saved (var-set) + "Reset variables in VAR-SET to possibly stored value in SAVE." + (dolist (elem save) + (pcase elem + (`(,var . ,val) ;ignore unbound variables + (when (and val (memq var var-set)) + (set var val))))))) + (cond (org-agenda-doing-sticky-redo + ;; Refreshing sticky agenda-buffer + ;; + ;; Preserve the value of `org-agenda-local-vars' variables. + (mapc #'make-local-variable org-agenda-local-vars) + (reset-saved org-agenda-local-vars) + (setq-local org-agenda-this-buffer-is-sticky t)) + (org-agenda-sticky + ;; Creating a sticky Agenda buffer for the first time + (mapc 'make-local-variable org-agenda-local-vars) + (setq-local org-agenda-this-buffer-is-sticky t)) + (t + ;; Creating a non-sticky agenda buffer + (setq-local org-agenda-this-buffer-is-sticky nil))) + (mapc #'make-local-variable agenda-local-vars-to-keep) + (reset-saved agenda-local-vars-to-keep))) (setq org-agenda-undo-list nil org-agenda-pending-undo-list nil org-agenda-bulk-marked-entries nil) @@ -2200,16 +2237,16 @@ The following commands are available: (setq indent-tabs-mode nil) (use-local-map org-agenda-mode-map) (easy-menu-add org-agenda-menu) - (if org-startup-truncated (setq truncate-lines t)) + (when org-startup-truncated (setq truncate-lines t)) (setq-local line-move-visual nil) (add-hook 'post-command-hook 'org-agenda-update-agenda-type nil 'local) (add-hook 'pre-command-hook 'org-unhighlight nil 'local) ;; Make sure properties are removed when copying text (if (boundp 'filter-buffer-substring-functions) (add-hook 'filter-buffer-substring-functions - (lambda (fun start end delete) + (lambda (fun start end delete) (substring-no-properties (funcall fun start end delete))) - nil t) + nil t) ;; Emacs >= 24.4. (add-function :filter-return (local 'filter-buffer-substring-function) #'substring-no-properties)) @@ -2363,9 +2400,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-remove-all) (org-defkey org-agenda-mode-map "~" 'org-agenda-limit-interactively) (org-defkey org-agenda-mode-map "<" 'org-agenda-filter-by-category) @@ -2375,6 +2413,7 @@ The following commands are available: (define-key org-agenda-mode-map "?" 'org-agenda-show-the-flagging-note) (org-defkey org-agenda-mode-map "\C-c\C-x\C-mg" 'org-mobile-pull) (org-defkey org-agenda-mode-map "\C-c\C-x\C-mp" 'org-mobile-push) +(org-defkey org-agenda-mode-map "\C-c\C-xI" 'org-info-find-node) (org-defkey org-agenda-mode-map [mouse-2] 'org-agenda-goto-mouse) (org-defkey org-agenda-mode-map [mouse-3] 'org-agenda-show-mouse) @@ -2445,8 +2484,20 @@ The following commands are available: :keys "v A"] "--" ["Remove Restriction" org-agenda-remove-restriction-lock org-agenda-restrict]) - ["Write view to file" org-agenda-write t] + ("Filter current view" + ["with generic interface" org-agenda-filter t] + "--" + ["by category at cursor" org-agenda-filter-by-category t] + ["by tag" org-agenda-filter-by-tag t] + ["by effort" org-agenda-filter-by-effort t] + ["by regexp" org-agenda-filter-by-regexp t] + ["by top-level headline" org-agenda-filter-by-top-headline t] + "--" + ["Remove all filtering" org-agenda-filter-remove-all t] + "--" + ["limit" org-agenda-limit-interactively t]) ["Rebuild buffer" org-agenda-redo t] + ["Write view to file" org-agenda-write t] ["Save all Org buffers" org-save-all-org-buffers t] "--" ["Show original entry" org-agenda-show t] @@ -2551,30 +2602,30 @@ that have been changed along." (interactive) (or org-agenda-allow-remote-undo (user-error "Check the variable `org-agenda-allow-remote-undo' to activate remote undo")) - (if (not (eq this-command last-command)) - (setq org-agenda-undo-has-started-in nil - org-agenda-pending-undo-list org-agenda-undo-list)) - (if (not org-agenda-pending-undo-list) - (user-error "No further undo information")) + (when (not (eq this-command last-command)) + (setq org-agenda-undo-has-started-in nil + org-agenda-pending-undo-list org-agenda-undo-list)) + (when (not org-agenda-pending-undo-list) + (user-error "No further undo information")) (let* ((entry (pop org-agenda-pending-undo-list)) buf line cmd rembuf) (setq cmd (pop entry) line (pop entry)) (setq rembuf (nth 2 entry)) (org-with-remote-undo rembuf (while (bufferp (setq buf (pop entry))) - (if (pop entry) - (with-current-buffer buf - (let ((last-undo-buffer buf) - (inhibit-read-only t)) - (unless (memq buf org-agenda-undo-has-started-in) - (push buf org-agenda-undo-has-started-in) - (make-local-variable 'pending-undo-list) - (undo-start)) - (while (and pending-undo-list - (listp pending-undo-list) - (not (car pending-undo-list))) - (pop pending-undo-list)) - (undo-more 1)))))) + (when (pop entry) + (with-current-buffer buf + (let ((last-undo-buffer buf) + (inhibit-read-only t)) + (unless (memq buf org-agenda-undo-has-started-in) + (push buf org-agenda-undo-has-started-in) + (make-local-variable 'pending-undo-list) + (undo-start)) + (while (and pending-undo-list + (listp pending-undo-list) + (not (car pending-undo-list))) + (pop pending-undo-list)) + (undo-more 1)))))) (org-goto-line line) (message "`%s' undone (buffer %s)" cmd (buffer-name rembuf)))) @@ -2797,9 +2848,9 @@ Pressing `<' twice means to restrict to the current subtree or region ;; If we have sticky agenda buffers, set a name for the buffer, ;; depending on the invoking keys. The user may still set this ;; as a command option, which will overwrite what we do here. - (if org-agenda-sticky - (setq org-agenda-buffer-name - (format "*Org Agenda(%s)*" org-keys))) + (when org-agenda-sticky + (setq org-agenda-buffer-name + (format "*Org Agenda(%s)*" org-keys))) ;; Establish the restriction, if any (when (and (not org-agenda-overriding-restriction) restriction) (put 'org-agenda-files 'org-restrict (list bfn)) @@ -2814,7 +2865,13 @@ Pressing `<' twice means to restrict to the current subtree or region (org-back-to-heading t) (move-marker org-agenda-restrict-begin (point)) (move-marker org-agenda-restrict-end - (progn (org-end-of-subtree t))))))) + (progn (org-end-of-subtree t))))) + ((and (eq restriction 'buffer) + (or (< 1 (point-min)) + (< (point-max) (1+ (buffer-size))))) + (setq org-agenda-restrict (current-buffer)) + (move-marker org-agenda-restrict-begin (point-min)) + (move-marker org-agenda-restrict-end (point-max))))) ;; For example the todo list should not need it (but does...) (cond @@ -2823,10 +2880,10 @@ Pressing `<' twice means to restrict to the current subtree or region (progn (setq type (nth 2 entry) org-match (eval (nth 3 entry)) lprops (nth 4 entry)) - (if org-agenda-sticky - (setq org-agenda-buffer-name - (or (and (stringp org-match) (format "*Org Agenda(%s:%s)*" org-keys org-match)) - (format "*Org Agenda(%s)*" org-keys)))) + (when org-agenda-sticky + (setq org-agenda-buffer-name + (or (and (stringp org-match) (format "*Org Agenda(%s:%s)*" org-keys org-match)) + (format "*Org Agenda(%s)*" org-keys)))) (put 'org-agenda-redo-command 'org-lprops lprops) (cond ((eq type 'agenda) @@ -2884,10 +2941,10 @@ Pressing `<' twice means to restrict to the current subtree or region (when note (message "FLAGGING-NOTE ([?] for more info): %s" (org-add-props - (replace-regexp-in-string - "\\\\n" "//" - (copy-sequence note)) - nil 'face 'org-warning)))))) + (replace-regexp-in-string + "\\\\n" "//" + (copy-sequence note)) + nil 'face 'org-warning)))))) t t)) ((equal org-keys "#") (call-interactively 'org-agenda-list-stuck-projects)) ((equal org-keys "/") (call-interactively 'org-occur-in-agenda-files)) @@ -3000,19 +3057,18 @@ s Search for keywords M Like m, but only TODO entries (symbol-name type) "Lambda expression")) (t "???")))) - (if org-agenda-menu-show-matcher - (setq line - (concat line ": " - (cond - ((stringp match) - (setq match (copy-sequence match)) - (org-add-props match nil 'face 'org-warning)) - ((listp type) - (format "set of %d commands" (length type)))))) - (if (org-string-nw-p match) - (add-text-properties - 0 (length line) (list 'help-echo - (concat "Matcher: " match)) line))) + (cond + ((not (org-string-nw-p match)) nil) + (org-agenda-menu-show-matcher + (setq line + (concat line ": " + (cond + ((stringp match) + (propertize match 'face 'org-warning)) + ((listp type) + (format "set of %d commands" (length type))))))) + (t + (org-add-props line nil 'help-echo (concat "Matcher: " match)))) (push line lines))) (setq lines (nreverse lines)) (when prefixes @@ -3048,21 +3104,47 @@ s Search for keywords M Like m, but only TODO entries ;; Make the window the right size (goto-char (point-min)) (if second-time - (if (not (pos-visible-in-window-p (point-max))) - (org-fit-window-to-buffer)) + (when (not (pos-visible-in-window-p (point-max))) + (org-fit-window-to-buffer)) (setq second-time t) (org-fit-window-to-buffer)) + ;; Hint to navigation if window too small for all information + (setq header-line-format + (when (not (pos-visible-in-window-p (point-max))) + "Use SPC, DEL, C-n or C-p to navigate.")) + ;; Ask for selection - (message "Press key for agenda command%s:" - (if (or restrict-ok org-agenda-overriding-restriction) - (if org-agenda-overriding-restriction - " (restriction lock active)" - (if restriction - (format " (restricted to %s)" restriction) - " (unrestricted)")) - "")) - (setq c (read-char-exclusive)) + (cl-loop + do (progn + (message "Press key for agenda command%s:" + (if (or restrict-ok org-agenda-overriding-restriction) + (if org-agenda-overriding-restriction + " (restriction lock active)" + (if restriction + (format " (restricted to %s)" restriction) + " (unrestricted)")) + "")) + (setq c (read-char-exclusive))) + until (not (memq c '(14 16 ?\s ?\d))) + do (cl-case c + (14 (if (not (pos-visible-in-window-p (point-max))) + (ignore-errors (scroll-up 1)) + (message "End of buffer") + (sit-for 1))) + (16 (if (not (pos-visible-in-window-p (point-min))) + (ignore-errors (scroll-down 1)) + (message "Beginning of buffer") + (sit-for 1))) + (?\s (if (not (pos-visible-in-window-p (point-max))) + (scroll-up nil) + (message "End of buffer") + (sit-for 1))) + (?\d (if (not (pos-visible-in-window-p (point-min))) + (scroll-down nil) + (message "Beginning of buffer") + (sit-for 1))))) + (message "") (cond ((assoc (char-to-string c) custom) @@ -3235,7 +3317,7 @@ todo The todo keyword, if any tags All tags including inherited ones, separated by colons date The relevant date, like 2007-2-14 time The time, like 15:00-16:50 -extra Sting with extra planning info +extra String with extra planning info priority-l The priority letter if any was given priority-n The computed numerical priority agenda-day The day in the agenda where this is listed" @@ -3245,11 +3327,9 @@ agenda-day The day in the agenda where this is listed" (org-tags-view nil cmd-key) (org-agenda nil cmd-key))) (set-buffer org-agenda-buffer-name) - (let* ((lines (org-split-string (buffer-string) "\n")) - line) - (while (setq line (pop lines)) - (catch 'next - (if (not (get-text-property 0 'org-category line)) (throw 'next nil)) + (let ((lines (org-split-string (buffer-string) "\n"))) + (dolist (line lines) + (when (get-text-property 0 'org-category line) (setq org-agenda-info (org-fix-agenda-info (text-properties-at 0 line))) (princ @@ -3266,14 +3346,14 @@ This ensures the export commands can easily use it." (when (setq tmp (plist-get props 'tags)) (setq props (plist-put props 'tags (mapconcat 'identity tmp ":")))) (when (setq tmp (plist-get props 'date)) - (if (integerp tmp) (setq tmp (calendar-gregorian-from-absolute tmp))) + (when (integerp tmp) (setq tmp (calendar-gregorian-from-absolute tmp))) (let ((calendar-date-display-form '(year "-" month "-" day))) '((format "%4d, %9s %2s, %4s" dayname monthname day year)) (setq tmp (calendar-date-string tmp))) (setq props (plist-put props 'date tmp))) (when (setq tmp (plist-get props 'day)) - (if (integerp tmp) (setq tmp (calendar-gregorian-from-absolute tmp))) + (when (integerp tmp) (setq tmp (calendar-gregorian-from-absolute tmp))) (let ((calendar-date-display-form '(year "-" month "-" day))) (setq tmp (calendar-date-string tmp))) (setq props (plist-put props 'day tmp)) @@ -3513,7 +3593,7 @@ removed from the entry content. Currently only `planning' is allowed here." (add-text-properties (match-beginning 0) (match-end 0) '(face org-link)))) (goto-char (point-min)) - (while (re-search-forward org-bracket-link-regexp (point-max) t) + (while (re-search-forward org-link-bracket-re (point-max) t) (set-text-properties (match-beginning 0) (match-end 0) nil)) (goto-char (point-min)) @@ -3536,15 +3616,15 @@ removed from the entry content. Currently only `planning' is allowed here." (replace-match ""))))) (goto-char (point-max)) (skip-chars-backward " \t\n") - (if (looking-at "[ \t\n]+\\'") (replace-match "")) + (when (looking-at "[ \t\n]+\\'") (replace-match "")) ;; find and remove min common indentation (goto-char (point-min)) (untabify (point-min) (point-max)) - (setq ind (org-get-indentation)) + (setq ind (current-indentation)) (while (not (eobp)) (unless (looking-at "[ \t]*$") - (setq ind (min ind (org-get-indentation)))) + (setq ind (min ind (current-indentation)))) (beginning-of-line 2)) (goto-char (point-min)) (while (not (eobp)) @@ -3586,6 +3666,11 @@ removed from the entry content. Currently only `planning' is allowed here." (defvar org-agenda-regexp-filter nil) (defvar org-agenda-effort-filter nil) (defvar org-agenda-top-headline-filter nil) + +(defvar org-agenda-represented-categories nil + "Cache for the list of all categories in the agenda.") +(defvar org-agenda-represented-tags nil + "Cache for the list of all categories in the agenda.") (defvar org-agenda-tag-filter-preset nil "A preset of the tags filter used for secondary agenda filtering. This must be a list of strings, each string must be a single tag preceded @@ -3596,6 +3681,20 @@ the entire agenda view. In a block agenda, it will not work reliably to define a filter for one of the individual blocks. You need to set it in the global options and expect it to be applied to the entire view.") +(defconst org-agenda-filter-variables + '((category . org-agenda-category-filter) + (tag . org-agenda-tag-filter) + (effort . org-agenda-effort-filter) + (regexp . org-agenda-regexp-filter)) + "Alist of filter types and associated variables") +(defun org-agenda-filter-any () + "Is any filter active?" + (let ((form (cons 'or (mapcar (lambda (x) + (if (or (symbol-value (cdr x)) + (get :preset-filter x)) + t nil)) + org-agenda-filter-variables)))) + (eval form))) (defvar org-agenda-category-filter-preset nil "A preset of the category filter used for secondary agenda filtering. This must be a list of strings, each string must be a single category @@ -3681,18 +3780,19 @@ FILTER-ALIST is an alist of filters we need to apply when (or wconf org-agenda-pre-window-conf)))) (defun org-agenda-prepare (&optional name) - (let ((filter-alist (if org-agenda-persistent-filter - (with-current-buffer - (get-buffer-create org-agenda-buffer-name) - (list `(tag . ,org-agenda-tag-filter) - `(re . ,org-agenda-regexp-filter) - `(effort . ,org-agenda-effort-filter) - `(cat . ,org-agenda-category-filter)))))) + (let ((filter-alist (when org-agenda-persistent-filter + (with-current-buffer + (get-buffer-create org-agenda-buffer-name) + `((tag . ,org-agenda-tag-filter) + (re . ,org-agenda-regexp-filter) + (effort . ,org-agenda-effort-filter) + (cat . ,org-agenda-category-filter)))))) (if (org-agenda-use-sticky-p) (progn (put 'org-agenda-tag-filter :preset-filter nil) (put 'org-agenda-category-filter :preset-filter nil) (put 'org-agenda-regexp-filter :preset-filter nil) + (put 'org-agenda-effort-filter :preset-filter nil) ;; Popup existing buffer (org-agenda-prepare-window (get-buffer org-agenda-buffer-name) filter-alist) @@ -3743,7 +3843,8 @@ FILTER-ALIST is an alist of filters we need to apply when (setq-local org-agenda-name name))) (setq buffer-read-only nil)))) -(defvar org-agenda-overriding-columns-format) ; From org-colview.el +(defvar org-overriding-columns-format) +(defvar org-local-columns-format) (defun org-agenda-finalize () "Finishing touch for the agenda buffer, called just before displaying it." (unless org-agenda-multi @@ -3758,13 +3859,11 @@ FILTER-ALIST is an alist of filters we need to apply when (org-agenda-align-tags)) (unless org-agenda-with-colors (remove-text-properties (point-min) (point-max) '(face nil))) - (if (and (boundp 'org-agenda-overriding-columns-format) - org-agenda-overriding-columns-format) - (setq-local org-agenda-overriding-columns-format - org-agenda-overriding-columns-format)) - (if (and (boundp 'org-agenda-view-columns-initially) - org-agenda-view-columns-initially) - (org-agenda-columns)) + (when (bound-and-true-p org-overriding-columns-format) + (setq-local org-local-columns-format + org-overriding-columns-format)) + (when org-agenda-view-columns-initially + (org-agenda-columns)) (when org-agenda-fontify-priorities (org-agenda-fontify-priorities)) (when (and org-agenda-dim-blocked-tasks org-blocker-hook) @@ -3773,9 +3872,9 @@ FILTER-ALIST is an alist of filters we need to apply when (when org-agenda-entry-text-mode (org-agenda-entry-text-hide) (org-agenda-entry-text-show)) - (if (and (functionp 'org-habit-insert-consistency-graphs) - (save-excursion (next-single-property-change (point-min) 'org-habit-p))) - (org-habit-insert-consistency-graphs)) + (when (and (featurep 'org-habit) + (save-excursion (next-single-property-change (point-min) 'org-habit-p))) + (org-habit-insert-consistency-graphs)) (setq org-agenda-type (org-get-at-bol 'org-agenda-type)) (unless (or (eq org-agenda-show-inherited-tags 'always) (and (listp org-agenda-show-inherited-tags) @@ -3791,10 +3890,12 @@ FILTER-ALIST is an alist of filters we need to apply when (while (equal (forward-line) 0) (when (setq mrk (get-text-property (point) 'org-hd-marker)) (put-text-property (point-at-bol) (point-at-eol) - 'tags (org-with-point-at mrk - (delete-dups - (mapcar 'downcase (org-get-tags-at)))))))))) + 'tags + (org-with-point-at mrk + (mapcar #'downcase (org-get-tags))))))))) (run-hooks 'org-agenda-finalize-hook) + (setq org-agenda-represented-tags nil + org-agenda-represented-categories nil) (when org-agenda-top-headline-filter (org-agenda-filter-top-headline-apply org-agenda-top-headline-filter)) @@ -3845,15 +3946,15 @@ FILTER-ALIST is an alist of filters we need to apply when (defun org-agenda-unmark-clocking-task () "Unmark the current clocking task." (mapc (lambda (o) - (if (eq (overlay-get o 'type) 'org-agenda-clocking) - (delete-overlay o))) + (when (eq (overlay-get o 'type) 'org-agenda-clocking) + (delete-overlay o))) (overlays-in (point-min) (point-max)))) (defun org-agenda-fontify-priorities () "Make highest priority lines bold, and lowest italic." (interactive) - (mapc (lambda (o) (if (eq (overlay-get o 'org-type) 'org-priority) - (delete-overlay o))) + (mapc (lambda (o) (when (eq (overlay-get o 'org-type) 'org-priority) + (delete-overlay o))) (overlays-in (point-min) (point-max))) (save-excursion (let (b e p ov h l) @@ -4053,12 +4154,11 @@ This check for agenda markers in all agenda buffers currently active." (defun org-agenda-entry-text-hide () "Remove any shown entry context." - (delq nil - (mapcar (lambda (o) - (if (eq (overlay-get o 'org-overlay-type) - 'agenda-entry-content) - (progn (delete-overlay o) t))) - (overlays-in (point-min) (point-max))))) + (mapc (lambda (o) + (when (eq (overlay-get o 'org-overlay-type) + 'agenda-entry-content) + (delete-overlay o))) + (overlays-in (point-min) (point-max)))) (defun org-agenda-get-day-face (date) "Return the face DATE should be displayed with." @@ -4099,28 +4199,31 @@ given in `org-agenda-start-on-weekday'. When WITH-HOUR is non-nil, only include scheduled and deadline items if they have an hour specification like [h]h:mm." (interactive "P") - (if org-agenda-overriding-arguments - (setq arg (car org-agenda-overriding-arguments) - start-day (nth 1 org-agenda-overriding-arguments) - span (nth 2 org-agenda-overriding-arguments))) - (if (and (integerp arg) (> arg 0)) - (setq span arg arg nil)) + (when org-agenda-overriding-arguments + (setq arg (car org-agenda-overriding-arguments) + start-day (nth 1 org-agenda-overriding-arguments) + span (nth 2 org-agenda-overriding-arguments))) + (when (and (integerp arg) (> arg 0)) + (setq span arg arg nil)) + (when (numberp span) + (unless (< 0 span) + (user-error "Agenda creation impossible for this span(=%d days)." span))) (catch 'exit (setq org-agenda-buffer-name (or org-agenda-buffer-tmp-name (and org-agenda-doing-sticky-redo org-agenda-buffer-name) - (if org-agenda-sticky - (cond ((and org-keys (stringp org-match)) - (format "*Org Agenda(%s:%s)*" org-keys org-match)) - (org-keys - (format "*Org Agenda(%s)*" org-keys)) - (t "*Org Agenda(a)*"))) + (when org-agenda-sticky + (cond ((and org-keys (stringp org-match)) + (format "*Org Agenda(%s:%s)*" org-keys org-match)) + (org-keys + (format "*Org Agenda(%s)*" org-keys)) + (t "*Org Agenda(a)*"))) "*Org Agenda*")) (org-agenda-prepare "Day/Week") (setq start-day (or start-day org-agenda-start-day)) - (if (stringp start-day) - ;; Convert to an absolute day number - (setq start-day (time-to-days (org-read-date nil t start-day)))) + (when (stringp start-day) + ;; Convert to an absolute day number + (setq start-day (time-to-days (org-read-date nil t start-day)))) (org-compile-prefix-format 'agenda) (org-set-sorting-strategy 'agenda) (let* ((span (org-agenda-ndays-to-span (or span org-agenda-span))) @@ -4128,8 +4231,8 @@ items if they have an hour specification like [h]h:mm." (sd (or start-day today)) (ndays (org-agenda-span-to-ndays span sd)) (org-agenda-start-on-weekday - (if (or (eq ndays 7) (eq ndays 14)) - org-agenda-start-on-weekday)) + (and (or (eq ndays 7) (eq ndays 14)) + org-agenda-start-on-weekday)) (thefiles (org-agenda-files nil 'ifmode)) (files thefiles) (start (if (or (null org-agenda-start-on-weekday) @@ -4162,28 +4265,27 @@ items if they have an hour specification like [h]h:mm." (w1 (org-days-to-iso-week d1)) (w2 (org-days-to-iso-week d2))) (setq s (point)) - (if org-agenda-overriding-header - (insert (org-add-props (copy-sequence org-agenda-overriding-header) - nil 'face 'org-agenda-structure) "\n") - (insert (org-agenda-span-name span) + (org-agenda--insert-overriding-header + (concat (org-agenda-span-name span) "-agenda" - (if (< (- d2 d1) 350) - (if (= w1 w2) - (format " (W%02d)" w1) - (format " (W%02d-W%02d)" w1 w2)) - "") + (cond ((<= 350 (- d2 d1)) "") + ((= w1 w2) (format " (W%02d)" w1)) + (t (format " (W%02d-W%02d)" w1 w2))) ":\n"))) - (add-text-properties s (1- (point)) (list 'face 'org-agenda-structure - 'org-date-line t)) - (org-agenda-mark-header-line s)) + ;; Add properties if we actually inserted a header. + (when (> (point) s) + (add-text-properties s (1- (point)) + (list 'face 'org-agenda-structure + 'org-date-line t)) + (org-agenda-mark-header-line s))) (while (setq d (pop day-numbers)) (setq date (calendar-gregorian-from-absolute d) s (point)) (if (or (setq todayp (= d today)) (and (not start-pos) (= d sd))) (setq start-pos (point)) - (if (and start-pos (not end-pos)) - (setq end-pos (point)))) + (when (and start-pos (not end-pos)) + (setq end-pos (point)))) (setq files thefiles rtnall nil) (while (setq file (pop files)) @@ -4223,34 +4325,33 @@ items if they have an hour specification like [h]h:mm." file date org-agenda-entry-types))))) (setq rtnall (append rtnall rtn)))) ;; all entries - (if org-agenda-include-diary - (let ((org-agenda-search-headline-for-time t)) - (require 'diary-lib) - (setq rtn (org-get-entries-from-diary date)) - (setq rtnall (append rtnall rtn)))) - (if (or rtnall org-agenda-show-all-dates) - (progn - (setq day-cnt (1+ day-cnt)) - (insert - (if (stringp org-agenda-format-date) - (format-time-string org-agenda-format-date - (org-time-from-absolute date)) - (funcall org-agenda-format-date date)) - "\n") - (put-text-property s (1- (point)) 'face - (org-agenda-get-day-face date)) - (put-text-property s (1- (point)) 'org-date-line t) - (put-text-property s (1- (point)) 'org-agenda-date-header t) - (put-text-property s (1- (point)) 'org-day-cnt day-cnt) - (when todayp - (put-text-property s (1- (point)) 'org-today t)) - (setq rtnall - (org-agenda-add-time-grid-maybe rtnall ndays todayp)) - (if rtnall (insert ;; all entries - (org-agenda-finalize-entries rtnall 'agenda) - "\n")) - (put-text-property s (1- (point)) 'day d) - (put-text-property s (1- (point)) 'org-day-cnt day-cnt)))) + (when org-agenda-include-diary + (let ((org-agenda-search-headline-for-time t)) + (require 'diary-lib) + (setq rtn (org-get-entries-from-diary date)) + (setq rtnall (append rtnall rtn)))) + (when (or rtnall org-agenda-show-all-dates) + (setq day-cnt (1+ day-cnt)) + (insert + (if (stringp org-agenda-format-date) + (format-time-string org-agenda-format-date + (org-time-from-absolute date)) + (funcall org-agenda-format-date date)) + "\n") + (put-text-property s (1- (point)) 'face + (org-agenda-get-day-face date)) + (put-text-property s (1- (point)) 'org-date-line t) + (put-text-property s (1- (point)) 'org-agenda-date-header t) + (put-text-property s (1- (point)) 'org-day-cnt day-cnt) + (when todayp + (put-text-property s (1- (point)) 'org-today t)) + (setq rtnall + (org-agenda-add-time-grid-maybe rtnall ndays todayp)) + (when rtnall (insert ;; all entries + (org-agenda-finalize-entries rtnall 'agenda) + "\n")) + (put-text-property s (1- (point)) 'day d) + (put-text-property s (1- (point)) 'org-day-cnt day-cnt))) (when (and org-agenda-clockreport-mode clocktable-start) (let ((org-agenda-files (org-agenda-files nil 'ifmode)) ;; the above line is to ensure the restricted range! @@ -4264,22 +4365,22 @@ items if they have an hour specification like [h]h:mm." (insert tbl))) (goto-char (point-min)) (or org-agenda-multi (org-agenda-fit-window-to-buffer)) - (unless (and (pos-visible-in-window-p (point-min)) - (pos-visible-in-window-p (point-max))) + (unless (or (not (get-buffer-window)) + (and (pos-visible-in-window-p (point-min)) + (pos-visible-in-window-p (point-max)))) (goto-char (1- (point-max))) (recenter -1) - (if (not (pos-visible-in-window-p (or start-pos 1))) - (progn - (goto-char (or start-pos 1)) - (recenter 1)))) + (when (not (pos-visible-in-window-p (or start-pos 1))) + (goto-char (or start-pos 1)) + (recenter 1))) (goto-char (or start-pos 1)) (add-text-properties (point-min) (point-max) `(org-agenda-type agenda org-last-args (,arg ,start-day ,span) org-redo-cmd ,org-agenda-redo-command org-series-cmd ,org-cmd)) - (if (eq org-agenda-show-log-scoped 'clockcheck) - (org-agenda-show-clocking-issues)) + (when (eq org-agenda-show-log-scoped 'clockcheck) + (org-agenda-show-clocking-issues)) (org-agenda-finalize) (setq buffer-read-only t) (message "")))) @@ -4379,10 +4480,10 @@ This command searches the agenda files, and in addition the files listed in `org-agenda-text-search-extra-files' unless a restriction lock is active." (interactive "P") - (if org-agenda-overriding-arguments - (setq todo-only (car org-agenda-overriding-arguments) - string (nth 1 org-agenda-overriding-arguments) - edit-at (nth 2 org-agenda-overriding-arguments))) + (when org-agenda-overriding-arguments + (setq todo-only (car org-agenda-overriding-arguments) + string (nth 1 org-agenda-overriding-arguments) + edit-at (nth 2 org-agenda-overriding-arguments))) (let* ((props (list 'face nil 'done-face 'org-agenda-done 'org-not-done-regexp org-not-done-regexp @@ -4407,12 +4508,12 @@ is active." (edit-at string)) 'org-agenda-search-history))) (catch 'exit - (if org-agenda-sticky - (setq org-agenda-buffer-name - (if (stringp string) - (format "*Org Agenda(%s:%s)*" - (or org-keys (or (and todo-only "S") "s")) string) - (format "*Org Agenda(%s)*" (or (and todo-only "S") "s"))))) + (when org-agenda-sticky + (setq org-agenda-buffer-name + (if (stringp string) + (format "*Org Agenda(%s:%s)*" + (or org-keys (or (and todo-only "S") "s")) string) + (format "*Org Agenda(%s)*" (or (and todo-only "S") "s"))))) (org-agenda-prepare "SEARCH") (org-compile-prefix-format 'search) (org-set-sorting-strategy 'search) @@ -4430,9 +4531,9 @@ is active." (when (equal (string-to-char words) ?:) (setq full-words t words (substring words 1))) - (if (or org-agenda-search-view-always-boolean - (member (string-to-char words) '(?- ?+ ?\{))) - (setq boolean t)) + (when (or org-agenda-search-view-always-boolean + (member (string-to-char words) '(?- ?+ ?\{))) + (setq boolean t)) (setq words (split-string words)) (let (www w) (while (setq w (pop words)) @@ -4452,12 +4553,12 @@ is active." (when boolean (let (wds w) (while (setq w (pop words)) - (if (or (equal (substring w 0 1) "\"") - (and (> (length w) 1) - (member (substring w 0 1) '("+" "-")) - (equal (substring w 1 2) "\""))) - (while (and words (not (equal (substring w -1) "\""))) - (setq w (concat w " " (pop words))))) + (when (or (equal (substring w 0 1) "\"") + (and (> (length w) 1) + (member (substring w 0 1) '("+" "-")) + (equal (substring w 1 2) "\""))) + (while (and words (not (equal (substring w -1) "\""))) + (setq w (concat w " " (pop words))))) (and (string-match "\\`\\([-+]?\\)\"" w) (setq w (replace-match "\\1" nil nil w))) (and (equal (substring w -1) "\"") (setq w (substring w 0 -1))) @@ -4484,14 +4585,14 @@ is active." (if (not regexps+) (setq regexp org-outline-regexp-bol) (setq regexp (pop regexps+)) - (if hdl-only (setq regexp (concat org-outline-regexp-bol ".*?" - regexp)))) + (when hdl-only (setq regexp (concat org-outline-regexp-bol ".*?" + regexp)))) (setq files (org-agenda-files nil 'ifmode)) ;; Add `org-agenda-text-search-extra-files' unless there is some ;; restriction. - (unless (get 'org-agenda-files 'org-restrict) - (when (eq (car org-agenda-text-search-extra-files) 'agenda-archives) - (pop org-agenda-text-search-extra-files) + (when (eq (car org-agenda-text-search-extra-files) 'agenda-archives) + (pop org-agenda-text-search-extra-files) + (unless (get 'org-agenda-files 'org-restrict) (setq files (org-add-archive-files files)))) ;; Uniquify files. However, let `org-check-agenda-file' handle ;; non-existent ones. @@ -4509,10 +4610,10 @@ is active." (setq buffer (if (file-exists-p file) (org-get-agenda-file-buffer file) (error "No such file %s" file))) - (if (not buffer) - ;; If file does not exist, make sure an error message is sent - (setq rtn (list (format "ORG-AGENDA-ERROR: No such org-file %s" - file)))) + (unless buffer + ;; If file does not exist, make sure an error message is sent + (setq rtn (list (format "ORG-AGENDA-ERROR: No such org-file %s" + file)))) (with-current-buffer buffer (with-syntax-table (org-search-syntax-table) (unless (derived-mode-p 'org-mode) @@ -4555,12 +4656,12 @@ is active." (point-at-bol) (if hdl-only (point-at-eol) end))) (mapc (lambda (wr) (when (string-match wr str) - (goto-char (1- end)) - (throw :skip t))) + (goto-char (1- end)) + (throw :skip t))) regexps-) (mapc (lambda (wr) (unless (string-match wr str) - (goto-char (1- end)) - (throw :skip t))) + (goto-char (1- end)) + (throw :skip t))) (if todo-only (cons (concat "^\\*+[ \t]+" org-not-done-regexp) @@ -4577,7 +4678,7 @@ is active." (and (eq org-agenda-show-inherited-tags t) (or (eq org-agenda-use-tag-inheritance t) (memq 'todo org-agenda-use-tag-inheritance)))) - tags (org-get-tags-at nil (not inherited-tags)) + tags (org-get-tags nil (not inherited-tags)) txt (org-agenda-format-item "" (buffer-substring-no-properties @@ -4594,25 +4695,25 @@ is active." (goto-char (1- end)))))))))) (setq rtn (nreverse ee)) (setq rtnall (append rtnall rtn))) - (if org-agenda-overriding-header - (insert (org-add-props (copy-sequence org-agenda-overriding-header) - nil 'face 'org-agenda-structure) "\n") - (insert "Search words: ") - (add-text-properties (point-min) (1- (point)) - (list 'face 'org-agenda-structure)) - (setq pos (point)) - (insert string "\n") - (add-text-properties pos (1- (point)) (list 'face 'org-warning)) - (setq pos (point)) - (unless org-agenda-multi - (insert (substitute-command-keys "\ + (org-agenda--insert-overriding-header + (with-temp-buffer + (insert "Search words: ") + (add-text-properties (point-min) (1- (point)) + (list 'face 'org-agenda-structure)) + (setq pos (point)) + (insert string "\n") + (add-text-properties pos (1- (point)) (list 'face 'org-warning)) + (setq pos (point)) + (unless org-agenda-multi + (insert (substitute-command-keys "\\<org-agenda-mode-map>\ Press `\\[org-agenda-manipulate-query-add]', \ `\\[org-agenda-manipulate-query-subtract]' to add/sub word, \ `\\[org-agenda-manipulate-query-add-re]', \ `\\[org-agenda-manipulate-query-subtract-re]' to add/sub regexp, \ -`\\[universal-argument] \\[org-agenda-redo]' to edit\n")) - (add-text-properties pos (1- (point)) - (list 'face 'org-agenda-structure)))) +`\\[universal-argument] \\[org-agenda-redo]' for a fresh search\n")) + (add-text-properties pos (1- (point)) + (list 'face 'org-agenda-structure))) + (buffer-string))) (org-agenda-mark-header-line (point-min)) (when rtnall (insert (org-agenda-finalize-entries rtnall 'search) "\n")) @@ -4651,31 +4752,31 @@ the list to these. When using `\\[universal-argument]', you will be prompted for a keyword. A numeric prefix directly selects the Nth keyword in `org-todo-keywords-1'." (interactive "P") - (if org-agenda-overriding-arguments - (setq arg org-agenda-overriding-arguments)) - (if (and (stringp arg) (not (string-match "\\S-" arg))) (setq arg nil)) + (when org-agenda-overriding-arguments + (setq arg org-agenda-overriding-arguments)) + (when (and (stringp arg) (not (string-match "\\S-" arg))) (setq arg nil)) (let* ((today (org-today)) (date (calendar-gregorian-from-absolute today)) - (kwds org-todo-keywords-for-agenda) (completion-ignore-case t) - (org-select-this-todo-keyword - (if (stringp arg) arg - (and arg (integerp arg) (> arg 0) - (nth (1- arg) kwds)))) - rtn rtnall files file pos) - (when (equal arg '(4)) - (setq org-select-this-todo-keyword - (completing-read "Keyword (or KWD1|K2D2|...): " - (mapcar #'list kwds) nil nil))) - (and (equal 0 arg) (setq org-select-this-todo-keyword nil)) + kwds org-select-this-todo-keyword rtn rtnall files file pos) (catch 'exit - (if org-agenda-sticky - (setq org-agenda-buffer-name - (if (stringp org-select-this-todo-keyword) - (format "*Org Agenda(%s:%s)*" (or org-keys "t") - org-select-this-todo-keyword) - (format "*Org Agenda(%s)*" (or org-keys "t"))))) + (when org-agenda-sticky + (setq org-agenda-buffer-name + (if (stringp org-select-this-todo-keyword) + (format "*Org Agenda(%s:%s)*" (or org-keys "t") + org-select-this-todo-keyword) + (format "*Org Agenda(%s)*" (or org-keys "t"))))) (org-agenda-prepare "TODO") + (setq kwds org-todo-keywords-for-agenda + org-select-this-todo-keyword (if (stringp arg) arg + (and (integerp arg) + (> arg 0) + (nth (1- arg) kwds)))) + (when (equal arg '(4)) + (setq org-select-this-todo-keyword + (completing-read "Keyword (or KWD1|K2D2|...): " + (mapcar #'list kwds) nil nil))) + (and (equal 0 arg) (setq org-select-this-todo-keyword nil)) (org-compile-prefix-format 'todo) (org-set-sorting-strategy 'todo) (setq org-agenda-redo-command @@ -4690,31 +4791,31 @@ for a keyword. A numeric prefix directly selects the Nth keyword in (org-check-agenda-file file) (setq rtn (org-agenda-get-day-entries file date :todo)) (setq rtnall (append rtnall rtn)))) - (if org-agenda-overriding-header - (insert (org-add-props (copy-sequence org-agenda-overriding-header) - nil 'face 'org-agenda-structure) "\n") - (insert "Global list of TODO items of type: ") - (add-text-properties (point-min) (1- (point)) - (list 'face 'org-agenda-structure - 'short-heading - (concat "ToDo: " - (or org-select-this-todo-keyword "ALL")))) - (org-agenda-mark-header-line (point-min)) - (insert (org-agenda-propertize-selected-todo-keywords - org-select-this-todo-keyword)) - (setq pos (point)) - (unless org-agenda-multi - (insert (substitute-command-keys "Available with \ -`N \\[org-agenda-redo]': (0)[ALL]")) - (let ((n 0) s) - (mapc (lambda (x) - (setq s (format "(%d)%s" (setq n (1+ n)) x)) - (if (> (+ (current-column) (string-width s) 1) (frame-width)) - (insert "\n ")) - (insert " " s)) - kwds)) - (insert "\n")) - (add-text-properties pos (1- (point)) (list 'face 'org-agenda-structure))) + (org-agenda--insert-overriding-header + (with-temp-buffer + (insert "Global list of TODO items of type: ") + (add-text-properties (point-min) (1- (point)) + (list 'face 'org-agenda-structure + 'short-heading + (concat "ToDo: " + (or org-select-this-todo-keyword "ALL")))) + (org-agenda-mark-header-line (point-min)) + (insert (org-agenda-propertize-selected-todo-keywords + org-select-this-todo-keyword)) + (setq pos (point)) + (unless org-agenda-multi + (insert (substitute-command-keys "Press \ +\\<org-agenda-mode-map>`N \\[org-agenda-redo]' (e.g. `0 \\[org-agenda-redo]') \ +to search again: (0)[ALL]")) + (let ((n 0)) + (dolist (k kwds) + (let ((s (format "(%d)%s" (cl-incf n) k))) + (when (> (+ (current-column) (string-width s) 1) (window-width)) + (insert "\n ")) + (insert " " s)))) + (insert "\n")) + (add-text-properties pos (1- (point)) (list 'face 'org-agenda-structure)) + (buffer-string))) (org-agenda-mark-header-line (point-min)) (when rtnall (insert (org-agenda-finalize-entries rtnall 'todo) "\n")) @@ -4735,9 +4836,9 @@ for a keyword. A numeric prefix directly selects the Nth keyword in "Show all headlines for all `org-agenda-files' matching a TAGS criterion. The prefix arg TODO-ONLY limits the search to TODO entries." (interactive "P") - (if org-agenda-overriding-arguments - (setq todo-only (car org-agenda-overriding-arguments) - match (nth 1 org-agenda-overriding-arguments))) + (when org-agenda-overriding-arguments + (setq todo-only (car org-agenda-overriding-arguments) + match (nth 1 org-agenda-overriding-arguments))) (let* ((org-tags-match-list-sublevels org-tags-match-list-sublevels) (completion-ignore-case t) @@ -4747,17 +4848,18 @@ The prefix arg TODO-ONLY limits the search to TODO entries." (when (and (stringp match) (not (string-match "\\S-" match))) (setq match nil)) (catch 'exit - (if org-agenda-sticky - (setq org-agenda-buffer-name - (if (stringp match) - (format "*Org Agenda(%s:%s)*" - (or org-keys (or (and todo-only "M") "m")) match) - (format "*Org Agenda(%s)*" (or (and todo-only "M") "m"))))) + ;; TODO: this code is repeated a lot... + (when org-agenda-sticky + (setq org-agenda-buffer-name + (if (stringp match) + (format "*Org Agenda(%s:%s)*" + (or org-keys (or (and todo-only "M") "m")) match) + (format "*Org Agenda(%s)*" (or (and todo-only "M") "m"))))) + (setq matcher (org-make-tags-matcher match)) ;; Prepare agendas (and `org-tag-alist-for-agenda') before ;; expanding tags within `org-make-tags-matcher' (org-agenda-prepare (concat "TAGS " match)) - (setq matcher (org-make-tags-matcher match) - match (car matcher) + (setq match (car matcher) matcher (cdr matcher)) (org-compile-prefix-format 'tags) (org-set-sorting-strategy 'tags) @@ -4792,24 +4894,25 @@ The prefix arg TODO-ONLY limits the search to TODO entries." matcher org--matcher-tags-todo-only)) (setq rtnall (append rtnall rtn)))))))) - (if org-agenda-overriding-header - (insert (org-add-props (copy-sequence org-agenda-overriding-header) - nil 'face 'org-agenda-structure) "\n") - (insert "Headlines with TAGS match: ") - (add-text-properties (point-min) (1- (point)) - (list 'face 'org-agenda-structure - 'short-heading - (concat "Match: " match))) - (setq pos (point)) - (insert match "\n") - (add-text-properties pos (1- (point)) (list 'face 'org-warning)) - (setq pos (point)) - (unless org-agenda-multi - (insert (substitute-command-keys - "Press `\\[universal-argument] \\[org-agenda-redo]' \ -to search again with new search string\n"))) - (add-text-properties pos (1- (point)) - (list 'face 'org-agenda-structure))) + (org-agenda--insert-overriding-header + (with-temp-buffer + (insert "Headlines with TAGS match: ") + (add-text-properties (point-min) (1- (point)) + (list 'face 'org-agenda-structure + 'short-heading + (concat "Match: " match))) + (setq pos (point)) + (insert match "\n") + (add-text-properties pos (1- (point)) (list 'face 'org-warning)) + (setq pos (point)) + (unless org-agenda-multi + (insert (substitute-command-keys + "Press \ +\\<org-agenda-mode-map>`\\[universal-argument] \\[org-agenda-redo]' \ +to search again\n"))) + (add-text-properties pos (1- (point)) + (list 'face 'org-agenda-structure)) + (buffer-string))) (org-agenda-mark-header-line (point-min)) (when rtnall (insert (org-agenda-finalize-entries rtnall 'tags) "\n")) @@ -4833,8 +4936,11 @@ used by user-defined selections using `org-agenda-skip-function'.") (defvar org-agenda-overriding-header nil "When set during agenda, todo and tags searches it replaces the header. -This variable should not be set directly, but custom commands can bind it -in the options section.") +If an empty string, no header will be inserted. If any other +string, it will be inserted as a header. If nil, a header will +be generated automatically according to the command. This +variable should not be set directly, but custom commands can bind +it in the options section.") (defun org-agenda-skip-entry-if (&rest conditions) "Skip entry if any of CONDITIONS is true. @@ -4981,14 +5087,14 @@ of what a project is and how to check if it stuck, customize the variable (format "^\\*+[ \t]+\\(%s\\)\\>" (mapconcat #'identity todo-wds "\\|")))) (tags-re (cond ((null tags) nil) - ((member "*" tags) - (eval-when-compile + ((member "*" tags) org-tag-line-re) + (tags + (let ((other-tags (format "\\(?:%s:\\)*" org-tag-re))) (concat org-outline-regexp-bol - ".*:[[:alnum:]_@#%]+:[ \t]*$"))) - (tags (concat org-outline-regexp-bol - ".*:\\(" - (mapconcat #'identity tags "\\|") - "\\):[[:alnum:]_@#%:]*[ \t]*$")) + ".*?[ \t]:" + other-tags + (regexp-opt tags t) + ":" other-tags "[ \t]*$"))) (t nil))) (re-list (delq nil (list todo-re tags-re gen-re))) (skip-re @@ -5090,23 +5196,10 @@ each date. It also removes lines that contain only whitespace." (while (re-search-forward "^ +\n" nil t) (replace-match "")) (goto-char (point-min)) - (if (re-search-forward "^Org mode dummy\n?" nil t) - (replace-match "")) + (when (re-search-forward "^Org mode dummy\n?" nil t) + (replace-match "")) (run-hooks 'org-agenda-cleanup-fancy-diary-hook)) -;; Make sure entries from the diary have the right text properties. -(eval-after-load "diary-lib" - '(if (boundp 'diary-modify-entry-list-string-function) - ;; We can rely on the hook, nothing to do - nil - ;; Hook not available, must use advice to make this work - (defadvice add-to-diary-list (before org-mark-diary-entry activate) - "Make the position visible." - (if (and org-disable-agenda-to-diary ;; called from org-agenda - (stringp string) - buffer-file-name) - (setq string (org-modify-diary-entry-string string)))))) - (defun org-modify-diary-entry-string (string) "Add text properties to string, allowing Org to act on it." (org-add-props string nil @@ -5184,14 +5277,14 @@ function from a program - use `org-agenda-get-day-entries' instead." (setq org-diary-last-run-time time) ;; If this is called during org-agenda, don't return any entries to ;; the calendar. Org Agenda will list these entries itself. - (if org-disable-agenda-to-diary (setq files nil)) + (when org-disable-agenda-to-diary (setq files nil)) (while (setq file (pop files)) (setq rtn (apply 'org-agenda-get-day-entries file date args)) (setq results (append results rtn))) (when results (setq results (mapcar (lambda (i) (replace-regexp-in-string - org-bracket-link-regexp "\\3" i)) results)) + org-link-bracket-re "\\2" i)) results)) (concat (org-agenda-finalize-entries results) "\n")))) ;;; Agenda entry finders @@ -5366,7 +5459,7 @@ and the timestamp type relevant for the sorting strategy in (and (eq org-agenda-show-inherited-tags t) (or (eq org-agenda-use-tag-inheritance t) (memq 'todo org-agenda-use-tag-inheritance)))) - tags (org-get-tags-at nil (not inherited-tags)) + tags (org-get-tags nil (not inherited-tags)) level (make-string (org-reduced-level (org-outline-level)) ? ) txt (org-agenda-format-item "" txt level category tags t) priority (1+ (org-get-priority txt))) @@ -5421,7 +5514,7 @@ This function is invoked if `org-agenda-todo-ignore-deadlines', (and org-agenda-todo-ignore-deadlines (re-search-forward org-deadline-time-regexp end t) (cond - ((memq org-agenda-todo-ignore-deadlines '(t all)) t) + ((eq org-agenda-todo-ignore-deadlines 'all) t) ((eq org-agenda-todo-ignore-deadlines 'far) (not (org-deadline-close-p (match-string 1)))) ((eq org-agenda-todo-ignore-deadlines 'future) @@ -5576,7 +5669,7 @@ displayed in agenda view." (or (eq org-agenda-use-tag-inheritance t) (memq 'agenda org-agenda-use-tag-inheritance))))) - (tags (org-get-tags-at nil (not inherited-tags))) + (tags (org-get-tags nil (not inherited-tags))) (level (make-string (org-reduced-level (org-outline-level)) ?\s)) (head (and (looking-at "\\*+[ \t]+\\(.*\\)") @@ -5640,7 +5733,7 @@ displayed in agenda view." (and (eq org-agenda-show-inherited-tags t) (or (eq org-agenda-use-tag-inheritance t) (memq 'agenda org-agenda-use-tag-inheritance)))) - tags (org-get-tags-at nil (not inherited-tags)) + tags (org-get-tags nil (not inherited-tags)) todo-state (org-get-todo-state) warntime (get-text-property (point) 'org-appt-warntime) extra nil) @@ -5728,10 +5821,10 @@ then those holidays will be skipped." (parts (delq nil (list - (if (memq 'closed items) (concat "\\<" org-closed-string)) - (if (memq 'clock items) (concat "\\<" org-clock-string)) - (if (memq 'state items) - (format "- State \"%s\".*?" org-todo-regexp))))) + (when (memq 'closed items) (concat "\\<" org-closed-string)) + (when (memq 'clock items) (concat "\\<" org-clock-string)) + (when (memq 'state items) + (format "- +State \"%s\".*?" org-todo-regexp))))) (parts-re (if parts (mapconcat 'identity parts "\\|") (error "`org-agenda-log-mode-items' is empty"))) (regexp (concat @@ -5745,7 +5838,7 @@ then those holidays will be skipped." 0 0 0 (nth 1 date) (car date) (nth 2 date))) 1 11)))) (org-agenda-search-headline-for-time nil) - marker hdmarker priority category level tags closedp + marker hdmarker priority category level tags closedp type statep clockp state ee txt extra timestr rest clocked inherited-tags) (goto-char (point-min)) (while (re-search-forward regexp nil t) @@ -5790,7 +5883,7 @@ then those holidays will be skipped." (and (eq org-agenda-show-inherited-tags t) (or (eq org-agenda-use-tag-inheritance t) (memq 'todo org-agenda-use-tag-inheritance)))) - tags (org-get-tags-at nil (not inherited-tags)) + tags (org-get-tags nil (not inherited-tags)) level (make-string (org-reduced-level (org-outline-level)) ? )) (looking-at "\\*+[ \t]+\\([^\r\n]+\\)") (setq txt (match-string 1)) @@ -5805,11 +5898,14 @@ then those holidays will be skipped." (statep (concat "State: (" state ")")) (t (concat "Clocked: (" clocked ")"))) txt level category tags timestr))) + (setq type (cond (closedp "closed") + (statep "state") + (t "clock"))) (setq priority 100000) (org-add-props txt props 'org-marker marker 'org-hd-marker hdmarker 'face 'org-agenda-done 'priority priority 'level level - 'type "closed" 'date date + 'type type 'date date 'undone-face 'org-warning 'done-face 'org-agenda-done) (push txt ee)) (goto-char (point-at-eol)))) @@ -5854,7 +5950,14 @@ See also the user option `org-agenda-clock-consistency-checks'." (error "No valid Clock line") (throw 'next t)) (unless (match-end 3) - (setq issue "No end time" + (setq issue + (format + "No end time: (%s)" + (org-duration-from-minutes + (floor + (- (float-time (org-current-time)) + (float-time (org-time-string-to-time (match-string 1)))) + 60))) face (or (plist-get pl :no-end-time-face) face)) (throw 'next t)) (setq ts (match-string 1) @@ -5904,15 +6007,15 @@ See also the user option `org-agenda-clock-consistency-checks'." (unless ok-list ;; there are no OK times for gaps... (throw 'exit nil)) - (if (> (- (/ t2 36000) (/ t1 36000)) 24) - ;; This is more than 24 hours, so it is OK. - ;; because we have at least one OK time, that must be in the - ;; 24 hour interval. - (throw 'exit t)) + (when (> (- (/ t2 36000) (/ t1 36000)) 24) + ;; This is more than 24 hours, so it is OK. + ;; because we have at least one OK time, that must be in the + ;; 24 hour interval. + (throw 'exit t)) ;; We have a shorter gap. ;; Now we have to get the minute of the day when these times are - (let* ((t1dec (decode-time t1)) - (t2dec (decode-time t2)) + (let* ((t1dec (org-decode-time t1)) + (t2dec (org-decode-time t2)) ;; compute the minute on the day (min1 (+ (nth 1 t1dec) (* 60 (nth 2 t1dec)))) (min2 (+ (nth 1 t2dec) (* 60 (nth 2 t2dec))))) @@ -5923,7 +6026,7 @@ See also the user option `org-agenda-clock-consistency-checks'." ;; Now check if any of the OK times is in the gap (mapc (lambda (x) ;; Wrap the time to after midnight if necessary - (if (< x min1) (setq x (+ x 1440))) + (when (< x min1) (setq x (+ x 1440))) ;; Check if in interval (and (<= min1 x) (>= min2 x) (throw 'exit t))) ok-list) @@ -6004,10 +6107,7 @@ specification like [h]h:mm." org-deadline-warning-days)) ;; Set pre-warning to deadline. (t 0)))) - (wdays (if suppress-prewarning - (let ((org-deadline-warning-days suppress-prewarning)) - (org-get-wdays s)) - (org-get-wdays s)))) + (wdays (or suppress-prewarning (org-get-wdays s)))) (cond ;; Only display deadlines at their base date, at future ;; repeat occurrences or in today agenda. @@ -6039,7 +6139,7 @@ specification like [h]h:mm." (or (eq org-agenda-use-tag-inheritance t) (memq 'agenda org-agenda-use-tag-inheritance))))) - (tags (org-get-tags-at nil (not inherited-tags))) + (tags (org-get-tags nil (not inherited-tags))) (time (cond ;; No time of day designation if it is only @@ -6154,6 +6254,7 @@ scheduled items with an hour specification like [h]h:mm." (diff (- current schedule)) (warntime (get-text-property (point) 'org-appt-warntime)) (pastschedp (< schedule today)) + (futureschedp (> schedule today)) (habitp (and (fboundp 'org-is-habit-p) (org-is-habit-p))) (suppress-delay (let ((deadline (and org-agenda-skip-scheduled-delay-if-deadline @@ -6191,7 +6292,8 @@ scheduled items with an hour specification like [h]h:mm." habitp (bound-and-true-p org-habit-show-all-today)) (when (or (and (> ddays 0) (< diff ddays)) - (> diff org-scheduled-past-days) + (> diff (or (and habitp org-habit-scheduled-past-days) + org-scheduled-past-days)) (> schedule current) (and (/= current schedule) (/= current today) @@ -6239,15 +6341,23 @@ scheduled items with an hour specification like [h]h:mm." (or (eq org-agenda-use-tag-inheritance t) (memq 'agenda org-agenda-use-tag-inheritance))))) - (tags (org-get-tags-at nil (not inherited-tags))) + (tags (org-get-tags nil (not inherited-tags))) (level (make-string (org-reduced-level (org-outline-level)) ?\s)) (head (buffer-substring (point) (line-end-position))) (time (cond - ;; No time of day designation if it is only - ;; a reminder. - ((and (/= current schedule) (/= current repeat)) nil) + ;; No time of day designation if it is only a + ;; reminder, except for habits, which always show + ;; the time of day. Habits are an exception + ;; because if there is a time of day, that is + ;; interpreted to mean they should usually happen + ;; then, even if doing the habit was missed. + ((and + (not habitp) + (/= current schedule) + (/= current repeat)) + nil) ((string-match " \\([012]?[0-9]:[0-9][0-9]\\)" s) (concat (substring s (match-beginning 1)) " ")) (t 'time))) @@ -6261,6 +6371,8 @@ scheduled items with an hour specification like [h]h:mm." head level category tags time nil habitp)) (face (cond ((and (not habitp) pastschedp) 'org-scheduled-previously) + ((and habitp futureschedp) + 'org-agenda-done) (todayp 'org-scheduled-today) (t 'org-scheduled))) (habitp (and habitp (org-habit-parse-todo)))) @@ -6324,59 +6436,59 @@ scheduled items with an hour specification like [h]h:mm." pos (current-buffer) (error-message-string err)))))) - (if (and (> (- d0 d1) -1) (> (- d2 d0) -1)) - ;; Only allow days between the limits, because the normal - ;; date stamps will catch the limits. - (save-excursion - (setq todo-state (org-get-todo-state)) - (setq donep (member todo-state org-done-keywords)) - (if (and donep org-agenda-skip-timestamp-if-done) - (throw :skip t)) - (setq marker (org-agenda-new-marker (point)) - category (org-get-category)) - (if (not (re-search-backward org-outline-regexp-bol nil t)) - (throw :skip nil) - (goto-char (match-beginning 0)) - (setq hdmarker (org-agenda-new-marker (point)) - inherited-tags - (or (eq org-agenda-show-inherited-tags 'always) - (and (listp org-agenda-show-inherited-tags) - (memq 'agenda org-agenda-show-inherited-tags)) - (and (eq org-agenda-show-inherited-tags t) - (or (eq org-agenda-use-tag-inheritance t) - (memq 'agenda org-agenda-use-tag-inheritance)))) - - tags (org-get-tags-at nil (not inherited-tags))) - (setq level (make-string (org-reduced-level (org-outline-level)) ? )) - (looking-at "\\*+[ \t]+\\(.*\\)") - (setq head (match-string 1)) - (let ((remove-re - (if org-agenda-remove-timeranges-from-blocks - (concat - "<" (regexp-quote s1) ".*?>" - "--" - "<" (regexp-quote s2) ".*?>") - nil))) - (setq txt (org-agenda-format-item - (format - (nth (if (= d1 d2) 0 1) - org-agenda-timerange-leaders) - (1+ (- d0 d1)) (1+ (- d2 d1))) - head level category tags - (cond ((and (= d1 d0) (= d2 d0)) - (concat "<" start-time ">--<" end-time ">")) - ((= d1 d0) - (concat "<" start-time ">")) - ((= d2 d0) - (concat "<" end-time ">"))) - remove-re)))) - (org-add-props txt props - 'org-marker marker 'org-hd-marker hdmarker - 'type "block" 'date date - 'level level - 'todo-state todo-state - 'priority (org-get-priority txt)) - (push txt ee)))) + (when (and (> (- d0 d1) -1) (> (- d2 d0) -1)) + ;; Only allow days between the limits, because the normal + ;; date stamps will catch the limits. + (save-excursion + (setq todo-state (org-get-todo-state)) + (setq donep (member todo-state org-done-keywords)) + (when (and donep org-agenda-skip-timestamp-if-done) + (throw :skip t)) + (setq marker (org-agenda-new-marker (point)) + category (org-get-category)) + (if (not (re-search-backward org-outline-regexp-bol nil t)) + (throw :skip nil) + (goto-char (match-beginning 0)) + (setq hdmarker (org-agenda-new-marker (point)) + inherited-tags + (or (eq org-agenda-show-inherited-tags 'always) + (and (listp org-agenda-show-inherited-tags) + (memq 'agenda org-agenda-show-inherited-tags)) + (and (eq org-agenda-show-inherited-tags t) + (or (eq org-agenda-use-tag-inheritance t) + (memq 'agenda org-agenda-use-tag-inheritance)))) + + tags (org-get-tags nil (not inherited-tags))) + (setq level (make-string (org-reduced-level (org-outline-level)) ? )) + (looking-at "\\*+[ \t]+\\(.*\\)") + (setq head (match-string 1)) + (let ((remove-re + (if org-agenda-remove-timeranges-from-blocks + (concat + "<" (regexp-quote s1) ".*?>" + "--" + "<" (regexp-quote s2) ".*?>") + nil))) + (setq txt (org-agenda-format-item + (format + (nth (if (= d1 d2) 0 1) + org-agenda-timerange-leaders) + (1+ (- d0 d1)) (1+ (- d2 d1))) + head level category tags + (cond ((and (= d1 d0) (= d2 d0)) + (concat "<" start-time ">--<" end-time ">")) + ((= d1 d0) + (concat "<" start-time ">")) + ((= d2 d0) + (concat "<" end-time ">"))) + remove-re)))) + (org-add-props txt props + 'org-marker marker 'org-hd-marker hdmarker + 'type "block" 'date date + 'level level + 'todo-state todo-state + 'priority (org-get-priority txt)) + (push txt ee)))) (goto-char pos))) ;; Sort the entries by expiration date. (nreverse ee))) @@ -6460,9 +6572,9 @@ Any match of REMOVE-RE will be removed from TXT." (tag (if tags (nth (1- (length tags)) tags) "")) (time-grid-trailing-characters (nth 2 org-agenda-time-grid)) time - (ts (if dotime (concat - (if (stringp dotime) dotime "") - (and org-agenda-search-headline-for-time txt)))) + (ts (when dotime (concat + (if (stringp dotime) dotime "") + (and org-agenda-search-headline-for-time txt)))) (time-of-day (and dotime (org-get-time-of-day ts))) stamp plain s0 s1 s2 rtn srp l duration breadcrumbs) @@ -6480,17 +6592,17 @@ Any match of REMOVE-RE will be removed from TXT." ;; If the times are in TXT (not in DOTIMES), and the prefix will list ;; them, we might want to remove them there to avoid duplication. ;; The user can turn this off with a variable. - (if (and org-prefix-has-time - org-agenda-remove-times-when-in-prefix (or stamp plain) - (string-match (concat (regexp-quote s0) " *") txt) - (not (equal ?\] (string-to-char (substring txt (match-end 0))))) - (if (eq org-agenda-remove-times-when-in-prefix 'beg) - (= (match-beginning 0) 0) - t)) - (setq txt (replace-match "" nil nil txt)))) + (when (and org-prefix-has-time + org-agenda-remove-times-when-in-prefix (or stamp plain) + (string-match (concat (regexp-quote s0) " *") txt) + (not (equal ?\] (string-to-char (substring txt (match-end 0))))) + (if (eq org-agenda-remove-times-when-in-prefix 'beg) + (= (match-beginning 0) 0) + t)) + (setq txt (replace-match "" nil nil txt)))) ;; Normalize the time(s) to 24 hour - (if s1 (setq s1 (org-get-time-of-day s1 'string t))) - (if s2 (setq s2 (org-get-time-of-day s2 'string t))) + (when s1 (setq s1 (org-get-time-of-day s1 'string t))) + (when s2 (setq s2 (org-get-time-of-day s2 'string t))) ;; Try to set s2 if s1 and ;; `org-agenda-default-appointment-duration' are set @@ -6506,7 +6618,7 @@ Any match of REMOVE-RE will be removed from TXT." (setq duration (- (org-duration-to-minutes s2) (org-duration-to-minutes s1))))) - (when (string-match "\\([ \t]+\\)\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$" txt) + (when (string-match org-tag-group-re txt) ;; Tags are in the string (if (or (eq org-agenda-remove-tags t) (and org-agenda-remove-tags @@ -6514,7 +6626,7 @@ Any match of REMOVE-RE will be removed from TXT." (setq txt (replace-match "" t t txt)) (setq txt (replace-match (concat (make-string (max (- 50 (length txt)) 1) ?\ ) - (match-string 2 txt)) + (match-string 1 txt)) t t txt)))) (when remove-re @@ -6526,14 +6638,16 @@ Any match of REMOVE-RE will be removed from TXT." (add-text-properties 0 (length txt) '(org-heading t) txt) ;; Prepare the variables needed in the eval of the compiled format - (if org-prefix-has-breadcrumbs - (setq breadcrumbs (org-with-point-at (org-get-at-bol 'org-marker) - (let ((s (org-display-outline-path nil nil "->" t))) - (if (eq "" s) "" (concat s "->")))))) + (when org-prefix-has-breadcrumbs + (setq breadcrumbs (org-with-point-at (org-get-at-bol 'org-marker) + (let ((s (org-format-outline-path (org-get-outline-path) + (1- (frame-width)) + nil org-agenda-breadcrumbs-separator))) + (if (eq "" s) "" (concat s org-agenda-breadcrumbs-separator)))))) (setq time (cond (s2 (concat (org-agenda-time-of-day-to-ampm-maybe s1) "-" (org-agenda-time-of-day-to-ampm-maybe s2) - (if org-agenda-timegrid-use-ampm " "))) + (when org-agenda-timegrid-use-ampm " "))) (s1 (concat (org-agenda-time-of-day-to-ampm-maybe s1) (if org-agenda-timegrid-use-ampm @@ -6543,19 +6657,17 @@ Any match of REMOVE-RE will be removed from TXT." extra (or (and (not habitp) extra) "") category (if (symbolp category) (symbol-name category) category) level (or level "")) - (if (string-match org-bracket-link-regexp category) + (if (string-match org-link-bracket-re category) (progn - (setq l (if (match-end 3) - (- (match-end 3) (match-beginning 3)) - (- (match-end 1) (match-beginning 1)))) + (setq l (string-width (or (match-string 2) (match-string 1)))) (when (< l (or org-prefix-category-length 0)) (setq category (copy-sequence category)) (org-add-props category nil 'extra-space (make-string (- org-prefix-category-length l 1) ?\ )))) - (if (and org-prefix-category-max-length - (>= (length category) org-prefix-category-max-length)) - (setq category (substring category 0 (1- org-prefix-category-max-length))))) + (when (and org-prefix-category-max-length + (>= (length category) org-prefix-category-max-length)) + (setq category (substring category 0 (1- org-prefix-category-max-length))))) ;; Evaluate the compiled format (setq rtn (concat (eval formatter) txt)) @@ -6581,8 +6693,8 @@ Any match of REMOVE-RE will be removed from TXT." The modified list may contain inherited tags, and tags matched by `org-agenda-hide-tags-regexp' will be removed." (when (or add-inherited hide-re) - (if (string-match "\\([ \t]+\\)\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$" txt) - (setq txt (substring txt 0 (match-beginning 0)))) + (when (string-match org-tag-group-re txt) + (setq txt (substring txt 0 (match-beginning 0)))) (setq tags (delq nil (mapcar (lambda (tg) @@ -6636,9 +6748,9 @@ TODAYP is t when the current agenda view is on today." (req (car org-agenda-time-grid)) (remove (member 'remove-match req)) new time) - (if (and (member 'require-timed req) (not have)) - ;; don't show empty grid - (throw 'exit list)) + (when (and (member 'require-timed req) (not have)) + ;; don't show empty grid + (throw 'exit list)) (while (setq time (pop gridtimes)) (unless (and remove (member time have)) (setq time (replace-regexp-in-string " " "0" (format "%04s" time))) @@ -6686,10 +6798,11 @@ and stored in the variable `org-prefix-format-compiled'." c (or (match-string 3 s) "") opt (match-beginning 1) start (1+ (match-beginning 0))) - (if (eq var 'time) (setq org-prefix-has-time t)) - (if (eq var 'tag) (setq org-prefix-has-tag t)) - (if (eq var 'effort) (setq org-prefix-has-effort t)) - (if (eq var 'breadcrumbs) (setq org-prefix-has-breadcrumbs t)) + (cl-case var + (time (setq org-prefix-has-time t)) + (tag (setq org-prefix-has-tag t)) + (effort (setq org-prefix-has-effort t)) + (breadcrumbs (setq org-prefix-has-breadcrumbs t))) (setq f (concat "%" (match-string 2 s) "s")) (when (eq var 'category) (setq org-prefix-category-length @@ -6697,8 +6810,8 @@ and stored in the variable `org-prefix-format-compiled'." (setq org-prefix-category-max-length (let ((x (match-string 2 s))) (save-match-data - (if (string-match "\\.[0-9]+" x) - (string-to-number (substring (match-string 0 x) 1))))))) + (and (string-match "\\.[0-9]+" x) + (string-to-number (substring (match-string 0 x) 1))))))) (if (eq var 'eval) (setq varform `(format ,f (org-eval ,(read (match-string 4 s))))) (if opt @@ -6746,7 +6859,7 @@ HH:MM." (not (eq (get-text-property 1 'face s) 'org-link))) (let* ((h (string-to-number (match-string 1 s))) (m (if (match-end 3) (string-to-number (match-string 3 s)) 0)) - (ampm (if (match-end 4) (downcase (match-string 4 s)))) + (ampm (when (match-end 4) (downcase (match-string 4 s)))) (am-p (equal ampm "am")) (h1 (cond ((not ampm) h) ((= h 12) (if am-p 0 12)) @@ -6823,7 +6936,7 @@ The optional argument TYPE tells the agenda type." "Limit the number of agenda entries." (let ((include (and limit (< limit 0)))) (if limit - (let ((fun (or fn (lambda (p) (if p 1)))) + (let ((fun (or fn (lambda (p) (when p 1)))) (lim 0)) (delq nil (mapcar @@ -6831,7 +6944,7 @@ The optional argument TYPE tells the agenda type." (let ((pval (funcall fun (get-text-property (1- (length e)) prop e)))) - (if pval (setq lim (+ lim pval))) + (when pval (setq lim (+ lim pval))) (cond ((and pval (<= lim (abs limit))) e) ((and include (not pval)) e)))) list))) @@ -6969,16 +7082,17 @@ The optional argument TYPE tells the agenda type." (tb (and plb (substring b plb))) (case-fold-search nil)) (when pla - (if (string-match (concat "\\`[ \t]*" (or (get-text-property 0 'org-todo-regexp a) "") - "\\([ \t]*\\[[a-zA-Z0-9]\\]\\)? *") ta) - (setq ta (substring ta (match-end 0)))) + (when (string-match (concat "\\`[ \t]*" (or (get-text-property 0 'org-todo-regexp a) "") + "\\([ \t]*\\[[a-zA-Z0-9]\\]\\)? *") ta) + (setq ta (substring ta (match-end 0)))) (setq ta (downcase ta))) (when plb - (if (string-match (concat "\\`[ \t]*" (or (get-text-property 0 'org-todo-regexp b) "") - "\\([ \t]*\\[[a-zA-Z0-9]\\]\\)? *") tb) - (setq tb (substring tb (match-end 0)))) + (when (string-match (concat "\\`[ \t]*" (or (get-text-property 0 'org-todo-regexp b) "") + "\\([ \t]*\\[[a-zA-Z0-9]\\]\\)? *") tb) + (setq tb (substring tb (match-end 0)))) (setq tb (downcase tb))) - (cond ((not ta) +1) + (cond ((not (or ta tb)) nil) + ((not ta) +1) ((not tb) -1) ((string-lessp ta tb) -1) ((string-lessp tb ta) +1)))) @@ -6987,7 +7101,8 @@ The optional argument TYPE tells the agenda type." "Compare the string values of the first tags of A and B." (let ((ta (car (last (get-text-property 1 'tags a)))) (tb (car (last (get-text-property 1 'tags b))))) - (cond ((not ta) +1) + (cond ((not (or ta tb)) nil) + ((not ta) +1) ((not tb) -1) ((string-lessp ta tb) -1) ((string-lessp tb ta) +1)))) @@ -7074,11 +7189,11 @@ their type." (alpha-down (if alpha-up (- alpha-up) nil)) (need-user-cmp (org-em 'user-defined-up 'user-defined-down ss)) user-defined-up user-defined-down) - (if (and need-user-cmp org-agenda-cmp-user-defined - (functionp org-agenda-cmp-user-defined)) - (setq user-defined-up - (funcall org-agenda-cmp-user-defined a b) - user-defined-down (if user-defined-up (- user-defined-up) nil))) + (when (and need-user-cmp org-agenda-cmp-user-defined + (functionp org-agenda-cmp-user-defined)) + (setq user-defined-up + (funcall org-agenda-cmp-user-defined a b) + user-defined-down (if user-defined-up (- user-defined-up) nil))) (cdr (assoc (eval (cons 'or org-agenda-sorting-strategy-selected)) '((-1 . t) (1 . nil) (nil . nil)))))) @@ -7111,58 +7226,69 @@ Argument ARG is the prefix argument." ;;;###autoload (defun org-agenda-set-restriction-lock (&optional type) - "Set restriction lock for agenda, to current subtree or file. -Restriction will be the file if TYPE is `file', or if type is the -universal prefix \\='(4), or if the cursor is before the first headline -in the file. Otherwise, restriction will be to the current subtree." + "Set restriction lock for agenda to current subtree or file. +When in a restricted subtree, remove it. + +The restriction will span over the entire file if TYPE is `file', +or if type is '(4), or if the cursor is before the first headline +in the file. Otherwise, only apply the restriction to the current +subtree." (interactive "P") - (org-agenda-remove-restriction-lock 'noupdate) - (and (equal type '(4)) (setq type 'file)) - (setq type (cond - (type type) - ((org-at-heading-p) 'subtree) - ((condition-case nil (org-back-to-heading t) (error nil)) - 'subtree) - (t 'file))) - (if (eq type 'subtree) - (progn - (setq org-agenda-restrict (current-buffer)) - (setq org-agenda-overriding-restriction 'subtree) - (put 'org-agenda-files 'org-restrict - (list (buffer-file-name (buffer-base-buffer)))) - (org-back-to-heading t) - (move-overlay org-agenda-restriction-lock-overlay - (point) - (if org-agenda-restriction-lock-highlight-subtree - (save-excursion (org-end-of-subtree t t) (point)) - (point-at-eol))) - (move-marker org-agenda-restrict-begin (point)) - (move-marker org-agenda-restrict-end - (save-excursion (org-end-of-subtree t t))) - (message "Locking agenda restriction to subtree")) - (put 'org-agenda-files 'org-restrict - (list (buffer-file-name (buffer-base-buffer)))) - (setq org-agenda-restrict nil) - (setq org-agenda-overriding-restriction 'file) - (move-marker org-agenda-restrict-begin nil) - (move-marker org-agenda-restrict-end nil) - (message "Locking agenda restriction to file")) - (setq current-prefix-arg nil) + (if (and org-agenda-overriding-restriction + (member org-agenda-restriction-lock-overlay + (overlays-at (point))) + (equal (overlay-start org-agenda-restriction-lock-overlay) + (point))) + (org-agenda-remove-restriction-lock 'noupdate) + (org-agenda-remove-restriction-lock 'noupdate) + (and (equal type '(4)) (setq type 'file)) + (setq type (cond + (type type) + ((org-at-heading-p) 'subtree) + ((condition-case nil (org-back-to-heading t) (error nil)) + 'subtree) + (t 'file))) + (if (eq type 'subtree) + (progn + (setq org-agenda-restrict (current-buffer)) + (setq org-agenda-overriding-restriction 'subtree) + (put 'org-agenda-files 'org-restrict + (list (buffer-file-name (buffer-base-buffer)))) + (org-back-to-heading t) + (move-overlay org-agenda-restriction-lock-overlay + (point) + (if org-agenda-restriction-lock-highlight-subtree + (save-excursion (org-end-of-subtree t t) (point)) + (point-at-eol))) + (move-marker org-agenda-restrict-begin (point)) + (move-marker org-agenda-restrict-end + (save-excursion (org-end-of-subtree t t))) + (message "Locking agenda restriction to subtree")) + (put 'org-agenda-files 'org-restrict + (list (buffer-file-name (buffer-base-buffer)))) + (setq org-agenda-restrict nil) + (setq org-agenda-overriding-restriction 'file) + (move-marker org-agenda-restrict-begin nil) + (move-marker org-agenda-restrict-end nil) + (message "Locking agenda restriction to file")) + (setq current-prefix-arg nil)) (org-agenda-maybe-redo)) (defun org-agenda-remove-restriction-lock (&optional noupdate) - "Remove the agenda restriction lock." + "Remove agenda restriction lock." (interactive "P") - (delete-overlay org-agenda-restriction-lock-overlay) - (delete-overlay org-speedbar-restriction-lock-overlay) - (setq org-agenda-overriding-restriction nil) - (setq org-agenda-restrict nil) - (put 'org-agenda-files 'org-restrict nil) - (move-marker org-agenda-restrict-begin nil) - (move-marker org-agenda-restrict-end nil) - (setq current-prefix-arg nil) - (message "Agenda restriction lock removed") - (or noupdate (org-agenda-maybe-redo))) + (if (not org-agenda-restrict) + (message "No agenda restriction to remove.") + (delete-overlay org-agenda-restriction-lock-overlay) + (delete-overlay org-speedbar-restriction-lock-overlay) + (setq org-agenda-overriding-restriction nil) + (setq org-agenda-restrict nil) + (put 'org-agenda-files 'org-restrict nil) + (move-marker org-agenda-restrict-begin nil) + (move-marker org-agenda-restrict-end nil) + (setq current-prefix-arg nil) + (message "Agenda restriction lock removed") + (or noupdate (org-agenda-maybe-redo)))) (defun org-agenda-maybe-redo () "If there is any window showing the agenda view, update it." @@ -7182,14 +7308,14 @@ in the file. Otherwise, restriction will be to the current subtree." ;;; Agenda commands (defun org-agenda-check-type (error &rest types) - "Check if agenda buffer is of allowed type. + "Check if agenda buffer or component is of allowed type. If ERROR is non-nil, throw an error, otherwise just return nil. Allowed types are `agenda' `todo' `tags' `search'." (cond ((not org-agenda-type) (error "No Org agenda currently displayed")) ((memq org-agenda-type types) t) (error - (error "Not allowed in %s-type agenda buffers" org-agenda-type)) + (error "Not allowed in '%s'-type agenda buffer or component" org-agenda-type)) (t nil))) (defun org-agenda-Quit () @@ -7361,11 +7487,15 @@ With a prefix argument, do so in all agenda buffers." (defvar org-agenda-filter-form nil) (defvar org-agenda-filtered-by-category nil) +(defsubst org-agenda-get-category () + "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." +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) @@ -7395,7 +7525,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 @@ -7407,46 +7538,60 @@ 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 a regular expression. -Regexp filters are cumulative. -With no prefix argument, keep entries matching the regexp. -With one prefix argument, filter out entries matching the regexp. -With two prefix arguments, remove the regexp filters." +(defun org-agenda-filter-by-regexp (strip-or-accumulate) + "Filter agenda entries by a regular expressions. +You will be prompted for the regular expression, and the agenda +view will only show entries 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") - (if (not (equal strip '(16))) - (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)) - (org-agenda-filter-show-all-re) - (message "Regexp filter removed"))) + (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 <)"))) - ;; Select appropriate duration. Ignore non-digit characters. + (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 " @@ -7458,15 +7603,149 @@ With two prefix arguments, remove the effort filters." (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 - (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")))) + (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 string may contain filter elements like + ++category ++tag ++<effort > and = are also allowed as effort operators ++/regexp/ + +Instead of `+', `-' is allowed to strip the agenda of matching entries. +`+' is optional if it is not required to separate two string parts. +Multiple filter elements can be concatenated without spaces, for example + + +work-John<0:10-/plot/ + +selects entries with category `work' and effort estimates below 10 minutes, +and deselects entries with tag `John' or matching the regexp `plot'. + +During entry of the filter, completion for tags, categories and effort +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. + +A single `\\[universal-argument]' prefix arg STRIP-OR-ACCUMULATE will negate the +entire filter, which can be useful in connection with the prompt history. + +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 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") + (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)) + (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)) + (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)) + (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) "+")) + (when negate + (setq pm (if (equal pm "+") "-" "+"))) + (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 because 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 +FLAG specifies the type of completion operation to perform. This +function is passed as a collection function to `completing-read', +which see." + (let ((completion-ignore-case t) ;tags are case-sensitive + (confirm (lambda (x) (stringp x))) + (prefix "") + (operator "") + table) + (when (string-match "^\\(.*\\([-+<>=]\\)\\)\\([^-+<>=]*\\)$" string) + (setq prefix (match-string 1 string) + operator (match-string 2 string) + string (match-string 3 string))) + (cond + ((member operator '("+" "-" "" nil)) + (setq table (append (org-agenda-get-represented-categories) + (org-agenda-get-represented-tags)))) + ((member operator '("<" ">" "=")) + (setq table (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") + " +"))) + (t (setq table nil))) + (pcase flag + (`t (all-completions string table confirm)) + (`lambda (assoc string table)) ;exact match? + (`nil + (pcase (try-completion string table confirm) + ((and completion (pred stringp)) + (concat prefix completion)) + (completion completion))) + (_ nil)))) (defun org-agenda-filter-remove-all () "Remove all filters from the current agenda buffer." @@ -7483,14 +7762,17 @@ With two prefix arguments, remove the effort filters." (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. @@ -7499,33 +7781,38 @@ should be used to exclude the search - the interactive user can also press `-' or `+' to switch between filtering and excluding." (interactive "P") (let* ((alist org-tag-alist-for-agenda) + (seen-chars nil) (tag-chars (mapconcat (lambda (x) (if (and (not (symbolp (car x))) - (cdr x)) - (char-to-string (cdr x)) + (cdr x) + (not (member (cdr x) seen-chars))) + (progn + (push (cdr x) seen-chars) + (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)))) + (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) (unless char (while (not (memq char valid-char-list)) - (message - "%s by tag [%s ]:tag-char, [TAB]:tag, %s[/]:off, [+/-]:filter/exclude%s, [q]:quit" - (if exclude "Exclude" "Filter") + (org-unlogged-message + "%s by tag%s: [%s ]tag-char [TAB]tag %s[\\]off [q]uit" + (if exclude "Exclude[+]" "Filter[-]") + (if expand "" " (no grouptag expand)") tag-chars - (if org-agenda-auto-exclude-function "[RET], " "") - (if expand "" ", no grouptag expand")) + (if org-agenda-auto-exclude-function "[RET] " "")) (setq char (read-char-exclusive)) ;; Excluding or filtering down (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)) @@ -7538,11 +7825,11 @@ also press `-' or `+' to switch between filtering and excluding." (setq org-agenda-tag-filter nil) (dolist (tag (org-agenda-get-represented-tags)) (let ((modifier (funcall org-agenda-auto-exclude-function tag))) - (if modifier - (push modifier org-agenda-tag-filter)))) - (if (not (null org-agenda-tag-filter)) - (org-agenda-filter-apply org-agenda-tag-filter 'tag expand)))) - ((eq char ?/) + (when modifier + (push modifier org-agenda-tag-filter)))) + (unless (null org-agenda-tag-filter) + (org-agenda-filter-apply org-agenda-tag-filter 'tag expand)))) + ((eq char ?\\) (org-agenda-filter-show-all-tag) (when (get 'org-agenda-tag-filter :preset-filter) (org-agenda-filter-apply org-agenda-tag-filter 'tag expand))) @@ -7559,21 +7846,36 @@ 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))))) -(defun org-agenda-get-represented-tags () - "Get a list of all tags currently represented in the agenda." - (let (p tags) - (save-excursion - (goto-char (point-min)) - (while (setq p (next-single-property-change (point) 'tags)) - (goto-char p) - (mapc (lambda (x) (add-to-list 'tags x)) - (get-text-property (point) 'tags)))) - tags)) +(defun org-agenda-get-represented-categories () + "Return a list of all categories used in this agenda buffer." + (or org-agenda-represented-categories + (when (derived-mode-p 'org-agenda-mode) + (let ((pos (point-min)) categories) + (while (and (< pos (point-max)) + (setq pos (next-single-property-change + pos 'org-category nil (point-max)))) + (push (get-text-property pos 'org-category) categories)) + (setq org-agenda-represented-categories + (nreverse (org-uniquify (delq nil categories)))))))) +(defun org-agenda-get-represented-tags () + "Return a list of all tags used in this agenda buffer. +These will be lower-case, for filtering." + (or org-agenda-represented-tags + (when (derived-mode-p 'org-agenda-mode) + (let ((pos (point-min)) tags-lists tt) + (while (and (< pos (point-max)) + (setq pos (next-single-property-change + pos 'tags nil (point-max)))) + (setq tt (get-text-property pos 'tags)) + (if tt (push tt tags-lists))) + (setq org-agenda-represented-tags + (nreverse (org-uniquify + (delq nil (apply 'append tags-lists))))))))) (defun org-agenda-filter-make-matcher (filter type &optional expand) "Create the form that tests a line for agenda filter. Optional @@ -7636,7 +7938,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)) @@ -7689,9 +7991,10 @@ When NO-OPERATOR is non-nil, do not add the + operator to returned tags." argument EXPAND can be used for the TYPE tag and will expand the tags in the FILTER if any of the tags in FILTER are grouptags." ;; Deactivate `org-agenda-entry-text-mode' when filtering - (if org-agenda-entry-text-mode (org-agenda-entry-text-mode)) + (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 @@ -7701,17 +8004,17 @@ tags in the FILTER if any of the tags in FILTER are grouptags." (save-excursion (goto-char (point-min)) (while (not (eobp)) - (if (org-get-at-bol 'org-marker) + (if (org-get-at-bol 'org-hd-marker) (progn (setq tags (org-get-at-bol 'tags) - cat (org-get-at-eol 'org-category 1) + cat (org-agenda-get-category) txt (org-get-at-bol 'txt)) - (if (not (eval org-agenda-filter-form)) - (org-agenda-filter-hide-line type)) + (unless (eval org-agenda-filter-form) + (org-agenda-filter-hide-line type)) (beginning-of-line 2)) (beginning-of-line 2)))) - (if (get-char-property (point) 'invisible) - (ignore-errors (org-agenda-previous-line))))) + (when (get-char-property (point) 'invisible) + (ignore-errors (org-agenda-previous-line))))) (defun org-agenda-filter-top-headline-apply (hl &optional negative) "Filter by top headline HL." @@ -7721,12 +8024,12 @@ tags in the FILTER if any of the tags in FILTER are grouptags." (while (not (eobp)) (let* ((pos (org-get-at-bol 'org-hd-marker)) (tophl (and pos (org-find-top-headline pos)))) - (if (and tophl (funcall (if negative 'identity 'not) - (string= hl tophl))) - (org-agenda-filter-hide-line 'top-headline))) + (when (and tophl (funcall (if negative 'identity 'not) + (string= hl tophl))) + (org-agenda-filter-hide-line 'top-headline))) (beginning-of-line 2))) - (if (get-char-property (point) 'invisible) - (org-agenda-previous-line)) + (when (get-char-property (point) 'invisible) + (org-agenda-previous-line)) (setq org-agenda-top-headline-filter hl org-agenda-filtered-by-top-headline t)) @@ -7744,7 +8047,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) @@ -7955,9 +8259,10 @@ With prefix ARG, go backward that many times the current span." (defun org-agenda-view-mode-dispatch () "Call one of the view mode commands." (interactive) - (message "View: [d]ay [w]eek for[t]night [m]onth [y]ear [SPC]reset [q]uit/abort - time[G]rid [[]inactive [f]ollow [l]og [L]og-all [c]lockcheck - [a]rch-trees [A]rch-files clock[R]eport include[D]iary [E]ntryText") + (org-unlogged-message + "View: [d]ay [w]eek for[t]night [m]onth [y]ear [SPC]reset [q]uit/abort + time[G]rid [[]inactive [f]ollow [l]og [L]og-all [c]lockcheck + [a]rch-trees [A]rch-files clock[R]eport include[D]iary [E]ntryText") (pcase (read-char-exclusive) (?\ (call-interactively 'org-agenda-reset-view)) (?d (call-interactively 'org-agenda-day-view)) @@ -8042,8 +8347,8 @@ SPAN may be `day', `week', `fortnight', `month', `year'." (org-agenda-check-type t 'agenda) (let* ((args (get-text-property (min (1- (point-max)) (point)) 'org-last-args)) (curspan (nth 2 args))) - (if (and (not n) (equal curspan span)) - (error "Viewing span is already \"%s\"" span)) + (when (and (not n) (equal curspan span)) + (error "Viewing span is already \"%s\"" span)) (let* ((sd (or (org-get-at-bol 'day) (nth 1 args) org-starting-day)) @@ -8107,11 +8412,10 @@ so that the date SD will be in that range." (org-agenda-check-type t 'agenda) (beginning-of-line 1) ;; This does not work if user makes date format that starts with a blank - (if (looking-at "^\\S-") (forward-char 1)) - (if (not (re-search-forward "^\\S-" nil t arg)) - (progn - (backward-char 1) - (error "No next date after this line in this buffer"))) + (when (looking-at-p "^\\S-") (forward-char 1)) + (unless (re-search-forward "^\\S-" nil t arg) + (backward-char 1) + (error "No next date after this line in this buffer")) (goto-char (match-beginning 0))) (defun org-agenda-previous-date-line (&optional arg) @@ -8119,8 +8423,8 @@ so that the date SD will be in that range." (interactive "p") (org-agenda-check-type t 'agenda) (beginning-of-line 1) - (if (not (re-search-backward "^\\S-" nil t arg)) - (error "No previous date before this line in this buffer"))) + (unless (re-search-backward "^\\S-" nil t arg) + (error "No previous date before this line in this buffer"))) ;; Initialize the highlight (defvar org-hl (make-overlay 1 1)) @@ -8276,56 +8580,51 @@ When called with a prefix argument, include all archive files as well." ((eq org-agenda-show-log 'clockcheck) " ClkCk") (org-agenda-show-log " Log") (t "")) + (if (org-agenda-filter-any) " " "") (if (or org-agenda-category-filter (get 'org-agenda-category-filter :preset-filter)) '(:eval (propertize - (concat " <" + (concat "[" (mapconcat 'identity (append (get 'org-agenda-category-filter :preset-filter) org-agenda-category-filter) "") - ">") + "]") 'face 'org-agenda-filter-category 'help-echo "Category used in filtering")) "") (if (or org-agenda-tag-filter (get 'org-agenda-tag-filter :preset-filter)) '(:eval (propertize - (concat " {" - (mapconcat + (concat (mapconcat 'identity (append (get 'org-agenda-tag-filter :preset-filter) org-agenda-tag-filter) - "") - "}") + "")) 'face 'org-agenda-filter-tags 'help-echo "Tags used in filtering")) "") (if (or org-agenda-effort-filter (get 'org-agenda-effort-filter :preset-filter)) '(:eval (propertize - (concat " {" - (mapconcat + (concat (mapconcat 'identity (append (get 'org-agenda-effort-filter :preset-filter) org-agenda-effort-filter) - "") - "}") + "")) 'face 'org-agenda-filter-effort 'help-echo "Effort conditions used in filtering")) "") (if (or org-agenda-regexp-filter (get 'org-agenda-regexp-filter :preset-filter)) '(:eval (propertize - (concat " [" - (mapconcat - 'identity + (concat (mapconcat + (lambda (x) (concat (substring x 0 1) "/" (substring x 1) "/")) (append (get 'org-agenda-regexp-filter :preset-filter) org-agenda-regexp-filter) - "") - "]") + "")) 'face 'org-agenda-filter-regexp 'help-echo "Regexp used in filtering")) "") (if org-agenda-archives-mode @@ -8373,7 +8672,7 @@ When called with a prefix argument, include all archive files as well." (goto (save-excursion (move-end-of-line 0) (previous-single-property-change (point) 'org-marker)))) - (if goto (goto-char goto)) + (when goto (goto-char goto)) (org-move-to-column col))) (org-agenda-do-context-action)) @@ -8432,7 +8731,7 @@ Point is in the buffer where the item originated.") (buffer (marker-buffer marker)) (pos (marker-position marker)) (type (org-get-at-bol 'type)) - dbeg dend (n 0) conf) + dbeg dend (n 0)) (org-with-remote-undo buffer (with-current-buffer buffer (save-excursion @@ -8444,14 +8743,20 @@ Point is in the buffer where the item originated.") dend (min (point-max) (1+ (point-at-eol))))) (goto-char dbeg) (while (re-search-forward "^[ \t]*\\S-" dend t) (setq n (1+ n))))) - (setq conf (or (eq t org-agenda-confirm-kill) - (and (numberp org-agenda-confirm-kill) - (> n org-agenda-confirm-kill)))) - (and conf - (not (y-or-n-p - (format "Delete entry with %d lines in buffer \"%s\"? " - n (buffer-name buffer)))) - (error "Abort")) + (when (or (eq t org-agenda-confirm-kill) + (and (numberp org-agenda-confirm-kill) + (> n org-agenda-confirm-kill))) + (let ((win-conf (current-window-configuration))) + (unwind-protect + (and + (prog2 + (org-agenda-tree-to-indirect-buffer nil) + (not (y-or-n-p + (format "Delete entry with %d lines in buffer \"%s\"? " + n (buffer-name buffer)))) + (kill-buffer org-last-indirect-buffer)) + (error "Abort")) + (set-window-configuration win-conf)))) (let ((org-agenda-buffer-name bufname-orig)) (org-remove-subtree-entries-from-agenda buffer dbeg dend)) (with-current-buffer buffer (delete-region dbeg dend)) @@ -8585,9 +8890,9 @@ It also looks at the text of the entry itself." ((and buffer lk) (mapcar (lambda(l) (with-current-buffer buffer - (setq trg (and (string-match org-bracket-link-regexp l) + (setq trg (and (string-match org-link-bracket-re l) (match-string 1 l))) - (if (or (not trg) (string-match org-any-link-re trg)) + (if (or (not trg) (string-match org-link-any-re trg)) (org-with-wide-buffer (goto-char marker) (when (search-forward l nil lkend) @@ -8601,11 +8906,11 @@ It also looks at the text of the entry itself." (goto-char (match-beginning 0)) (org-open-at-point))))) lk)) - ((or (org-in-regexp (concat "\\(" org-bracket-link-regexp "\\)")) + ((or (org-in-regexp (concat "\\(" org-link-bracket-re "\\)")) (save-excursion (beginning-of-line 1) - (looking-at (concat ".*?\\(" org-bracket-link-regexp "\\)")))) - (org-open-link-from-string (match-string 1))) + (looking-at (concat ".*?\\(" org-link-bracket-re "\\)")))) + (org-link-open-from-string (match-string 1))) (t (message "No link to open here"))))) (defun org-agenda-copy-local-variable (var) @@ -8623,8 +8928,8 @@ displayed Org file fills the frame." (interactive) (if (and org-return-follows-link (not (org-get-at-bol 'org-marker)) - (org-in-regexp org-bracket-link-regexp)) - (org-open-link-from-string (match-string 0)) + (org-in-regexp org-link-bracket-re)) + (org-link-open-from-string (match-string 0)) (let* ((marker (or (org-get-at-bol 'org-marker) (org-agenda-error))) (buffer (marker-buffer marker)) @@ -8660,9 +8965,8 @@ if it was hidden in the outline." When called repeatedly, scroll the window that is displaying the buffer. -With a `\\[universal-argument]' prefix, use `org-show-entry' instead of \ -`outline-show-subtree' -to display the item, so that drawers and logbooks stay folded." +With a `\\[universal-argument]' prefix argument, display the item, but \ +fold drawers." (interactive "P") (let ((win (selected-window))) (if (and (window-live-p org-agenda-show-window) @@ -8671,7 +8975,13 @@ to display the item, so that drawers and logbooks stay folded." (select-window org-agenda-show-window) (ignore-errors (scroll-up))) (org-agenda-goto t) - (if arg (org-show-entry) (outline-show-subtree)) + (org-show-entry) + (if arg (org-cycle-hide-drawers 'children) + (org-with-wide-buffer + (narrow-to-region (org-entry-beginning-position) + (org-entry-end-position)) + (org-show-all '(drawers)))) + (when arg ) (setq org-agenda-show-window (selected-window))) (select-window win))) @@ -8703,7 +9013,7 @@ if it was hidden in the outline." (set-window-start (selected-window) (point-at-bol)) (cond ((= more 0) - (outline-hide-subtree) + (org-flag-subtree t) (save-excursion (org-back-to-heading) (run-hook-with-args 'org-cycle-hook 'folded)) @@ -8755,8 +9065,8 @@ docstring of `org-agenda-show-1'." (if (equal org-agenda-cycle-counter 0) (setq org-agenda-cycle-counter 2) (setq org-agenda-cycle-counter (1+ org-agenda-cycle-counter)) - (if (> org-agenda-cycle-counter 3) - (setq org-agenda-cycle-counter 0))))) + (when (> org-agenda-cycle-counter 3) + (setq org-agenda-cycle-counter 0))))) (org-agenda-show-1 org-agenda-cycle-counter)) (defun org-agenda-recenter (arg) @@ -8775,8 +9085,8 @@ docstring of `org-agenda-show-1'." (defun org-agenda-check-no-diary () "Check if the entry is a diary link and abort if yes." - (if (org-get-at-bol 'org-agenda-diary-link) - (org-agenda-error))) + (when (org-get-at-bol 'org-agenda-diary-link) + (org-agenda-error))) (defun org-agenda-error () "Throw an error when a command is not allowed in the agenda." @@ -8822,7 +9132,7 @@ the dedicated frame." (with-current-buffer buffer (save-excursion (goto-char pos) - (funcall 'org-tree-to-indirect-buffer arg))))) + (org-tree-to-indirect-buffer arg))))) (defvar org-last-heading-marker (make-marker) "Marker pointing to the headline that last changed its TODO state @@ -8852,6 +9162,7 @@ the same tree node, and the headline of the tree node in the Org file." (hdmarker (org-get-at-bol 'org-hd-marker)) (todayp (org-agenda-today-p (org-get-at-bol 'day))) (inhibit-read-only t) + org-loop-over-headlines-in-active-region org-agenda-headline-snapshot-before-repeat newhead just-one) (org-with-remote-undo buffer (with-current-buffer buffer @@ -8912,9 +9223,7 @@ If FORCE-TAGS is non-nil, the car of it returns the new tags." (line (org-current-line)) (org-agenda-buffer (current-buffer)) (thetags (with-current-buffer (marker-buffer hdmarker) - (org-with-wide-buffer - (goto-char hdmarker) - (org-get-tags-at)))) + (org-get-tags hdmarker))) props m pl undone-face done-face finish new dotime level cat tags) (save-excursion (goto-char (point-max)) @@ -8926,7 +9235,7 @@ If FORCE-TAGS is non-nil, the car of it returns the new tags." (equal m hdmarker)) (setq props (text-properties-at (point)) dotime (org-get-at-bol 'dotime) - cat (org-get-at-eol 'org-category 1) + cat (org-agenda-get-category) level (org-get-at-bol 'level) tags thetags new @@ -8970,32 +9279,35 @@ If FORCE-TAGS is non-nil, the car of it returns the new tags." (beginning-of-line 0))))) (defun org-agenda-align-tags (&optional line) - "Align all tags in agenda items to `org-agenda-tags-column'." + "Align all tags in agenda items to `org-agenda-tags-column'. +When optional argument LINE is non-nil, align tags only on the +current line." (let ((inhibit-read-only t) (org-agenda-tags-column (if (eq 'auto org-agenda-tags-column) (- (window-text-width)) org-agenda-tags-column)) + (end (and line (line-end-position))) l c) (save-excursion - (goto-char (if line (point-at-bol) (point-min))) - (while (re-search-forward "\\([ \t]+\\)\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$" - (if line (point-at-eol) nil) t) + (goto-char (if line (line-beginning-position) (point-min))) + (while (re-search-forward org-tag-group-re end t) (add-text-properties - (match-beginning 2) (match-end 2) + (match-beginning 1) (match-end 1) (list 'face (delq nil (let ((prop (get-text-property - (match-beginning 2) 'face))) + (match-beginning 1) 'face))) (or (listp prop) (setq prop (list prop))) (if (memq 'org-tag prop) prop (cons 'org-tag prop)))))) - (setq l (- (match-end 2) (match-beginning 2)) + (setq l (string-width (match-string 1)) c (if (< org-agenda-tags-column 0) (- (abs org-agenda-tags-column) l) org-agenda-tags-column)) - (delete-region (match-beginning 1) (match-end 1)) (goto-char (match-beginning 1)) + (delete-region (save-excursion (skip-chars-backward " \t") (point)) + (point)) (insert (org-add-props - (make-string (max 1 (- c (current-column))) ?\ ) + (make-string (max 1 (- c (current-column))) ?\s) (plist-put (copy-sequence (text-properties-at (point))) 'face nil)))) (goto-char (point-min)) @@ -9035,7 +9347,7 @@ Called with a universal prefix arg, show the priority instead of setting it." (widen) (goto-char pos) (org-show-context 'agenda) - (funcall 'org-priority force-direction) + (org-priority force-direction) (end-of-line 1) (setq newhead (org-get-heading))) (org-agenda-change-all-lines newhead hdmarker) @@ -9061,7 +9373,7 @@ Called with a universal prefix arg, show the priority instead of setting it." (org-show-context 'agenda) (if tag (org-toggle-tag tag onoff) - (call-interactively 'org-set-tags)) + (call-interactively #'org-set-tags-command)) (end-of-line 1) (setq newhead (org-get-heading))) (org-agenda-change-all-lines newhead hdmarker) @@ -9180,9 +9492,9 @@ Called with a universal prefix arg, show the priority instead of setting it." cdate (calendar-absolute-from-gregorian (list (nth 4 cdate) (nth 3 cdate) (nth 5 cdate))) today (org-today)) - (if (> today cdate) - ;; immediately shift to today - (setq arg (- today cdate)))) + (when (> today cdate) + ;; immediately shift to today + (setq arg (- today cdate)))) (org-timestamp-change arg (or what 'day)) (when (and (org-at-date-range-p) (re-search-backward org-tr-regexp-both (point-at-bol))) @@ -9315,7 +9627,6 @@ ARG is passed through to `org-deadline'." (widen) (goto-char pos) (org-show-context 'agenda) - (org-cycle-hide-drawers 'children) (org-clock-in arg) (setq newhead (org-get-heading))) (org-agenda-change-all-lines newhead hdmarker)) @@ -9355,8 +9666,8 @@ buffer, display it in another window." (interactive) (let (pos) (mapc (lambda (o) - (if (eq (overlay-get o 'type) 'org-agenda-clocking) - (setq pos (overlay-start o)))) + (when (eq (overlay-get o 'type) 'org-agenda-clocking) + (setq pos (overlay-start o)))) (overlays-in (point-min) (point-max))) (cond (pos (goto-char pos)) ;; If the currently clocked entry is not in the agenda @@ -9442,62 +9753,64 @@ the resulting entry will not be shown. When TEXT is empty, switch to (find-file-noselect org-agenda-diary-file)) (widen) (goto-char (point-min)) - (cond - ((eq type 'anniversary) - (or (re-search-forward "^\\*[ \t]+Anniversaries" nil t) - (progn - (or (org-at-heading-p t) - (progn - (outline-next-heading) - (insert "* Anniversaries\n\n") - (beginning-of-line -1))))) - (outline-next-heading) - (org-back-over-empty-lines) - (backward-char 1) - (insert "\n") - (insert (format "%%%%(org-anniversary %d %2d %2d) %s" - (nth 2 d1) (car d1) (nth 1 d1) text))) - ((eq type 'day) - (let ((org-prefix-has-time t) - (org-agenda-time-leading-zero t) - fmt time time2) - (if org-agenda-insert-diary-extract-time - ;; Use org-agenda-format-item to parse text for a time-range and - ;; remove it. FIXME: This is a hack, we should refactor - ;; that function to make time extraction available separately - (setq fmt (org-agenda-format-item nil text nil nil nil t) - time (get-text-property 0 'time fmt) - time2 (if (> (length time) 0) - ;; split-string removes trailing ...... if - ;; no end time given. First space - ;; separates time from date. - (concat " " (car (split-string time "\\."))) - nil) - text (get-text-property 0 'txt fmt))) - (if (eq org-agenda-insert-diary-strategy 'top-level) - (org-agenda-insert-diary-as-top-level text) - (require 'org-datetree) - (org-datetree-find-date-create d1) - (org-agenda-insert-diary-make-new-entry text)) - (org-insert-time-stamp (org-time-from-absolute - (calendar-absolute-from-gregorian d1)) - nil nil nil nil time2)) - (end-of-line 0)) - ((eq type 'block) - (if (> (calendar-absolute-from-gregorian d1) - (calendar-absolute-from-gregorian d2)) - (setq d1 (prog1 d2 (setq d2 d1)))) - (if (eq org-agenda-insert-diary-strategy 'top-level) - (org-agenda-insert-diary-as-top-level text) - (require 'org-datetree) - (org-datetree-find-date-create d1) - (org-agenda-insert-diary-make-new-entry text)) - (org-insert-time-stamp (org-time-from-absolute - (calendar-absolute-from-gregorian d1))) - (insert "--") - (org-insert-time-stamp (org-time-from-absolute - (calendar-absolute-from-gregorian d2))) - (end-of-line 0))) + (cl-case type + (anniversary + (or (re-search-forward "^\\*[ \t]+Anniversaries" nil t) + (progn + (or (org-at-heading-p t) + (progn + (outline-next-heading) + (insert "* Anniversaries\n\n") + (beginning-of-line -1))))) + (outline-next-heading) + (org-back-over-empty-lines) + (backward-char 1) + (insert "\n") + (insert (format "%%%%(org-anniversary %d %2d %2d) %s" + (nth 2 d1) (car d1) (nth 1 d1) text))) + (day + (let ((org-prefix-has-time t) + (org-agenda-time-leading-zero t) + fmt time time2) + (when org-agenda-insert-diary-extract-time + ;; Use org-agenda-format-item to parse text for a time-range and + ;; remove it. FIXME: This is a hack, we should refactor + ;; that function to make time extraction available separately + (setq fmt (org-agenda-format-item nil text nil nil nil t) + time (get-text-property 0 'time fmt) + time2 (if (> (length time) 0) + ;; split-string removes trailing ...... if + ;; no end time given. First space + ;; separates time from date. + (concat " " (car (split-string time "\\."))) + nil) + text (get-text-property 0 'txt fmt))) + (if (eq org-agenda-insert-diary-strategy 'top-level) + (org-agenda-insert-diary-as-top-level text) + (require 'org-datetree) + (org-datetree-find-date-create d1) + (org-agenda-insert-diary-make-new-entry text)) + (org-insert-time-stamp (org-time-from-absolute + (calendar-absolute-from-gregorian d1)) + nil nil nil nil time2)) + (end-of-line 0)) + ((block) ;; Wrap this in (strictly unnecessary) parens because + ;; otherwise the indentation gets confused by the + ;; special meaning of 'block + (when (> (calendar-absolute-from-gregorian d1) + (calendar-absolute-from-gregorian d2)) + (setq d1 (prog1 d2 (setq d2 d1)))) + (if (eq org-agenda-insert-diary-strategy 'top-level) + (org-agenda-insert-diary-as-top-level text) + (require 'org-datetree) + (org-datetree-find-date-create d1) + (org-agenda-insert-diary-make-new-entry text)) + (org-insert-time-stamp (org-time-from-absolute + (calendar-absolute-from-gregorian d1))) + (insert "--") + (org-insert-time-stamp (org-time-from-absolute + (calendar-absolute-from-gregorian d2))) + (end-of-line 0))) (if (string-match "\\S-" text) (progn (set-window-configuration cw) @@ -9554,9 +9867,9 @@ entries in that Org file." (if (not (eq org-agenda-diary-file 'diary-file)) (org-agenda-diary-entry-in-org-file) (require 'diary-lib) - (let* ((char (progn - (message "Diary entry: [d]ay [w]eekly [m]onthly [y]early [a]nniversary [b]lock [c]yclic") - (read-char-exclusive))) + (let* ((char (read-char-exclusive + "Diary entry: [d]ay [w]eekly [m]onthly [y]early\ + [a]nniversary [b]lock [c]yclic")) (cmd (cdr (assoc char '((?d . diary-insert-entry) (?w . diary-insert-weekly-entry) @@ -9701,8 +10014,20 @@ This is a command that has to be installed in `calendar-mode-map'." 'org-marked-entry-overlay)) (defun org-agenda-bulk-mark (&optional arg) - "Mark the entry at point for future bulk action." + "Mark entries for future bulk action. + +When ARG is nil or one and region is not active then mark the +entry at point. + +When ARG is nil or one and region is active then mark the entries +in the region. + +When ARG is greater than one mark ARG lines." (interactive "p") + (when (and (or (not arg) (= arg 1)) (use-region-p)) + (setq arg (count-lines (region-beginning) (region-end))) + (goto-char (region-beginning)) + (deactivate-mark)) (dotimes (i (or arg 1)) (unless (org-get-at-bol 'org-agenda-diary-link) (let* ((m (org-get-at-bol 'org-hd-marker)) @@ -9720,9 +10045,9 @@ This is a command that has to be installed in `calendar-mode-map'." (goto-char (next-single-property-change (point) 'org-hd-marker))) (beginning-of-line 2)) (while (and (get-char-property (point) 'invisible) (not (eobp))) - (beginning-of-line 2)) - (message "%d entries marked for bulk action" - (length org-agenda-bulk-marked-entries)))))) + (beginning-of-line 2))))) + (message "%d entries marked for bulk action" + (length org-agenda-bulk-marked-entries))) (defun org-agenda-bulk-mark-all () "Mark all entries for future agenda bulk action." @@ -9744,8 +10069,8 @@ This is a command that has to be installed in `calendar-mode-map'." (setq entries-marked (1+ entries-marked)) (call-interactively 'org-agenda-bulk-mark))))) - (if (not entries-marked) - (message "No entry matching this regexp.")))) + (unless entries-marked + (message "No entry matching this regexp.")))) (defun org-agenda-bulk-unmark (&optional arg) "Unmark the entry at point for future bulk action." @@ -9817,8 +10142,9 @@ bulk action." "Execute an remote-editing action on all marked entries. The prefix arg is passed through to the command if possible." (interactive "P") - ;; Make sure we have markers, and only valid ones. - (unless org-agenda-bulk-marked-entries (user-error "No entries are marked")) + ;; When there is no mark, act on the agenda entry at point. + (if (not org-agenda-bulk-marked-entries) + (save-excursion (org-agenda-bulk-mark))) (dolist (m org-agenda-bulk-marked-entries) (unless (and (markerp m) (marker-buffer m) @@ -9827,8 +10153,8 @@ The prefix arg is passed through to the command if possible." (user-error "Marker %s for bulk command is invalid" m))) ;; Prompt for the bulk command. - (message - (concat (if org-agenda-persistent-marks "Bulk (persistent): " "Bulk: ") + (org-unlogged-message + (concat "Bulk (" (if org-agenda-persistent-marks "" "don't ") "[p]ersist marks): " "[$]arch [A]rch->sib [t]odo [+/-]tag [s]chd [d]eadline [r]efile " "[S]catter [f]unction " (and org-agenda-bulk-custom-functions @@ -10145,9 +10471,9 @@ details and examples. If an entry has a APPT_WARNTIME property, its value will be used to override `appt-message-warning-time'." (interactive "P") - (if refresh (setq appt-time-msg-list nil)) - (if (eq filter t) - (setq filter (read-from-minibuffer "Regexp filter: "))) + (when refresh (setq appt-time-msg-list nil)) + (when (eq filter t) + (setq filter (read-from-minibuffer "Regexp filter: "))) (let* ((cnt 0) ; count added events (scope (or args '(:deadline* :scheduled* :timestamp))) (org-agenda-new-buffers nil) @@ -10155,7 +10481,8 @@ to override `appt-message-warning-time'." ;; Do not use `org-today' here because appt only takes ;; time and without date as argument, so it may pass wrong ;; information otherwise - (today (org-date-to-gregorian (time-to-days nil))) + (today (org-date-to-gregorian + (time-to-days nil))) (org-agenda-restrict nil) (files (org-agenda-files 'unrestricted)) entries file (org-agenda-buffer nil)) @@ -10167,12 +10494,12 @@ to override `appt-message-warning-time'." (append entries (apply 'org-agenda-get-day-entries file today scope))))) - ;; Map thru entries and find if we should filter them out + ;; Map through entries and find if we should filter them out (mapc (lambda (x) (let* ((evt (org-trim (replace-regexp-in-string - org-bracket-link-regexp "\\3" + org-link-bracket-re "\\2" (or (get-text-property 1 'txt x) "")))) (cat (get-text-property (1- (length x)) 'org-category x)) (tod (get-text-property 1 'time-of-day x)) |