2019-07-27 17:06:55 +02:00
|
|
|
;;; lang/org/autoload/org-link.el -*- lexical-binding: t; -*-
|
2017-11-08 22:51:46 +01:00
|
|
|
|
2020-04-24 20:41:56 -04:00
|
|
|
(defun +org--relative-path (path root)
|
2020-01-26 02:20:39 -05:00
|
|
|
(if (and buffer-file-name (file-in-directory-p buffer-file-name root))
|
|
|
|
(file-relative-name path)
|
|
|
|
path))
|
|
|
|
|
2020-04-24 20:41:56 -04:00
|
|
|
(defun +org--read-link-path (key dir &optional fn)
|
|
|
|
(let ((file (funcall (or fn #'read-file-name) (format "%s: " (capitalize key)) dir)))
|
|
|
|
(format "%s:%s" key (file-relative-name file dir))))
|
2020-01-26 02:20:39 -05:00
|
|
|
|
2021-07-31 12:29:30 -04:00
|
|
|
;;;###autoload
|
|
|
|
(defun +org-read-link-description-at-point (&optional default context)
|
|
|
|
"TODO"
|
|
|
|
(if (and (stringp default) (not (string-empty-p default)))
|
|
|
|
(string-trim default)
|
|
|
|
(if-let* ((context (or context (org-element-context)))
|
|
|
|
(context (org-element-lineage context '(link) t))
|
|
|
|
(beg (org-element-property :contents-begin context))
|
|
|
|
(end (org-element-property :contents-end context)))
|
|
|
|
(unless (= beg end)
|
|
|
|
(replace-regexp-in-string
|
|
|
|
"[ \n]+" " " (string-trim (buffer-substring-no-properties beg end)))))))
|
|
|
|
|
2017-11-08 22:51:46 +01:00
|
|
|
;;;###autoload
|
2020-04-24 20:41:56 -04:00
|
|
|
(defun +org-define-basic-link (key dir-var &rest plist)
|
|
|
|
"Define a link with some basic completion & fontification.
|
|
|
|
|
|
|
|
KEY is the name of the link type. DIR-VAR is the directory variable to resolve
|
|
|
|
links relative to. PLIST is passed to `org-link-set-parameters' verbatim.
|
|
|
|
|
|
|
|
Links defined with this will be rendered in the `error' face if the file doesn't
|
|
|
|
exist, and `org-link' otherwise."
|
|
|
|
(declare (indent 2))
|
2020-04-25 01:23:55 -04:00
|
|
|
(let ((requires (plist-get plist :requires))
|
|
|
|
(dir-fn (if (functionp dir-var)
|
|
|
|
dir-var
|
2020-04-25 01:55:54 -04:00
|
|
|
(lambda () (symbol-value dir-var)))))
|
2020-04-24 20:41:56 -04:00
|
|
|
(apply #'org-link-set-parameters
|
|
|
|
key
|
|
|
|
:complete (lambda ()
|
|
|
|
(if requires (mapc #'require (doom-enlist requires)))
|
2020-04-25 01:23:55 -04:00
|
|
|
(+org--relative-path (+org--read-link-path key (funcall dir-fn))
|
|
|
|
(funcall dir-fn)))
|
|
|
|
:follow (lambda (link)
|
|
|
|
(org-link-open-as-file (expand-file-name link (funcall dir-fn)) nil))
|
2020-04-24 20:41:56 -04:00
|
|
|
:face (lambda (link)
|
2020-05-22 22:11:14 +08:00
|
|
|
(let* ((path (expand-file-name link (funcall dir-fn)))
|
|
|
|
(option-index (string-match-p "::\\(.*\\)\\'" path))
|
|
|
|
(file-name (substring path 0 option-index)))
|
|
|
|
(if (file-exists-p file-name)
|
|
|
|
'org-link
|
|
|
|
'error)))
|
2020-04-24 20:41:56 -04:00
|
|
|
(doom-plist-delete plist :requires))))
|
|
|
|
|
|
|
|
|
|
|
|
;;
|
|
|
|
;;; Image data functions (for custom inline images)
|
2017-11-08 22:51:46 +01:00
|
|
|
|
|
|
|
;;;###autoload
|
2020-04-24 20:41:56 -04:00
|
|
|
(defun +org-image-file-data-fn (protocol link _description)
|
|
|
|
"Intepret LINK as an image file path and return its data."
|
2020-04-25 01:23:55 -04:00
|
|
|
(setq
|
|
|
|
link (expand-file-name
|
2020-05-15 01:44:53 -04:00
|
|
|
link (pcase protocol
|
|
|
|
("download"
|
|
|
|
(or (if (require 'org-download nil t) org-download-image-dir)
|
|
|
|
(if (require 'org-attach) org-attach-id-dir)
|
|
|
|
default-directory))
|
|
|
|
("attachment"
|
|
|
|
(require 'org-attach)
|
|
|
|
org-attach-id-dir)
|
|
|
|
(_ default-directory))))
|
2020-04-24 20:41:56 -04:00
|
|
|
(when (and (file-exists-p link)
|
|
|
|
(image-type-from-file-name link))
|
|
|
|
(with-temp-buffer
|
2020-04-25 01:23:55 -04:00
|
|
|
(set-buffer-multibyte nil)
|
|
|
|
(setq buffer-file-coding-system 'binary)
|
2020-04-24 20:41:56 -04:00
|
|
|
(insert-file-contents-literally link)
|
2020-04-25 01:23:55 -04:00
|
|
|
(buffer-substring-no-properties (point-min) (point-max)))))
|
2019-03-07 00:15:15 -05:00
|
|
|
|
|
|
|
;;;###autoload
|
2020-04-24 20:41:56 -04:00
|
|
|
(defun +org-inline-image-data-fn (_protocol link _description)
|
2019-03-07 00:15:15 -05:00
|
|
|
"Interpret LINK as base64-encoded image data."
|
|
|
|
(base64-decode-string link))
|
|
|
|
|
|
|
|
;;;###autoload
|
2020-04-24 20:41:56 -04:00
|
|
|
(defun +org-http-image-data-fn (protocol link _description)
|
|
|
|
"Interpret LINK as an URL to an image file."
|
2020-06-28 01:02:55 -04:00
|
|
|
(when (and (image-type-from-file-name link)
|
|
|
|
(not (eq org-display-remote-inline-images 'skip)))
|
|
|
|
(if-let (buf (url-retrieve-synchronously (concat protocol ":" link)))
|
2019-03-07 00:15:15 -05:00
|
|
|
(with-current-buffer buf
|
|
|
|
(goto-char (point-min))
|
|
|
|
(re-search-forward "\r?\n\r?\n" nil t)
|
|
|
|
(buffer-substring-no-properties (point) (point-max)))
|
|
|
|
(message "Download of image \"%s\" failed" link)
|
|
|
|
nil)))
|
2019-10-25 20:00:06 -04:00
|
|
|
|
2021-07-31 02:01:37 -04:00
|
|
|
(defvar +org--gif-timers nil)
|
|
|
|
;;;###autoload
|
|
|
|
(defun +org-play-gif-at-point-h ()
|
|
|
|
"Play the gif at point, while the cursor remains there (looping)."
|
|
|
|
(dolist (timer +org--gif-timers (setq +org--gif-timers nil))
|
|
|
|
(when (timerp (cdr timer))
|
|
|
|
(cancel-timer (cdr timer)))
|
|
|
|
(image-animate (car timer) nil 0))
|
|
|
|
(when-let* ((ov (cl-find-if
|
|
|
|
(lambda (it) (overlay-get it 'org-image-overlay))
|
|
|
|
(overlays-at (point))))
|
|
|
|
(dov (overlay-get ov 'display))
|
|
|
|
(pt (point)))
|
|
|
|
(when (image-animated-p dov)
|
|
|
|
(push (cons
|
|
|
|
dov (run-with-idle-timer
|
|
|
|
0.5 nil
|
|
|
|
(lambda (dov)
|
|
|
|
(when (equal
|
|
|
|
ov (cl-find-if
|
|
|
|
(lambda (it) (overlay-get it 'org-image-overlay))
|
|
|
|
(overlays-at (point))))
|
|
|
|
(message "playing gif")
|
|
|
|
(image-animate dov nil t)))
|
|
|
|
dov))
|
|
|
|
+org--gif-timers))))
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +org-play-all-gifs-h ()
|
|
|
|
"Continuously play all gifs in the visible buffer."
|
|
|
|
(dolist (ov (overlays-in (point-min) (point-max)))
|
|
|
|
(when-let* (((overlay-get ov 'org-image-overlay))
|
|
|
|
(dov (overlay-get ov 'display))
|
|
|
|
((image-animated-p dov))
|
|
|
|
(w (selected-window)))
|
|
|
|
(while-no-input
|
|
|
|
(run-with-idle-timer
|
|
|
|
0.3 nil
|
|
|
|
(lambda (dov)
|
|
|
|
(when (pos-visible-in-window-p (overlay-start ov) w nil)
|
|
|
|
(unless (plist-get (cdr dov) :animate-buffer)
|
|
|
|
(image-animate dov))))
|
|
|
|
dov)))))
|
|
|
|
|
2019-10-25 20:00:06 -04:00
|
|
|
|
|
|
|
;;
|
|
|
|
;;; Commands
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +org/remove-link ()
|
|
|
|
"Unlink the text at point."
|
|
|
|
(interactive)
|
|
|
|
(unless (org-in-regexp org-link-bracket-re 1)
|
|
|
|
(user-error "No link at point"))
|
|
|
|
(save-excursion
|
|
|
|
(let ((label (if (match-end 2)
|
|
|
|
(match-string-no-properties 2)
|
|
|
|
(org-link-unescape (match-string-no-properties 1)))))
|
|
|
|
(delete-region (match-beginning 0) (match-end 0))
|
|
|
|
(insert label))))
|
2021-07-31 02:01:37 -04:00
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +org/play-gif-at-point ()
|
|
|
|
"TODO"
|
|
|
|
(interactive)
|
|
|
|
(unless (eq 'org-mode major-mode)
|
|
|
|
(user-error "Not in org-mode"))
|
|
|
|
(or (+org-play-gif-at-point-h)
|
|
|
|
(user-error "No gif at point")))
|
|
|
|
|
2021-07-31 12:29:30 -04:00
|
|
|
|
|
|
|
;;
|
|
|
|
;;; Org-link parameters
|
|
|
|
|
|
|
|
;;; doom-module:
|
|
|
|
(defun +org-link--doom-module--read-link (link)
|
|
|
|
(cl-destructuring-bind (category &optional module flag)
|
|
|
|
(let ((desc (+org-read-link-description-at-point link)))
|
|
|
|
(if (string-prefix-p "+" (string-trim-left desc))
|
|
|
|
(list nil nil (intern desc))
|
|
|
|
(mapcar #'intern (split-string desc " " nil))))
|
|
|
|
(list :category category
|
|
|
|
:module module
|
|
|
|
:flag flag)))
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +org-link--doom-module-follow-fn (link)
|
|
|
|
(cl-destructuring-bind (&key category module flag)
|
|
|
|
(+org-link--doom-module--read-link link)
|
|
|
|
(when category
|
2021-08-01 22:38:47 -04:00
|
|
|
(let ((doom-modules-dirs (list doom-modules-dir)))
|
|
|
|
(if-let* ((path (doom-module-locate-path category module))
|
|
|
|
(path (or (car (doom-glob path "README.org"))
|
|
|
|
path)))
|
|
|
|
(find-file path)
|
|
|
|
(user-error "Can't find Doom module '%s'" link))))
|
2021-07-31 12:29:30 -04:00
|
|
|
(when flag
|
|
|
|
(goto-char (point-min))
|
2021-10-12 21:26:05 +02:00
|
|
|
(and (re-search-forward "^\\*+ \\(?:TODO \\)?Module flags")
|
|
|
|
(re-search-forward (format "^\\s-*- \\+%s ::[ \n]"
|
|
|
|
(substring (symbol-name flag) 1))
|
2021-07-31 12:29:30 -04:00
|
|
|
(save-excursion (org-get-next-sibling)
|
|
|
|
(point)))
|
|
|
|
(recenter)))))
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +org-link--doom-module-face-fn (link)
|
|
|
|
(cl-destructuring-bind (&key category module flag)
|
|
|
|
(+org-link--doom-module--read-link link)
|
|
|
|
(if (doom-module-locate-path category module)
|
|
|
|
`(:inherit org-priority
|
|
|
|
:weight bold)
|
|
|
|
'error)))
|
|
|
|
|
|
|
|
|
|
|
|
;;; doom-package:
|
|
|
|
;;;###autoload
|
|
|
|
(defun +org-link--doom-package-follow-fn (link)
|
|
|
|
"TODO"
|
|
|
|
(doom/describe-package
|
|
|
|
(intern-soft
|
|
|
|
(+org-read-link-description-at-point link))))
|
|
|
|
|
|
|
|
|
|
|
|
;;; kbd:
|
|
|
|
|
|
|
|
(defun +org--describe-kbd (keystr)
|
|
|
|
(dolist (key `(("<leader>" . ,doom-leader-key)
|
|
|
|
("<localleader>" . ,doom-localleader-key)
|
|
|
|
("<prefix>" . ,(if (bound-and-true-p evil-mode)
|
|
|
|
(concat doom-leader-key " u")
|
|
|
|
"C-u"))
|
|
|
|
("<help>" . ,(if (bound-and-true-p evil-mode)
|
|
|
|
(concat doom-leader-key " h")
|
|
|
|
"C-h"))
|
|
|
|
("\\<M-" . "alt-")
|
|
|
|
("\\<S-" . "shift-")
|
|
|
|
("\\<s-" . "super-")
|
|
|
|
("\\<C-" . "ctrl-")))
|
|
|
|
(setq keystr
|
|
|
|
(replace-regexp-in-string (car key) (cdr key)
|
|
|
|
keystr t t)))
|
|
|
|
keystr)
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +org-read-kbd-at-point (&optional default context)
|
|
|
|
"TODO"
|
|
|
|
(+org--describe-kbd
|
|
|
|
(+org-read-link-description-at-point default context)))
|