From e50d8b8733ef97996c8c50c399f9370f4662d6c4 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Sun, 18 Feb 2018 04:30:49 -0500 Subject: [PATCH] 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? --- modules/lang/org/+attach.el | 20 ++++-- modules/lang/org/autoload/org-attach.el | 84 ++++++++++++++++--------- 2 files changed, 72 insertions(+), 32 deletions(-) diff --git a/modules/lang/org/+attach.el b/modules/lang/org/+attach.el index 2a12ca509..131e3fea0 100644 --- a/modules/lang/org/+attach.el +++ b/modules/lang/org/+attach.el @@ -5,22 +5,29 @@ ;; 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: ;; -;; + 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 media files (pdfs, zips, etc) with a ;; filetype icon and short link. -;; + TODO Offers an attachment management system. ;; Some commands of interest: ;; + `org-download-screenshot' ;; + `+org-attach/file' ;; + `+org-attach/url' -;; + :org [FILE/URL] +;; + `+org-attach/sync' (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 :commands (org-download-dnd org-download-dnd-base64) :init @@ -58,7 +65,11 @@ (advice-add #'org-download--fullname :filter-return #'+org-attach*download-fullname)) + ;; +;; Bootstrap +;; + (defun +org|init-attach () (setq org-attach-directory (expand-file-name +org-attach-dir +org-dir)) ;; A shorter link to attachments @@ -67,6 +78,7 @@ (push (car (last (split-string +org-attach-dir "/" t))) projectile-globally-ignored-directories) + ;; (after! recentf (push (format "%s.+$" (regexp-quote org-attach-directory)) recentf-exclude))) diff --git a/modules/lang/org/autoload/org-attach.el b/modules/lang/org/autoload/org-attach.el index 2d6e01490..d27bfdad8 100644 --- a/modules/lang/org/autoload/org-attach.el +++ b/modules/lang/org/autoload/org-attach.el @@ -14,35 +14,58 @@ ((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." -;; (unless (eq major-mode 'org-mode) -;; (user-error "Not an org buffer")) -;; (org-save-outline-visibility nil -;; (let ((attachments '()) -;; element) -;; (when (and (file-directory-p org-attach-directory) -;; (> (length (file-expand-wildcards (expand-file-name "*" org-attach-directory))) 0)) -;; (save-excursion -;; (goto-char (point-min)) -;; (while (progn (org-next-link) (not org-link-search-failed)) -;; (setq element (org-element-context)) -;; (when-let* (file (and (eq (org-element-type element) 'link) -;; (expand-file-name (org-element-property :path element)))) -;; (when (and (string= (org-element-property :type element) "file") -;; (string= (concat (file-name-base (directory-file-name (file-name-directory file))) "/") -;; org-attach-directory) -;; (file-exists-p file)) -;; (push file attachments)))))) -;; (cl-remove-duplicates attachments)))) +;; +(defvar +org-attachments nil + "A list of all indexed attachments in `+org-dir'.") + +(defvar +org-attachments-files value + "A list of all attachments in `org-attach-directory'.") + +(defun +org-attachments--list (&optional beg end) + "Return a list of all attachment file names in the current buffer between BEG +and END (defaults to `point-min' and `point-max')." + (let ((case-fold-search t) + attachments) + (or end (setq end (point-max))) + (org-save-outline-visibility nil + (org-with-wide-buffer + (goto-char (or beg (point-min))) + (while (search-forward "[[attach:" end t) + (let* ((context (save-match-data (org-element-context))) + (link (expand-file-name (org-link-unescape (org-element-property :path context)) + org-attach-directory))) + (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 (defun +org-attach/file (path) @@ -84,6 +107,11 @@ the cursor." (copy-alist dnd-protocol-alist)))) (dnd-handle-one-url nil action uri)))) + +;; +;; Advice +;; + ;;;###autoload (defun +org-attach*insert-link (_link filename) "Produces and inserts a link to FILENAME into the document.