web-search.el 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. ;;; web-search.el --- Search for text on the Internet
  2. ;; Copyright (C) 2013-2014 Alex Kost
  3. ;; Author: Alex Kost <alezost@gmail.com>
  4. ;; Created: 25 Jan 2013
  5. ;; Version: 0.1
  6. ;; Package-Requires: ((cl-lib "0.5"))
  7. ;; URL: https://github.com/alezost/web-search.el
  8. ;; Keywords: tools
  9. ;; This program is free software; you can redistribute it and/or modify
  10. ;; it under the terms of the GNU General Public License as published by
  11. ;; the Free Software Foundation, either version 3 of the License, or
  12. ;; (at your option) any later version.
  13. ;; This program is distributed in the hope that it will be useful,
  14. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. ;; GNU General Public License for more details.
  17. ;; You should have received a copy of the GNU General Public License
  18. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. ;;; Commentary:
  20. ;; This file provides functions for searching selected/prompted text on
  21. ;; the Internet using different engines defined in `web-search-engines'.
  22. ;; To install the package, add the following to your emacs init file:
  23. ;;
  24. ;; (add-to-list 'load-path "/path/to/web-search")
  25. ;; (autoload 'web-search "web-search" nil t)
  26. ;; Use "M-x web-search" to perform a search. Also you can use
  27. ;; `web-search-<engine>' commands which are generated after the file is
  28. ;; loaded.
  29. ;; To define a new search engine or replace the existing one, use
  30. ;; `web-search-add-engine' function, for example:
  31. ;;
  32. ;; (web-search-add-engine 'google-groups "Google Groups"
  33. ;; "http://groups.google.com/groups?q=%s")
  34. ;; (web-search-add-engine 'wikipedia-en "Wikipedia (english)"
  35. ;; "http://en.wikipedia.org/w/index.php?search=%s")
  36. ;;
  37. ;; After evaluating the above code, you can use the "google-groups"
  38. ;; engine with "M-x web-search-google-groups" or "M-x web-search". Also
  39. ;; a new URL will be used for the "wikipedia-en" engine.
  40. ;;; Code:
  41. (require 'cl-lib)
  42. (cl-defstruct (web-search-engine
  43. (:constructor nil) ; no default constructor
  44. (:constructor web-search-create-engine
  45. (name title url &optional filter))
  46. (:copier nil))
  47. name title url filter)
  48. (defvar web-search-engines nil
  49. "List of search engines.
  50. Each engine is a structure that consists of the following
  51. elements:
  52. NAME Internal name (symbol), used for generating search commands.
  53. TITLE String, used in prompts.
  54. URL Searching URL, where \"%s\" is replaced by a searching text.
  55. FILTER (optional) If non-nil, a searching text is passed through
  56. this filter function before browsing the final URL.
  57. To add an engine to this list, use `web-search-add-engine'.")
  58. ;;;###autoload
  59. (defconst web-search-default-engines
  60. '((duckduckgo "DuckDuckGo"
  61. "https://duckduckgo.com/?q=%s")
  62. (google "Google"
  63. "http://www.google.com/search?q=%s")
  64. (yahoo "Yahoo"
  65. "http://search.yahoo.com/search?p=%s")
  66. (github "Github"
  67. "http://github.com/search?q=%s&type=Everything")
  68. (emacswiki "EmacsWiki"
  69. "http://www.google.com/cse?cx=004774160799092323420:6-ff2s0o6yi&q=%s&sa=Search&siteurl=emacswiki.org/")
  70. (archwiki "ArchWiki"
  71. "https://wiki.archlinux.org/index.php?search=%s")
  72. (debbugs "GNU Bug Tracker"
  73. "http://debbugs.gnu.org/cgi/bugreport.cgi?bug=%s")
  74. (wikipedia-en "Wikipedia (english)"
  75. "http://en.wikipedia.org/wiki/%s")
  76. (wiktionary-en "Wiktionary (english)"
  77. "http://en.wiktionary.org/wiki/%s")
  78. (tfd "The Free Dictionary"
  79. "http://www.thefreedictionary.com/%s")
  80. (ip "IP address"
  81. "http://www.ip-address.org/lookup/ip-locator.php?track=%s"
  82. web-search-clean-ip))
  83. "List of default engines.")
  84. (defmacro web-search-define-get-engine-function (slot)
  85. "Define function for getting engine structure by SLOT.
  86. Name of the defined function is `web-search-get-engine-by-SLOT'.
  87. SLOT is a name (symbol) of one of the slots of an engine
  88. structure (see `web-search-engines' for details)."
  89. (let* ((slot-str (symbol-name slot))
  90. (slot-getter (intern (concat "web-search-engine-" slot-str)))
  91. (fun-name (intern (concat "web-search-get-engine-by-" slot-str)))
  92. (fun-desc (concat "Return engine from `web-search-engines' by its "
  93. (upcase slot-str) ".\n"
  94. "Return nil, if there is no such engine.")))
  95. `(defun ,fun-name (,slot)
  96. ,fun-desc
  97. (cl-loop for engine in web-search-engines
  98. if (equal ,slot (,slot-getter engine))
  99. return engine))))
  100. (web-search-define-get-engine-function name)
  101. (web-search-define-get-engine-function title)
  102. (defun web-search-delete-engine (name)
  103. "Delete engine with NAME from `web-search-engines'."
  104. (setq web-search-engines
  105. (cl-delete-if (lambda (engine)
  106. (eq name (web-search-engine-name engine)))
  107. web-search-engines)))
  108. (defun web-search-add-engine (name title url &optional filter)
  109. "Add new web-search engine.
  110. The added engine will be available in `web-search' command. Also
  111. a new function `web-search-NAME' will be generated. If there is
  112. an engine with such NAME, it will be replaced with the new one.
  113. For the meaning of NAME, TITLE, URL and FILTER, see
  114. `web-search-engines'."
  115. (web-search-delete-engine name)
  116. (push (web-search-create-engine name title url filter)
  117. web-search-engines)
  118. (eval `(web-search-define-search-engine-function ,name)))
  119. (defun web-search-clean-ip (str)
  120. "Return IP address by substituting '-' with '.' in STR."
  121. ;; Just in case if IP looks like this: 123-45-678-90
  122. (replace-regexp-in-string "-" "." str))
  123. (defun web-search-prompt-for-string (&optional prompt)
  124. "Prompt for and return a search string.
  125. Use PROMPT if it is specified.
  126. If there is a selected region, it is used as a default value."
  127. (read-string (or prompt "Search for: ")
  128. (and (use-region-p)
  129. (buffer-substring-no-properties
  130. (region-beginning) (region-end)))))
  131. (defun web-search-prompt-for-engine (&optional prompt)
  132. "Prompt for and return a name of the search engine.
  133. Use PROMPT if it is specified."
  134. (web-search-engine-name
  135. (web-search-get-engine-by-title
  136. (completing-read (or prompt "Search engine: ")
  137. (sort (mapcar #'web-search-engine-title
  138. web-search-engines)
  139. #'string<)
  140. nil t))))
  141. ;;;###autoload
  142. (defun web-search (string engine-name)
  143. "Search for STRING on the Internet.
  144. ENGINE-NAME is a name (symbol) of an ENGINE from
  145. `web-search-engines'.
  146. Interactively, prompt for STRING (use selected region as a
  147. default value) and engine."
  148. (interactive
  149. (list (web-search-prompt-for-string)
  150. (web-search-prompt-for-engine)))
  151. (let ((engine (web-search-get-engine-by-name engine-name)))
  152. (or engine
  153. (error "Search engine '%S' does not exist" engine-name))
  154. (let* ((engine-url (web-search-engine-url engine))
  155. (engine-fun (web-search-engine-filter engine))
  156. (search-str (url-hexify-string
  157. (if engine-fun
  158. (funcall engine-fun string)
  159. string))))
  160. (browse-url (format engine-url search-str)))))
  161. (defmacro web-search-define-search-engine-function (engine-name)
  162. "Define function for searching text with a particular engine.
  163. Name of the defined function is `web-search-ENGINE-NAME'.
  164. ENGINE-NAME is a name (symbol) of an engine from `web-search-engines'."
  165. (let* ((engine (web-search-get-engine-by-name engine-name))
  166. (engine-title (web-search-engine-title engine))
  167. (fun-name (intern (concat "web-search-" (symbol-name engine-name))))
  168. (fun-desc (concat "Search for STRING with '" engine-title "'.\n"
  169. "Interactively, prompt for STRING (use selected region as a\n"
  170. "default value).")))
  171. `(defun ,fun-name (string)
  172. ,fun-desc
  173. (interactive
  174. (list (web-search-prompt-for-string
  175. (format "Search %s: " ,engine-title))))
  176. (web-search string ',engine-name))))
  177. (mapc (lambda (args)
  178. (apply #'web-search-add-engine args))
  179. web-search-default-engines)
  180. ;; Autoload `web-search-<engine>' commands:
  181. ;;;###autoload (mapc (lambda (engine) (autoload (intern (concat "web-search-" (symbol-name (car engine)))) "web-search" nil t)) web-search-default-engines)
  182. (provide 'web-search)
  183. ;;; web-search.el ends here