Skip to content

Latest commit

 

History

History
595 lines (536 loc) · 23.1 KB

extras.org

File metadata and controls

595 lines (536 loc) · 23.1 KB

Extras

org-hide-drawers

(defun org-cycle-hide-all-drawers ()
  "Hide all drawers"
  (interactive)
  (org-cycle-hide-drawers 'all))
(defun org-cycle-hide-drawers (state)
  "Re-hide all drawers after a visibility state change."
  (when (and (derived-mode-p 'org-mode)
             (not (memq state '(overview folded contents))))
    (save-excursion
      (let* ((globalp (memq state '(contents all)))
             (beg (if globalp
                      (point-min)
                    (point)))
             (end (if globalp
                      (point-max)
                    (if (eq state 'children)
                        (save-excursion
                          (outline-next-heading)
                          (point))
                      (org-end-of-subtree t)))))
        (goto-char beg)
        (while (re-search-forward org-drawer-regexp end t)
          (save-excursion
            (beginning-of-line 1)
            (when (looking-at org-drawer-regexp)
              (let* ((start (1- (match-beginning 0)))
                     (limit
                      (save-excursion
                        (outline-next-heading)
                        (point)))
                     (msg (format
                           (concat
                            "org-cycle-hide-drawers:  "
                            "`:END:`"
                            " line missing at position %s")
                           (1+ start))))
                (if (re-search-forward "^[ \t]*:END:" limit t)
                    (outline-flag-region start (point-at-eol) t)
                  (user-error msg))))))))))

Org Helpers Extras

(defun nm/task-is-active-proj ()
  "Checks if task is a Project with child subtask"
  (and (bh/is-project-p)
       (nm/has-subtask-active-p)))

(defun nm/task-has-next-condition ()
  "Checks task to see if it meets NEXT state critera and returns t."
  (interactive)
  (save-excursion
    (and (bh/is-task-p)
         (or (nm/checkbox-active-exist-p) (nm/is-scheduled-p) (nm/exist-context-tag-p))
         (and (not (member "WAIT" (org-get-tags))) (not (equal (org-get-todo-state) "DONE"))))))

(defun nm/task-has-todo-condition ()
  "Checks to see if task conditions meet TODO crtieria, and returns t if so."
  (interactive)
  (save-excursion
    (and (bh/is-task-p)
         (and (not (nm/checkbox-active-exist-p)) (not (nm/is-scheduled-p)) (not (nm/exist-context-tag-p)))
         (and (not (member "WAIT" (org-get-tags))) (not (equal (org-get-todo-state) "DONE"))))))

(defun nm/task-has-done-condition ()
  "Checks if task is considered DONE, and returns t."
  (interactive)
  (save-excursion
    (and (bh/is-task-p)
         (and (not (nm/checkbox-active-exist-p)) (not (nm/is-scheduled-p)) (not (nm/exist-context-tag-p)))
         (nm/checkbox-done-exist-p))))

(defun nm/task-has-wait-condition ()
  "Checks if task has conditions for WAIT state, retunrs t."
  (interactive)
  (and (bh/is-task-p)
       (member "WAIT" (org-get-tags))
       (not (equal (org-get-todo-state) "DONE"))
       (not (member "SOMEDAY" (org-get-tags)))))

(defun nm/checkbox-active-exist-p ()
  "Checks if a checkbox that's not marked DONE exist in the tree."
  (interactive)
  (save-excursion
    (org-back-to-heading)
    (let ((end (save-excursion (org-end-of-subtree t))))
      (search-forward-regexp "^[-+] \\[\\W].+\\|^[1-9].\\W\\[\\W]" end t))))

(defun nm/checkbox-done-exist-p ()
  "Checks if a checkbox that's not marked DONE exist in the tree."
  (interactive)
  (save-excursion
    (org-back-to-heading)
    (let ((end (save-excursion (org-end-of-subtree t))))
      (search-forward-regexp "^[-+] \\[X].+\\|^[1-9].\\W\\[X]" end t))))

(defun nm/has-subtask-done-p ()
  "Returns t for any heading that has a subtask is DONE state."
  (interactive)
  (org-back-to-heading t)
  (let ((end (save-excursion (org-end-of-subtree t))))
    (outline-end-of-heading)
    (save-excursion
      (re-search-forward (concat "^\*+ " "\\(DONE\\|KILL\\)") nil end))))

(defun nm/has-subtask-active-p ()
  "Returns t for any heading that has subtasks."
  (save-restriction
    (widen)
    (org-back-to-heading t)
    (let ((end (save-excursion (org-end-of-subtree t))))
      (outline-end-of-heading)
      (save-excursion
        (re-search-forward (concat "^\*+ " "\\(NEXT\\|WAIT\\|TODO\\)") end t)))))

(defun nm/exist-tag-p (arg)
  "If headline has ARG tag keyword assigned, return t."
  (interactive)
  (let ((end (save-excursion (end-of-line))))
    (save-excursion
      (member arg (org-get-tags)))))

(defconst nm/context-tags ".+\s:@\\w.+:\\|.+:@\\w.+:")

(defun nm/exist-context-tag-p (&optional arg)
  "If headline has context tag keyword assigned, return t."
  (interactive)
  (goto-char (org-entry-beginning-position))
  (let ((end (save-excursion (line-end-position))))
    (re-search-forward nm/context-tags end t)))

(defun nm/is-scheduled-p ()
  "Checks task for SCHEDULE and if found, return t."
  (save-excursion
    (org-back-to-heading)
    (let ((end (save-excursion (org-end-of-subtree t))))
      (re-search-forward org-scheduled-regexp end t))))

(defun nm/skip-project-tasks ()
  "Show non-project tasks. Skip project and sub-project tasks, habits, and project related tasks."
  (save-restriction
    (widen)
    (let* ((subtree-end (save-excursion (org-end-of-subtree t))))
      (cond
       ((bh/is-project-p) subtree-end)
       ((oh/is-scheduled-p) subtree-end)
       ((org-is-habit-p) subtree-end)
       ((bh/is-project-subtree-p) subtree-end)
       (t nil)))))

(defun nm/skip-projects-and-habits-and-single-tasks ()
  "Skip trees that are projects, tasks that are habits, single non-project tasks"
  (save-restriction
    (widen)
    (let ((next-headline (save-excursion (or (outline-next-heading) (point-max)))))
      (cond
       ((org-is-habit-p) next-headline)
       ((nm/is-scheduled-p) next-headline)
       ((bh/is-project-p) next-headline)
       ((and (bh/is-task-p) (not (bh/is-project-subtree-p))) next-headline)
       (t nil)))))

(defun nm/skip-scheduled ()
  "Skip headlines that are scheduled."
  (save-restriction
    (widen)
    (let ((next-headline (save-excursion (or (outline-next-heading) (point-max)))))
      (cond
       ((nm/is-scheduled-p) next-headline)
       (t nil)))))

(add-hook! 'org-checkbox-statistics-hook #'nm/statistics-update-task)
(add-hook 'before-save-hook #'nm/update-task-conditions)

(defvar org-tasks-properties-metadata "SOURCE" "List of property values used to clarify task items.")

(defun nm/statistics-update-task ()
  "Update task state when statistics checker runs"
  (when (and (bh/is-task-p) (nm/checkbox-active-exist-p)) (org-todo "NEXT"))
  (when (and (bh/is-task-p) (not (nm/checkbox-active-exist-p)) (nm/checkbox-done-exist-p)) (org-todo "DONE")))

; TODO Write interactive menu selection to ask user what value they want to update (tags, schedule, checkboxes).
(defun nm/update-task-tags ()
  "Update all child tasks in buffer that are missing a TAG value."
  (interactive)
  (org-map-entries (lambda ()
                     (message (nm/org-get-headline-title))
                     (when (and (bh/is-task-p) (null (org-get-tags)))
                       (org-set-tags-command))) t 'file))

(defun nm/update-task-conditions ()
  "Update task states depending on their conditions."
  (interactive)
  (org-map-entries (lambda ()
                     (when (nm/task-is-active-proj) (org-todo "ACTIVE"))) t))

Refiling to next.org

We use Jethro’s function to process bulk agenda items…

  • [ ] Write a new function to process bulk agenda items
(defun jethro/org-process-inbox ()
  "Called in org-agenda-mode, processes all inbox items."
  (interactive)
  (org-agenda-bulk-mark-regexp "inbox:")
  (jethro/bulk-process-entries))
(defvar jethro/org-current-effort "1:00"
  "Current effort for agenda items.")

Set our effort to “…”

(defun jethro/my-org-agenda-set-effort (effort)
  "Set the effort property for the current headline."
  (interactive
   (list (read-string (format "Effort [%s]: " jethro/org-current-effort) nil nil jethro/org-current-effort)))
  (setq jethro/org-current-effort effort)
  (org-agenda-check-no-diary)
  (let* ((hdmarker (or (org-get-at-bol 'org-hd-marker)
                       (org-agenda-error)))
         (buffer (marker-buffer hdmarker))
         (pos (marker-position hdmarker))
         (inhibit-read-only t)
         newhead)
    (org-with-remote-undo buffer
      (with-current-buffer buffer
        (widen)
        (goto-char pos)
        (org-show-context 'agenda)
        (funcall-interactively 'org-set-effort nil jethro/org-current-effort)
        (end-of-line 1)
        (setq newhead (org-get-heading)))
      (org-agenda-change-all-lines newhead hdmarker))))

Function to process a single item in our inbox

(defun jethro/org-agenda-process-inbox-item ()
  "Process a single item in the org-agenda."
  (org-with-wide-buffer
   (org-agenda-set-tags)
   (org-agenda-set-property)
   (org-agenda-priority)
   (call-interactively 'org-agenda-schedule)
   (call-interactively 'jethro/my-org-agenda-set-effort)
   (org-agenda-refile nil nil t)))

Bulk process entries

(defun jethro/bulk-process-entries ()
  (if (not (null org-agenda-bulk-marked-entries))
      (let ((entries (reverse org-agenda-bulk-marked-entries))
            (processed 0)
            (skipped 0))
        (dolist (e entries)
          (let ((pos (text-property-any (point-min) (point-max) 'org-hd-marker e)))
            (if (not pos)
                (progn (message "Skipping removed entry at %s" e)
                       (cl-incf skipped))
              (goto-char pos)
              (let (org-loop-over-headlines-in-active-region) (funcall 'jethro/org-agenda-process-inbox-item))
              ;; `post-command-hook' is not run yet.  We make sure any
              ;; pending log note is processed.
              (when (or (memq 'org-add-log-note (default-value 'post-command-hook))
                        (memq 'org-add-log-note post-command-hook))
                (org-add-log-note))
              (cl-incf processed))))
        (org-agenda-redo)
        (unless org-agenda-persistent-marks (org-agenda-bulk-unmark-all))
        (message "Acted on %d entries%s%s"
                 processed
                 (if (= skipped 0)
                     ""
                   (format ", skipped %d (disappeared before their turn)"
                           skipped))
                 (if (not org-agenda-persistent-marks) "" " (kept marked)")))))

Initiate capture from agenda

(defun jethro/org-inbox-capture ()
  (interactive)
  "Capture a task in agenda mode."
  (org-capture nil "i"))

Extra things to sort

;;; ~/.doom.d/customs.el -*- lexical-binding: t; -*-

;(add-hook 'org-archive-hook (lambda ()
;                              "Set tags to archived headline"
;                              (counsel-org-tag)))

(defun zyro/move-line-after-meta-data ()
  "Takes the point and moves it to the line after the Properties drawer"
  (interactive)
  (when (org-at-heading-p) (org-end-of-meta-data) (evil-insert 1) (beginning-of-line))
  (when (looking-at "^[	 ]*:LOGBOOK:[	 ]*\n\\(?:.*\n\\)*?[	 ]*:END:[	 ]*$")
    (re-search-forward "^[ \t]*:END:[ \t]*$" nil t) (end-of-line) (newline))
  (when (looking-at-p "^[ 	]*:\\(\\(?:\\w\\|[-_]\\)+\\):[ 	]*$")
    (re-search-forward "^[ \t]*:END:[ \t]*$" nil t) (end-of-line) (newline)))

(defun zyro/capture-daily-notes ()
  "Capture template for ORG-ROAM daily notes."
  (interactive)
  (expand-file-name (format "%s.org" (format-time-string "%Y-%m-%d")) org-roam-directory))

(defun zyro/add-note ()
  "Add note to headline"
  (interactive)
  (or
   (org-next-visible-heading 1)
   (goto-char (point-max)))
  (forward-line -1)
  (when (org-at-item-p) (forward-line -1))
  (newline)
  (insert (format "- %s" (read-string "Task name: "))))

(defun zyro/capture-file-name ()
  "Generate filename at time of capture"
  (setq zyro/capture-headline (read-string "Document Title: "))
  (expand-file-name (concat (doom-project-root) "diary/" (format "(%s)%s.org" (format-time-string "%b-%d-%Y") zyro/capture-headline))))

;(defun zyro/capture-file-name ()
;  "Generate filename at time of capture"
;  (setq zyro/capture-headline (read-string "Document Title: "))
;  (let ((dirname (read-directory-name "Pick directory: " (concat (doom-project-root) "notes/")))
;        (zyro/capture-headline (read-string "Document Title: ")))
;    (expand-file-name (concat dirname
;                              (format "(%s)%s.org" (format-time-string "%b-%d-%Y") zyro/capture-headline)))))

;(defun zyro/diary--capture-to-folder ()
;  "Diary capture captured to destination folder"
;  (interactive)
;  (let ((dirname (directory-files (concat (doom-project-root) "notes/") nil "^\\w+$"))
;        (dirfold (concat (doom-project-root) "notes/")))
;    (defvar zyro/capture-headline (read-string "Document Title: "))
;    (expand-file-name (concat dirfold (completing-read "Select directory: " dirname) "/" (format "(%s)%s.org" (format-time-string "%b-%d-%Y") zyro/capture-headline)))))

(defun zyro/select-task-type ()
  "Select task file from a list defined by '+org-capture-task-files'"
  (list (format "%stasks/%s" (doom-project-root) (ivy-completing-read "Select task file: " +org-capture-task-files))))

(defun zyro/capture--existing-file ()
  "Test"
  (interactive)
  (let ((filecandid (read-file-name "Select directory: " (concat (doom-project-root) "tasks/"))))
    (let ((org-agenda-files (list filecandid)))
      (if (null (car org-agenda-files))
          (error "%s is not visiting a file" (buffer-name (current-buffer)))
        (counsel-org-agenda-headlines)
        (org-next-visible-heading 1)
        (next-line -1)))))

(defvar +org-capture-next-file "next.org")
(defvar +org-capture-refs-file "refs.org")
(defvar +org-capture-someday-file "someday.org")
(defvar +org-capture-task-files '("breakfix.org" "sustaining.org" "coding.org" "inquiries.org" "escalations.org" "defects.org"))

(defun zyro/create-new-task ()
  "Add task in buffer"
  (interactive)
  (+org/insert-item-below 1)
  (org-metaright)
  (insert (format "TODO %s" (read-string "Task name: ")))
  (newline)
  (insert (format "[[%s][Link to case]]" (read-string "URL: "))))

(defun counsel-narrow ()
  "Narrow with counsel"
  (interactive)
  (counsel-imenu)
  (org-narrow-to-subtree))

(defun +org-move-next-headline-and-narrow ()
  "Move to NEXT headline on same level and narrow"
  (interactive)
  (widen)
  (outline-forward-same-level 1)
  (org-narrow-to-subtree))

(defun +org-gtd-references ()
  "GTD References file"
  (interactive)
  (find-file (read-file-name "Choose: " +org-gtd-refs-project)))

(defun helm-org-rifle-project-files ()
  "Rifle projects files"
  (interactive)
  (helm-org-rifle-directories (doom-project-root)))

(defun my-agenda-prefix ()
 (format "%s" (my-agenda-indent-string (org-current-level))))

(defun my-agenda-indent-string (level)
  (if (= level 1)
      ""
    (let ((str ""))
      (while (> level 2)
        (setq level (1- level)
              str (concat str "")))
      (concat str ""))))

(defvar org-archive-directory "~/.org/archives/")

(defun org-archive-file ()
  "Moves the current buffer to the archived folder"
  (interactive)
  (let ((old (or (buffer-file-name) (user-error "Not visiting a file")))
        (dir (read-directory-name "Move to: " org-archive-directory)))
    (write-file (expand-file-name (file-name-nondirectory old) dir) t)
    (delete-file old)))
(provide 'org-archive-file)

(defun org-capture-headline-finder (&optional arg)
  "Like `org-todo-list', but using only the current buffer's file."
  (interactive "P")
  (let ((org-agenda-files (list (buffer-file-name (current-buffer)))))
    (if (null (car org-agenda-files))
        (error "%s is not visiting a file" (buffer-name (current-buffer)))
      (counsel-org-agenda-headlines)))
  (goto-char (org-end-of-subtree)))
(defun +org-find-headline-narrow ()
  "Find a headline and narrow to it"
  (interactive)
  (widen)
  (let ((org-agenda-files (list (buffer-file-name (current-buffer)))))
    (if (null (car org-agenda-files))
        (error "%s is not visiting a file" (buffer-name (current-buffer)))
      (counsel-org-agenda-headlines)))
  (org-narrow-to-subtree))
;(defun org-capture-refile-hook ()
;  "Refile before finalizing capture to project TOOD file"
;  (interactive)
;  (let ((org-agenda-files (list (+org-capture-project-todo-file))))
;    (counsel-org-agenda-headlines)))
;(defun eos/org-custom-id-get (&optional pom create prefix)
;  "Get the CUSTOM_ID property of the entry at point-or-marker POM.
;   If POM is nil, refer to the entry at point. If the entry does
;   not have an CUSTOM_ID, the function returns nil. However, when
;   CREATE is non nil, create a CUSTOM_ID if none is present
;   already. PREFIX will be passed through to `org-id-new'. In any
;   case, the CUSTOM_ID of the entry is returned."
;  (interactive)
;  (org-with-point-at pom
;    (let ((id (org-entry-get nil "CUSTOM_ID")))
;      (cond
;       ((and id (stringp id) (string-match "\\S-" id))
;        id)
;       (create
;        (org-entry-put pom "CUSTOM_ID" (read-string (format "Set CUSTOM_ID for %s: " (substring-no-properties (org-format-outline-path (org-get-outline-path t nil))))
;                       (helm-org-rifle--make-default-custom-id (nth 4 (org-heading-components)))))
;        (org-id-add-location id (buffer-file-name (buffer-base-buffer)))
;        id)))))
;(defun eos/org-add-ids-to-headlines-in-file ()
;  "Add CUSTOM_ID properties to all headlines in the
;   current file which do not already have one."
;  (interactive)
;  (org-map-entries (lambda () (eos/org-custom-id-get (point) 'create))))
;(add-hook 'org-capture-before-finalize-hook
;          (lambda () (eos/org-custom-id-get (point) '(create))))
(defun org-update-cookies-after-save()
  (interactive)
  (let ((current-prefix-arg '(4)))
    (org-update-statistics-cookies "ALL")))

(add-hook 'org-mode-hook
          (lambda ()
            (add-hook 'before-save-hook 'org-update-cookies-after-save nil 'make-it-local)))
(provide 'org-update-cookies-after-save)
(setq-default truncate-lines t)

(defun jethro/truncate-lines-hook ()
  (setq truncate-lines nil))

(add-hook 'text-mode-hook 'jethro/truncate-lines-hook)

;;; Hide all drawers

;(defun org-cycle-hide-all-drawers ()
;  "Hide all drawers"
;  (interactive)
;  (org-cycle-hide-drawers 'all))

;(defun org-cycle-hide-drawers (state)
;  "Re-hide all drawers after a visibility state change."
;  (when (and (derived-mode-p 'org-mode)
;             (not (memq state '(overview folded contents))))
;    (save-excursion
;      (let* ((globalp (memq state '(contents all)))
;             (beg (if globalp
;                    (point-min)
;                    (point)))
;             (end (if globalp
;                    (point-max)
;                    (if (eq state 'children)
;                      (save-excursion
;                        (outline-next-heading)
;                        (point))
;                      (org-end-of-subtree t)))))
;        (goto-char beg)
;        (while (re-search-forward org-drawer-regexp end t)
;          (save-excursion
;            (beginning-of-line 1)
;            (when (looking-at org-drawer-regexp)
;              (let* ((start (1- (match-beginning 0)))
;                     (limit
;                       (save-excursion
;                         (outline-next-heading)
;                           (point)))
;                     (msg (format
;                            (concat
;                              "org-cycle-hide-drawers:  "
;                              "`:END:`"
;                              " line missing at position %s")
;                            (1+ start))))
;                (if (re-search-forward "^[ \t]*:END:" limit t)
;                  (outline-flag-region start (point-at-eol) t)
;                  (user-error msg))))))))))

;------------ Show calendar busy state
(defface busy-1  '((t :foreground "black" :background "#eceff1")) "")
(defface busy-2  '((t :foreground "black" :background "#cfd8dc")) "")
(defface busy-3  '((t :foreground "black" :background "#b0bec5")) "")
(defface busy-4  '((t :foreground "black" :background "#90a4ae")) "")
(defface busy-5  '((t :foreground "white" :background "#78909c")) "")
(defface busy-6  '((t :foreground "white" :background "#607d8b")) "")
(defface busy-7  '((t :foreground "white" :background "#546e7a")) "")
(defface busy-8  '((t :foreground "white" :background "#455a64")) "")
(defface busy-9  '((t :foreground "white" :background "#37474f")) "")
(defface busy-10 '((t :foreground "white" :background "#263238")) "")

;(defadvice calendar-generate-month
;  (after highlight-weekend-days (month year indent) activate)
;  "Highlight weekend days"
;  (dotimes (i 31)
;    (let ((date (list month (1+ i) year))
;          (count (length (org-agenda-get-day-entries
;                          (list (org-agenda-files)) (list month (1+ i) year)))))
;      (cond ((= count 0) ())
;            ((= count 1) (calendar-mark-visible-date date 'busy-1))
;            ((= count 2) (calendar-mark-visible-date date 'busy-2))
;            ((= count 3) (calendar-mark-visible-date date 'busy-3))
;            ((= count 4) (calendar-mark-visible-date date 'busy-4))
;            ((= count 5) (calendar-mark-visible-date date 'busy-5))
;            ((= count 6) (calendar-mark-visible-date date 'busy-6))
;            ((= count 7) (calendar-mark-visible-date date 'busy-7))
;            ((= count 8) (calendar-mark-visible-date date 'busy-8))
;            ((= count 9) (calendar-mark-visible-date date 'busy-9))
;            (t  (calendar-mark-visible-date date 'busy-10))))))

;; Set default column view headings: Task Effort Clock_Summary
(setq org-columns-default-format "%TODO %40ITEM(Task) %40SUMMARY(Summary)")

(defvar yant/last-note-taken ""
  "Text of the last note taken.")

(define-advice org-store-log-note (:before (&rest args) yant/org-store-last-note)
  "Store the last saved note into `yant/last-note-taken'."
  (let ((txt (buffer-string)))
    (while (string-match "\\`# .*\n[ \t\n]*" txt)
      (setq txt (replace-match "" t t txt)))
    (when (string-match "\\s-+\\'" txt)
      (setq txt (replace-match " " t t txt)))
    (when (string-match "\n" txt)
      (setq txt (replace-match " " t t txt)))
    (if (not (seq-empty-p txt))
        (setq yant/last-note-taken txt))))

(defmacro org-with-point-at-org-buffer (&rest body)
  "If in agenda, put the point into the corresponding org buffer."
  `(cond ((eq major-mode 'org-agenda-mode)
          (when-let ((marker (org-get-at-bol 'org-hd-marker)))
            (org-with-point-at marker
              ,@body)))
         ((eq major-mode 'org-mode)
          (org-with-wide-buffer
           ,@body))
         (t (display-warning :warning "Trying to call org function in non-org buffer."))))

(define-advice org-store-log-note (:after (&rest args) yant/org-save-last-note-into-summary-prop)
  "Save the last saved note into SUMMARY property."
  (when (and (not org-note-abort) (not (seq-empty-p yant/last-note-taken)))
    (if (eq major-mode 'org-agenda-mode)
        (org-with-point-at-org-buffer
         (org-set-property "SUMMARY" (or yant/last-note-taken "")))
      (org-set-property "SUMMARY" (or yant/last-note-taken "")))
    (setq yant/last-note-taken nil)))