PageRenderTime 79ms CodeModel.GetById 45ms RepoModel.GetById 1ms app.codeStats 0ms

/navigation/helm/helm-net.el

https://github.com/budevg/emacs-conf
Emacs Lisp | 584 lines | 457 code | 72 blank | 55 comment | 20 complexity | c9908643ba156e31bc3d51ed4d5a7d66 MD5 | raw file
  1. ;;; helm-net.el --- helm browse url and search web. -*- lexical-binding: t -*-
  2. ;; Copyright (C) 2012 ~ 2018 Thierry Volpiatto <thierry.volpiatto@gmail.com>
  3. ;; This program is free software; you can redistribute it and/or modify
  4. ;; it under the terms of the GNU General Public License as published by
  5. ;; the Free Software Foundation, either version 3 of the License, or
  6. ;; (at your option) any later version.
  7. ;; This program is distributed in the hope that it will be useful,
  8. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. ;; GNU General Public License for more details.
  11. ;; You should have received a copy of the GNU General Public License
  12. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ;;; Code:
  14. (require 'cl-lib)
  15. (require 'helm)
  16. (require 'helm-help)
  17. (require 'url)
  18. (require 'xml)
  19. (require 'browse-url)
  20. (defgroup helm-net nil
  21. "Net related applications and libraries for Helm."
  22. :group 'helm)
  23. (defcustom helm-google-suggest-default-browser-function nil
  24. "The browse url function you prefer to use with google suggest.
  25. When nil, use the first browser function available
  26. See `helm-browse-url-default-browser-alist'."
  27. :group 'helm-net
  28. :type 'symbol)
  29. (defcustom helm-home-url "https://www.google.com"
  30. "Default url to use as home url."
  31. :group 'helm-net
  32. :type 'string)
  33. (defcustom helm-surfraw-default-browser-function nil
  34. "The browse url function you prefer to use with surfraw.
  35. When nil, fallback to `browse-url-browser-function'."
  36. :group 'helm-net
  37. :type 'symbol)
  38. (defcustom helm-google-suggest-url
  39. "https://encrypted.google.com/complete/search?output=toolbar&q=%s"
  40. "URL used for looking up Google suggestions.
  41. This is a format string, don't forget the `%s'."
  42. :type 'string
  43. :group 'helm-net)
  44. (defcustom helm-google-suggest-search-url
  45. "https://encrypted.google.com/search?ie=utf-8&oe=utf-8&q=%s"
  46. "URL used for Google searching.
  47. This is a format string, don't forget the `%s'."
  48. :type 'string
  49. :group 'helm-net)
  50. (defvaralias 'helm-google-suggest-use-curl-p 'helm-net-prefer-curl)
  51. (make-obsolete-variable 'helm-google-suggest-use-curl-p 'helm-net-prefer-curl "1.7.7")
  52. (defcustom helm-net-prefer-curl nil
  53. "When non--nil use CURL external program to fetch data.
  54. Otherwise `url-retrieve-synchronously' is used."
  55. :type 'boolean
  56. :group 'helm-net)
  57. (defcustom helm-surfraw-duckduckgo-url
  58. "https://duckduckgo.com/lite/?q=%s&kp=1"
  59. "The duckduckgo url.
  60. This is a format string, don't forget the `%s'.
  61. If you have personal settings saved on duckduckgo you should have
  62. a personal url, see your settings on duckduckgo."
  63. :type 'string
  64. :group 'helm-net)
  65. (defcustom helm-wikipedia-suggest-url
  66. "https://en.wikipedia.org/w/api.php?action=opensearch&search=%s"
  67. "Url used for looking up Wikipedia suggestions.
  68. This is a format string, don't forget the `%s'."
  69. :type 'string
  70. :group 'helm-net)
  71. (defcustom helm-search-suggest-action-wikipedia-url
  72. "https://en.wikipedia.org/wiki/Special:Search?search=%s"
  73. "The Wikipedia search url.
  74. This is a format string, don't forget the `%s'."
  75. :type 'string
  76. :group 'helm-net)
  77. (defcustom helm-wikipedia-summary-url
  78. "https://en.wikipedia.org/w/api.php?action=parse&format=json&prop=text&section=0&page=%s"
  79. "URL for getting the summary of a Wikipedia topic.
  80. This is a format string, don't forget the `%s'."
  81. :type 'string
  82. :group 'helm-net)
  83. (defcustom helm-search-suggest-action-youtube-url
  84. "https://www.youtube.com/results?aq=f&search_query=%s"
  85. "The Youtube search url.
  86. This is a format string, don't forget the `%s'."
  87. :type 'string
  88. :group 'helm-net)
  89. (defcustom helm-search-suggest-action-imdb-url
  90. "http://www.imdb.com/find?s=all&q=%s"
  91. "The IMDb search url.
  92. This is a format string, don't forget the `%s'."
  93. :type 'string
  94. :group 'helm-net)
  95. (defcustom helm-search-suggest-action-google-maps-url
  96. "https://maps.google.com/maps?f=q&source=s_q&q=%s"
  97. "The Google Maps search url.
  98. This is a format string, don't forget the `%s'."
  99. :type 'string
  100. :group 'helm-net)
  101. (defcustom helm-search-suggest-action-google-news-url
  102. "https://www.google.com/search?safe=off&prmd=nvlifd&source=lnms&tbs=nws:1&q=%s"
  103. "The Google News search url.
  104. This is a format string, don't forget the `%s'."
  105. :type 'string
  106. :group 'helm-net)
  107. (defcustom helm-google-suggest-actions
  108. '(("Google Search" . helm-google-suggest-action)
  109. ("Wikipedia" . (lambda (candidate)
  110. (helm-search-suggest-perform-additional-action
  111. helm-search-suggest-action-wikipedia-url
  112. candidate)))
  113. ("Youtube" . (lambda (candidate)
  114. (helm-search-suggest-perform-additional-action
  115. helm-search-suggest-action-youtube-url
  116. candidate)))
  117. ("IMDb" . (lambda (candidate)
  118. (helm-search-suggest-perform-additional-action
  119. helm-search-suggest-action-imdb-url
  120. candidate)))
  121. ("Google Maps" . (lambda (candidate)
  122. (helm-search-suggest-perform-additional-action
  123. helm-search-suggest-action-google-maps-url
  124. candidate)))
  125. ("Google News" . (lambda (candidate)
  126. (helm-search-suggest-perform-additional-action
  127. helm-search-suggest-action-google-news-url
  128. candidate))))
  129. "List of actions for google suggest sources."
  130. :group 'helm-net
  131. :type '(alist :key-type string :value-type function))
  132. (defcustom helm-browse-url-firefox-new-window "-new-tab"
  133. "Allow choosing to browse url in new window or new tab.
  134. Can be \"-new-tab\" (default) or \"-new-window\"."
  135. :group 'helm-net
  136. :type '(radio
  137. (const :tag "New tab" "-new-tab")
  138. (const :tag "New window" "-new-window")))
  139. ;;; Additional actions for search suggestions
  140. ;;
  141. ;;
  142. ;; Internal
  143. (defun helm-search-suggest-perform-additional-action (url query)
  144. "Perform the search via URL using QUERY as input."
  145. (browse-url (format url (url-hexify-string query))))
  146. (defun helm-net--url-retrieve-sync (request parser)
  147. (if helm-net-prefer-curl
  148. (with-temp-buffer
  149. (call-process "curl" nil t nil request)
  150. (funcall parser))
  151. (with-current-buffer (url-retrieve-synchronously request)
  152. (funcall parser))))
  153. ;;; Google Suggestions
  154. ;;
  155. ;;
  156. (defun helm-google-suggest-parser ()
  157. (cl-loop
  158. with result-alist = (xml-get-children
  159. (car (xml-parse-region
  160. (point-min) (point-max)))
  161. 'CompleteSuggestion)
  162. for i in result-alist collect
  163. (cdr (cl-caadr (assq 'suggestion i)))))
  164. (defun helm-google-suggest-fetch (input)
  165. "Fetch suggestions for INPUT from XML buffer."
  166. (let ((request (format helm-google-suggest-url
  167. (url-hexify-string input))))
  168. (helm-net--url-retrieve-sync
  169. request #'helm-google-suggest-parser)))
  170. (defun helm-google-suggest-set-candidates (&optional request-prefix)
  171. "Set candidates with result and number of google results found."
  172. (let ((suggestions (helm-google-suggest-fetch
  173. (or (and request-prefix
  174. (concat request-prefix
  175. " " helm-pattern))
  176. helm-pattern))))
  177. (if (member helm-pattern suggestions)
  178. suggestions
  179. ;; if there is no suggestion exactly matching the input then
  180. ;; prepend a Search on Google item to the list
  181. (append
  182. suggestions
  183. (list (cons (format "Search for '%s' on Google" helm-input)
  184. helm-input))))))
  185. (defun helm-ggs-set-number-result (num)
  186. (if num
  187. (progn
  188. (and (numberp num) (setq num (number-to-string num)))
  189. (cl-loop for i in (reverse (split-string num "" t))
  190. for count from 1
  191. append (list i) into C
  192. when (= count 3)
  193. append (list ",") into C
  194. and do (setq count 0)
  195. finally return
  196. (replace-regexp-in-string
  197. "^," "" (mapconcat 'identity (reverse C) ""))))
  198. "?"))
  199. (defun helm-google-suggest-action (candidate)
  200. "Default action to jump to a google suggested candidate."
  201. (let ((arg (format helm-google-suggest-search-url
  202. (url-hexify-string candidate))))
  203. (helm-aif helm-google-suggest-default-browser-function
  204. (funcall it arg)
  205. (helm-browse-url arg))))
  206. (defvar helm-google-suggest-default-function
  207. 'helm-google-suggest-set-candidates
  208. "Default function to use in helm google suggest.")
  209. (defvar helm-source-google-suggest
  210. (helm-build-sync-source "Google Suggest"
  211. :candidates (lambda ()
  212. (funcall helm-google-suggest-default-function))
  213. :action 'helm-google-suggest-actions
  214. :volatile t
  215. :keymap helm-map
  216. :requires-pattern 3))
  217. (defun helm-google-suggest-emacs-lisp ()
  218. "Try to emacs lisp complete with google suggestions."
  219. (helm-google-suggest-set-candidates "emacs lisp"))
  220. ;;; Wikipedia suggestions
  221. ;;
  222. ;;
  223. (declare-function json-read-from-string "json" (string))
  224. (defun helm-wikipedia-suggest-fetch ()
  225. "Fetch Wikipedia suggestions and return them as a list."
  226. (require 'json)
  227. (let ((request (format helm-wikipedia-suggest-url
  228. (url-hexify-string helm-pattern))))
  229. (helm-net--url-retrieve-sync
  230. request #'helm-wikipedia--parse-buffer)))
  231. (defun helm-wikipedia--parse-buffer ()
  232. (goto-char (point-min))
  233. (when (re-search-forward "^\\[.+\\[\\(.*\\)\\]\\]" nil t)
  234. (cl-loop for i across (aref (json-read-from-string (match-string 0)) 1)
  235. collect i into result
  236. finally return (or result
  237. (append
  238. result
  239. (list (cons (format "Search for '%s' on wikipedia"
  240. helm-pattern)
  241. helm-pattern)))))))
  242. (defvar helm-wikipedia--summary-cache (make-hash-table :test 'equal))
  243. (defun helm-wikipedia-show-summary (input)
  244. "Show Wikipedia summary for INPUT in new buffer."
  245. (interactive)
  246. (let ((buffer (get-buffer-create "*helm wikipedia summary*"))
  247. (summary (helm-wikipedia--get-summary input)))
  248. (with-current-buffer buffer
  249. (visual-line-mode)
  250. (erase-buffer)
  251. (insert summary)
  252. (pop-to-buffer (current-buffer))
  253. (goto-char (point-min)))))
  254. (defun helm-wikipedia-persistent-action (candidate)
  255. (unless (string= (format "Search for '%s' on wikipedia"
  256. helm-pattern)
  257. (helm-get-selection nil t))
  258. (message "Fetching summary from Wikipedia...")
  259. (let ((buf (get-buffer-create "*helm wikipedia summary*"))
  260. (result (helm-wikipedia--get-summary candidate)))
  261. (with-current-buffer buf
  262. (erase-buffer)
  263. (setq cursor-type nil)
  264. (insert result)
  265. (fill-region (point-min) (point-max))
  266. (goto-char (point-min)))
  267. (display-buffer buf))))
  268. (defun helm-wikipedia--get-summary (input)
  269. "Return Wikipedia summary for INPUT as string.
  270. Follows any redirections from Wikipedia, and stores results in
  271. `helm-wikipedia--summary-cache'."
  272. (let (result)
  273. (while (progn
  274. (setq result (or (gethash input helm-wikipedia--summary-cache)
  275. (puthash input
  276. (helm-wikipedia--fetch-summary input)
  277. helm-wikipedia--summary-cache)))
  278. (when (and result
  279. (listp result))
  280. (setq input (cdr result))
  281. (message "Redirected to %s" input)
  282. t)))
  283. (unless result
  284. (error "Error when getting summary."))
  285. result))
  286. (defun helm-wikipedia--fetch-summary (input)
  287. (let* ((request (format helm-wikipedia-summary-url
  288. (url-hexify-string input))))
  289. (helm-net--url-retrieve-sync
  290. request #'helm-wikipedia--parse-summary)))
  291. (defun helm-wikipedia--parse-summary ()
  292. (goto-char (point-min))
  293. (when (search-forward "{" nil t)
  294. (let ((result (cdr (assq '*
  295. (assq 'text
  296. (assq 'parse
  297. (json-read-from-string
  298. (buffer-substring-no-properties
  299. (1- (point)) (point-max)))))))))
  300. (when result
  301. (if (string-match "<span class=\"redirectText\"><a href=[^>]+>\\([^<]+\\)" result)
  302. (cons 'redirect (match-string 1 result))
  303. ;; find the beginning of the summary text in the result
  304. ;; check if there is a table before the summary and skip that
  305. (when (or (string-match "</table>\\(\n<div.*?</div>\\)?\n<p>" result)
  306. ;; otherwise just find the first paragraph
  307. (string-match "<p>" result))
  308. ;; remove cruft and do a simple formatting
  309. (replace-regexp-in-string
  310. "Cite error: .*" ""
  311. (replace-regexp-in-string
  312. "&#160;" ""
  313. (replace-regexp-in-string
  314. "\\[[^\]]+\\]" ""
  315. (replace-regexp-in-string
  316. "<[^>]*>" ""
  317. (replace-regexp-in-string
  318. "</p>\n<p>" "\n\n"
  319. (substring result (match-end 0)))))))))))))
  320. (defvar helm-wikipedia-map
  321. (let ((map (copy-keymap helm-map)))
  322. (define-key map (kbd "<C-return>") 'helm-wikipedia-show-summary-action)
  323. map)
  324. "Keymap for `helm-wikipedia-suggest'.")
  325. (defvar helm-source-wikipedia-suggest
  326. (helm-build-sync-source "Wikipedia Suggest"
  327. :candidates #'helm-wikipedia-suggest-fetch
  328. :action '(("Wikipedia" . (lambda (candidate)
  329. (helm-search-suggest-perform-additional-action
  330. helm-search-suggest-action-wikipedia-url
  331. candidate)))
  332. ("Show summary in new buffer (C-RET)" . helm-wikipedia-show-summary))
  333. :persistent-action #'helm-wikipedia-persistent-action
  334. :persistent-help "show summary"
  335. :volatile t
  336. :keymap helm-wikipedia-map
  337. :requires-pattern 3))
  338. (defun helm-wikipedia-show-summary-action ()
  339. "Exit Helm buffer and call `helm-wikipedia-show-summary' with selected candidate."
  340. (interactive)
  341. (with-helm-alive-p
  342. (helm-exit-and-execute-action 'helm-wikipedia-show-summary)))
  343. ;;; Web browser functions.
  344. ;;
  345. ;;
  346. ;; If default setting of `w3m-command' is not
  347. ;; what you want and you modify it, you will have to reeval
  348. ;; also `helm-browse-url-default-browser-alist'.
  349. (defvar helm-browse-url-chromium-program "chromium-browser")
  350. (defvar helm-browse-url-uzbl-program "uzbl-browser")
  351. (defvar helm-browse-url-conkeror-program "conkeror")
  352. (defvar helm-browse-url-opera-program "opera")
  353. (defvar helm-browse-url-default-browser-alist
  354. `((,(or (and (boundp 'w3m-command) w3m-command)
  355. "/usr/bin/w3m") . w3m-browse-url)
  356. (,browse-url-firefox-program . browse-url-firefox)
  357. (,helm-browse-url-chromium-program . helm-browse-url-chromium)
  358. (,helm-browse-url-conkeror-program . helm-browse-url-conkeror)
  359. (,helm-browse-url-opera-program . helm-browse-url-opera)
  360. (,helm-browse-url-uzbl-program . helm-browse-url-uzbl)
  361. (,browse-url-kde-program . browse-url-kde)
  362. (,browse-url-gnome-moz-program . browse-url-gnome-moz)
  363. (,browse-url-mozilla-program . browse-url-mozilla)
  364. (,browse-url-galeon-program . browse-url-galeon)
  365. (,browse-url-netscape-program . browse-url-netscape)
  366. (,browse-url-mosaic-program . browse-url-mosaic)
  367. (,browse-url-xterm-program . browse-url-text-xterm)
  368. ("emacs" . eww-browse-url))
  369. "*Alist of \(executable . function\) to try to find a suitable url browser.")
  370. (cl-defun helm-generic-browser (url cmd-name &rest args)
  371. "Browse URL with NAME browser."
  372. (let ((proc (concat cmd-name " " url)))
  373. (message "Starting %s..." cmd-name)
  374. (apply 'start-process proc nil cmd-name
  375. (append args (list url)))
  376. (set-process-sentinel
  377. (get-process proc)
  378. (lambda (process event)
  379. (when (string= event "finished\n")
  380. (message "%s process %s" process event))))))
  381. ;;;###autoload
  382. (defun helm-browse-url-firefox (url &optional _ignore)
  383. "Same as `browse-url-firefox' but detach from emacs.
  384. So when you quit emacs you can keep your firefox session open
  385. and not be prompted to kill firefox process.
  386. NOTE: Probably not supported on some systems (e.g Windows)."
  387. (interactive (list (read-string "URL: " (browse-url-url-at-point))
  388. nil))
  389. (setq url (browse-url-encode-url url))
  390. (let ((process-environment (browse-url-process-environment)))
  391. (call-process-shell-command
  392. (format "(%s %s %s &)"
  393. browse-url-firefox-program
  394. helm-browse-url-firefox-new-window
  395. (shell-quote-argument url)))))
  396. ;;;###autoload
  397. (defun helm-browse-url-opera (url &optional _ignore)
  398. "Browse URL with opera browser and detach from emacs.
  399. So when you quit emacs you can keep your opera session open
  400. and not be prompted to kill opera process.
  401. NOTE: Probably not supported on some systems (e.g Windows)."
  402. (interactive (list (read-string "URL: " (browse-url-url-at-point))
  403. nil))
  404. (setq url (browse-url-encode-url url))
  405. (let ((process-environment (browse-url-process-environment)))
  406. (call-process-shell-command
  407. (format "(%s %s &)"
  408. helm-browse-url-opera-program (shell-quote-argument url)))))
  409. ;;;###autoload
  410. (defun helm-browse-url-chromium (url &optional _ignore)
  411. "Browse URL with google chrome browser."
  412. (interactive "sURL: ")
  413. (helm-generic-browser
  414. url helm-browse-url-chromium-program))
  415. ;;;###autoload
  416. (defun helm-browse-url-uzbl (url &optional _ignore)
  417. "Browse URL with uzbl browser."
  418. (interactive "sURL: ")
  419. (helm-generic-browser url helm-browse-url-uzbl-program "-u"))
  420. ;;;###autoload
  421. (defun helm-browse-url-conkeror (url &optional _ignore)
  422. "Browse URL with conkeror browser."
  423. (interactive "sURL: ")
  424. (helm-generic-browser url helm-browse-url-conkeror-program))
  425. (defun helm-browse-url-default-browser (url &rest args)
  426. "Find the first available browser and ask it to load URL."
  427. (let ((default-browser-fn
  428. (cl-loop for (exe . fn) in helm-browse-url-default-browser-alist
  429. thereis (and exe (executable-find exe) (fboundp fn) fn))))
  430. (if default-browser-fn
  431. (apply default-browser-fn url args)
  432. (error "No usable browser found"))))
  433. (defun helm-browse-url (url &rest args)
  434. "Default command to browse URL."
  435. (if browse-url-browser-function
  436. (browse-url url args)
  437. (helm-browse-url-default-browser url args)))
  438. ;;; Surfraw
  439. ;;
  440. ;; Need external program surfraw.
  441. ;; <http://surfraw.alioth.debian.org/>
  442. ;; Internal
  443. (defvar helm-surfraw-engines-history nil)
  444. (defvar helm-surfraw-input-history nil)
  445. (defvar helm-surfraw--elvi-cache nil)
  446. (defun helm-build-elvi-list ()
  447. "Return list of all engines and descriptions handled by surfraw."
  448. (or helm-surfraw--elvi-cache
  449. (setq helm-surfraw--elvi-cache
  450. (cdr (with-temp-buffer
  451. (call-process "surfraw" nil t nil "-elvi")
  452. (split-string (buffer-string) "\n"))))))
  453. ;;;###autoload
  454. (defun helm-surfraw (pattern engine)
  455. "Preconfigured `helm' to search PATTERN with search ENGINE."
  456. (interactive
  457. (list
  458. (let* ((default (if (use-region-p)
  459. (buffer-substring-no-properties
  460. (region-beginning) (region-end))
  461. (thing-at-point 'symbol)))
  462. (prompt (if default
  463. (format "SearchFor (default %s): " default)
  464. "SearchFor: ")))
  465. (read-string prompt nil 'helm-surfraw-input-history default))
  466. (helm-comp-read
  467. "Engine: "
  468. (helm-build-elvi-list)
  469. :must-match t
  470. :name "Surfraw Search Engines"
  471. :del-input nil
  472. :history helm-surfraw-engines-history)))
  473. (let* ((engine-nodesc (car (split-string engine)))
  474. (url (if (string= engine-nodesc "duckduckgo")
  475. ;; "sr duckduckgo -p foo" is broken, workaround.
  476. (format helm-surfraw-duckduckgo-url
  477. (url-hexify-string pattern))
  478. (with-temp-buffer
  479. (apply 'call-process "surfraw" nil t nil
  480. (append (list engine-nodesc "-p") (split-string pattern)))
  481. (replace-regexp-in-string
  482. "\n" "" (buffer-string)))))
  483. (browse-url-browser-function (or helm-surfraw-default-browser-function
  484. browse-url-browser-function)))
  485. (if (string= engine-nodesc "W")
  486. (helm-browse-url helm-home-url)
  487. (helm-browse-url url)
  488. (setq helm-surfraw-engines-history
  489. (cons engine (delete engine helm-surfraw-engines-history))))))
  490. ;;;###autoload
  491. (defun helm-google-suggest ()
  492. "Preconfigured `helm' for google search with google suggest."
  493. (interactive)
  494. (helm-other-buffer 'helm-source-google-suggest "*helm google*"))
  495. ;;;###autoload
  496. (defun helm-wikipedia-suggest ()
  497. "Preconfigured `helm' for Wikipedia lookup with Wikipedia suggest."
  498. (interactive)
  499. (helm :sources 'helm-source-wikipedia-suggest
  500. :buffer "*helm wikipedia*"))
  501. (provide 'helm-net)
  502. ;; Local Variables:
  503. ;; byte-compile-warnings: (not obsolete)
  504. ;; coding: utf-8
  505. ;; indent-tabs-mode: nil
  506. ;; End:
  507. ;;; helm-net.el ends here