summary refs log tree commit diff
path: root/emacs.d/custom/fcuny-defuns.el
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--emacs.d/custom/fcuny-defuns.el225
1 files changed, 225 insertions, 0 deletions
diff --git a/emacs.d/custom/fcuny-defuns.el b/emacs.d/custom/fcuny-defuns.el
index 40bd4ce..88e73a3 100644
--- a/emacs.d/custom/fcuny-defuns.el
+++ b/emacs.d/custom/fcuny-defuns.el
@@ -74,4 +74,229 @@
                             (decode-coding-string title 'utf-8))
       (concat "[[" url "][" title "]]"))))
 
+(defun fcuny/org-archive-subtree-as-completed ()
+  "Archives the current subtree to today's current journal entry."
+  (interactive)
+  (ignore-errors
+    ;; According to the docs for `org-archive-subtree', the state should be
+    ;; automatically marked as DONE, but I don't notice that:
+    (when (not (equal "DONE" (org-get-todo-state)))
+      (org-todo "DONE")))
+
+  (let* ((org-archive-file (or org-default-completed-file
+                               (fcuny/this-month-archive-entry)))
+         (org-archive-location (format "%s::" org-archive-file)))
+    (org-archive-subtree)))
+
+(defun fcuny/this-month-archive-entry ()
+  "Return the full pathname to the month's archive entry file.
+Granted, this assumes each journal's file entry to be formatted
+with year/month, as in `201901' for January 4th.
+
+Note: `org-journal-dir' variable must be set to the directory
+where all good journal entries live, e.g. ~/journal."
+  (let* ((daily-name   (format "%s-archive.org" (format-time-string "%Y-%m")))
+         (file-name    (concat org-archive-dir daily-name)))
+    (expand-file-name file-name)))
+
+(defun fcuny/org-subtree-region ()
+  "Return a list of the start and end of a subtree."
+  (save-excursion
+    (list (progn (org-back-to-heading) (point))
+          (progn (org-end-of-subtree)  (point)))))
+
+(defun fcuny/org-refile-directly (file-dest)
+  "Move the current subtree to the end of FILE-DEST.
+If SHOW-AFTER is non-nil, show the destination window,
+otherwise, this destination buffer is not shown."
+  (interactive "fDestination: ")
+
+  (defun dump-it (file contents)
+    (find-file-other-window file-dest)
+    (goto-char (point-max))
+    (insert "\n" contents))
+
+  (save-excursion
+    (let* ((region (fcuny/org-subtree-region))
+           (contents (buffer-substring (first region) (second region))))
+      (apply 'kill-region region)
+      (save-window-excursion (dump-it file-dest contents)))))
+
+(defun fcuny/org-refile-to-task ()
+  "Refile (move) the current Org subtree to `org-default-tasks-file'."
+  (interactive)
+  (fcuny/org-refile-directly org-default-tasks-file))
+
+(defun fcuny/org-refile-to-task ()
+  "Refile (move) the current Org subtree to `org-default-tasks-file'."
+  (interactive)
+  (fcuny/org-refile-directly org-default-tasks-file))
+
+(defun fcuny/org-refile-to-personal-notes ()
+  "Refile (move) the current Org subtree to `org-default-notes-file'."
+  (interactive)
+  (fcuny/org-refile-directly org-default-notes-file))
+
+(defun fcuny/org-refile-subtree-to-file (dir)
+  "Archive the org-mode subtree and create an entry in the
+directory folder specified by DIR. It attempts to move as many of
+the subtree's properties and other features to the new file."
+  (interactive "DDestination: ")
+  (let* ((props      (fcuny/org-subtree-metadata))
+         (head       (plist-get props :header))
+         (body       (plist-get props :body))
+         (tags       (plist-get props :tags))
+         (properties (plist-get props :properties))
+         (area       (plist-get props :region))
+         (filename   (fcuny/org-filename-from-title head))
+         (filepath   (format "%s/%s.org" dir filename)))
+    (apply #'delete-region area)
+    (fcuny/org-create-org-file filepath head body tags properties)))
+
+(defun fcuny/org-set-file-property (key value &optional spot)
+  "Make sure file contains a top-level, file-wide property.
+KEY is something like `TITLE' or `FILETAGS'. This function makes
+sure that the property contains the contents of VALUE, and if the
+file doesn't have the property, it is inserted at either SPOT, or
+if nil,the top of the file."
+  (save-excursion
+    (goto-char (point-min))
+    (let ((case-fold-search t))
+      (if (re-search-forward (format "^#\\+%s:\s*\\(.*\\)" key) nil t)
+          (replace-match value nil nil nil 1)
+
+        (cond
+         ;; if SPOT is a number, go to it:
+         ((numberp spot) (goto-char spot))
+         ;; If SPOT is not given, jump to first blank line:
+         ((null spot) (progn (goto-char (point-min))
+                             (re-search-forward "^\s*$" nil t)))
+         (t (goto-char (point-min))))
+
+        (insert (format "#+%s: %s\n" (upcase key) value))))))
+
+(defun fcuny/org-create-org-file (filepath header body tags properties)
+  "Create a new Org file by FILEPATH. The contents of the file is
+pre-populated with the HEADER, BODY and any associated TAGS."
+  (find-file-other-window filepath)
+  (fcuny/org-set-file-property "TITLE" header t)
+  (when tags
+    (fcuny/org-set-file-property "FILETAGS" (s-join " " tags)))
+
+  ;; Insert any drawer properties as #+PROPERTY entries:
+  (when properties
+    (goto-char (point-min))
+    (or (re-search-forward "^\s*$" nil t) (point-max))
+    (--map (insert (format "#+PROPERTY: %s %s" (first it) (second it))) properties))
+
+  ;; My auto-insert often adds an initial headline for a subtree, and in this
+  ;; case, I don't want that... Yeah, this isn't really globally applicable,
+  ;; but it shouldn't cause a problem for others.
+  (when (re-search-forward "^\\* [0-9]$" nil t)
+    (replace-match ""))
+
+  (delete-blank-lines)
+  (goto-char (point-max))
+  (insert "\n")
+  (insert body))
+
+(defun fcuny/org-subtree-metadata ()
+  "Return a list of key aspects of an org-subtree. Includes the
+following: header text, body contents, list of tags, region list
+of the start and end of the subtree."
+  (save-excursion
+    ;; Jump to the parent header if not already on a header
+    (when (not (org-at-heading-p))
+      (org-previous-visible-heading 1))
+
+    (let* ((context (org-element-context))
+           (attrs   (second context))
+           (props   (org-entry-properties)))
+
+      (list :region     (list (plist-get attrs :begin) (plist-get attrs :end))
+            :header     (plist-get attrs :title)
+            :tags       (fcuny/org-get-subtree-tags props)
+            :properties (fcuny/org-get-subtree-properties attrs)
+            :body       (fcuny/org-get-subtree-content attrs)))))
+
+(defun fcuny/org-filename-from-title (title)
+  "Creates a useful filename based on a header string, TITLE.
+For instance, given the string:    What's all this then?
+     This function will return:    whats-all-this-then"
+  (let* ((no-letters (rx (one-or-more (not alphanumeric))))
+         (init-try (->> title
+                        downcase
+                        (replace-regexp-in-string "'" "")
+                        (replace-regexp-in-string no-letters "-"))))
+    (string-trim init-try "-+" "-+")))
+
+(defun fcuny/org-get-subtree-content (attributes)
+  "Return the contents of the current subtree as a string."
+  (let ((header-components '(clock diary-sexp drawer headline inlinetask
+                             node-property planning property-drawer section)))
+
+      (goto-char (plist-get attributes :contents-begin))
+
+      ;; Walk down past the properties, etc.
+      (while
+          (let* ((cntx (org-element-context))
+                 (elem (first cntx))
+                 (props (second cntx)))
+            (when (member elem header-components)
+              (goto-char (plist-get props :end)))))
+
+      ;; At this point, we are at the beginning of what we consider
+      ;; the contents of the subtree, so we can return part of the buffer:
+      (buffer-substring-no-properties (point) (org-end-of-subtree))))
+
+(defun fcuny/org-get-subtree-properties (attributes)
+  "Return a list of tuples of a subtrees properties where the keys are strings."
+
+  (defun symbol-upcase? (sym)
+    (let ((case-fold-search nil))
+      (string-match-p "^:[A-Z]+$" (symbol-name sym))))
+
+  (defun convert-tuple (tup)
+    (let ((key (first tup))
+          (val (second tup)))
+      (list (substring (symbol-name key) 1) val)))
+
+  (->> attributes
+       (-partition 2)                         ; Convert plist to list of tuples
+       (--filter (symbol-upcase? (first it))) ; Remove lowercase tuples
+       (-map 'convert-tuple)))
+
+(defun fcuny/org-get-subtree-tags (&optional props)
+  "Given the properties, PROPS, from a call to
+`org-entry-properties', return a list of tags."
+  (unless props
+     (setq props (org-entry-properties)))
+  (let ((tag-label "ALLTAGS" ))
+    (-some->> props
+         (assoc tag-label)
+         cdr
+         substring-no-properties
+         (s-split ":")
+         (--filter (not (equalp "" it))))))
+
+(defun fcuny/org-refile-to-projects-dir ()
+  "Move the current subtree to a file in the `projects' directory."
+  (interactive)
+  (fcuny/org-refile-subtree-to-file org-default-projects-dir))
+
+(defun fcuny/org-refile-to-personal-dir ()
+  "Move the current subtree to a file in the `personal' directory."
+  (interactive)
+  (fcuny/org-refile-subtree-to-file org-default-personal-dir))
+
+(defun fcuny/org-refile-to-incubate ()
+  "Refile (move) the current Org subtree to `org-default-incubate-fire'."
+  (interactive)
+  (fcuny/org-refile-directly org-default-incubate-file))
+
+(defun fcuny/org-refile-to-technical-dir ()
+  "Move the current subtree to a file in the `technical' directory."
+  (interactive)
+  (fcuny/org-refile-subtree-to-file org-default-technical-dir))
+
 (provide 'fcuny-defuns)