web.el 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. ;; (require 'request)
  2. ;; (require 's)
  3. (setq w3m-fill-column 80)
  4. ;;;
  5. ;;; EWW
  6. ;;;
  7. (setq shr-width 80)
  8. (setq-default shr-use-fonts nil)
  9. ;; Not white background in dark themes.
  10. ;; Origin <https://emacs.stackexchange.com/a/3523>
  11. (setq shr-color-visible-luminance-min 100)
  12. (advice-add #'shr-colorize-region
  13. :around (defun shr-no-colourise-region (&rest ignore)))
  14. ;;;
  15. ;;; Search engines
  16. ;;;
  17. (with-eval-after-load 'engine-mode
  18. (setq engine/keybinding-prefix "C-c k")
  19. (engine/set-keymap-prefix (kbd engine/keybinding-prefix))
  20. (engine-mode))
  21. (when (macrop #'defengine)
  22. (defengine arch-packages
  23. "https://www.archlinux.org/packages/?sort=&q=%s")
  24. (defengine cpan
  25. "http://search.cpan.org/search?query=%s&mode=all")
  26. (defengine cve
  27. "https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=%s")
  28. (defengine debfiles
  29. "https://packages.debian.org/search?searchon=contents&keywords=%s")
  30. (defengine debcodesearch
  31. "https://codesearch.debian.net/search?q=%s")
  32. (defengine duckduckgo
  33. "https://duckduckgo.com/?q=%s"
  34. :keybinding "d")
  35. (defengine explainshell
  36. "https://www.explainshell.com/explain?cmd=%s")
  37. (defengine debfiles
  38. "https://packages.debian.org/search?searchon=contents&keywords=%s")
  39. (defengine fdroid
  40. "https://f-droid.org/packages/#q=%s")
  41. (defengine fedora-cgit
  42. "https://fedorapeople.org/cgit/?q=%s")
  43. (defengine github
  44. "https://github.com/search?ref=simplesearch&q=%s"
  45. :keybinding "h")
  46. (defengine github-gpl
  47. (concat "https://github.com/search?ref=simplesearch&q=%s"
  48. "+license%%3Agpl"))
  49. (defengine github-hippie
  50. (mapconcat 'identity
  51. '("https://github.com/search?ref=simplesearch&q=%s"
  52. "objectivec" "java" "javascript" "csharp" "kotlin"
  53. "swift" "php" "vue" "autohotkey")
  54. "+-language:"))
  55. (defengine github-hippie-gpl
  56. (concat (mapconcat 'identity
  57. '("https://github.com/search?ref=simplesearch&q=%s"
  58. "objectivec" "java" "javascript" "csharp"
  59. "kotlin" "swift" "php" "vue" "autohotkey")
  60. "+-language:")
  61. "+license%%3Agpl"))
  62. (defengine google
  63. "https://www.google.com/search?ie=utf-8&oe=utf-8&q=%s")
  64. (defengine google-instant
  65. "https://www.google.com/webhp?#q=%s&btnI=I")
  66. (defengine google-door-music
  67. ;; https://github.com/gotbletu/dotfiles/blob/66b2ce9744564a48717c97163a5c34ad1b56d50e/surfraw/.config/surfraw/elvi/opendir_music
  68. (concat "https://www.google.com/search?q=%s"
  69. "%%20%%2B(.ogg|.mp3|.wav|.ac3|.flac|.wma|.m4a)"
  70. "%%20%%2Bintitle:%%22index%%20of%%22%%20"
  71. "-inurl:(jsp|pl|php|html|aspx|htm|cf|shtml)%%20"
  72. "-inurl:(listen77|mp3raid|mp3toss|mp3drug|index_of|wallywashis)"))
  73. (defengine google-video
  74. "https://www.google.com/search?q=%s&tbm=vid")
  75. (defengine guix-hydra
  76. "https://hydra.gnu.org/search?query=%s"
  77. :keybinding "c")
  78. (defengine guix-hydra-job
  79. ;; e.g. gource-0.47
  80. "https://hydra.gnu.org/job/gnu/master/%s")
  81. (defengine nixos-hydra
  82. "https://hydra.nixos.org/search?query=%s")
  83. (defengine nixos-hydra-job
  84. "https://hydra.nixos.org/job/gnu/master/%s.x86_64-linux")
  85. (defmacro wi-defengine-ml-gnu (idxname &optional message-id)
  86. `(defengine ,(if message-id
  87. (intern (concat (symbol-name idxname) "-message-id"))
  88. idxname)
  89. (concat "https://lists.gnu.org/archive/cgi-bin/namazu.cgi?query="
  90. (if ,message-id "" "%s")
  91. "&submit=Search%%21"
  92. (if ,message-id "%%2Bmessage-id%%3A%s" "")
  93. "&idxname=" ,(symbol-name idxname)
  94. "&max=20"
  95. "&result=normal"
  96. "&sort=score")))
  97. (defengine listinfo-gnu "https://lists.gnu.org/mailman/listinfo/%s")
  98. (wi-defengine-ml-gnu info-gnus-english)
  99. (wi-defengine-ml-gnu emacs-devel t)
  100. (wi-defengine-ml-gnu emacs-devel)
  101. (wi-defengine-ml-gnu emacs-orgmode t)
  102. (wi-defengine-ml-gnu emacs-orgmode)
  103. (wi-defengine-ml-gnu guix-devel t)
  104. (wi-defengine-ml-gnu guix-devel)
  105. (wi-defengine-ml-gnu guix-help t)
  106. (wi-defengine-ml-gnu guix-help)
  107. (wi-defengine-ml-gnu help-gnu-emacs t)
  108. (wi-defengine-ml-gnu help-gnu-emacs)
  109. (wi-defengine-ml-gnu info-gnus-english-message-id)
  110. (defengine guix-help+devel
  111. (concat "https://lists.gnu.org/archive/cgi-bin/namazu.cgi"
  112. "?query=%s"
  113. "&submit=Search%%21"
  114. "&idxname=guix-devel"
  115. "&idxname=help-guix"
  116. "&max=20"
  117. "&result=normal"
  118. "&sort=score"))
  119. (defengine guix-all
  120. (concat "https://lists.gnu.org/archive/cgi-bin/namazu.cgi"
  121. "?query=%s"
  122. "&submit=Search%%21"
  123. "&idxname=bug-guix"
  124. "&idxname=guix-patches"
  125. "&idxname=guix-devel"
  126. "&idxname=help-guix"
  127. "&max=20"
  128. "&result=normal"
  129. "&sort=score"))
  130. (defengine guix-all-date
  131. (concat "https://lists.gnu.org/archive/cgi-bin/namazu.cgi"
  132. "?query=%s"
  133. "&submit=Search%%21"
  134. "&idxname=guix-devel"
  135. "&idxname=help-guix"
  136. "&idxname=bug-guix"
  137. "&idxname=guix-patches"
  138. "&max=20"
  139. "&result=normal"
  140. "&sort=date%%3Alate")
  141. :keybinding "g")
  142. (defengine mankier
  143. "https://www.mankier.com/?q=%s")
  144. (defengine melpa
  145. "https://melpa.org/#/?q=%s"
  146. :keybinding "m")
  147. (defengine openhub
  148. "https://www.openhub.net/p?ref=homepage&query=%s")
  149. (defengine reddit-unixporn
  150. "https://www.reddit.com/r/unixporn/search?q=%s&restrict_sr=on")
  151. (defengine rfcs
  152. "http://pretty-rfc.herokuapp.com/search?q=%s")
  153. (defengine searx
  154. "http://searx.tk/?q=%s")
  155. (defengine stack-overflow
  156. "https://stackoverflow.com/search?q=%s")
  157. (defengine startpage
  158. "https://www.startpage.com/do/search?query=%s"
  159. :keybinding "s")
  160. (defengine startpage-hippie
  161. (concat "https://www.startpage.com/do/dsearch?query=%s"
  162. "+c"
  163. "+-c%%2B%%2B"
  164. "+-c%%23&cat=web"
  165. "&pl=opensearch"
  166. "&language=english"))
  167. (defengine tldr
  168. "https://tldr.ostera.io/%s")
  169. (defengine wikipedia
  170. "http://www.wikipedia.org/search-redirect.php?language=en&go=Go&search=%s")
  171. (defengine wiktionary
  172. (concat "https://www.wikipedia.org/search-redirect.php?family=wiktionary"
  173. "&language=en" "&go=Go" "&search=%s"))
  174. (defengine metal-archives
  175. "https://www.metal-archives.com/search?searchString=%s&type=band_name")
  176. (defengine libgen
  177. (concat "http://libgen.io/search.php?req=%s&"
  178. "lg_topic=libgen&"
  179. "open=0&"
  180. "view=simple&"
  181. "res=25&"
  182. "phrase=1&"
  183. "column=def"))
  184. (defengine youtube
  185. "https://www.youtube.com/results?aq=f&oq=&search_query=%s")
  186. (defengine youtube-latest
  187. "https://www.youtube.com/results?sp=CAJQFA%%253D%%253D&search_query=%s")
  188. (defengine youtube-live
  189. "https://www.youtube.com/results?sp=EgJAAQ%%253D%%253D&search_query=%s")
  190. (defengine youtube-rss-channel
  191. "https://www.youtube.com/feeds/videos.xml?channel_id=%s")
  192. (defengine youtube-rss-user
  193. "https://www.youtube.com/feeds/videos.xml?user=%s")
  194. (defengine webarchive
  195. "https://web.archive.org/web/*/%s"))
  196. ;;;
  197. ;;; libraries.io
  198. ;;;
  199. (defvar libraries-io-url-regexp
  200. (rx "http" (zero-or-one "s") "://libraries.io"
  201. "/" (one-or-more (or alphanumeric "-" "."))
  202. "/" (one-or-more (or alphanumeric "-" "."))))
  203. (defun libraries-io-browse-url (url &optional _new-window)
  204. (request
  205. (concat (let* ((urlobj (url-generic-parse-url url))
  206. (host (url-host urlobj)))
  207. (s-replace host (concat host "/api") url))
  208. "?api_key=" (password-store-get "libraries.io/api/token"))
  209. :parser 'json-read
  210. :success (cl-function
  211. (lambda (&key data &allow-other-keys)
  212. (browse-url (alist-get 'repository_url data) _new-window)))
  213. :sync t))
  214. ;;;
  215. ;;; browse-url
  216. ;;;
  217. (defvar browse-url-streamlink-program "streamlink")
  218. (defvar browse-url-streamlink-arguments '("-p" "mpv"))
  219. (defvar browse-url-streamlink-quality "best")
  220. (defun browse-url-streamlink (url &optional new-window)
  221. "Ask the mpv video player to load URL.
  222. Defaults to the URL around or before point. Passes the strings
  223. in the variable `browse-url-streamlink-arguments' to mpv."
  224. (interactive (browse-url-interactive-arg "URL: "))
  225. (let* ((process-environment (browse-url-process-environment)))
  226. (apply 'start-process
  227. (concat "streamlink " url)
  228. nil
  229. browse-url-streamlink-program
  230. `(,@browse-url-streamlink-arguments
  231. ,url
  232. ,browse-url-streamlink-quality))))
  233. (defcustom browse-url-mpv-program "mpv"
  234. "The name by which to invoke MPV."
  235. :type 'string
  236. :group 'browse-url)
  237. (defcustom browse-url-mpv-arguments '()
  238. "Arguments passed to mpv with `browse-url-mpv'."
  239. :type 'list
  240. :group 'browse-url)
  241. (defcustom browse-url-mpv-headphones t
  242. "Non-nil if browse-url-mpv in headphones."
  243. :type 'boolean
  244. :group 'browse-url)
  245. (setq browse-url-mpv-arguments '("--keep-open=no"))
  246. (defun toggle-browse-url-mpv-arguments ()
  247. "If browse-url-mpv-headphones non-nil set it to t and set
  248. `browse-url-mpv-arguments' headphones."
  249. (interactive)
  250. (if browse-url-mpv-headphones
  251. (progn (setq browse-url-mpv-arguments '("--volume=50"))
  252. (setq browse-url-mpv-headphones nil))
  253. (setq browse-url-mpv-arguments
  254. (list "--volume=50" "--no-resume-playback"
  255. "--keep-open=no"
  256. (concat "--audio-device=" ‎wi-headphones)))
  257. (setq browse-url-mpv-headphones t))
  258. (message "MPV for headphones is %s"
  259. (if browse-url-mpv-headphones "enabled" "disabled")))
  260. (defun browse-url-mpv (url &optional new-window)
  261. "Ask the mpv video player to load URL.
  262. Defaults to the URL around or before point. Passes the strings
  263. in the variable `browse-url-mpv-arguments' to mpv."
  264. (interactive (flet ((browse-url-url-at-point ; do not add `http://' prefix
  265. () (or (thing-at-point 'url t)
  266. (let ((f (thing-at-point 'filename t)))
  267. f))))
  268. (browse-url-interactive-arg "URL: ")))
  269. (setq url (browse-url-encode-url url))
  270. (let* ((process-environment (browse-url-process-environment)))
  271. (apply 'start-process
  272. (concat "mpv " url) nil
  273. browse-url-mpv-program
  274. (append
  275. browse-url-mpv-arguments
  276. (list url)))))
  277. (defun browse-url-chromium-no-toolbar (url &optional _new-window)
  278. "Ask the Chromium WWW browser to load URL.
  279. Default to the URL around or before point. The strings in
  280. variable `browse-url-chromium-arguments' are also passed to
  281. Chromium.
  282. The optional argument NEW-WINDOW is not used."
  283. (interactive (browse-url-interactive-arg "URL: "))
  284. (setq url (browse-url-encode-url url))
  285. (let* ((process-environment (browse-url-process-environment)))
  286. (apply 'start-process
  287. (concat "chromium " url) nil
  288. browse-url-chromium-program
  289. (append
  290. browse-url-chromium-arguments
  291. (list (concat "--app=" url))))))
  292. (defvar wi-debian-paste-regexp
  293. (rx-to-string
  294. `(and "http" (* "s") "://paste.debian.net/" (+ alnum) (* "/")) t)
  295. "Regexp matching Debian paste URL.")
  296. (defvar wi-url-gnu-lists-regexp
  297. (rx-to-string
  298. `(and "http" (* "s") "://lists.gnu.org" (* alnum)) t)
  299. "Regexp matching GNU mailing lists URL.")
  300. (defun wi-debian-paste-raw (str)
  301. "Return a raw URL from original STR."
  302. (funcall (-lambda ((protocol s domain nth s))
  303. (mapconcat 'identity
  304. (list protocol s domain "plain" nth s)
  305. "/"))
  306. (split-string str "/")))
  307. (defun wi-browse-url-paste-debian (url &optional new-window)
  308. "Download a snippet from paste.debian.net URL and open it in a buffer.
  309. If NEW-WINDOW is non-nil, then whenever a document would
  310. otherwise be loaded in a new window"
  311. (wi-wget-switch (wi-debian-paste-raw url)))
  312. (defvar wi-lwn-regexp
  313. (rx-to-string
  314. `(and "http" (* "s") "://lwn.net/Articles/"
  315. (+ alnum) (* "/") (* "rss")) t)
  316. "Regexp matching LWN GNU/Linux news site.")
  317. (defvar wi-url-hydra-regexp
  318. (rx-to-string
  319. `(and "http" (* "s") "://hydra.gnu.org" (* "/")) t)
  320. "Regexp matching GNU Hydra CI.")
  321. (defvar wi-url-gnunet-bot-log-regexp
  322. (rx "http" (zero-or-one "s") "://gnunet.org/bot/log/"
  323. (one-or-more alphabetic) (zero-or-one "/") line-end)
  324. "Regexp matching GNU Hydra CI.")
  325. (defvar wi-twitch-url-regexp
  326. (rx "http" (zero-or-more "s") "://" (zero-or-more "www.")
  327. "twitch.tv")
  328. "Regexp matching Twitch.")
  329. (defvar wi-twitch-video-url-regexp
  330. (concat wi-twitch-url-regexp
  331. (rx "/videos/" (one-or-more digit) line-end))
  332. "Regexp matching Twitch videos web-page.")
  333. (defvar youtube-url-regexp
  334. (rx "http" (zero-or-more "s") "://" (zero-or-more "www.")
  335. "youtube.com")
  336. "Regexp matching YouTube.")
  337. (defvar youtube-url-video-regexp
  338. (concat youtube-url-regexp
  339. (rx "/watch?v="
  340. (one-or-more (or alphanumeric "-" "_"))
  341. line-end))
  342. "Regexp matching YouTube videos web-page.")
  343. (defvar youtube-short-url-video-regexp
  344. (rx "http" (zero-or-more "s") "://" (zero-or-more "www.")
  345. "youtu.be/" (one-or-more (or alphanumeric "-" "_")) line-end)
  346. "Regexp matching YouTube short URL.")
  347. (defvar wi-url-github-regexp
  348. (rx "http" (zero-or-one "s") "://github.com")
  349. "Regexp matching GitHub.")
  350. (defvar wi-url-melpa-regexp
  351. (rx "http" (zero-or-one "s") "://melpa.org")
  352. "Regexp matching Melpa.")
  353. (defun youtube-free-url (url)
  354. "Convert youtube.com to hooktube.com URL and put into `kill-ring'.
  355. WARNING: hooktube.com requries non-free JavaScript."
  356. (interactive
  357. (let ((clipboard (x-get-clipboard)))
  358. (list
  359. (if (string-match-p youtube-url-video-regexp
  360. clipboard)
  361. clipboard
  362. (read-string "YouTube video URL: ")))))
  363. (kill-new (concat "https://hooktube.com/watch?v="
  364. (car (last (split-string (car (last (split-string url
  365. "/")))
  366. "="))))))
  367. (cl-defmacro wi-url-savannah-git-commit-regexp (repository &optional (news))
  368. `(rx "http" (zero-or-one "s") "://git.savannah.gnu.org/"
  369. (zero-or-one "c") ,(format "git/%s.git/commit/" repository)
  370. ,(if news "etc/NEWS" "") "?id="
  371. (zero-or-more alphanumeric)
  372. line-end))
  373. (defvar wi-url-emacs-git-commit-regexp
  374. (wi-url-savannah-git-commit-regexp "emacs"))
  375. (defvar wi-url-emacs-git-commit-news-regexp
  376. (wi-url-savannah-git-commit-regexp "emacs" (list :news t)))
  377. (defvar wi-url-guix-git-commit-regexp
  378. (wi-url-savannah-git-commit-regexp "guix"))
  379. (setq browse-url-firefox-program "firefox")
  380. (setq browse-url-handlers
  381. `(("^ftp://.*" . browse-ftp-tramp)
  382. (,(format "^%s\\(%s\\)?\\([[:digit:]]+\\)$"
  383. "https?://\\(debbugs\\|bugs\\)\\.gnu\\.org/"
  384. (regexp-quote "cgi/bugreport.cgi?bug="))
  385. . debbugs-browse-url)
  386. ;; (,youtube-url-video-regexp . browse-url-mpv)
  387. ;; (,youtube-short-url-video-regexp . browse-url-mpv)
  388. (,wi-twitch-video-url-regexp . browse-url-mpv)
  389. (,wi-twitch-url-regexp . browse-url-streamlink)
  390. (,wi-url-hydra-regexp . browse-url-firefox)
  391. (,wi-lwn-regexp . eww-browse-url)
  392. (,wi-url-gnu-lists-regexp . eww-browse-url)
  393. (,wi-url-gnunet-bot-log-regexp . eww-browse-url)
  394. (,wi-debian-paste-regexp . wi-browse-url-paste-debian)
  395. (,wi-url-github-regexp . browse-url-firefox)
  396. (,wi-url-melpa-regexp . browse-url-firefox)
  397. (,wi-url-emacs-git-commit-regexp . browse-url-emacs-git-commit)
  398. (,wi-url-emacs-git-commit-news-regexp . browse-url-emacs-git-commit)
  399. (,wi-url-guix-git-commit-regexp . browse-url-guix-git-commit)
  400. (,libraries-io-url-regexp . libraries-io-browse-url)
  401. ("." . browse-url-firefox)))
  402. (defun wi-info-remote-copy-current-node ()
  403. "Copy URL to current Info node."
  404. (interactive)
  405. (kill-new
  406. (concat "https://www.gnu.org/software/"
  407. (file-name-sans-extension
  408. (file-name-nondirectory Info-current-file))
  409. "/manual/html_node/"
  410. (let ((split-str (split-string Info-current-node " ")))
  411. (if (> (length split-str) 1)
  412. (mapconcat 'identity split-str "-")
  413. Info-current-node))
  414. ".html")))
  415. ;;;
  416. ;;; GitHub
  417. ;;;
  418. (defvar wi-github-url-regexp
  419. (rx "http" (zero-or-one "s") "://github.com"))
  420. (defvar wi-github-user-url-regexp
  421. (concat wi-github-url-regexp
  422. (rx "/" letter (one-or-more alphanumeric))))
  423. (defvar wi-github-user-repo-url-regexp
  424. (concat wi-github-user-url-regexp
  425. (rx "/" (one-or-more (or alphanumeric "-" ".")))))
  426. (defvar wi-github-user-repo-commit-url-regexp
  427. (concat wi-github-user-repo-url-regexp
  428. (rx "/commit" "/" (one-or-more alphanumeric))))
  429. (defun wi-clipboard-github-url-to-commit (url)
  430. "Return in kill ring a commit hash from GitHub user's repository
  431. commit URL.
  432. https://github.com/USER/REPO/commit/SHA1-HASH => SHA1-HASH"
  433. (interactive
  434. (let ((clipboard (x-get-clipboard)))
  435. (list
  436. (if (string-match-p wi-github-user-repo-commit-url-regexp
  437. clipboard)
  438. clipboard
  439. (read-string "Github user's repository commit URL: ")))))
  440. (kill-new (car (last (split-string url "/")))))
  441. ;;;
  442. ;;; Wget
  443. ;;;
  444. (defun wi-wget-switch (url)
  445. "Download a file with wget and open it in buffer"
  446. (interactive "sDownload URL: ")
  447. (let ((buffer (generate-new-buffer "*wget*")))
  448. (with-current-buffer buffer
  449. (insert (shell-command-to-string
  450. (mapconcat 'identity (list "wget" "-q" "-O-" url)
  451. " ")))
  452. (special-mode))
  453. (switch-to-buffer buffer)))
  454. (defun wi-wget-switch-gunzip (url)
  455. "Download a file with wget and open it in buffer"
  456. (interactive "sDownload URL: ")
  457. (let ((buffer (generate-new-buffer "*wget*"))
  458. (command (mapconcat 'identity
  459. (list "wget" "-q" "-O-" url " | " "gunzip")
  460. " ")))
  461. (message command)
  462. (with-current-buffer buffer
  463. (insert (shell-command-to-string command))
  464. (special-mode))
  465. (switch-to-buffer buffer)))
  466. ;;;
  467. ;;; Chromium
  468. ;;;
  469. (with-eval-after-load 'atomic-chrome
  470. (let ((map atomic-chrome-edit-mode-map))
  471. ;; (define-key map (kbd "C-c '") 'anywhere-exit)
  472. ;; (define-key map (kbd "C-c i") 'ispell-buffer)
  473. (define-key map (kbd "C-c v") 'ivy-yasnippet)))