summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS3
-rw-r--r--bog-todo2
-rw-r--r--bog.el45
3 files changed, 49 insertions, 1 deletions
diff --git a/NEWS b/NEWS
index dbef01f..c11a25c 100644
--- a/NEWS
+++ b/NEWS
@@ -34,6 +34,9 @@
- New commands =bog-next-non-heading-citekey= and
=bog-previous-non-heading-citekey=
+- New command =bog-list-orphan-citekeys= finds citekeys that are
+ referred to in the notes but don't have their own heading.
+
** Other changes
- =bog-goto-citekey-heading-in-buffer= and
diff --git a/bog-todo b/bog-todo
index 785ae48..7309776 100644
--- a/bog-todo
+++ b/bog-todo
@@ -24,7 +24,7 @@ Perhaps use a cache file. This could be used in
* Notes
-** ENH Find in-text citekeys that don't have a header
+** DONE Find in-text citekeys that don't have a header
** CANCELED Check for duplicate citekeys headings
:LOGBOOK:
diff --git a/bog.el b/bog.el
index 4207855..dc4899d 100644
--- a/bog.el
+++ b/bog.el
@@ -345,6 +345,44 @@ word constituents."
(--keep it
(org-map-entries 'bog-citekey-from-heading nil 'file)))
+(defun bog-non-heading-citekeys-in-file (file)
+ "Return all non-heading citekeys in FILE."
+ (let (refs
+ case-fold-search)
+ (with-temp-buffer
+ (org-mode)
+ (insert-file-contents file)
+ (while (re-search-forward bog-citekey-format nil t)
+ (unless (or (org-at-heading-p)
+ (org-at-property-p))
+ (push (match-string-no-properties 0) refs))))
+ (-distinct refs)))
+
+(defun bog-list-orphan-citekeys (&optional file)
+ "List in citekeys that appear in notes but don't have heading.
+With prefix FILE, include only orphan citekeys from that file."
+ (interactive (list (and current-prefix-arg
+ (bog-read-note-file-name))))
+ (let ((files (or (and file (list file))
+ (bog-notes)))
+ (heading-cks (bog-all-heading-citekeys))
+ cks)
+ (with-current-buffer (get-buffer-create "*Bog orphan citekeys*")
+ (delete-region (point-min) (point-max))
+ (insert "\n")
+ (-each files
+ (lambda (f)
+ (setq cks
+ (--> (bog-non-heading-citekeys-in-file f)
+ (-difference it heading-cks)
+ (-sort (lambda (x y) (string-lessp x y)) it)
+ (mapconcat #'identity it "\n")))
+ (unless (equal cks "")
+ (insert (format "* %s\n\n" (file-name-nondirectory f)))
+ (insert (concat cks "\n\n")))))
+ (org-mode)
+ (show-all))))
+
;;; Citekey-associated files
@@ -740,6 +778,13 @@ level `bog-refile-maxlevel' are considered."
(concat (file-name-as-directory bog-note-directory)
"*.org"))))
+(defun bog-read-note-file-name ()
+ (let ((nodir-files (-annotate #'file-name-nondirectory
+ (bog-notes))))
+ (cdr (assoc (org-icompleting-read "File: "
+ (-map #'car nodir-files))
+ nodir-files))))
+
(defun bog-search-notes (&optional todo-only string)
"Search notes using `org-search-view'.
With prefix argument TODO-ONLY, only TODO entries are searched.