18 Commits e564fa2039 ... 316ba8ab97

Author SHA1 Message Date
  c1-g 316ba8ab97 add: Load org-fc 3 years ago
  c1-g d440f37117 change: Promote splitted headline to file-level 3 years ago
  c1-g 811110b982 change: Don't move splitted files to org-roam-directory 3 years ago
  c1-g 7cb8e88ea8 change: Substitute ID with CUSTOM_ID right after splittiing 3 years ago
  c1-g b0dadaf5d9 change: Unhide everything before splitting file 3 years ago
  c1-g 0122630c8b change: Format split file suffix by the maximum length 3 years ago
  c1-g 0056395e8d change: No output buffer 3 years ago
  c1-g e0f8debb41 fix: extract TITLE keyword as string 3 years ago
  c1-g b75d3fd3f2 change: parent property has its own title 3 years ago
  c1-g 8ceff9a163 fix: bir-ref-article-property → bir-article-property 3 years ago
  c1-g 8a61c5b57e change: Inherit article property first 3 years ago
  c1-g 26ce29e386 change: bir-directory as a subdir of org-roam-directory 3 years ago
  c1-g 32b77f20dd change: bir-split will place files in bir-directory 3 years ago
  c1-g 11e43ed24d add: bir-mercy-complete 3 years ago
  c1-g 022bfc7b86 change: Use ids instead of org elements 3 years ago
  c1-g d49aefd9fd change: Simplify bir-mercy-update by always use the current buffer 3 years ago
  c1-g d35fd3bc4e change: Defaults to current-buffer 3 years ago
  c1-g dc2f03d818 add: bir-mercy 3 years ago
5 changed files with 278 additions and 34 deletions
  1. 3 1
      bir-core.el
  2. 19 14
      bir-extract.el
  3. 224 0
      bir-mercy.el
  4. 31 19
      bir-split.el
  5. 1 0
      bir.el

+ 3 - 1
bir-core.el

@@ -31,7 +31,7 @@
   "Bastardized Incremental Reading in Emacs."
   :group 'external)
 
