From 2bd9b151b5a71e06ab7c464cac1d2301673356f9 Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Wed, 15 Feb 2012 14:34:00 +0200 Subject: [PATCH] fixed #63 - switched for full-ack to ack-and-a-half --- init.el | 5 +- modules/prelude-ack.el | 47 ++++ modules/prelude-packages.el | 3 +- vendor/ack-and-a-half.el | 425 ++++++++++++++++++++++++++++++++++++ 4 files changed, 477 insertions(+), 3 deletions(-) create mode 100644 modules/prelude-ack.el create mode 100644 vendor/ack-and-a-half.el diff --git a/init.el b/init.el index 4819f3e..9f0b0e9 100644 --- a/init.el +++ b/init.el @@ -77,7 +77,6 @@ by Prelude.") (require 'prelude-coffee) (require 'prelude-common-lisp) (require 'prelude-emacs-lisp) -(require 'prelude-erc) (require 'prelude-groovy) (require 'prelude-haskell) (require 'prelude-js) @@ -90,6 +89,10 @@ by Prelude.") (require 'prelude-scheme) (require 'prelude-xml) +;; tools & utilities support +(require 'prelude-ack) +(require 'prelude-erc) + ;; load the personal settings (this includes `custom-file') (when (file-exists-p prelude-personal-dir) (mapc 'load (directory-files prelude-personal-dir nil "^[^#].*el$"))) diff --git a/modules/prelude-ack.el b/modules/prelude-ack.el new file mode 100644 index 0000000..5e2336f --- /dev/null +++ b/modules/prelude-ack.el @@ -0,0 +1,47 @@ +;;; prelude-ack.el --- Emacs Prelude: ack support +;; +;; Copyright (c) 2011 Bozhidar Batsov +;; +;; Author: Bozhidar Batsov +;; URL: http://www.emacswiki.org/cgi-bin/wiki/Prelude +;; Version: 1.0.0 +;; Keywords: convenience + +;; This file is not part of GNU Emacs. + +;;; Commentary: + +;; Some basic configuration for ack-and-a-half.el. + +;;; License: + +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +;;; Code: + +(autoload 'ack-and-a-half-same "ack-and-a-half" nil t) +(autoload 'ack-and-a-half "ack-and-a-half" nil t) +(autoload 'ack-and-a-half-find-file-same "ack-and-a-half" nil t) +(autoload 'ack-and-a-half-find-file "ack-and-a-half" nil t) + +;; create shorter aliases +(defalias 'ack 'ack-and-a-half) +(defalias 'ack-same 'ack-and-a-half-same) +(defalias 'ack-find-file 'ack-and-a-half-find-file) +(defalias 'ack-find-file-same 'ack-and-a-half-find-file-same) + +(provide 'prelude-ack) +;;; prelude-ack.el ends here diff --git a/modules/prelude-packages.el b/modules/prelude-packages.el index 2367c19..4857303 100644 --- a/modules/prelude-packages.el +++ b/modules/prelude-packages.el @@ -39,7 +39,7 @@ (package-initialize) (defvar prelude-packages - '(auctex clojure-mode coffee-mode deft full-ack gist groovy-mode haml-mode + '(auctex clojure-mode coffee-mode deft gist groovy-mode haml-mode haskell-mode inf-ruby magit markdown-mode paredit projectile python sass-mode rainbow-mode scss-mode solarized-theme yaml-mode yari zenburn-theme) @@ -61,5 +61,4 @@ (package-install p)))) (provide 'prelude-packages) - ;;; prelude-packages.el ends here diff --git a/vendor/ack-and-a-half.el b/vendor/ack-and-a-half.el new file mode 100644 index 0000000..51eeda5 --- /dev/null +++ b/vendor/ack-and-a-half.el @@ -0,0 +1,425 @@ +;;; ack-and-a-half.el --- Yet another front-end for ack +;; +;; Copyright (C) 2011 Jacob Helwig +;; +;; Author: Jacob Helwig +;; Version: 0.0.1 +;; Homepage: http://technosorcery.net +;; +;; This file is NOT part of GNU Emacs. +;; +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation; either version 2, 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 +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program ; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. +;; +;;; Commentary: +;; +;; ack-and-a-half.el provides a simple compilation mode for the perl +;; grep-a-like ack (http://petdance.com/ack/). +;; +;; Add the following to your .emacs: +;; +;; (add-to-list 'load-path "/path/to/ack-and-a-half") +;; (autoload 'ack-and-a-half-same "ack-and-a-half" nil t) +;; (autoload 'ack-and-a-half "ack-and-a-half" nil t) +;; (autoload 'ack-and-a-half-find-file-same "ack-and-a-half" nil t) +;; (autoload 'ack-and-a-half-find-file "ack-and-a-half" nil t) +;; (defalias 'ack 'ack-and-a-half) +;; (defalias 'ack-same 'ack-and-a-half-same) +;; (defalias 'ack-find-file 'ack-and-a-half-find-file) +;; (defalias 'ack-find-file-same 'ack-and-a-half-find-file-same) +;; +;; Run `ack' to search for all files and `ack-same' to search for +;; files of the same type as the current buffer. +;; +;; `next-error' and `previous-error' can be used to jump to the +;; matches. +;; +;; `ack-find-file' and `ack-find-same-file' use ack to list the files +;; in the current project. It's a convenient, though slow, way of +;; finding files. +;; + +(eval-when-compile (require 'cl)) +(require 'compile) +(require 'grep) + +(add-to-list 'debug-ignored-errors + "^Moved \\(back before fir\\|past la\\)st match$") +(add-to-list 'debug-ignored-errors "^File .* not found$") + +(define-compilation-mode ack-and-a-half-mode "Ack" + "Ack results compilation mode." + (set (make-local-variable 'compilation-disable-input) t) + (set (make-local-variable 'compilation-error-face) grep-hit-face)) + +(defgroup ack-and-a-half nil "Yet another front end for ack." + :group 'tools + :group 'matching) + +; TODO Determine how to fall back to using ack-grep if ack is not found. +(defcustom ack-and-a-half-executable (executable-find "ack") + "*The location of the ack executable" + :group 'ack-and-a-half + :type 'file) + +(defcustom ack-and-a-half-arguments nil + "*Extra arguments to pass to ack." + :group 'ack-and-a-half + :type '(repeat (string))) + +(defcustom ack-and-a-half-mode-type-alist nil + "*File type(s) to search per major mode. (ack-and-a-half-same) +This overrides values in `ack-and-a-half-mode-type-default-alist'. +The car in each list element is a major mode, and the rest +is a list of strings passed to the --type flag of ack when running +`ack-and-a-half-same'." + :group 'ack-and-a-half + :type '(repeat (cons (symbol :tag "Major mode") + (repeat (string :tag "ack --type"))))) + +(defcustom ack-and-a-half-mode-extension-alist nil + "*File extensions to search per major mode. (ack-and-a-half-same) +This overrides values in `ack-and-a-half-mode-extension-default-alist'. +The car in each list element is a major mode, and the rest +is a list of file extensions to be searched in addition to +the type defined in `ack-and-a-half-mode-type-alist' when +running `ack-and-a-half-same'." + :group 'ack-and-a-half + :type '(repeat (cons (symbol :tag "Major mode") + (repeat :tag "File extensions" (string))))) + +(defcustom ack-and-a-half-ignore-case 'smart + "*Ignore case when searching +The special value 'smart enables the ack option \"smart-case\"." + :group 'ack-and-a-half + :type '(choice (const :tag "Case sensitive" nil) + (const :tag "Smart case" 'smart) + (const :tag "Case insensitive" t))) + +(defcustom ack-and-a-half-regexp-search t + "*Default to regular expression searching. +Giving a prefix argument to `ack-and-a-half' toggles this option." + :group 'ack-and-a-half + :type '(choice (const :tag "Literal searching" nil) + (const :tag "Regular expression searching" t))) + +(defcustom ack-and-a-half-use-environment t + "*Use .ackrc and ACK_OPTIONS when searching." + :group 'ack-and-a-half + :type '(choice (const :tag "Ignore environment" nil) + (const :tag "Use environment" t))) + +(defcustom ack-and-a-half-root-directory-functions '(ack-and-a-half-guess-project-root) + "*List of functions used to find the base directory to ack from. +These functions are called until one returns a directory. If successful, +`ack-and-a-half' is run from that directory instead of from `default-directory'. +The directory is verified by the user depending on `ack-and-a-half-prompt-for-directory'." + :group 'ack-and-a-half + :type '(repeat function)) + +(defcustom ack-and-a-half-project-root-file-patterns + '(".project\\'" + ".xcodeproj\\'" + ".sln\\'" + "\\`Project.ede\\'" + "\\`.git\\'" + "\\`.bzr\\'" + "\\`_darcs\\'" + "\\`.hg\\'") + "*List of file patterns for the project root (used by `ack-and-a-half-guess-project-root'. +Each element is a regular expression. If a file matching any element is +found in a directory, then that directory is assumed to be the project +root by `ack-and-a-half-guess-project-root'." + :group 'ack-and-a-half + :type '(repeat (string :tag "Regular expression"))) + +(defcustom ack-and-a-half-prompt-for-directory 'unless-guessed + "*Prompt for directory in which to run ack. +If this is 'unless-guessed, then the value determined by `ack-and-a-half-root-directory-functions' +is used without confirmation. If it is nil, then the directory is never +confirmed. If t, then always prompt for the directory to use." + :group 'ack-and-a-half + :type '(choice (const :tag "Don't prompt" nil) + (const :tag "Don't prompt when guessed" 'unless-guessed) + (const :tag "Always prompt" t))) + +;;; Default setting lists ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defconst ack-and-a-half-mode-type-default-alist + '((actionscript-mode "actionscript") + (LaTeX-mode "tex") + (TeX-mode "tex") + (asm-mode "asm") + (batch-file-mode "batch") + (c++-mode "cpp") + (c-mode "cc") + (cfmx-mode "cfmx") + (cperl-mode "perl") + (csharp-mode "csharp") + (css-mode "css") + (emacs-lisp-mode "elisp") + (erlang-mode "erlang") + (espresso-mode "java") + (fortran-mode "fortran") + (haskell-mode "haskell") + (hexl-mode "binary") + (html-mode "html") + (java-mode "java") + (javascript-mode "js") + (jde-mode "java") + (js2-mode "js") + (jsp-mode "jsp") + (latex-mode "tex") + (lisp-mode "lisp") + (lua-mode "lua") + (makefile-mode "make") + (mason-mode "mason") + (nxml-mode "xml") + (objc-mode "objc" "objcpp") + (ocaml-mode "ocaml") + (parrot-mode "parrot") + (perl-mode "perl") + (php-mode "php") + (plone-mode "plone") + (python-mode "python") + (ruby-mode "ruby") + (scheme-mode "scheme") + (shell-script-mode "shell") + (skipped-mode "skipped") + (smalltalk-mode "smalltalk") + (sql-mode "sql") + (tcl-mode "tcl") + (tex-mode "tex") + (tt-mode "tt") + (vb-mode "vb") + (vim-mode "vim") + (xml-mode "xml") + (yaml-mode "yaml")) + "Default values for `ack-and-a-half-mode-type-alist'.") + +(defconst ack-and-a-half-mode-extension-default-alist + '((d-mode "d")) + "Default values for `ack-and-a-half-mode-extension-alist'.") + +(defun ack-and-a-half-create-type (extensions) + (list "--type-set" + (concat "ack-and-a-half-custom-type=" (mapconcat 'identity extensions ",")) + "--type" "ack-and-a-half-custom-type")) + +(defun ack-and-a-half-type-for-major-mode (mode) + "Return the --type and --type-set arguments to use with ack for major mode MODE." + (let ((types (cdr (or (assoc mode ack-and-a-half-mode-type-alist) + (assoc mode ack-and-a-half-mode-type-default-alist)))) + (ext (cdr (or (assoc mode ack-and-a-half-mode-extension-alist) + (assoc mode ack-and-a-half-mode-extension-default-alist)))) + result) + (dolist (type types) + (push type result) + (push "--type" result)) + (if ext + (if types + `("--type-add" ,(concat (car types) + "=" (mapconcat 'identity ext ",")) + . ,result) + (ack-and-a-half-create-type ext)) + result))) + +;;; Project root ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defun ack-and-a-half-guess-project-root () + "Guess the project root directory. +This is intended to be used in `ack-and-a-half-root-directory-functions'." + (catch 'root + (let ((dir (expand-file-name (if buffer-file-name + (file-name-directory buffer-file-name) + default-directory))) + (pattern (mapconcat 'identity ack-and-a-half-project-root-file-patterns "\\|"))) + (while (not (equal dir "/")) + (when (directory-files dir nil pattern t) + (throw 'root dir)) + (setq dir (file-name-directory (directory-file-name dir))))))) + +;;; Commands ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defvar ack-and-a-half-directory-history nil + "Directories recently searched with `ack-and-a-half'.") +(defvar ack-and-a-half-literal-history nil + "Strings recently searched for with `ack-and-a-half'.") +(defvar ack-and-a-half-regexp-history nil + "Regular expressions recently searched for with `ack-and-a-half'.") + +(defsubst ack-and-a-half-read (regexp) + (read-from-minibuffer (if regexp "ack pattern: " "ack literal search: ") + nil nil nil + (if regexp 'ack-and-a-half-regexp-history 'ack-and-a-half-literal-history))) + +(defun ack-and-a-half-read-dir () + (let ((dir (run-hook-with-args-until-success 'ack-and-a-half-root-directory-functions))) + (if ack-and-a-half-prompt-for-directory + (if (and dir (eq ack-and-a-half-prompt-for-directory 'unless-guessed)) + dir + (read-directory-name "Directory: " dir dir t)) + (or dir + (and buffer-file-name (file-name-and-directory buffer-file-name)) + default-directory)))) + +(defsubst ack-and-a-half-xor (a b) + (if a (not b) b)) + +(defun ack-and-a-half-interactive () + "Return the (interactive) arguments for `ack-and-a-half' and `ack-and-a-half-same'." + (let ((regexp (ack-and-a-half-xor current-prefix-arg ack-and-a-half-regexp-search))) + (list (ack-and-a-half-read regexp) + regexp + (ack-and-a-half-read-dir)))) + +(defun ack-and-a-half-type () + (or (ack-and-a-half-type-for-major-mode major-mode) + (when buffer-file-name + (ack-and-a-half-create-type (list (file-name-extension buffer-file-name)))))) + +(defun ack-and-a-half-option (name enabled) + (format "--%s%s" (if enabled "" "no") name)) + +(defun ack-and-a-half-arguments-from-options (regexp) + (let ((arguments (list "--nocolor" "--nogroup" + (ack-and-a-half-option "smart-case" (eq ack-and-a-half-ignore-case 'smart-case)) + (ack-and-a-half-option "env" ack-and-a-half-use-environment)))) + (unless ack-and-a-half-ignore-case + (push "-i" arguments)) + (unless regexp + (push "--literal" arguments)) + arguments)) + +(defun ack-and-a-half-string-replace (from to string &optional re) + "Replace all occurrences of FROM with TO in STRING. +All arguments are strings. +When optional fourth argument is non-nil, treat the from as a regular expression." + (let ((pos 0) + (res "") + (from (if re from (regexp-quote from)))) + (while (< pos (length string)) + (if (setq beg (string-match from string pos)) + (progn + (setq res (concat res + (substring string pos (match-beginning 0)) + to)) + (setq pos (match-end 0))) + (progn + (setq res (concat res (substring string pos (length string)))) + (setq pos (length string))))) + res)) + +(defun ack-and-a-half-shell-quote (string) + "Wrap in single quotes, and quote existing single quotes to make shell safe." + (concat "'" (ack-and-a-half-string-replace "'" "'\\''" string) "'")) + +(defun ack-and-a-half-run (directory regexp &rest arguments) + "Run ack in DIRECTORY with ARGUMENTS." + (setq default-directory + (if directory + (file-name-as-directory (expand-file-name directory)) + default-directory)) + (setq arguments (append ack-and-a-half-arguments + (nconc (ack-and-a-half-arguments-from-options regexp) + arguments))) + (compilation-start (mapconcat 'identity (nconc (list ack-and-a-half-executable) arguments) " ") + 'ack-and-a-half-mode)) + +(defun ack-and-a-half-read-file (prompt choices) + (if ido-mode + (ido-completing-read prompt choices nil t) + (require 'iswitchb) + (with-no-warnings + (let ((iswitchb-make-buflist-hook + (lambda () (setq iswitchb-temp-buflist choices)))) + (iswitchb-read-buffer prompt nil t))))) + +(defun ack-and-a-half-list-files (directory &rest arguments) + (with-temp-buffer + (let ((default-directory directory)) + (when (eq 0 (apply 'call-process ack-and-a-half-executable nil t nil "-f" "--print0" + arguments)) + (goto-char (point-min)) + (let ((beg (point-min)) + files) + (while (re-search-forward "\0" nil t) + (push (buffer-substring beg (match-beginning 0)) files) + (setq beg (match-end 0))) + files))))) + +(defun ack-and-a-half-version-string () + "Return the ack version string." + (with-temp-buffer + (call-process ack-executable nil t nil "--version") + (goto-char (point-min)) + (re-search-forward " +") + (buffer-substring (point) (point-at-eol)))) + +;;; Public interface ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;;###autoload +(defun ack-and-a-half (pattern &optional regexp directory) + "Run ack. +PATTERN is interpreted as a regular expression, iff REGEXP is non-nil. If +called interactively, the value of REGEXP is determined by `ack-and-a-half-regexp-search'. +A prefix arg toggles the behavior. +DIRECTORY is the root directory. If called interactively, it is determined by +`ack-and-a-half-project-root-file-patterns'. The user is only prompted, if +`ack-and-a-half-prompt-for-directory' is set." + (interactive (ack-and-a-half-interactive)) + (ack-and-a-half-run directory regexp (ack-and-a-half-shell-quote pattern))) + +;;;###autoload +(defun ack-and-a-half-same (pattern &optional regexp directory) + "Run ack with --type matching the current `major-mode'. +The types of files searched are determined by `ack-and-a-half-mode-type-alist' and +`ack-and-a-half-mode-extension-alist'. If no type is configured, the buffer's +file extension is used for the search. +PATTERN is interpreted as a regular expression, iff REGEXP is non-nil. If +called interactively, the value of REGEXP is determined by `ack-and-a-half-regexp-search'. +A prefix arg toggles that value. +DIRECTORY is the directory in which to start searching. If called +interactively, it is determined by `ack-and-a-half-project-root-file-patterns`. +The user is only prompted, if `ack-and-a-half-prompt-for-directory' is set.`" + (interactive (ack-and-a-half-interactive)) + (let ((type (ack-and-a-half-type))) + (if type + (apply 'ack-and-a-half-run directory regexp (append type (list (ack-and-a-half-shell-quote pattern)))) + (ack-and-a-half pattern regexp directory)))) + +;;;###autoload +(defun ack-and-a-half-find-file (&optional directory) + "Prompt to find a file found by ack in DIRECTORY." + (interactive (list (ack-and-a-half-read-dir))) + (find-file (expand-file-name + (ack-and-a-half-read-file + "Find file: " + (ack-and-a-half-list-files directory)) + directory))) + +;;;###autoload +(defun ack-and-a-half-find-file-same (&optional directory) + "Prompt to find a file found by ack in DIRECTORY." + (interactive (list (ack-and-a-half-read-dir))) + (find-file (expand-file-name + (ack-and-a-half-read-file + "Find file: " + (apply 'ack-and-a-half-list-files directory (ack-and-a-half-type))) + directory))) + +;;; End ack-and-a-half.el ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(provide 'ack-and-a-half)