I prefer not to invent new variables when they aren't strictly necessary. org-directory is one such variable (although the other path variables are still necessary).
146 lines
5.7 KiB
EmacsLisp
146 lines
5.7 KiB
EmacsLisp
;;; lang/org/autoload/org-attach.el -*- lexical-binding: t; -*-
|
|
;;;###if (featurep! +attach)
|
|
|
|
(defun +org-attach--icon (path)
|
|
(char-to-string
|
|
(pcase (downcase (file-name-extension path))
|
|
((or "jpg" "jpeg" "png" "gif") ?)
|
|
("pdf" ?)
|
|
((or "ppt" "pptx") ?)
|
|
((or "xls" "xlsx") ?)
|
|
((or "doc" "docx") ?)
|
|
((or "ogg" "mp3" "wav" "aiff" "flac") ?)
|
|
((or "mp4" "mov" "avi") ?)
|
|
((or "zip" "gz" "tar" "7z" "rar") ?)
|
|
(_ ?))))
|
|
|
|
|
|
;;
|
|
(defvar +org-attachments nil
|
|
"A list of all indexed attachments in `org-directory'.")
|
|
|
|
(defvar +org-attachments-files nil
|
|
"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-directory' 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-directory "\\.org$"))
|
|
(insert-file-contents-literally org-file))
|
|
(setq +org-attachments (+org-attachments--list)))
|
|
;; clean up
|
|
(let ((deleted 0))
|
|
(dolist (file (cl-set-difference +org-attachments-files +org-attachments
|
|
:test #'string=))
|
|
(message "Deleting orphaned attachment: %s" file)
|
|
(cl-incf deleted)
|
|
(unless arg
|
|
(delete-file (expand-file-name file org-attach-directory))))
|
|
(message "Buffer's attachments synced (%d deleted)" deleted)))
|
|
|
|
;;;###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)
|
|
"Copies the file at PATH to `+org-attach-dir' and places an org link to it at
|
|
the cursor."
|
|
(interactive "fAttach file: ")
|
|
(+org-attach/uri path))
|
|
|
|
;;;###autoload
|
|
(defun +org-attach/uri (uri)
|
|
"Downloads the file at URL and place an org link to it at the cursor."
|
|
(interactive "sUri/file: ")
|
|
(unless (eq major-mode 'org-mode)
|
|
(user-error "Not in an org buffer"))
|
|
(require 'org-download)
|
|
(let ((raw-uri (url-unhex-string uri)))
|
|
(condition-case ex
|
|
(cond ((string-match-p "^data:image/png;base64," uri)
|
|
(org-download-dnd-base64 uri nil))
|
|
((image-type-from-file-name raw-uri)
|
|
(org-download-image raw-uri))
|
|
(t
|
|
(let ((new-path (expand-file-name (org-download--fullname raw-uri))))
|
|
;; Download the file
|
|
(if (string-match-p (concat "^" (regexp-opt '("http" "https" "nfs" "ftp" "file")) ":/") uri)
|
|
(url-copy-file raw-uri new-path)
|
|
(copy-file uri new-path))
|
|
;; insert the link
|
|
(org-download-insert-link raw-uri new-path))))
|
|
(error
|
|
(user-error "Failed to attach file: %s" (error-message-string ex))))))
|
|
|
|
;;;###autoload
|
|
(defun +org-attach-download-dnd (uri action)
|
|
"TODO"
|
|
(if (eq major-mode 'org-mode)
|
|
(+org-attach/uri uri)
|
|
(let ((dnd-protocol-alist
|
|
(rassq-delete-all '+org-attach-download-dnd
|
|
(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.
|
|
|
|
If FILENAME is an image, produce an attach:%s path, otherwise use file:%s (with
|
|
an file icon produced by `+org-attach--icon')."
|
|
(if (looking-back "^[ \t]+" (line-beginning-position))
|
|
(delete-region (match-beginning 0) (match-end 0))
|
|
(newline))
|
|
(cond ((image-type-from-file-name filename)
|
|
(insert
|
|
(concat (if (= org-download-image-html-width 0) ""
|
|
(format "#+attr_html: :width %dpx\n" org-download-image-html-width))
|
|
(if (= org-download-image-latex-width 0) ""
|
|
(format "#+attr_latex: :width %dcm\n" org-download-image-latex-width))
|
|
(cond ((file-in-directory-p filename org-attach-directory)
|
|
(format "[[attach:%s]]" (file-relative-name filename org-attach-directory)))
|
|
((file-in-directory-p filename org-directory)
|
|
(format org-download-link-format (file-relative-name filename org-directory)))
|
|
(t
|
|
(format org-download-link-format filename)))))
|
|
(org-display-inline-images))
|
|
(t
|
|
(insert
|
|
(format "%s [[./%s][%s]] "
|
|
(+org-attach--icon filename)
|
|
(file-relative-name filename (file-name-directory buffer-file-name))
|
|
(file-name-nondirectory (directory-file-name filename)))))))
|
|
|