-(defcustom bir-directory org-roam-directory
+(defcustom bir-directory (expand-file-name "bir/" org-roam-directory)
   "TODO"
   :type 'string
   :group 'bir)
@@ -51,6 +51,8 @@
   :type 'string
   :group 'bir)
 
+(add-to-list 'org-fc-directories bir-directory)
+
 
 (provide 'bir-core)
 ;;; bir-core.el ends here

+ 19 - 14
bir-extract.el

@@ -31,23 +31,28 @@
      (list (region-beginning) (region-end) nil t)))
   (let* ((text (or text (buffer-substring beg end)))
          (article-title (if (= (org-outline-level) 0)
-                            (org-collect-keywords '("TITLE"))
+                            (cadar (org-collect-keywords '("TITLE")))
                           (save-excursion (while (org-up-heading-safe))
                                           (or (org-entry-get nil "title")
                                               (nth 4 (org-heading-components))))))
-         (article-prop (org-entry-get nil bir-ref-article-property t t))
-         (article-id (or (plist-get (org-element--get-global-node-properties) :ID)
-                         article-prop
+         (article-prop (org-entry-get nil bir-article-property t t))
+         (article-id (or article-prop
+                         (plist-get (org-element--get-global-node-properties) :ID)
                          (save-excursion (while (org-up-heading-safe))
                                          (org-id-get-create))))
          (article (or article-prop
                       (org-link-make-string (concat "id:" article-id) article-title)))
+         (parent-title (if (= (org-outline-level) 0)
+                           (cadar (org-collect-keywords '("TITLE")))
+                         (save-excursion (or (org-entry-get nil "title")
+                                             (org-up-heading-safe)
+                                             (nth 4 (org-heading-components))))))
          (parent (save-excursion
                    (org-back-to-heading-or-point-min t)
                    (if (string= (org-id-get) article-id)
                        article
                      (org-link-make-string (concat "id:" (org-id-get-create))
-                                           (nth 4 (org-heading-components))))))
+                                           parent-title))))
          (template-info nil)
          (node (org-roam-node-at-point))
          (refs (ignore-errors (org-roam-node-refs (org-roam-node-from-id article-id))))
@@ -71,8 +76,8 @@
       (user-error "%s exists. Aborting" file-path))
     (with-temp-buffer
       (org-mode)
-      (org-set-property bir-ref-article-property article)
-      (org-set-property bir-ref-parent-property parent)
+      (org-set-property bir-article-property article)
+      (org-set-property bir-parent-property parent)
       (when refs (org-roam-property-add "ROAM_REFS" (concat "cite:&" (car refs))))
       (write-region (point-min) (point-max) file-path nil t nil t)
       (setq id (org-id-get-create))
@@ -99,14 +104,14 @@
                           (save-excursion (while (org-up-heading-safe))
                                           (or (org-entry-get nil "title")
                                               (nth 4 (org-heading-components))))))
-         (article-prop (org-entry-get nil bir-ref-article-property t))
+         (article-prop (org-entry-get nil bir-article-property t))
          (article-id (or (plist-get (org-element--get-global-node-properties) :ID)
                          (when article-prop
                            (progn (string-match org-link-bracket-re article-prop)
                                   (substring-no-properties (match-string 1 article-prop) 3)))
                          (save-excursion (while (org-up-heading-safe))
                                          (org-id-get-create))))
-         (article (or (org-entry-get nil bir-ref-article-property t t)
+         (article (or (org-entry-get nil bir-article-property t t)
                       (org-link-make-string (concat "id:" article-id) article-title)))
          (parent (save-excursion
                    (org-back-to-heading-or-point-min t)
@@ -143,8 +148,8 @@
       (org-fc--region-to-cloze beg end nil hint)
       (org-mode)
       (org-with-point-at (point-min)
-        (org-set-property bir-ref-article-property article)
-        (org-set-property bir-ref-parent-property parent)
+        (org-set-property bir-article-property article)
+        (org-set-property bir-parent-property parent)
         (when refs (org-roam-property-add "ROAM_REFS" (concat "cite:&" (car refs)))))
       (org-delete-property "id")
       (write-region (point-min) (point-max) file-path nil t nil t)
@@ -166,7 +171,7 @@
     (org-id-get-create)
     (let* ((template-info nil)
            (title (org-roam-get-keyword "TITLE"))
-           (article-prop (org-entry-get nil bir-ref-article-property t))
+           (article-prop (org-entry-get nil bir-article-property t))
            (article-id (or (plist-get (org-element--get-global-node-properties) :ID)
                            (when article-prop
                              (progn (string-match org-link-bracket-re article-prop)
@@ -202,8 +207,8 @@
         (let ((kill-do-not-save-duplicates t))
           (org-cut-subtree))
         (with-temp-buffer
-          (org-set-property bir-ref-article-property article)
-          (org-set-property bir-ref-parent-property parent)
+          (org-set-property bir-article-property article)
+          (org-set-property bir-parent-property parent)
           (when refs (org-set-property "ROAM_REFS" (concat "cite:&" (car refs))))
           (org-paste-subtree 1 nil nil t)
           (write-region (point-min) (point-max) file-path nil t)

+ 224 - 0
bir-mercy.el

@@ -0,0 +1,224 @@
+;;; bir-mercy.el --- Mercy interface for BIR         -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2022  c1-g
+
+;; Author: c1-g <char1iegordon@protonmail.com>
+;; Keywords: extensions
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; 
+
+;;; Code:
+(require 'widget)
+
+(eval-when-compile
+  (require 'wid-edit))
+
+(defvar-local bir-mercy--last-edited-field nil)
+
+(defvar-local bir-mercy--total nil)
+
+(defvar-local bir-mercy--origin-buffer nil)
+
+(defun bir-mercy-time-in (days)
+  (let ((seconds (* days 60 60 24))
+        (now (time-to-seconds)))
+    (format-time-string "%b %d, %Y" (seconds-to-time (+ now seconds)))))
+
+(defmacro bir-mercy-with-widget-deactivated (widget &rest body)
+  (declare (debug (body)))
+  `(let ((widget ,widget))
+     (if (widget-apply ,widget :active)
+         (progn ,@body)
+       (widget-apply ,widget :activate)
+       (progn ,@body)
+       (widget-apply ,widget :deactivate))))
+
+
+(defun bir-mercy (&optional match scope &rest skip)
+  (interactive)
+  (let* ((buf (get-buffer-create "*Mercy scheduling*"))
+         (ids (org-map-entries #'org-id-get-create match scope skip))
+         (total (length ids))
+         (origin (current-buffer))
+         (inhibit-read-only t))
+    (with-current-buffer buf
+      (remove-overlays)
+      (erase-buffer)
+      (kill-all-local-variables)
+      (setq bir-mercy--origin-buffer origin)
+      (setq bir-mercy--total total)
+      (add-hook 'after-change-functions #'bir-maybe-get-widget-after-change nil t)
+      (add-hook 'after-change-functions #'widget-after-change nil t)
+      (widget-insert (format "Elements to schedule %s"
+                             (propertize (make-string 1 ?\s) 'display '(space :align-to 45))))
+      (widget-apply (widget-create 'integer
+                                   :size 7
+                                   :tag 'total
+                                   :format "%v\n\n"
+                                   total)
+                    :deactivate)
+      (widget-insert (make-string fill-column ?-) "\n\n")
+      (widget-create 'integer
+                     :size 10
+                     :valid-regexp "[[:digit:]]+"
+                     :tag 'elt-per-day
+                     :format (format "Number of elements per day: %s%%v"
+                                     (propertize (make-string 1 ?\s) 'display '(space :align-to 45)))
+                     total)
+      (widget-insert "\n\n")
+      (widget-create 'integer
+                     :valid-regexp "[[:digit:]]+"
+                     :tag 'period
+                     :size 10
+                     :format (format "Scheduling period: %s%%v"
+                                     (propertize (make-string 1 ?\s) 'display '(space :align-to 45)))
+                     1)
+      (widget-apply (widget-create 'text
+                                   :tag 'date
+                                   :size 12
+                                   :format " %v\n\n"
+                                   (bir-mercy-time-in 1))
+                    :deactivate)
+      (widget-insert (make-string fill-column ?-) "\n\n")
+      (widget-insert "Choosing OK will result in scheduling ")
+      (widget-apply (widget-create 'integer
+                                   :size 7
+                                   :tag 'total
+                                   :format "%v"
+                                   total)
+                    :deactivate)
+      (widget-insert "  elements collected from\na collecting period of ")
+      (widget-apply (widget-create 'integer
+                                   :valid-regexp "[[:digit:]]+"
+                                   :size 4
+                                   :tag 'period
+                                   :format "%v"
+                                   1)
+                    :deactivate)
+      (widget-insert " days(s) in a period from today till ")
+      (widget-apply (widget-create 'text
+                                   :tag 'date
+                                   :size 12
+                                   :format "%v\n"
+                                   (bir-mercy-time-in 1))
+                    :deactivate)
+      (widget-insert "with ")
+      (widget-apply (widget-create 'integer
+                                   :valid-regexp "[[:digit:]]+"
+                                   :size 5
+                                   :tag 'elt-per-day
+                                   :format "%v"
+                                   total)
+                    :deactivate)
+      (widget-insert " repetitions per day.")
+      (widget-insert "\n\n")
+      (widget-insert (propertize (make-string 1 ?\s) 'display '(space :align-to 40)))
+      (widget-insert " ")
+      (widget-create 'push-button
+                     :notify (apply-partially #'bir-mercy-complete ids)
+                     :button-face 'org-checkbox-statistics-done
+                     "✓ OK")
+      (widget-insert " ")
+      (widget-create 'push-button
+                     :notify (lambda (&rest ignore)
+                               (kill-buffer))
+                     :button-face 'org-checkbox-statistics-todo
+                     "❌ Cancel")
+      (widget-insert " ")
+      (widget-create 'push-button
+                     :notify (lambda (&rest ignore)
+                               (bir-mercy-update bir-mercy--last-edited-field))
+                     :button-face 'org-date
+                     "⭯ Update")
+      (widget-insert " ")
+      (use-local-map widget-keymap)
+      (widget-setup)
+      (display-buffer-in-side-window buf '((dedicated . t)
+                                           (side . right)))
+      (select-window (get-buffer-window buf))
+      (let ((fit-window-to-buffer-horizontally t))
+        (fit-window-to-buffer)))))
+
+(defun bir-maybe-get-widget-after-change (from to _old)
+  (let ((field (widget-field-find from))
+        (other (widget-field-find to)))
+    (when field
+      (unless (eq field other)
+        (error "Change in different fields"))
+      (setq-local bir-mercy--last-edited-field field))))
+
+(defun bir-mercy-complete (ids &rest _ignore)
+  "docstring"
+  (let* ((widgets (cl-remove-if-not
+                   (lambda (w)
+                     (widget-apply w :active))
+                   widget-field-list))
+         (elt-per-day (widget-value (seq-find (lambda (w)
+                                                (eq 'elt-per-day (widget-get w :tag)))
+                                              widgets)))
+         (period (widget-value (seq-find (lambda (w)
+                                           (eq 'period (widget-get w :tag)))
+                                         widgets)))
+         (ids (seq-partition ids elt-per-day)))
+    (with-current-buffer bir-mercy--origin-buffer
+      (save-excursion
+        (dotimes (i period)
+          (dolist (id (nth i ids))
+            (goto-char (org-find-entry-with-id id))
+            (org-schedule nil (format "+%d" i))))))))
+
+(defun bir-mercy-update (field)
+  (when field
+    (let* ((widgets (seq-group-by (lambda (w)
+                                    (widget-get w :tag))
+                                  widget-field-list))
+
+           (active-period-widget (car
+                                  (cl-remove-if-not
+                                   (lambda (w)
+                                     (widget-apply w :active))
+                                   (alist-get 'period widgets))))
+
+           (tag (widget-get field :tag))
+           (period))
+      (when (memq tag '(elt-per-day period))
+        (dolist (widget (alist-get tag widgets))
+          (bir-mercy-with-widget-deactivated
+           widget
+           (widget-value-set
+            widget
+            (widget-value field))))
+        (dolist (widget (alist-get (car (delq tag '(elt-per-day period))) widgets))
+          (bir-mercy-with-widget-deactivated
+           widget
+           (widget-value-set
+            widget
+            (round (/ bir-mercy--total
+                      (widget-value field)))))))
+      (when active-period-widget
+        (dolist (date-widget (alist-get 'date widgets))
+          (bir-mercy-with-widget-deactivated
+           date-widget
+           (widget-value-set
+            date-widget
+            (bir-mercy-time-in (widget-value active-period-widget))))))
+      (widget-setup))))
+
+
+(provide 'bir-mercy)
+;;; bir-mercy.el ends here

+ 31 - 19
bir-split.el

@@ -24,6 +24,8 @@
 
 ;;; Code:
 
+(require 'org-fc)
+
 (defun bir-split (arg)
   (interactive "P")
   (let* ((file-level-p (org-with-point-at (point-min) (= (org-outline-level) 0)))
@@ -41,32 +43,34 @@
 
          (refs (org-roam-node-refs (org-roam-node-from-id article-id)))
 
-         (split-dir (file-name-as-directory
-                     (make-temp-file (concat article-id "-") t "-bir-split")))
+         (split-dir (file-name-as-directory (expand-file-name article-id bir-directory)))
+         (count (how-many org-outline-regexp-bol (point-min) (point-max)))
          ids
          split-files
          template)
+    (make-directory split-dir t)
     (setq ids (org-map-entries #'org-id-get-create))
     (save-buffer)
-    (start-process "bir-split" "test" (or (executable-find "gcsplit")
-                                          (executable-find "csplit"))
-                   "-s" "-z" "-f" (shell-quote-argument split-dir) "-b" "%d.org"
+    (org-show-all)
+    (start-process "bir-split" nil (or (executable-find "gcsplit")
+                                       (executable-find "csplit"))
+                   "-s" "-z" "-f" (shell-quote-argument split-dir)
+                   "-b" (format "%%0%dd.org"
+                                (string-width (number-to-string count)))
                    (shell-quote-argument (buffer-file-name))
                    "/^\\*\\+ /" "{*}")
     (setq split-files (directory-files split-dir t directory-files-no-dot-files-regexp))
+    (when split-files
+      (org-map-entries (lambda ()
+                         (org-set-property "CUSTOM_ID" (org-id-get))
+                         (org-delete-property "ID")))
+      (save-buffer)
+      (org-roam-db-update-file))
     (dolist (file split-files)
       (with-temp-buffer
         (org-mode)
         (insert-file-contents file)
         (goto-char (point-min))
-        (unless (and (= (org-outline-level) 0) (org-collect-keywords '("TITLE")))
-          (save-excursion
-            (insert "#+TITLE: " (format "%s: %s\n"
-                                        article-title
-                                        (nth 4 (org-heading-components))))))
-        (write-region (point-min) (point-max) file nil t)
-        (org-set-property bir-article-property article)
-        (when refs (org-set-property "ROAM_REFS" (concat "cite:&" (car refs))))
         (setq template (org-roam-format-template
                         (string-trim (org-capture-fill-template org-roam-extract-new-file-path))
                         (lambda (key default-val)
@@ -81,14 +85,22 @@
                              (t (let ((r (read-from-minibuffer (format "%s: " key) default-val)))
                                   (plist-put template-info ksym r)
                                   r)))))))
-        (rename-file file (expand-file-name template (file-name-as-directory org-roam-directory)))
+        (write-region (point-min) (point-max) file nil t)
+        (org-set-property bir-article-property article)
+        (when refs (org-set-property "ROAM_REFS" (concat "cite:&" (car refs))))
+        (org-fc-type-topic-init)
+        (when (> (org-outline-level) 0)
+          (org-schedule '(4))
+          (let ((title (nth 4 (org-heading-components)))
+                (tags (nth 5 (org-heading-components))))
+            (beginning-of-line)
+            (kill-line 1)
+            (org-roam-end-of-meta-data 'full)
+            (insert "#+TITLE: " title "\n")
+            (when tags (insert "#+FILETAGS: " tags "\n"))))
         (save-buffer)
         (org-roam-db-update-file)))
-    (when split-files
-      (org-map-entries (lambda ()
-                         (org-set-property "CUSTOM_ID" (org-id-get))
-                         (org-delete-property "ID")))
-      ids)))
+    ids))
 
 (provide 'bir-split)
 ;;; bir-split.el ends here

+ 1 - 0
bir.el

@@ -27,6 +27,7 @@
 (require 'bir-core)
 (require 'bir-extract)
 (require 'bir-split)
+(require 'bir-mercy)
 
 
 (provide 'bir)