;;;; -*- mode:emacs-lisp;coding:utf-8 -*-
;;;;**************************************************************************
;;;;FILE:               pjb-searches.el
;;;;LANGUAGE:           emacs lisp
;;;;SYSTEM:             POSIX
;;;;USER-INTERFACE:     NONE
;;;;DESCRIPTION
;;;;
;;;;    Web and grep searchers.
;;;;
;;;;AUTHORS
;;;;    <PJB> Pascal J. Bourguignon <pjb@informatimago.com>
;;;;MODIFICATIONS
;;;;    2015-08-20 <PJB> Extracted from ~/rc/emacs-common.el
;;;;BUGS
;;;;LEGAL
;;;;    AGPL3
;;;;
;;;;    Copyright Pascal J. Bourguignon 2015 - 2015
;;;;
;;;;    This program is free software: you can redistribute it and/or modify
;;;;    it under the terms of the GNU Affero General Public License as published by
;;;;    the Free Software Foundation, either version 3 of the License, or
;;;;    (at your option) any later version.
;;;;
;;;;    This program is distributed in the hope that it will be useful,
;;;;    but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;;;    GNU Affero General Public License for more details.
;;;;
;;;;    You should have received a copy of the GNU Affero General Public License
;;;;    along with this program.  If not, see <http://www.gnu.org/licenses/>.
;;;;**************************************************************************

