;;; org-cliplink.el --- insert org-mode links from the clipboard -*- lexical-binding: t -*- ;; Copyright (C) 2014 Alexey Kutepov a.k.a rexim ;; Author: Alexey Kutepov ;; Maintainer: Alexey Kutepov ;; URL: http://github.com/rexim/org-cliplink ;; Version: 0.2 ;; Package-Requires: ((emacs "24.4")) ;; Permission is hereby granted, free of charge, to any person ;; obtaining a copy of this software and associated documentation ;; files (the "Software"), to deal in the Software without ;; restriction, including without limitation the rights to use, copy, ;; modify, merge, publish, distribute, sublicense, and/or sell copies ;; of the Software, and to permit persons to whom the Software is ;; furnished to do so, subject to the following conditions: ;; The above copyright notice and this permission notice shall be ;; included in all copies or substantial portions of the Software. ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS ;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ;; SOFTWARE. ;;; Usage: ;; ;; Bind `org-cliplink` function to something. For example, put ;; this line in your init file: ;; (global-set-key (kbd "C-x p i") 'org-cliplink) ;; ;; Then copy any http/https URL to the clipboard, switch to ;; the Emacs window and hit `C-x p i`. ;;; Commentary: ;; ;; A simple command that takes a URL from the clipboard and inserts an ;; org-mode link with a title of a page found by the URL into the ;; current buffer ;; ;; This code was a part of my Emacs config almost a year. I decided to ;; publish it as a separate package in case someone needs this feature ;; too. ;;; Code: (require 'em-glob) (require 'subr-x) ; for string-trim (require 'org-cliplink-string) (require 'org-cliplink-transport) (defconst org-cliplink-basic-escape-alist '((""" . "\"") ;; " - double-quote ("&" . "&") ;; & - ampersand ("<" . "<") ;; < - less-than (">" . ">"))) ;; > - greater-than (defconst org-cliplink-iso8869-1-escape-alist '((" " . "\u00A0") ;; non-breaking space ("¡" . "\u00A1") ;; inverted exclamation mark ("¢" . "\u00A2") ;; cent sign ("£" . "\u00A3") ;; pound sign ("¤" . "\u00A4") ;; currency sign ("¥" . "\u00A5") ;; yen sign = yuan sign ("¦" . "\u00A6") ;; broken bar = broken vertical bar ("§" . "\u00A7") ;; section sign ("¨" . "\u00A8") ;; diaeresis = spacing diaeresis ("©" . "\u00A9") ;; © - copyright sign ("ª" . "\u00AA") ;; feminine ordinal indicator ("«" . "\u00AB") ;; left-pointing double angle quotation mark = left pointing guillemet ("¬" . "\u00AC") ;; not sign ("­" . "\u00AD") ;; soft hyphen = discretionary hyphen ("®" . "\u00AE") ;; ® - registered trademark sign ("¯" . "\u00AF") ;; macron = spacing macron = overline = APL overbar ("°" . "\u00B0") ;; degree sign ("±" . "\u00B1") ;; plus-minus sign = plus-or-minus sign ("²" . "\u00B2") ;; superscript two = superscript digit two = squared ("³" . "\u00B3") ;; superscript three = superscript digit three = cubed ("´" . "\u00B4") ;; acute accent = spacing acute ("µ" . "\u00B5") ;; micro sign ("¶" . "\u00B6") ;; pilcrow sign = paragraph sign ("·" . "\u00B7") ;; middle dot = Georgian comma = Greek middle dot ("¸" . "\u00B8") ;; cedilla = spacing cedilla ("¹" . "\u00B9") ;; superscript one = superscript digit one ("º" . "\u00BA") ;; masculine ordinal indicator ("»" . "\u00BB") ;; right-pointing double angle quotation mark = right pointing guillemet ("¼" . "\u00BC") ;; vulgar fraction one quarter = fraction one quarter ("½" . "\u00BD") ;; vulgar fraction one half = fraction one half ("¾" . "\u00BE") ;; vulgar fraction three quarters = fraction three quarters ("¿" . "\u00BF") ;; inverted question mark = turned question mark ("À" . "\u00C0") ;; À - uppercase A, grave accent ("Á" . "\u00C1") ;; Á - uppercase A, acute accent ("Â" . "\u00C2") ;;  - uppercase A, circumflex accent ("Ã" . "\u00C3") ;; à - uppercase A, tilde ("Ä" . "\u00C4") ;; Ä - uppercase A, umlaut ("Å" . "\u00C5") ;; Å - uppercase A, ring ("Æ" . "\u00C6") ;; Æ - uppercase AE ("Ç" . "\u00C7") ;; Ç - uppercase C, cedilla ("È" . "\u00C8") ;; È - uppercase E, grave accent ("É" . "\u00C9") ;; É - uppercase E, acute accent ("Ê" . "\u00CA") ;; Ê - uppercase E, circumflex accent ("Ë" . "\u00CB") ;; Ë - uppercase E, umlaut ("Ì" . "\u00CC") ;; Ì - uppercase I, grave accent ("Í" . "\u00CD") ;; Í - uppercase I, acute accent ("Î" . "\u00CE") ;; Î - uppercase I, circumflex accent ("Ï" . "\u00CF") ;; Ï - uppercase I, umlaut ("Ð" . "\u00D0") ;; Ð - uppercase Eth, Icelandic ("Ñ" . "\u00D1") ;; Ñ - uppercase N, tilde ("Ò" . "\u00D2") ;; Ò - uppercase O, grave accent ("Ó" . "\u00D3") ;; Ó - uppercase O, acute accent ("Ô" . "\u00D4") ;; Ô - uppercase O, circumflex accent ("Õ" . "\u00D5") ;; Õ - uppercase O, tilde ("Ö" . "\u00D6") ;; Ö - uppercase O, umlaut ("×" . "\u00D7") ;; multiplication sign ("Ø" . "\u00D8") ;; Ø - uppercase O, slash ("Ù" . "\u00D9") ;; Ù - uppercase U, grave accent ("Ú" . "\u00DA") ;; Ú - uppercase U, acute accent ("Û" . "\u00DB") ;; Û - uppercase U, circumflex accent ("Ü" . "\u00DC") ;; Ü - uppercase U, umlaut ("Ý" . "\u00DD") ;; Ý - uppercase Y, acute accent ("Þ" . "\u00DE") ;; Þ - uppercase THORN, Icelandic ("ß" . "\u00DF") ;; ß - lowercase sharps, German ("à" . "\u00E0") ;; à - lowercase a, grave accent ("á" . "\u00E1") ;; á - lowercase a, acute accent ("â" . "\u00E2") ;; â - lowercase a, circumflex accent ("ã" . "\u00E3") ;; ã - lowercase a, tilde ("ä" . "\u00E4") ;; ä - lowercase a, umlaut ("å" . "\u00E5") ;; å - lowercase a, ring ("æ" . "\u00E6") ;; æ - lowercase ae ("ç" . "\u00E7") ;; ç - lowercase c, cedilla ("è" . "\u00E8") ;; è - lowercase e, grave accent ("é" . "\u00E9") ;; é - lowercase e, acute accent ("ê" . "\u00EA") ;; ê - lowercase e, circumflex accent ("ë" . "\u00EB") ;; ë - lowercase e, umlaut ("ì" . "\u00EC") ;; ì - lowercase i, grave accent ("í" . "\u00ED") ;; í - lowercase i, acute accent ("î" . "\u00EE") ;; î - lowercase i, circumflex accent ("ï" . "\u00EF") ;; ï - lowercase i, umlaut ("ð" . "\u00F0") ;; ð - lowercase eth, Icelandic ("ñ" . "\u00F1") ;; ñ - lowercase n, tilde ("ò" . "\u00F2") ;; ò - lowercase o, grave accent ("ó" . "\u00F3") ;; ó - lowercase o, acute accent ("ô" . "\u00F4") ;; ô - lowercase o, circumflex accent ("õ" . "\u00F5") ;; õ - lowercase o, tilde ("ö" . "\u00F6") ;; ö - lowercase o, umlaut ("÷" . "\u00F7") ;; division sign ("ø" . "\u00F8") ;; ø - lowercase o, slash ("ù" . "\u00F9") ;; ù - lowercase u, grave accent ("ú" . "\u00FA") ;; ú - lowercase u, acute accent ("û" . "\u00FB") ;; û - lowercase u, circumflex accent ("ü" . "\u00FC") ;; ü - lowercase u, umlaut ("ý" . "\u00FD") ;; ý - lowercase y, acute accent ("þ" . "\u00FE") ;; þ - lowercase thorn, Icelandic ("ÿ" . "\u00FF"))) ;; ÿ - lowercase y, umlaut (defconst org-cliplink-html40-extended-escape-alist '( ;; ("ƒ" . "\u0192") ;; latin small f with hook = function= florin, U+0192 ISOtech --> ;; ("Α" . "\u0391") ;; greek capital letter alpha, U+0391 --> ("Β" . "\u0392") ;; greek capital letter beta, U+0392 --> ("Γ" . "\u0393") ;; greek capital letter gamma,U+0393 ISOgrk3 --> ("Δ" . "\u0394") ;; greek capital letter delta,U+0394 ISOgrk3 --> ("Ε" . "\u0395") ;; greek capital letter epsilon, U+0395 --> ("Ζ" . "\u0396") ;; greek capital letter zeta, U+0396 --> ("Η" . "\u0397") ;; greek capital letter eta, U+0397 --> ("Θ" . "\u0398") ;; greek capital letter theta,U+0398 ISOgrk3 --> ("Ι" . "\u0399") ;; greek capital letter iota, U+0399 --> ("Κ" . "\u039A") ;; greek capital letter kappa, U+039A --> ("Λ" . "\u039B") ;; greek capital letter lambda,U+039B ISOgrk3 --> ("Μ" . "\u039C") ;; greek capital letter mu, U+039C --> ("Ν" . "\u039D") ;; greek capital letter nu, U+039D --> ("Ξ" . "\u039E") ;; greek capital letter xi, U+039E ISOgrk3 --> ("Ο" . "\u039F") ;; greek capital letter omicron, U+039F --> ("Π" . "\u03A0") ;; greek capital letter pi, U+03A0 ISOgrk3 --> ("Ρ" . "\u03A1") ;; greek capital letter rho, U+03A1 --> ;; ("Σ" . "\u03A3") ;; greek capital letter sigma,U+03A3 ISOgrk3 --> ("Τ" . "\u03A4") ;; greek capital letter tau, U+03A4 --> ("Υ" . "\u03A5") ;; greek capital letter upsilon,U+03A5 ISOgrk3 --> ("Φ" . "\u03A6") ;; greek capital letter phi,U+03A6 ISOgrk3 --> ("Χ" . "\u03A7") ;; greek capital letter chi, U+03A7 --> ("Ψ" . "\u03A8") ;; greek capital letter psi,U+03A8 ISOgrk3 --> ("Ω" . "\u03A9") ;; greek capital letter omega,U+03A9 ISOgrk3 --> ("α" . "\u03B1") ;; greek small letter alpha,U+03B1 ISOgrk3 --> ("β" . "\u03B2") ;; greek small letter beta, U+03B2 ISOgrk3 --> ("γ" . "\u03B3") ;; greek small letter gamma,U+03B3 ISOgrk3 --> ("δ" . "\u03B4") ;; greek small letter delta,U+03B4 ISOgrk3 --> ("ε" . "\u03B5") ;; greek small letter epsilon,U+03B5 ISOgrk3 --> ("ζ" . "\u03B6") ;; greek small letter zeta, U+03B6 ISOgrk3 --> ("η" . "\u03B7") ;; greek small letter eta, U+03B7 ISOgrk3 --> ("θ" . "\u03B8") ;; greek small letter theta,U+03B8 ISOgrk3 --> ("ι" . "\u03B9") ;; greek small letter iota, U+03B9 ISOgrk3 --> ("κ" . "\u03BA") ;; greek small letter kappa,U+03BA ISOgrk3 --> ("λ" . "\u03BB") ;; greek small letter lambda,U+03BB ISOgrk3 --> ("μ" . "\u03BC") ;; greek small letter mu, U+03BC ISOgrk3 --> ("ν" . "\u03BD") ;; greek small letter nu, U+03BD ISOgrk3 --> ("ξ" . "\u03BE") ;; greek small letter xi, U+03BE ISOgrk3 --> ("ο" . "\u03BF") ;; greek small letter omicron, U+03BF NEW --> ("π" . "\u03C0") ;; greek small letter pi, U+03C0 ISOgrk3 --> ("ρ" . "\u03C1") ;; greek small letter rho, U+03C1 ISOgrk3 --> ("ς" . "\u03C2") ;; greek small letter final sigma,U+03C2 ISOgrk3 --> ("σ" . "\u03C3") ;; greek small letter sigma,U+03C3 ISOgrk3 --> ("τ" . "\u03C4") ;; greek small letter tau, U+03C4 ISOgrk3 --> ("υ" . "\u03C5") ;; greek small letter upsilon,U+03C5 ISOgrk3 --> ("φ" . "\u03C6") ;; greek small letter phi, U+03C6 ISOgrk3 --> ("χ" . "\u03C7") ;; greek small letter chi, U+03C7 ISOgrk3 --> ("ψ" . "\u03C8") ;; greek small letter psi, U+03C8 ISOgrk3 --> ("ω" . "\u03C9") ;; greek small letter omega,U+03C9 ISOgrk3 --> ("ϑ" . "\u03D1") ;; greek small letter theta symbol,U+03D1 NEW --> ("ϒ" . "\u03D2") ;; greek upsilon with hook symbol,U+03D2 NEW --> ("ϖ" . "\u03D6") ;; greek pi symbol, U+03D6 ISOgrk3 --> ;; ("•" . "\u2022") ;; bullet = black small circle,U+2022 ISOpub --> ;; ("…" . "\u2026") ;; horizontal ellipsis = three dot leader,U+2026 ISOpub --> ("′" . "\u2032") ;; prime = minutes = feet, U+2032 ISOtech --> ("″" . "\u2033") ;; double prime = seconds = inches,U+2033 ISOtech --> ("‾" . "\u203E") ;; overline = spacing overscore,U+203E NEW --> ("⁄" . "\u2044") ;; fraction slash, U+2044 NEW --> ;; ("℘" . "\u2118") ;; script capital P = power set= Weierstrass p, U+2118 ISOamso --> ("ℑ" . "\u2111") ;; blackletter capital I = imaginary part,U+2111 ISOamso --> ("ℜ" . "\u211C") ;; blackletter capital R = real part symbol,U+211C ISOamso --> ("™" . "\u2122") ;; trade mark sign, U+2122 ISOnum --> ("ℵ" . "\u2135") ;; alef symbol = first transfinite cardinal,U+2135 NEW --> ;; ;; ("←" . "\u2190") ;; leftwards arrow, U+2190 ISOnum --> ("↑" . "\u2191") ;; upwards arrow, U+2191 ISOnum--> ("→" . "\u2192") ;; rightwards arrow, U+2192 ISOnum --> ("↓" . "\u2193") ;; downwards arrow, U+2193 ISOnum --> ("↔" . "\u2194") ;; left right arrow, U+2194 ISOamsa --> ("↵" . "\u21B5") ;; downwards arrow with corner leftwards= carriage return, U+21B5 NEW --> ("⇐" . "\u21D0") ;; leftwards double arrow, U+21D0 ISOtech --> ;; ("⇑" . "\u21D1") ;; upwards double arrow, U+21D1 ISOamsa --> ("⇒" . "\u21D2") ;; rightwards double arrow,U+21D2 ISOtech --> ;; ("⇓" . "\u21D3") ;; downwards double arrow, U+21D3 ISOamsa --> ("⇔" . "\u21D4") ;; left right double arrow,U+21D4 ISOamsa --> ;; ("∀" . "\u2200") ;; for all, U+2200 ISOtech --> ("∂" . "\u2202") ;; partial differential, U+2202 ISOtech --> ("∃" . "\u2203") ;; there exists, U+2203 ISOtech --> ("∅" . "\u2205") ;; empty set = null set = diameter,U+2205 ISOamso --> ("∇" . "\u2207") ;; nabla = backward difference,U+2207 ISOtech --> ("∈" . "\u2208") ;; element of, U+2208 ISOtech --> ("∉" . "\u2209") ;; not an element of, U+2209 ISOtech --> ("∋" . "\u220B") ;; contains as member, U+220B ISOtech --> ;; ("∏" . "\u220F") ;; n-ary product = product sign,U+220F ISOamsb --> ;; ("∑" . "\u2211") ;; n-ary summation, U+2211 ISOamsb --> ;; ("−" . "\u2212") ;; minus sign, U+2212 ISOtech --> ("∗" . "\u2217") ;; asterisk operator, U+2217 ISOtech --> ("√" . "\u221A") ;; square root = radical sign,U+221A ISOtech --> ("∝" . "\u221D") ;; proportional to, U+221D ISOtech --> ("∞" . "\u221E") ;; infinity, U+221E ISOtech --> ("∠" . "\u2220") ;; angle, U+2220 ISOamso --> ("∧" . "\u2227") ;; logical and = wedge, U+2227 ISOtech --> ("∨" . "\u2228") ;; logical or = vee, U+2228 ISOtech --> ("∩" . "\u2229") ;; intersection = cap, U+2229 ISOtech --> ("∪" . "\u222A") ;; union = cup, U+222A ISOtech --> ("∫" . "\u222B") ;; integral, U+222B ISOtech --> ("∴" . "\u2234") ;; therefore, U+2234 ISOtech --> ("∼" . "\u223C") ;; tilde operator = varies with = similar to,U+223C ISOtech --> ;; ("≅" . "\u2245") ;; approximately equal to, U+2245 ISOtech --> ("≈" . "\u2248") ;; almost equal to = asymptotic to,U+2248 ISOamsr --> ("≠" . "\u2260") ;; not equal to, U+2260 ISOtech --> ("≡" . "\u2261") ;; identical to, U+2261 ISOtech --> ("≤" . "\u2264") ;; less-than or equal to, U+2264 ISOtech --> ("≥" . "\u2265") ;; greater-than or equal to,U+2265 ISOtech --> ("⊂" . "\u2282") ;; subset of, U+2282 ISOtech --> ("⊃" . "\u2283") ;; superset of, U+2283 ISOtech --> ;; ("⊆" . "\u2286") ;; subset of or equal to, U+2286 ISOtech --> ("⊇" . "\u2287") ;; superset of or equal to,U+2287 ISOtech --> ("⊕" . "\u2295") ;; circled plus = direct sum,U+2295 ISOamsb --> ("⊗" . "\u2297") ;; circled times = vector product,U+2297 ISOamsb --> ("⊥" . "\u22A5") ;; up tack = orthogonal to = perpendicular,U+22A5 ISOtech --> ("⋅" . "\u22C5") ;; dot operator, U+22C5 ISOamsb --> ;; ;; ("⌈" . "\u2308") ;; left ceiling = apl upstile,U+2308 ISOamsc --> ("⌉" . "\u2309") ;; right ceiling, U+2309 ISOamsc --> ("⌊" . "\u230A") ;; left floor = apl downstile,U+230A ISOamsc --> ("⌋" . "\u230B") ;; right floor, U+230B ISOamsc --> ("⟨" . "\u2329") ;; left-pointing angle bracket = bra,U+2329 ISOtech --> ;; ("⟩" . "\u232A") ;; right-pointing angle bracket = ket,U+232A ISOtech --> ;; ;; ("◊" . "\u25CA") ;; lozenge, U+25CA ISOpub --> ;; ("♠" . "\u2660") ;; black spade suit, U+2660 ISOpub --> ;; ("♣" . "\u2663") ;; black club suit = shamrock,U+2663 ISOpub --> ("♥" . "\u2665") ;; black heart suit = valentine,U+2665 ISOpub --> ("♦" . "\u2666") ;; black diamond suit, U+2666 ISOpub --> ;; ("Œ" . "\u0152") ;; -- latin capital ligature OE,U+0152 ISOlat2 --> ("œ" . "\u0153") ;; -- latin small ligature oe, U+0153 ISOlat2 --> ;; ("Š" . "\u0160") ;; -- latin capital letter S with caron,U+0160 ISOlat2 --> ("š" . "\u0161") ;; -- latin small letter s with caron,U+0161 ISOlat2 --> ("Ÿ" . "\u0178") ;; -- latin capital letter Y with diaeresis,U+0178 ISOlat2 --> ;; ("ˆ" . "\u02C6") ;; -- modifier letter circumflex accent,U+02C6 ISOpub --> ("˜" . "\u02DC") ;; small tilde, U+02DC ISOdia --> ;; (" " . "\u2002") ;; en space, U+2002 ISOpub --> (" " . "\u2003") ;; em space, U+2003 ISOpub --> (" " . "\u2009") ;; thin space, U+2009 ISOpub --> ("‌" . "\u200C") ;; zero width non-joiner,U+200C NEW RFC 2070 --> ("‍" . "\u200D") ;; zero width joiner, U+200D NEW RFC 2070 --> ("‎" . "\u200E") ;; left-to-right mark, U+200E NEW RFC 2070 --> ("‏" . "\u200F") ;; right-to-left mark, U+200F NEW RFC 2070 --> ("–" . "\u2013") ;; en dash, U+2013 ISOpub --> ("—" . "\u2014") ;; em dash, U+2014 ISOpub --> ("‘" . "\u2018") ;; left single quotation mark,U+2018 ISOnum --> ("’" . "\u2019") ;; right single quotation mark,U+2019 ISOnum --> ("‚" . "\u201A") ;; single low-9 quotation mark, U+201A NEW --> ("“" . "\u201C") ;; left double quotation mark,U+201C ISOnum --> ("”" . "\u201D") ;; right double quotation mark,U+201D ISOnum --> ("„" . "\u201E") ;; double low-9 quotation mark, U+201E NEW --> ("†" . "\u2020") ;; dagger, U+2020 ISOpub --> ("‡" . "\u2021") ;; double dagger, U+2021 ISOpub --> ("‰" . "\u2030") ;; per mille sign, U+2030 ISOtech --> ("‹" . "\u2039") ;; single left-pointing angle quotation mark,U+2039 ISO proposed --> ;; ("›" . "\u203A") ;; single right-pointing angle quotation mark,U+203A ISO proposed --> ;; ("€" . "\u20AC"))) ;; -- euro sign, U+20AC NEW --> (defun org-cliplink-escape-numeric-match (s) (char-to-string (string-to-number (match-string 1 s)))) (defvar org-cliplink-escape-alist (append org-cliplink-basic-escape-alist org-cliplink-iso8869-1-escape-alist org-cliplink-html40-extended-escape-alist '(("\\[" . "{") ("\\]" . "}") ("&#\\([0-9]+\\);" . org-cliplink-escape-numeric-match)))) (defgroup org-cliplink nil "A simple command that takes a URL from the clipboard and inserts an org-mode link with a title of a page found by the URL into the current buffer." :prefix "org-cliplink-" :group 'wp :link '(url-link "https://github.com/rexim/org-cliplink")) (defcustom org-cliplink-max-length 80 "Max length of the title. Org-cliplink cuts any title that exceeds the limit. Minimum possible value is 4." :group 'org-cliplink :type '(choice integer (const :tag "off" nil))) (defcustom org-cliplink-secrets-path "~/.org-cliplink-secrets.el" "Path to file that keeps your org-cliplink related secrets. It can be any sensitive information like password to different services." :group 'org-cliplink :type 'string) (defcustom org-cliplink-title-replacements '(("https://github.com/.+/?" ("\\(.*\\) · \\(?:Issue\\|Pull Request\\) #\\([0-9]+\\) · \\(.*\\) · GitHub" "\\3#\\2 \\1")) ("https://twitter.com/.+/status/[[:digit:]]+/?" (".+ on Twitter: \\(.+\\)" "\\1"))) "A list of rules for formatting titles. Each entry has the form (URL-REGEXP . (TITLE-REGEXP . REPLACEMENT))." :group 'org-cliplink :type '(repeat (list string (list string string)))) (defcustom org-cliplink-transport-implementation 'url-el "The transport implementation. Supported transports are `url-el' and `curl'. `curl' is experimental so use it on your own risk." :group 'org-cliplink :type 'symbol) (defcustom org-cliplink-curl-transport-arguments '() "Additional arguments for cURL. Used when the current transport implementation is set to `curl'." :group 'org-cliplink :type '(repeat string)) (defcustom org-cliplink-simpleclip-source nil "Clipboard source. Non-nil means use system clipboard as source. The clipboard content will be provided by `simpleclip', requiring simpleclip.el to be installed. When nil, use the first element of kill-ring as source" :group 'org-cliplink :type 'boolean) (defun org-cliplink-clipboard-content () (let ((content (if (and org-cliplink-simpleclip-source (fboundp 'simpleclip-get-contents)) (simpleclip-get-contents) (current-kill 0)))) (string-trim (substring-no-properties content)))) (defun org-cliplink-parse-raw-header (raw-header) (let ((start 0) (result-header nil)) (while (string-match "^\\(.+?\\): \\(.+?\\)\r?$" raw-header start) (let ((header-name (match-string 1 raw-header)) (header-value (match-string 2 raw-header))) (setq result-header (cons (cons header-name header-value) result-header)) (setq start (match-end 2)))) result-header)) (defun org-cliplink-parse-response () (goto-char (point-min)) (search-forward-regexp "^\r?$") (let ((content (buffer-substring (+ (point) 1) (point-max))) (raw-header (buffer-substring (point-min) (point)))) (cons (org-cliplink-parse-raw-header raw-header) content))) (defun org-cliplink-extract-title-from-html (html) (let* ((case-fold-search t) (start0 (string-match "" html start0))) (end (string-match "" html)) (chars-to-skip (length ">"))) (if (and start end (< start end)) (substring html (+ start chars-to-skip) end) nil))) (defun org-cliplink-escape-html4 (s) (when s (let ((case-replace nil) (case-fold-search nil) (result s)) (dolist (x org-cliplink-escape-alist result) (setq result (replace-regexp-in-string (car x) (cdr x) result)))))) (defun org-cliplink-title-for-url (url title) "Replace title using configured rules. Find the first entry (URL-REGEXP (TITLE-REGEXP REPLACEMENT)) in `org-cliplink-title-replacements' where URL-REGEXP matches URL, and return TITLE with any matches for TITLE-REGEXP replaced by REPLACEMENT. If no URL-REGEXP matches URL, or if the first matching entry's TITLE-REGEXP does not match TITLE, return the original TITLE." (save-match-data (cl-loop for (url-re (title-re rep)) in org-cliplink-title-replacements when (string-match url-re url) return (replace-regexp-in-string title-re rep title) finally return title))) (defun org-cliplink-org-mode-link-transformer (url title) (if title (format "[[%s][%s]]" url (org-cliplink-elide-string (org-cliplink-escape-html4 (org-cliplink-title-for-url url title)) org-cliplink-max-length)) (format "[[%s]]" url))) (defun org-cliplink-insert-org-mode-link-callback (url title) (insert (org-cliplink-org-mode-link-transformer url title))) (defun org-cliplink-uncompress-gziped-text (text) (let ((filename (make-temp-file "org-cliplink" nil ".gz"))) (write-region text nil filename) (with-auto-compression-mode (with-temp-buffer (insert-file-contents filename) (delete-file filename) (buffer-string))))) (defun org-cliplink-extract-and-prepare-title-from-current-buffer () (let* ((response (org-cliplink-parse-response)) (header (car response)) (content (if (and (string= "gzip" (cdr (assoc "Content-Encoding" header))) (not (string= "gzip" url-mime-encoding-string))) (org-cliplink-uncompress-gziped-text (cdr response)) (cdr response))) (decoded-content (decode-coding-string content (quote utf-8)))) (org-cliplink-straight-string (org-cliplink-extract-title-from-html decoded-content)))) (defun org-cliplink-read-secrets () (when (file-exists-p org-cliplink-secrets-path) (with-temp-buffer (insert-file-contents org-cliplink-secrets-path) (car (read-from-string (buffer-string)))))) (defun org-cliplink-check-basic-auth-for-url (url) (let ((basic-auth-secrets (plist-get (org-cliplink-read-secrets) :basic-auth)) (result nil)) (while (and (not result) basic-auth-secrets) (let ((secret (car basic-auth-secrets))) (when (string-match (eshell-glob-regexp (plist-get secret :url-pattern)) url) (setq result secret))) (pop basic-auth-secrets)) result)) ;;;###autoload (defun org-cliplink-retrieve-title (url title-callback) (let* ((dest-buffer (current-buffer)) (basic-auth (org-cliplink-check-basic-auth-for-url url)) (url-retrieve-callback (lambda (status) (ignore status) (let ((title (org-cliplink-extract-and-prepare-title-from-current-buffer))) (with-current-buffer dest-buffer (funcall title-callback url title)))))) (if (equal 'curl org-cliplink-transport-implementation) (org-cliplink-http-get-request--curl url url-retrieve-callback basic-auth org-cliplink-curl-transport-arguments) (org-cliplink-http-get-request--url-el url url-retrieve-callback basic-auth)))) ;;;###autoload (defun org-cliplink-insert-transformed-title (url transformer) "Takes the URL, asynchronously retrieves the title and applies a custom TRANSFORMER which transforms the url and title and insert the required text to the current buffer." (org-cliplink-retrieve-title url (lambda (url title) (insert (funcall transformer url title))))) ;;;###autoload (defun org-cliplink-retrieve-title-synchronously (url) (when (member (url-type (url-generic-parse-url url)) '("http" "https")) (let ((response-buffer (url-retrieve-synchronously url t))) (when response-buffer (with-current-buffer response-buffer (org-cliplink-elide-string (org-cliplink-escape-html4 (org-cliplink-extract-and-prepare-title-from-current-buffer)) org-cliplink-max-length)))))) ;;;###autoload (defun org-cliplink () "Takes a URL from the clipboard and inserts an org-mode link with the title of a page found by the URL into the current buffer" (interactive) (org-cliplink-insert-transformed-title (org-cliplink-clipboard-content) 'org-cliplink-org-mode-link-transformer)) ;;;###autoload (defun org-cliplink-capture () "org-cliplink version for org-capture templates. Makes synchronous request. Returns the link instead of inserting it to the current buffer. Doesn't support Basic Auth. Doesn't support cURL transport." (interactive) (let ((url (org-cliplink-clipboard-content))) (org-cliplink-org-mode-link-transformer url (org-cliplink-retrieve-title-synchronously url)))) (provide 'org-cliplink) ;;; org-cliplink.el ends here