;; gnugol.el - Web search using the gnugol command line utility ;; Copyright (C) 2010 Dave Täht ;; License: GNU Public License, version 3 ;; Author: Dave Taht ;; Maintainer: d + gnugol AT taht.net ;; Created: Dec-2008 ;; Version: See git tree ;; Keywords: extensions, web, search, google ;; This is an interface to the gnugol command line ;; web search utility, which can be obtained at: ;; http://gnugol.taht.net ;; I find gnugol useful enough to stick on a function key ;; in my keybindings.el file elsewhere. ;; (define-key global-map [f6] 'gnugol) ;; FIXME: Convert all to defcustom and add support for args (defcustom gnugol-cmd "gnugol" "Shell command to invoke gnugol." :type 'string :group 'gnugol) (defcustom gnugol-default-opts nil "Additional default options for gnugol." :type 'string :group 'gnugol) (defcustom gnugol-default-engine nil "Default search engine backend for gnugol. Presently supported are: google: Full support (license key recommended) duck: (duckduckgo) bing: (with a license key) dummy: (useful for testing) credits: various options like about, licenses etc." :type 'string :group 'gnugol) (defcustom gnugol-default-nresults 8 "Default number of results for gnugol to return." :type 'integer :group 'gnugol) (defcustom gnugol-default-safe-mode "1" "Default safe mode to search in: 0 = none, 1 = moderate, 2 = active" :type 'string :group 'gnugol) (defcustom gnugol-default-header "0" "Default output header for gnugol. 0 = no header. " :type 'string :group 'gnugol) (defcustom gnugol-default-footer "0" "Default output footer for gnugol. 0 = no footer" :type 'string :group 'gnugol) (defcustom gnugol-default-output-format "org" "Default output format for gnugol." :type 'string :group 'gnugol) (defcustom gnugol-default-output-buffer "*gnugol*" "Output buffer. Set this to something like ~/org/gnugol_history.org if you want to keep your history." :type 'string :group 'gnugol) (defcustom gnugol-default-input-language nil "Set this to your preferred language 2 character code if you want to override the LANG variable in your environment." :type 'string :group 'gnugol) (defcustom gnugol-default-output-language nil "Set this to the preferred language 2 character code to **restrict** the results you get to this language." :type 'string :group 'gnugol) (defcustom gnugol-search-maxlen 200 "Maximum string length of search term. This saves on sending accidentally large queries to the search engine." :type 'integer :group 'gnugol) (defcustom gnugol-default-search-maxwords 4 "Maximum number of words to search for." :type 'integer :group 'gnugol) (defcustom gnugol-default-output-mode-sensitive nil "Be sensitive to the current buffer mode. Output results in that format." :type 'boolean :group 'gnugol) (defcustom gnugol-default-timestamp-output nil "Timestamp the output." :type 'boolean :group 'gnugol) ;; FIXME Haven't decided if doing a ring buffer would be useful (defcustom gnugol-ring-max 16 "Maximum length of search ring before oldest elements are thrown away." :type 'integer :group 'gnugol) ;; FIXME figure out how to search the buffer-modes ;; (defun gnugol-get-output-mode ;; "Get the gnugol output mode from the current buffer mode." ;; (if gnugol-default-output-mode-sensitive ;; () ;; ("org"))) (defun gnugol-url-encode (str) "URL-encode STR." (interactive "sURL-encode: ") (message "%s" (url-hexify-string str))) (defun gnugol-url-decode (str) "URL-decode STR." (interactive "sURL-decode: ") (message "%s" (decode-coding-string (url-unhex-string str) 'utf-8))) ;; FIXME: gnugol should act more like "woman" ;; FIXME: gnugol should maybe output info and LISP format ;; FIXME: If there is a visible gnugol buffer change focus to that rather than the current ;; FIXME: Add hooks for further washing the data ;; FIXME: add next, prev, and refresh buttons ; [[gnugol: :next :pos 4 str][more]] [[gnugol: prev][prev]] [[refresh]] ;; FIXME: Document this shell shortcut into org or write org mode version ;; FIXME: search forward in the buffer for an existing ;; set of keywords rather than call gnugol ;; (if (search-forward (concat "[[Search: " str )) () (gnugol-cmd str)))? ;; FIXME: Sanitize the shell arguments to prevent abuse !! For example no CRs ;; regexp? I'm *nearly* certain that url escaping and shell quoting the args ;; is good enough. ;; FIXME: actually, going to the 4th char in on the title would work best ;; FIXME: make gnugol opts be local ;; FIXME: CNTR-U should set the position ;; FIXME: a query with the same keywords as the previous should fetch more ;; results (maybe) (defun gnugol (str) "Search the web via gnugol, bring up results in org buffer." (interactive "sSearch: ") (if (< (length str) gnugol-search-maxlen) (let (newbuffer) (setq gnugol-opts (concat (if gnugol-default-opts (concat gnugol-default-opts) ()) (if gnugol-default-nresults (concat " -n " (int-to-string gnugol-default-nresults) ())) (if gnugol-default-engine (concat " -e " gnugol-default-engine)) (if gnugol-default-output-format (concat " -o " gnugol-default-output-format) ()) (if gnugol-default-header (concat " -H " gnugol-default-header) ()) (if gnugol-default-footer (concat " -F " gnugol-default-footer) ()) ;; (if gnugol-default-safe-mode (concat " -S " gnugol-default-safe-mode) ()) (if gnugol-default-input-language (concat " -l " gnugol-default-input-language) ()) (if gnugol-default-output-language (concat " -L " gnugol-default-output-language) ()) )) (setq gnugol-full-cmd (concat gnugol-cmd " " gnugol-opts " -U -- " (shell-quote-argument (gnugol-url-encode str)))) ;; FIXME: Open the file, or reload the file if not a *gnugol* buffer (setq newbuffer (get-buffer-create gnugol-default-output-buffer)) (set-buffer newbuffer) ;; FIXME: Set mode of buffer based on the extension (org-mode) (goto-char (point-min)) ;; FIXME what we want to do is something like this but I'm getting it wrong ;; (if (search-forward (concat "[Search: " str "]")) () ;; (message "%s" gnugol-full-cmd) (save-excursion (insert-string (concat "* [[gnugol: " str "][Search: " str "]]\n")) (insert (shell-command-to-string gnugol-full-cmd) ) (switch-to-buffer newbuffer) ) ;; (goto-char (+ point-min 4)) ) ( (beep) (message "search string too long")))) (defun gnugol-search-selection () "Do a gnugol search based on a region" (interactive) (let (start end term url) (if (or (not (fboundp 'region-exists-p)) (region-exists-p)) (progn (setq start (region-beginning) end (region-end)) (if (> (- start end) gnugol-search-maxlen) (setq term (buffer-substring start (+ start gnugol-search-maxlen))) (setq term (buffer-substring start end))) (gnugol term)) (beep) (message "Region not active")))) ;; Do I really understand lexical scoping yet? (defun gnugol-search-dummy(str) "Search the dummy engine via gnugol. (Useful for debugging)" (interactive "sSearch: ") (let (gnugol-default-engine) (setq gnugol-default-engine "dummy") (gnugol str) ) ) (defun gnugol-search-credits(str) "Search the local credits engine via gnugol." (interactive "sSearch: ") (let (gnugol-default-engine) (setq gnugol-default-engine "credits") (gnugol str) ) ) (defun gnugol-search-bing(str) "Search bing via gnugol." (interactive "sSearch: ") (let (gnugol-default-engine) (setq gnugol-default-engine "bing") (gnugol str) ) ) (defun gnugol-search-duck(str) "Search duckduckgo via gnugol." (interactive "sSearch: ") (let (gnugol-default-engine) (setq gnugol-default-engine "duck") (gnugol str) ) ) (defun gnugol-search-stackapps(str) "Search stackapps via gnugol." (interactive "sSearch: ") (let (gnugol-default-engine) (setq gnugol-default-engine "stackapps") (gnugol str) ) ) (defun gnugol-search-google(str) "Search google via gnugol." (interactive "sSearch: ") (let (gnugol-default-engine) (setq gnugol-default-engine "google") (gnugol str) ) ) ;; This are examples of using a site specific search (defun gnugol-search-emacswiki(str) "Search emacswiki via gnugol." (interactive "sSearch: ") (gnugol-search-google (concat "site:www.emacswiki.org " str)) ) (defun gnugol-search-gnugol(str) "Search gnugol site via gnugol." (interactive "sGnugol Search: ") (gnugol-search-google (concat "site:gnugol.taht.net " str)) ) (defun gnugol-search-koders(str) "Search koders.com site via gnugol." (interactive "sSearch Koders: ") (gnugol-search-google (concat "site:www.koders.com " str)) ) (defun gnugol-search-stackapps-google(str) "Search stackoverflow site via gnugol." (interactive "sSearch stackapps: ") (gnugol-search-google (concat "site:stackoverflow.com " str)) ) ;; It would be nice to do the above via command completion ;; (gnugol-search-site arg, arg), but this isn't right ;; I need to prepend args somehow ;; (defun gnugol-search-site(str) ;; "Search any specific site via gnugol." ;; (interactive "site: ") ;; (gnugol-search-google (concat "site:" str)) ;; ) ;; And this is just around so that I test output formats ;; It suffers because I would like it to toss output ;; into a buffer formatted in that format... (defun gnugol-test(str) "Output gnugol's test data." (interactive "sMode: ") (let (gnugol-default-output-format) (setq gnugol-default-output-format str) (gnugol-search-dummy "all") ) ) ;; FIXME: I'd really like a way to split the current window ;; at 80 characters and bring up the search on the ;; right. AND override my default url opener to be ;; the internal emacs web browser for sites on a ;; whitelist. ;; FIXME: NOTHING BELOW HERE ACTUALLY WORKS at all YET ;; (in contrast to the above which only sort of works) ;; FIXME: add hooks for additional modes - ;; FIXME: For the into-pt stuff, be sensitive to the mode ;; If I'm in markdown format, return markdown ;; org, do org ;; html, do html. Etc. ;; C mode, put it in comments ;; etc ;; FIXME: simplify navigation in org-mode buffer with minor mode ;; add n and p to move to the links? CNTRL-arrows are good enough ;; ;; FIXME: Add robust interface ;; gnugol-thing-at-pt ;; gnugol-into-pt ;; gnugol-thing-at-pt-into-pt (provide 'gnugol)