From a7cbd1d6035d48ac0f739d77fdb51c9b463d72f5 Mon Sep 17 00:00:00 2001 From: Leo Liu Date: Thu, 4 Oct 2012 22:12:53 +0800 Subject: [PATCH] Sync ack.el and pcmpl-ack.el to version 0.8 --- packages/ack/README.rst | 22 ++++++- packages/ack/ack.el | 128 ++++++++++++++++++++++++++++++-------- packages/ack/pcmpl-ack.el | 126 ++++++++++++++++--------------------- 3 files changed, 176 insertions(+), 100 deletions(-) diff --git a/packages/ack/README.rst b/packages/ack/README.rst index 24d041327..f70234035 100644 --- a/packages/ack/README.rst +++ b/packages/ack/README.rst @@ -1,7 +1,7 @@ ========================================== Emacs Interface to command-line tool ack ========================================== - + From http://betterthangrep.com/ ack is a tool like grep, designed for programmers with large trees @@ -10,6 +10,8 @@ From http://betterthangrep.com/ ack is written purely in Perl, and takes advantage of the power of Perl's regular expressions. +This package is part of `GNU ELPA `_. + Feature requests and bug reports are welcome. Thanks. Features @@ -18,11 +20,19 @@ Features - Neither ``--nogroup`` nor ``--noheading`` is required - Handle colors using the standard library ``ansi-color.el`` - Completion for ack options while reading from the minibuffer +- Support ``git grep``, ``hg grep`` and ``bzr grep`` - Support both emacs 23 and 24 Screenshots ----------- +.. figure:: http://i.imgur.com/mrk8k.png + :width: 400 px + :target: http://i.imgur.com/mrk8k.png + :alt: ack-git-grep.png + + ``git --no-pager grep -n --color 'hg grep'`` + .. figure:: http://i.imgur.com/a72Ap.png :width: 400 px :target: http://i.imgur.com/a72Ap.png @@ -48,8 +58,8 @@ or:: (autoload 'ack "ack" nil t) -Completion -~~~~~~~~~~ +Completion (optional) +~~~~~~~~~~~~~~~~~~~~~ Place ``pcmpl-ack.el`` in the ``load-path`` and add:: @@ -70,8 +80,14 @@ While reading ack command and args from the minibuffer, the following key bindings may be useful: - ``M-I`` => insert a template for case-insensitive file name search +- ``M-G`` => insert a template for ``git grep``, ``hg grep`` or ``bzr grep`` - ``TAB`` => completion for ack options +Bugs +---- + +https://github.com/leoliu/ack-el/issues + Contributors ------------ Phillip Lord diff --git a/packages/ack/ack.el b/packages/ack/ack.el index cddb00657..78bfd8179 100644 --- a/packages/ack/ack.el +++ b/packages/ack/ack.el @@ -3,9 +3,10 @@ ;; Copyright (C) 2012 Leo Liu ;; Author: Leo Liu +;; Version: 0.8 ;; Keywords: tools, processes, convenience ;; Created: 2012-03-24 -;; Version: 0.7 +;; URL: https://github.com/leoliu/ack-el ;; 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 @@ -37,17 +38,6 @@ :group 'tools :group 'processes) -(defcustom ack-project-pattern-list - (list (concat "\\`" (regexp-quote dir-locals-file) "\\'") - "\\`Project\\.ede\\'" - "\\.xcodeproj\\'" ; xcode - "\\`\\.ropeproject\\'" ; python rope - ;; ".git" ".svn" ".hg" ".bzr" ".CVS" - "\\`\\.\\(?:CVS\\|bzr\\|git\\|hg\\|svn\\)\\'") - "A list of regexps that match files in a project root." - :type '(repeat string) - :group 'ack) - ;; Used implicitly by `define-compilation-mode' (defcustom ack-scroll-output nil "Similar to `compilation-scroll-output' but for the *Ack* buffer." @@ -67,6 +57,33 @@ environment variable and ~/.ackrc, which you can disable by the :type 'string :group 'ack) +(defcustom ack-vc-grep-commands + '((".git" . "git --no-pager grep --color -n -i") + (".hg" . "hg grep -n -i") + ;; Plugin bzr-grep required for bzr < 2.6 + (".bzr" . "bzr grep --color=always -n -i")) + "An alist of vc grep commands for `ack-skel-vc-grep'. +Each element is of the form (VC_DIR . CMD)." + :type '(repeat (cons string string)) + :group 'ack) + +(defcustom ack-default-directory-function 'ack-default-directory + "A function to return the default directory for `ack'. +It is called with one arg, the prefix arg to `ack'." + :type 'function + :group 'ack) + +(defcustom ack-project-root-patterns + (list (concat "\\`" (regexp-quote dir-locals-file) "\\'") + "\\`Project\\.ede\\'" + "\\.xcodeproj\\'" ; xcode + "\\`\\.ropeproject\\'" ; python rope + "\\`\\.\\(?:CVS\\|bzr\\|git\\|hg\\|svn\\)\\'") + "A list of regexps to match files in a project root. +Used by `ack-guess-project-root'." + :type '(repeat string) + :group 'ack) + ;;; ======== END of USER OPTIONS ======== (defvar ack-history nil "History list for ack.") @@ -207,6 +224,33 @@ This gets tacked on the end of the generated expressions.") (defvar ack--ansi-color-last-marker) +(defvar ack-process-setup-function 'ack-process-setup) + +(defun ack-process-setup () + ;; Handle `hg grep' output + (when (string-match-p "^[ \t]*hg[ \t]" (car compilation-arguments)) + (setq compilation-error-regexp-alist + '(("^\\(.+?:[0-9]+:\\)\\(?:\\([0-9]+\\):\\)?" 1 2))) + (when (< emacs-major-version 24) + (setq font-lock-keywords (compilation-mode-font-lock-keywords))) + (make-local-variable 'compilation-parse-errors-filename-function) + (setq compilation-parse-errors-filename-function + (lambda (file) + (save-match-data + (if (string-match "\\(.+\\):\\([0-9]+\\):" file) + (match-string 1 file) + file))))) + ;; Handle `bzr grep' output + (when (string-match-p "^[ \t]*bzr[ \t]" (car compilation-arguments)) + (make-local-variable 'compilation-parse-errors-filename-function) + (setq compilation-parse-errors-filename-function + (lambda (file) + (save-match-data + ;; 'bzr grep -r' has files like `termcolor.py~147' + (if (string-match "\\(.+\\)~\\([0-9]+\\)" file) + (match-string 1 file) + file)))))) + (define-compilation-mode ack-mode "Ack" "A compilation mode tailored for ack." (set (make-local-variable 'compilation-disable-input) t) @@ -224,12 +268,30 @@ This gets tacked on the end of the generated expressions.") nil)))))) (defun ack-skel-file () - "Insert a template for case-insensitive filename search." + "Insert a template for case-insensitive file name search." (interactive) (delete-minibuffer-contents) (let ((ack (or (car (split-string ack-command nil t)) "ack"))) (skeleton-insert '(nil ack " -g '(?i:" _ ")'")))) +(defvar project-root) ; dynamically bound in `ack' + +(defun ack-skel-vc-grep () + "Insert a template for vc grep search." + (interactive) + (let* ((regexp (concat "\\`" (regexp-opt + (mapcar 'car ack-vc-grep-commands)) + "\\'")) + (root (or (ack-guess-project-root default-directory regexp) + (error "Cannot locate vc project root"))) + (which (car (directory-files root nil regexp))) + (cmd (or (cdr (assoc which ack-vc-grep-commands)) + (error "No command provided for `%s grep'" + (substring which 1))))) + (setq project-root root) + (delete-minibuffer-contents) + (skeleton-insert '(nil cmd " '" _ "'")))) + (defvar ack-minibuffer-local-map (let ((map (make-sparse-keymap))) (set-keymap-parent map minibuffer-local-map) @@ -237,13 +299,14 @@ This gets tacked on the end of the generated expressions.") 'completion-at-point 'pcomplete)) (define-key map "\M-I" 'ack-skel-file) + (define-key map "\M-G" 'ack-skel-vc-grep) (define-key map "'" 'skeleton-pair-insert-maybe) map) "Keymap used for reading `ack' command and args in minibuffer.") (defun ack-guess-project-root (start-directory &optional regexp) (let ((regexp (or regexp - (mapconcat 'identity ack-project-pattern-list "\\|"))) + (mapconcat 'identity ack-project-root-patterns "\\|"))) (parent (file-name-directory (directory-file-name (expand-file-name start-directory))))) (if (directory-files start-directory nil regexp) @@ -251,26 +314,41 @@ This gets tacked on the end of the generated expressions.") (unless (equal parent start-directory) (ack-guess-project-root parent regexp))))) +(defun ack-default-directory (arg) + "A function for `ack-default-directory-function'. +With no \\[universal-argument], return `default-directory'; +With one \\[universal-argument], find the project root according to +`ack-project-root-patterns'; +Otherwise, interactively choose a directory." + (cond + ((not arg) default-directory) + ((= (prefix-numeric-value arg) 4) + (or (ack-guess-project-root default-directory) + (ack-default-directory '(16)))) + (t (read-directory-name "In directory: " nil nil t)))) + ;;;###autoload (defun ack (command-args &optional directory) "Run ack using COMMAND-ARGS and collect output in a buffer. -With prefix, ask for the DIRECTORY to run ack; otherwise the -current project root is used. +When called interactively, the value of DIRECTORY is provided by +`ack-default-directory-function'. The following keys are available while reading from the minibuffer: \\{ack-minibuffer-local-map}" (interactive - (list (minibuffer-with-setup-hook (if (>= emacs-major-version 24) - 'shell-completion-vars - 'pcomplete-shell-setup) - (read-from-minibuffer "Run ack (like this): " - ack-command ack-minibuffer-local-map - nil 'ack-history)) - (if current-prefix-arg - (read-directory-name "In directory: " nil nil t) - (ack-guess-project-root default-directory)))) + (let ((project-root (funcall ack-default-directory-function + current-prefix-arg)) + ;; Disable completion cycling; see http://debbugs.gnu.org/12221 + (completion-cycle-threshold nil)) + (list (minibuffer-with-setup-hook (if (>= emacs-major-version 24) + 'shell-completion-vars + 'pcomplete-shell-setup) + (read-from-minibuffer "Run ack (like this): " + ack-command ack-minibuffer-local-map + nil 'ack-history)) + project-root))) (let ((default-directory (expand-file-name (or directory default-directory)))) (compilation-start command-args 'ack-mode))) diff --git a/packages/ack/pcmpl-ack.el b/packages/ack/pcmpl-ack.el index ace80d23a..dfac43534 100644 --- a/packages/ack/pcmpl-ack.el +++ b/packages/ack/pcmpl-ack.el @@ -5,6 +5,7 @@ ;; Author: Leo Liu ;; Keywords: tools, processes, convenience ;; Created: 2012-09-26 +;; URL: https://github.com/leoliu/ack-el ;; 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 @@ -30,80 +31,21 @@ ;; Usage: ;; - To complete short options type '-' first ;; - To complete long options type '--' first -;; - Color name completion is also supported following -;; --color-filename=, --color-match= and --color-lineno=. +;; - Color name completion is supported following +;; --color-filename=, --color-match= and --color-lineno= +;; - Type completion is supported following --type= ;;; Code: (require 'pcomplete) -(defvar pcmpl-ack-short-options - (mapconcat (lambda (o) (substring o 1)) - '("-a" "-A" "-B" "-C" "-c" "-f" "-G" "-g" - "-H" "-h" "-i" "-l" "-L" "-m" "-n" - "-o" "-Q" "-r" "-R" - "-u" "-v" "-w" "-1") - "") - "Short options for the `ack' command.") - -(defvar pcmpl-ack-long-options - '("--after-context=" - "--all-types" - "--before-context=" - "--break" - "--nobreak" - "--color" - "--nocolor" - "--colour" - "--nocolour" - "--color-filename=" - "--color-match=" - "--color-lineno=" - "--column" - "--context=" - "--count" - "--env" - "--noenv" - "--files-with-matches" - "--files-without-matches" - "--flush" - "--follow" - "--nofollow" - "--group" - "--nogroup" - "--heading" - "--noheading" - "--ignore-case" - "--ignore-dir=" - "--noignore-dir=" - "--invert-match" - "--line=" - "--literal" - "--match" - "--max-count=" - "--no-filename" - "--output=" - "--pager=" - "--nopager" - "--passthru" - "--print0" - "--recurse" - "--norecurse" - "--smart-case" - "--nosmart-case" - "--sort-files" - "--type=" - "--type-add" - "--type-set" - "--unrestricted" - "--with-filename" - "--word-regexp" - "--help" - "--help-types" - "--man" - "--thpppt" - "--version") - "Long options for the `ack' command.") +(defcustom pcmpl-ack-program + (file-name-nondirectory (or (executable-find "ack-grep") + (executable-find "ack") + "ack")) + "Name of the ack program." + :type 'file + :group 'pcomplete) (defvar pcmpl-ack-color-options '("clear" @@ -131,13 +73,47 @@ "on_white") "Color names for the `ack' command.") +(defun pcmpl-ack-run (buffer &rest args) + "Run ack with ARGS and send the output to BUFFER." + (condition-case nil + (apply 'call-process (or pcmpl-ack-program "ack") nil buffer nil args) + (file-error -1))) + +(defun pcmpl-ack-short-options () + "Short options for the `ack' command." + (with-temp-buffer + (let (options) + (when (zerop (pcmpl-ack-run t "--help")) + (goto-char (point-min)) + (while (re-search-forward "^ -\\([^-]\\)" nil t) + (push (match-string 1) options)) + (mapconcat 'identity (nreverse options) ""))))) + +(defun pcmpl-ack-long-options (&optional arg) + "Long options for the `ack' command." + (with-temp-buffer + (let (options) + (when (zerop (pcmpl-ack-run t (or arg "--help"))) + (goto-char (point-min)) + (while (re-search-forward + "\\(?: ?\\|, \\)\\(--\\(\\[no\\]\\)?\\([[:alnum:]-]+=?\\)\\)" + nil t) + (if (not (match-string 2)) + (push (match-string 1) options) + (push (concat "--" (match-string 3)) options) + (push (concat "--no" (match-string 3)) options))) + (nreverse options))))) + +(defun pcmpl-ack-type-options () + "A list of types for the `ack' command." + (pcmpl-ack-long-options "--help-types")) + ;;;###autoload (defun pcomplete/ack () "Completion for the `ack' command. Start an argument with '-' to complete short options and '--' for long options." ;; No space after = - (add-to-list 'pcomplete-suffix-list ?=) (while t (if (pcomplete-match "^-" 0) (cond @@ -147,9 +123,15 @@ long options." ((pcomplete-match "^--\\(?:no\\)?ignore-dir=\\(\\S-*\\)" 0) (pcomplete-here* (pcomplete-dirs) (pcomplete-match-string 1 0) t)) + ((pcomplete-match "^--type=\\(\\S-*\\)" 0) + (pcomplete-here* (mapcar (lambda (type-option) + (substring type-option 2)) + (pcmpl-ack-type-options)) + (pcomplete-match-string 1 0) t)) ((pcomplete-match "^--" 0) - (pcomplete-here* pcmpl-ack-long-options)) - (t (pcomplete-opt pcmpl-ack-short-options))) + (pcomplete-here* (append (pcmpl-ack-long-options) + (pcmpl-ack-type-options)))) + (t (pcomplete-opt (pcmpl-ack-short-options)))) (pcomplete-here* (pcomplete-dirs-or-entries))))) ;;;###autoload -- 2.39.2