;; (require 'google)
;; (setq google-license-key "dF18sc1QFHLPxvBVqwv/WxCbYR18GHbp")
;; ;; Then M-x google-search RET
;; ;; or M-x google-search-region RET
;; (defalias 'url-retrieve-synchronously 'url-retrieve)

(defun %search-region (start end thing search-function)
  (when start
    (cond
      ((null end)
       (let ((bounds (bounds-of-thing-at-point thing)))
         (if bounds
             (%search-region (car bounds) (cdr bounds) thing search-function)
             (call-interactively search-function))))
      ((= start end)
       (call-interactively search-function))
      (t
       (funcall search-function (buffer-substring-no-properties start end))))))

;; (if (or (not mark-active) (eql (point) (mark)))
;;     "string"
;;     (buffer-substring-no-properties (min (point) (mark))
;;                                     (max (point) (mark))))


(defparameter *whitespaces* '(32 9 10 13))

(defmacro with-browser-for-apple-documentation (&rest body)
  `(let ((browse-url-browser-function (if (and (eq window-system 'ns)
                                              (eq system-type 'darwin))
                                         'browse-url-generic
                                         'browse-url-firefox2)))
    ,@body))

(defun osx-search (search-string)
  "Search a string with Apple."
  (interactive "sApple Developer Documentation Search: ")
  (with-browser-for-apple-documentation
   (browse-url
    (format "https://developer.apple.com/library/mac/search/?q=%s"
            (browse-url-url-encode-chars
             (cl:string-trim *whitespaces* search-string)
             "[^A-Za-z0-9]")))))

(defun osx-search-region (start end)
  "Search the text in the region with Apple."
  (interactive "r")
  (%search-region start end 'symbol 'osx-search))

;; (debug-on-entry 'browse-url)
(defun ios-search (search-string)
  "Search a string with Apple."
  (interactive "sApple Developer Documentation Search: ")
  (with-browser-for-apple-documentation
      (browse-url
       (format "https://developer.apple.com/library/ios/search/?q=%s"
               (browse-url-url-encode-chars
                (cl:string-trim *whitespaces* search-string)
                "[^A-Za-z0-9]")))))

(defun ios-search-region (start end)
  "Search the text in the region with Apple."
  (interactive "r")
  (%search-region start end 'symbol 'ios-search))


(defun android-search (search-string)
  "Search a string with Android."
  (interactive "sAndroid Developer Documentation Search: ")
  (browse-url (or (when (and (search "." search-string) (not (search ".." search-string)))
                    (let ((words (split-string search-string "\\.")))
                      (when (and (<= 3 (length words))
                                 (every (lambda (word)
                                          (and (alpha-char-p (aref word 0))
                                               (every (function alphanumericp) word)))

                                        words))
                        (format "http://developer.android.com/reference/%s.html"
                                (mapconcat (function identity) words "/")))))
                  (format "http://developer.android.com/reference/index.html?q=%s"
                          (browse-url-url-encode-chars
                           (cl:string-trim *whitespaces* search-string)
                           "[^A-Za-z0-9]")))))

(defun android-search-region (start end)
  "Search the text in the region with Android."
  (interactive "r")
  (%search-region start end 'symbol 'android-search))


(defvar *pjb-search-exclude*            '("debug" "release" ".svn" ".git" ".hg" ".cvs"))
(defvar *pjb-search-include-extensions* '("xib" "h" "c" "m" "hh" "cc" "mm" "hxx" "cxx" "swift"
                                          "lisp" "" "cl" "el"
                                          "rb" "java" "xml"
                                          "logs" "txt" "html" "iml"
                                          "json" "md" "prefs"
                                          "project" "properties"
                                          "sh" "bash"))
(defun git-search (search-string)
  "Search a regex in the current git repository (with `find-grep' and `grep-find-command')."
  (interactive "sSearch Git Regexp: ")
  (let ((exclude *pjb-search-exclude*)
        (include (mapcar (lambda (extension) (format "\\*.%s" extension))
                         *pjb-search-include-extensions*)))
    (find-grep (format "find %S \\( \\( %s \\) -prune \\) -o -type f %s -print0 | xargs -0 grep -nHi -e %s"

                       (expand-file-name (vc-git-root (or (buffer-file-name) default-directory)))
                       (mapconcat (lambda (name) (format "-name %s" name)) exclude " -o ")
                       (if include
                           (format "\\( %s \\)" (mapconcat (lambda (name) (format "-name %s" name)) include " -o "))
                           "")
                       (shell-quote-argument search-string)))))

(defun git-search-region (start end)
  "Search the text in the region in the current git repository."
  (interactive "r")
  (%search-region start end 'symbol 'git-search))

(defun git-search-symbol-at-point ()
  "Search the symbol at point in the current git repository."
  (interactive)
  (git-search (symbol-name (symbol-at-point))))

(defun project-search (search-string)
  "Search a regex in the current project (with `find-grep' and `grep-find-command')."
  (interactive "sSearch Project Regexp: ")
  (find-grep (concat grep-find-command " " (shell-quote-argument search-string))))

(defun project-search-region (start end)
  "Search the text in the region in the current project (with `find-grep' and `grep-find-command')."
  (interactive "r")
  (%search-region start end 'symbol 'project-search))


(defun google-search (search-string)
  "Search a string with Google."
  (interactive "sGoogle Search: ")
  (browse-url
   (format "http://www.google.com/search?as_q=%s&num=50&hl=en&ie=ISO8869-1&btnG=Google+Search&as_epq=&as_oq=&as_eq=&lr=&as_ft=i&as_filetype=&as_qdr=all&as_nlo=&as_nhi=&as_occt=any&as_dt=i&as_s
itesearch=&safe=images"
           (browse-url-url-encode-chars
            (cl:string-trim *whitespaces* search-string)
            "[^A-Za-z0-9]"))))

(defun google-search-region (start end)
  (interactive "r")
  (%search-region start end 'symbol 'google-search))


(defparameter *acronym-search-url* "http://www.acronymfinder.com/%s.html")
;;  "http://www.cygwin.com/acronyms/#%s"
(defun acronym-search (acronym-string)
  (interactive "sAcronym Search: ")
  (browse-url (format *acronym-search-url* acronym-string)))

(defun acronym-search-region (start end)
  (interactive "r")
  (%search-region start end 'symbol 'acronym-search))



(defun includes-search (string)
  (interactive "sIncludes Search: ")
  (find-grep (format "find /usr/include/ /usr/local/include/ -type f -exec grep -n -i %s {} /dev/null \\; #" (shell-quote-argument string))))

(defun includes-search-region (start end)
  (interactive "r")
  (%search-region start end 'symbol 'includes-search))

(defalias 'grep-includes 'includes-search)


(defun hyperspec-search (string)
  (interactive "sHyperspec Search: ")
  (find-grep (format "find '%s' -type f -print|while read f ; do lynx -dump -nolist \"$f\" | grep -i '%s' && echo \"$f:1:-\" ; done #" (shell-quote-argument *hyperspec-path*) string)))

(defun hyperspec-search-region (start end)
  (interactive "r")
  (%search-region start end 'symbol 'hyperspec-search))

(defalias 'grep-hyperspec 'hyperspec-search)


(defun here-search (pattern)
  "Does an egrep  in the current directory just asking for a pattern."
  (interactive (list (read-from-minibuffer (format "In %s egrep pattern: " (shell-quote-argument default-directory)))))
  (check-type pattern string)
  (if (string-equal "" pattern)
      (error "The empty string matches everything. Are you happy?")
      (grep (format "egrep -n -e '%s' `find . -type f -print` /dev/null" pattern))))

(defun here-search-region (start end)
  (interactive "r")
  (%search-region start end 'symbol 'here-search))


(global-set-key (kbd "C-h 0")
                (lambda ()
                  (interactive)
                  (message (format "C-h 1 %s  C-h 2 google  C-h 3 acronym  C-h 4 project  C-h 5 includes  C-h 6 hyperspec  C-h 7 this directory"
                                   (let* ((search (format "%s" (local-key-binding (kbd "C-h 1") t)))
                                          (dash   (search "-" search)))
                                     (if dash
                                         (subseq search 0 dash)
                                         search))))))

;;(global-set-key (kbd "C-h 1") 'android-search-region)
;;(global-set-key (kbd "C-h 1") 'osx-search-region)

(global-set-key (kbd "C-h 1") 'ios-search-region)
(global-set-key (kbd "C-h 2") 'google-search-region)
(global-set-key (kbd "C-h 3") 'acronym-search-region)
(global-set-key (kbd "C-h 4") 'project-search-region)
(global-set-key (kbd "C-h 5") 'includes-search-region)
(global-set-key (kbd "C-h 6") 'hyperspec-search-region)
(global-set-key (kbd "C-h 7") 'here-search-region)
(global-set-key (kbd "C-h 8") 'git-search-region)

(global-set-key (kbd "H-s")   'git-search-symbol-at-point)
(global-set-key (kbd "C-H-s") 'git-search)
(global-set-key (kbd "M-H-s") 'git-search-region)


(defun set-osx-search-region-function ()
  (interactive)
  (local-set-key (kbd "C-h 1") 'osx-search-region))
(defun set-ios-search-region-function ()
  (interactive)
  (local-set-key (kbd "C-h 1") 'ios-search-region))
(defun set-android-search-region-function ()
  (interactive)
  (local-set-key (kbd "C-h 1") 'android-search-region)
  (local-set-key (kbd "C-h 0") 'android-browse-documentation-of-class-at-point))


(add-hook 'objc-mode-hook 'set-osx-search-region-function)
(add-hook 'objc-mode-hook 'set-ios-search-region-function)
(add-hook 'java-mode-hook 'set-android-search-region-function)

;;;; THE END ;;;;
ViewGit