lang/org: add +org-attach/sync (attachment management)

lang/org now supports the attach:* link abbreviation. Running
+org/attach-sync will index all such links across org files in +org-dir
and delete any files in org-attach-directory that aren't referenced.

TODO: have a separate directory for attach:* attachments and vanilla
attachments?
This commit is contained in:
Henrik Lissner 2018-02-18 04:30:49 -05:00
parent f4a0311834
commit e50d8b8733
No known key found for this signature in database
GPG key ID: 5F6C0EA160557395
2 changed files with 72 additions and 32 deletions

View file

@ -5,22 +5,29 @@
;; I believe Org's native attachment system is over-complicated and litters ;; I believe Org's native attachment system is over-complicated and litters
;; files with metadata I don't want. So I wrote my own, which: ;; files with metadata I don't want. So I wrote my own, which:
;; ;;
;; + Causes attachments to be placed in a centralized location, ;; + Places attachments in a centralized location (`+org-attach-dir' in
;; `+org-dir'), using an attach:* link abbreviation.
;; + Use `+org-attach/sync' to index all attachments in `+org-dir' that use the
;; attach:* abbreviation and delete orphaned ones that are no longer
;; referenced.
;; + Adds drag-and-drop support for images (with inline image preview) ;; + Adds drag-and-drop support for images (with inline image preview)
;; + Adds drag-and-drop support for media files (pdfs, zips, etc) with a ;; + Adds drag-and-drop support for media files (pdfs, zips, etc) with a
;; filetype icon and short link. ;; filetype icon and short link.
;; + TODO Offers an attachment management system.
;; Some commands of interest: ;; Some commands of interest:
;; + `org-download-screenshot' ;; + `org-download-screenshot'
;; + `+org-attach/file' ;; + `+org-attach/file'
;; + `+org-attach/url' ;; + `+org-attach/url'
;; + :org [FILE/URL] ;; + `+org-attach/sync'
(defvar +org-attach-dir ".attach/" (defvar +org-attach-dir ".attach/"
"Where to store attachments (relative to current org file).") "Where to store attachments relative to `+org-dir'.")
;;
;; Plugins
;;
(def-package! org-download (def-package! org-download
:commands (org-download-dnd org-download-dnd-base64) :commands (org-download-dnd org-download-dnd-base64)
:init :init
@ -58,7 +65,11 @@
(advice-add #'org-download--fullname (advice-add #'org-download--fullname
:filter-return #'+org-attach*download-fullname)) :filter-return #'+org-attach*download-fullname))
;; ;;
;; Bootstrap
;;
(defun +org|init-attach () (defun +org|init-attach ()
(setq org-attach-directory (expand-file-name +org-attach-dir +org-dir)) (setq org-attach-directory (expand-file-name +org-attach-dir +org-dir))
;; A shorter link to attachments ;; A shorter link to attachments
@ -67,6 +78,7 @@
(push (car (last (split-string +org-attach-dir "/" t))) (push (car (last (split-string +org-attach-dir "/" t)))
projectile-globally-ignored-directories) projectile-globally-ignored-directories)
;;
(after! recentf (after! recentf
(push (format "%s.+$" (regexp-quote org-attach-directory)) (push (format "%s.+$" (regexp-quote org-attach-directory))
recentf-exclude))) recentf-exclude)))

View file

@ -14,35 +14,58 @@
((or "zip" "gz" "tar" "7z" "rar") ?) ((or "zip" "gz" "tar" "7z" "rar") ?)
(_ ?)))) (_ ?))))
;; (defun +org-attach-cleanup ()
;; ;; "Deletes any attachments that are no longer present in the org-mode buffer."
;; (let* ((attachments-local (+org-attachments))
;; (attachments (directory-files org-attach-directory t "^[^.]" t))
;; (to-delete (cl-set-difference attachments-local attachments)))
;; ;; TODO
;; to-delete))
;; (defun +org-attachments () ;;
;; "List all attachments in the current buffer." (defvar +org-attachments nil
;; (unless (eq major-mode 'org-mode) "A list of all indexed attachments in `+org-dir'.")
;; (user-error "Not an org buffer"))
;; (org-save-outline-visibility nil (defvar +org-attachments-files value
;; (let ((attachments '()) "A list of all attachments in `org-attach-directory'.")
;; element)
;; (when (and (file-directory-p org-attach-directory) (defun +org-attachments--list (&optional beg end)
;; (> (length (file-expand-wildcards (expand-file-name "*" org-attach-directory))) 0)) "Return a list of all attachment file names in the current buffer between BEG
;; (save-excursion and END (defaults to `point-min' and `point-max')."
;; (goto-char (point-min)) (let ((case-fold-search t)
;; (while (progn (org-next-link) (not org-link-search-failed)) attachments)
;; (setq element (org-element-context)) (or end (setq end (point-max)))
;; (when-let* (file (and (eq (org-element-type element) 'link) (org-save-outline-visibility nil
;; (expand-file-name (org-element-property :path element)))) (org-with-wide-buffer
;; (when (and (string= (org-element-property :type element) "file") (goto-char (or beg (point-min)))
;; (string= (concat (file-name-base (directory-file-name (file-name-directory file))) "/") (while (search-forward "[[attach:" end t)
;; org-attach-directory) (let* ((context (save-match-data (org-element-context)))
;; (file-exists-p file)) (link (expand-file-name (org-link-unescape (org-element-property :path context))
;; (push file attachments)))))) org-attach-directory)))
;; (cl-remove-duplicates attachments)))) (when (and (equal "file" (org-element-property :type context))
(file-in-directory-p link org-attach-directory))
(push (file-name-nondirectory link) attachments))))))
(cl-delete-duplicates attachments :test #'string=)))
;;;###autoload
(defun +org-attach/sync (arg)
"Reindex all attachments in `+org-dir' and delete orphaned attachments in
`org-attach-directory'. If ARG (universal arg), conduct a dry run."
(declare (interactive-only t))
(interactive "P")
(message "Reloading")
(setq +org-attachments-files (directory-files org-attach-directory nil "^[^.]" t))
(with-temp-buffer
(delay-mode-hooks (org-mode))
(dolist (org-file (directory-files-recursively +org-dir "\\.org$"))
(insert-file-contents-literally org-file))
(setq +org-attachments (+org-attachments--list)))
;; clean up
(dolist (file (cl-set-difference +org-attachments-files +org-attachments
:test #'string=))
(message "Deleting orphaned attachment: %s" file)
(unless arg
(delete-file (expand-file-name file org-attach-directory))))
(message "Buffer's attachments synced"))
;;;###autoload
(defun +org-attach/find-file ()
"Open a file from `org-attach-directory'."
(interactive)
(doom-project-browse org-attach-directory))
;;;###autoload ;;;###autoload
(defun +org-attach/file (path) (defun +org-attach/file (path)
@ -84,6 +107,11 @@ the cursor."
(copy-alist dnd-protocol-alist)))) (copy-alist dnd-protocol-alist))))
(dnd-handle-one-url nil action uri)))) (dnd-handle-one-url nil action uri))))
;;
;; Advice
;;
;;;###autoload ;;;###autoload
(defun +org-attach*insert-link (_link filename) (defun +org-attach*insert-link (_link filename)
"Produces and inserts a link to FILENAME into the document. "Produces and inserts a link to FILENAME into the document.