summaryrefslogtreecommitdiff
path: root/helm-match-plugin.el
diff options
context:
space:
mode:
authorThierry Volpiatto <thierry.volpiatto@gmail.com>2012-03-15 11:41:21 +0100
committerThierry Volpiatto <thierry.volpiatto@gmail.com>2012-03-15 11:41:21 +0100
commitb016418949e60bc5e9cb4f135a17d129b2b2f28a (patch)
treed72b2348a2e4c897bae07c58540562cb29c7fb46 /helm-match-plugin.el
parent9f2a372ad30a377398645aae773848a6de58bd83 (diff)
Forking to emacs-helm
Diffstat (limited to 'helm-match-plugin.el')
-rw-r--r--helm-match-plugin.el677
1 files changed, 677 insertions, 0 deletions
diff --git a/helm-match-plugin.el b/helm-match-plugin.el
new file mode 100644
index 00000000..befc425b
--- /dev/null
+++ b/helm-match-plugin.el
@@ -0,0 +1,677 @@
+;;; helm-match-plugin.el --- Multiple regexp matching methods for helm
+
+;; Author: rubikitch <rubikitch@ruby-lang.org>
+
+;; Maintainers: rubikitch <rubikitch@ruby-lang.org>
+;; Thierry Volpiatto <thierry.volpiatto@gmail.com>
+
+;; Copyright (C) 2008~2012, rubikitch, all rights reserved.
+;; Copyright (C) 2011~2012, Thierry Volpiatto, all rights reserved.
+
+;; Keywords: helm, matching
+;; X-URL: <http://repo.or.cz/w/helm-config.git>
+
+;; This file 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 file 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.
+
+;;; Auto documentation
+;; ------------------
+;;
+;; * User variables
+;; [EVAL] (autodoc-document-lisp-buffer :type 'user-variable :prefix "helm-mp" :var-value t)
+;; `helm-mp-matching-method'
+;; Default Value: multi3
+;; `helm-mp-highlight-delay'
+;; Default Value: 0.7
+;; `helm-mp-highlight-threshold'
+;; Default Value: 2
+;;
+;; * Internal variables
+;; [EVAL] (autodoc-document-lisp-buffer :type 'internal-variable :prefix "helm-mp" :var-value t)
+;; `helm-mp-default-match-functions'
+;; Default Value: (helm-mp-exact-match helm-mp-3-match)
+;; `helm-mp-default-search-functions'
+;; Default Value: (helm-mp-exact-search helm-mp-3-search)
+;; `helm-mp-default-search-backward-functions'
+;; Default Value: (helm-mp-exact-search-backward helm-mp-3-search-backward)
+;; `helm-mp-space-regexp'
+;; Default Value: "[\\ ] "
+;; `helm-mp-exact-pattern-str'
+;; Default Value: "autod"
+;; `helm-mp-exact-pattern-real'
+;; Default Value: "\nautod\n"
+;; `helm-mp-prefix-pattern-str'
+;; Default Value: nil
+;; `helm-mp-prefix-pattern-real'
+;; Default Value: nil
+;; `helm-mp-1-pattern-str'
+;; Default Value: nil
+;; `helm-mp-1-pattern-real'
+;; Default Value: nil
+;; `helm-mp-2-pattern-str'
+;; Default Value: nil
+;; `helm-mp-2-pattern-real'
+;; Default Value: nil
+;; `helm-mp-3-pattern-str'
+;; Default Value: "autod"
+;; `helm-mp-3-pattern-list'
+;; Default Value: ((identity . "autod"))
+;; `helm-mp-initial-highlight-delay'
+;; Default Value: nil
+;;
+;; * Helm match plugin Functions
+;; [EVAL] (autodoc-document-lisp-buffer :type 'function :prefix "helm-mp")
+;; `helm-mp-set-matching-method'
+;; `helm-mp-make-regexps'
+;; `helm-mp-1-make-regexp'
+;; `helm-mp-exact-get-pattern'
+;; `helm-mp-exact-match'
+;; `helm-mp-exact-search'
+;; `helm-mp-exact-search-backward'
+;; `helm-mp-prefix-get-pattern'
+;; `helm-mp-prefix-match'
+;; `helm-mp-prefix-search'
+;; `helm-mp-prefix-search-backward'
+;; `helm-mp-1-get-pattern'
+;; `helm-mp-1-match'
+;; `helm-mp-1-search'
+;; `helm-mp-1-search-backward'
+;; `helm-mp-2-get-pattern'
+;; `helm-mp-2-match'
+;; `helm-mp-2-search'
+;; `helm-mp-2-search-backward'
+;; `helm-mp-3-get-patterns'
+;; `helm-mp-3-get-patterns-internal'
+;; `helm-mp-3-match'
+;; `helm-mp-3-search-base'
+;; `helm-mp-3-search'
+;; `helm-mp-3-search-backward'
+;; `helm-mp-3p-match'
+;; `helm-mp-3p-search'
+;; `helm-mp-3p-search-backward'
+;; `helm-mp-highlight-match'
+;; `helm-mp-highlight-region'
+;; `helm-mp-highlight-match-internal'
+
+;; *** END auto-documentation
+
+;;; Commentary:
+
+;; Change helm.el matching algorithm humanely.
+;; It gives helm.el search refinement functionality.
+;; exact match -> prefix match -> multiple regexp match
+
+;;; Commands:
+;;
+;; Below are complete command list:
+;;
+;;
+;;; Customizable Options:
+;;
+;; Below are customizable option list:
+;;
+;; `helm-grep-candidates-fast-directory-regexp'
+;; *Directory regexp where a RAM disk (or tmpfs) is mounted.
+;; default = nil
+
+;; A query of multiple regexp match is space-delimited string.
+;; Helm displays candidates which matches all the regexps.
+;; A regexp with "!" prefix means not matching the regexp.
+;; To include spaces to a regexp, prefix "\" before space,
+;; it is controlled by `helm-mp-space-regexp' variable.
+
+;; If multiple regexps are specified, first one also tries to match the source name.
+;; If you want to disable this feature, evaluate
+;; (setq helm-mp-match-source-name nil) .
+;; NOTE: This is obsolete and disabled in helm versions >= 1.3.7
+
+;; This file highlights patterns like `occur'. Note that patterns
+;; longer than `helm-mp-highlight-threshold' are highlighted. And
+;; region out of screen is highlighted after
+;; `helm-mp-highlight-delay' seconds.
+;;
+;; Highlight in Emacs is time-consuming process for slow computers. To
+;; disable it is to set nil to `helm-mp-highlight-delay'.
+
+;; helm-match-plugin is enable by default in helm.
+;; To disable/enable it use M-x helm-c-toggle-match-plugin.
+
+;;; Code:
+
+(require 'helm)
+(require 'cl)
+
+
+;;;; Match-plugin
+
+;; Internal
+(defvar helm-mp-default-match-functions nil)
+(defvar helm-mp-default-search-functions nil)
+(defvar helm-mp-default-search-backward-functions nil)
+
+(defun helm-mp-set-matching-method (var key)
+ "Default function to set matching methods in helm match plugin."
+ (set-default var key)
+ (case (symbol-value var)
+ (multi1 (setq helm-mp-default-match-functions
+ '(helm-mp-exact-match helm-mp-1-match)
+ helm-mp-default-search-functions
+ '(helm-mp-exact-search helm-mp-1-search)
+ helm-mp-default-search-backward-functions
+ '(helm-mp-exact-search-backward
+ helm-mp-1-search-backward)))
+ (multi2 (setq helm-mp-default-match-functions
+ '(helm-mp-exact-match helm-mp-2-match)
+ helm-mp-default-search-functions
+ '(helm-mp-exact-search helm-mp-2-search)
+ helm-mp-default-search-backward-functions
+ '(helm-mp-exact-search-backward
+ helm-mp-2-search-backward)))
+ (multi3 (setq helm-mp-default-match-functions
+ '(helm-mp-exact-match helm-mp-3-match)
+ helm-mp-default-search-functions
+ '(helm-mp-exact-search helm-mp-3-search)
+ helm-mp-default-search-backward-functions
+ '(helm-mp-exact-search-backward
+ helm-mp-3-search-backward)))
+ (multi3p (setq helm-mp-default-match-functions
+ '(helm-mp-exact-match helm-mp-3p-match)
+ helm-mp-default-search-functions
+ '(helm-mp-exact-search helm-mp-3p-search)
+ helm-mp-default-search-backward-functions
+ '(helm-mp-exact-search-backward
+ helm-mp-3p-search-backward)))
+ (t (error "Unknow value: %s" helm-mp-matching-method))))
+
+(defgroup helm-match-plugin nil
+ "Helm match plugin."
+ :group 'helm)
+
+(defcustom helm-mp-matching-method 'multi3
+ "Matching method for helm match plugin.
+You can set here different methods to match candidates in helm.
+Here are the possible value of this symbol and their meaning:
+- multi1: Respect order, prefix of pattern must match.
+- multi2: Same but with partial match.
+- multi3: The best, multiple regexp match, allow negation.
+- multi3p: Same but prefix must match.
+Default is multi3."
+ :type '(radio :tag "Matching methods for helm"
+ (const :tag "Multiple regexp 1 ordered with prefix match" multi1)
+ (const :tag "Multiple regexp 2 ordered with partial match" multi2)
+ (const :tag "Multiple regexp 3 matching no order, partial, best." multi3)
+ (const :tag "Multiple regexp 3p matching with prefix match" multi3p))
+ :set 'helm-mp-set-matching-method
+ :group 'helm-match-plugin)
+
+(defface helm-match
+ '((t (:inherit match)))
+ "Face used to highlight matches."
+ :group 'helm-match-plugin)
+
+(defcustom helm-mp-highlight-delay 0.7
+ "Highlight matches with `helm-match' face after this many seconds.
+ If nil, no highlight. "
+ :type 'integer
+ :group 'helm-match-plugin)
+
+(defcustom helm-mp-highlight-threshold 2
+ "Minimum length of pattern to highlight.
+The smaller this value is, the slower highlight is."
+ :type 'integer
+ :group 'helm-match-plugin)
+
+
+
+;;; Build regexps
+;;
+;;
+(defvar helm-mp-space-regexp "[\\ ] "
+ "Regexp to represent space itself in multiple regexp match.")
+
+(defun helm-mp-make-regexps (pattern)
+ "Split PATTERN if it contain spaces and return resulting list.
+If spaces in PATTERN are escaped, don't split at this place.
+i.e \"foo bar\"=> (\"foo\" \"bar\")
+but \"foo\ bar\"=> (\"foobar\")."
+ (if (string= pattern "")
+ '("")
+ (loop for s in (split-string
+ (replace-regexp-in-string helm-mp-space-regexp
+ "\000\000" pattern)
+ " " t)
+ collect (replace-regexp-in-string "\000\000" " " s))))
+
+(defun helm-mp-1-make-regexp (pattern)
+ "Replace spaces in PATTERN with \"\.*\"."
+ (mapconcat 'identity (helm-mp-make-regexps pattern) ".*"))
+
+
+;;; Exact match.
+;;
+;;
+;; Internal.
+(defvar helm-mp-exact-pattern-str nil)
+(defvar helm-mp-exact-pattern-real nil)
+
+(defun helm-mp-exact-get-pattern (pattern)
+ (unless (equal pattern helm-mp-exact-pattern-str)
+ (setq helm-mp-exact-pattern-str pattern
+ helm-mp-exact-pattern-real (concat "\n" pattern "\n")))
+ helm-mp-exact-pattern-real)
+
+
+(defun helm-mp-exact-match (str &optional pattern)
+ (string= str (or pattern helm-pattern)))
+
+(defun helm-mp-exact-search (pattern &rest ignore)
+ (and (search-forward (helm-mp-exact-get-pattern pattern) nil t)
+ (forward-line -1)))
+
+(defun helm-mp-exact-search-backward (pattern &rest ignore)
+ (and (search-backward (helm-mp-exact-get-pattern pattern) nil t)
+ (forward-line 1)))
+
+
+;;; Prefix match
+;;
+;;
+;; Internal
+(defvar helm-mp-prefix-pattern-str nil)
+(defvar helm-mp-prefix-pattern-real nil)
+
+(defun helm-mp-prefix-get-pattern (pattern)
+ (unless (equal pattern helm-mp-prefix-pattern-str)
+ (setq helm-mp-prefix-pattern-str pattern
+ helm-mp-prefix-pattern-real (concat "\n" pattern)))
+ helm-mp-prefix-pattern-real)
+
+(defun helm-mp-prefix-match (str &optional pattern)
+ (setq pattern (or pattern helm-pattern))
+ (let ((len (length pattern)))
+ (and (<= len (length str))
+ (string= (substring str 0 len) pattern ))))
+
+(defun helm-mp-prefix-search (pattern &rest ignore)
+ (search-forward (helm-mp-prefix-get-pattern pattern) nil t))
+
+(defun helm-mp-prefix-search-backward (pattern &rest ignore)
+ (and (search-backward (helm-mp-prefix-get-pattern pattern) nil t)
+ (forward-line 1)))
+
+
+;;; Multiple regexp patterns 1 (order is preserved / prefix).
+;;
+;;
+;; Internal
+(defvar helm-mp-1-pattern-str nil)
+(defvar helm-mp-1-pattern-real nil)
+
+(defun helm-mp-1-get-pattern (pattern)
+ (unless (equal pattern helm-mp-1-pattern-str)
+ (setq helm-mp-1-pattern-str pattern
+ helm-mp-1-pattern-real
+ (concat "^" (helm-mp-1-make-regexp pattern))))
+ helm-mp-1-pattern-real)
+
+(defun* helm-mp-1-match (str &optional (pattern helm-pattern))
+ (string-match (helm-mp-1-get-pattern pattern) str))
+
+(defun helm-mp-1-search (pattern &rest ignore)
+ (re-search-forward (helm-mp-1-get-pattern pattern) nil t))
+
+(defun helm-mp-1-search-backward (pattern &rest ignore)
+ (re-search-backward (helm-mp-1-get-pattern pattern) nil t))
+
+
+;;; Multiple regexp patterns 2 (order is preserved / partial).
+;;
+;;
+;; Internal
+(defvar helm-mp-2-pattern-str nil)
+(defvar helm-mp-2-pattern-real nil)
+
+(defun helm-mp-2-get-pattern (pattern)
+ (unless (equal pattern helm-mp-2-pattern-str)
+ (setq helm-mp-2-pattern-str pattern
+ helm-mp-2-pattern-real
+ (concat "^.*" (helm-mp-1-make-regexp pattern))))
+ helm-mp-2-pattern-real)
+
+(defun* helm-mp-2-match (str &optional (pattern helm-pattern))
+ (string-match (helm-mp-2-get-pattern pattern) str))
+
+(defun helm-mp-2-search (pattern &rest ignore)
+ (re-search-forward (helm-mp-2-get-pattern pattern) nil t))
+
+(defun helm-mp-2-search-backward (pattern &rest ignore)
+ (re-search-backward (helm-mp-2-get-pattern pattern) nil t))
+
+
+;;; Multiple regexp patterns 3 (permutation).
+;;
+;;
+;; Internal
+(defvar helm-mp-3-pattern-str nil)
+(defvar helm-mp-3-pattern-list nil)
+
+(defun helm-mp-3-get-patterns (pattern)
+ "Return `helm-mp-3-pattern-list', a list of predicate/regexp cons cells.
+e.g ((identity . \"foo\") (identity . \"bar\")).
+This is done only if `helm-mp-3-pattern-str' is same as PATTERN."
+ (unless (equal pattern helm-mp-3-pattern-str)
+ (setq helm-mp-3-pattern-str pattern
+ helm-mp-3-pattern-list
+ (helm-mp-3-get-patterns-internal pattern)))
+ helm-mp-3-pattern-list)
+
+(defun helm-mp-3-get-patterns-internal (pattern)
+ "Return a list of predicate/regexp cons cells.
+e.g ((identity . \"foo\") (identity . \"bar\"))."
+ (unless (string= pattern "")
+ (loop for pat in (helm-mp-make-regexps pattern)
+ collect (if (string= "!" (substring pat 0 1))
+ (cons 'not (substring pat 1))
+ (cons 'identity pat)))))
+
+(defun helm-mp-3-match (str &optional pattern)
+ "Check if PATTERN match STR.
+When PATTERN contain a space, it is splitted and matching is done
+with the several resulting regexps against STR.
+e.g \"bar foo\" will match \"foobar\" and \"barfoo\".
+Argument PATTERN, a string, is transformed in a list of
+cons cell with `helm-mp-3-get-patterns' if it contain a space.
+e.g \"foo bar\"=>((identity . \"foo\") (identity . \"bar\")).
+Then each predicate of cons cell(s) is called with regexp of same
+cons cell against STR (a candidate).
+i.e (identity (string-match \"foo\" \"foo bar\")) => t."
+ (let ((pat (helm-mp-3-get-patterns (or pattern helm-pattern))))
+ (loop for (predicate . regexp) in pat
+ always (funcall predicate (string-match regexp str)))))
+
+(defun helm-mp-3-search-base (pattern searchfn1 searchfn2)
+ (loop with pat = (if (stringp pattern)
+ (helm-mp-3-get-patterns pattern)
+ pattern)
+ while (funcall searchfn1 (or (cdar pat) "") nil t)
+ for bol = (point-at-bol)
+ for eol = (point-at-eol)
+ if (loop for (pred . str) in (cdr pat) always
+ (progn (goto-char bol)
+ (funcall pred (funcall searchfn2 str eol t))))
+ do (goto-char eol) and return t
+ else do (goto-char eol)
+ finally return nil))
+
+(defun helm-mp-3-search (pattern &rest ignore)
+ (when (stringp pattern)
+ (setq pattern (helm-mp-3-get-patterns pattern)))
+ (helm-mp-3-search-base
+ pattern 're-search-forward 're-search-forward))
+
+(defun helm-mp-3-search-backward (pattern &rest ignore)
+ (when (stringp pattern)
+ (setq pattern (helm-mp-3-get-patterns pattern)))
+ (helm-mp-3-search-base
+ pattern 're-search-backward 're-search-backward))
+
+
+;;; mp-3p- (multiple regexp pattern 3 with prefix search)
+;;
+;;
+(defun helm-mp-3p-match (str &optional pattern)
+ "Check if PATTERN match STR.
+Same as `helm-mp-3-match' but more strict, matching against prefix also.
+e.g \"bar foo\" will match \"barfoo\" but not \"foobar\" contrarily to
+`helm-mp-3-match'."
+ (let* ((pat (helm-mp-3-get-patterns (or pattern helm-pattern)))
+ (first (car pat)))
+ (and (funcall (car first) (helm-mp-prefix-match str (cdr first)))
+ (loop for (predicate . regexp) in (cdr pat)
+ always (funcall predicate (string-match regexp str))))))
+
+(defun helm-mp-3p-search (pattern &rest ignore)
+ (when (stringp pattern)
+ (setq pattern (helm-mp-3-get-patterns pattern)))
+ (helm-mp-3-search-base
+ pattern 'helm-mp-prefix-search 're-search-forward))
+
+(defun helm-mp-3p-search-backward (pattern &rest ignore)
+ (when (stringp pattern)
+ (setq pattern (helm-mp-3-get-patterns pattern)))
+ (helm-mp-3-search-base
+ pattern 'helm-mp-prefix-search-backward 're-search-backward))
+
+
+;;; source compiler
+;;
+;;
+(defun helm-compile-source--match-plugin (source)
+ (let ((searchers (if (assoc 'search-from-end source)
+ helm-mp-default-search-backward-functions
+ helm-mp-default-search-functions)))
+ `(,(if (or (assoc 'candidates-in-buffer source)
+ (equal '(identity) (assoc-default 'match source)))
+ '(match identity)
+ `(match ,@helm-mp-default-match-functions
+ ,@(assoc-default 'match source)))
+ (search ,@searchers
+ ,@(assoc-default 'search source))
+ ,@source)))
+(add-to-list 'helm-compile-source-functions 'helm-compile-source--match-plugin t)
+
+
+;;; Highlight matches.
+;;
+;;
+(defun helm-mp-highlight-match ()
+ "Highlight matches after `helm-mp-highlight-delay' seconds."
+ (when (and helm-mp-highlight-delay
+ (not (string= helm-pattern "")))
+ (helm-mp-highlight-match-internal (window-end (helm-window)))
+ (run-with-idle-timer helm-mp-highlight-delay nil
+ 'helm-mp-highlight-match-internal
+ (with-current-buffer helm-buffer (point-max)))))
+(add-hook 'helm-update-hook 'helm-mp-highlight-match)
+
+(defun helm-mp-highlight-region (start end regexp face)
+ (save-excursion
+ (goto-char start)
+ (let (me)
+ (while (and (setq me (re-search-forward regexp nil t))
+ (< (point) end)
+ (< 0 (- (match-end 0) (match-beginning 0))))
+ (unless (helm-pos-header-line-p)
+ (put-text-property (match-beginning 0) me 'face face))))))
+
+(defun helm-mp-highlight-match-internal (end)
+ (when (helm-window)
+ (set-buffer helm-buffer)
+ (let ((requote (loop for (pred . re) in
+ (helm-mp-3-get-patterns helm-pattern)
+ when (and (eq pred 'identity)
+ (>= (length re)
+ helm-mp-highlight-threshold))
+ collect re into re-list
+ finally return
+ (if (and re-list (>= (length re-list) 1))
+ (mapconcat 'identity re-list "\\|")
+ (regexp-quote helm-pattern)))))
+ (when (>= (length requote) helm-mp-highlight-threshold)
+ (helm-mp-highlight-region
+ (point-min) end requote 'helm-match)))))
+
+
+;;; Toggle helm-match-plugin
+;;
+;;
+(defvar helm-mp-initial-highlight-delay nil)
+
+;;;###autoload
+(defun helm-mp-toggle-match-plugin ()
+ "Turn on/off multiple regexp matching in helm.
+i.e helm-match-plugin."
+ (interactive)
+ (let ((helm-match-plugin-enabled
+ (member 'helm-compile-source--match-plugin
+ helm-compile-source-functions)))
+ (flet ((disable-match-plugin ()
+ (setq helm-compile-source-functions
+ (delq 'helm-compile-source--match-plugin
+ helm-compile-source-functions))
+ (setq helm-mp-initial-highlight-delay
+ helm-mp-highlight-delay)
+ (setq helm-mp-highlight-delay nil))
+ (enable-match-plugin ()
+ (unless helm-mp-initial-highlight-delay
+ (setq helm-mp-initial-highlight-delay
+ helm-mp-highlight-delay))
+ (setq helm-compile-source-functions
+ (cons 'helm-compile-source--match-plugin
+ helm-compile-source-functions))
+ (unless helm-mp-highlight-delay
+ (setq helm-mp-highlight-delay
+ helm-mp-initial-highlight-delay))))
+ (if helm-match-plugin-enabled
+ (when (y-or-n-p "Really disable match-plugin? ")
+ (disable-match-plugin)
+ (message "Helm-match-plugin disabled"))
+ (when (y-or-n-p "Really enable match-plugin? ")
+ (enable-match-plugin)
+ (message "Helm-match-plugin enabled"))))))
+
+
+;;;; Grep-candidates plug-in
+
+(defcustom helm-grep-candidates-fast-directory-regexp nil
+ "*Directory regexp where a RAM disk (or tmpfs) is mounted.
+
+If non-nil, grep-candidates plugin gets faster because it uses
+grep as synchronous process.
+
+ex. (setq helm-grep-candidates-fast-directory-regexp \"^/tmp/\")"
+ :type 'string
+ :group 'helm)
+
+(defun agp-candidates (&optional filter)
+ "Normal version of grep-candidates candidates function.
+Grep is run by asynchronous process."
+ (start-process-shell-command
+ "helm-grep-candidates" nil
+ (agp-command-line-2 filter (helm-attr-defined 'search-from-end))))
+
+(defun agp-candidates-synchronous-grep (&optional filter)
+ "Faster version of grep-candidates candidates function.
+Grep is run by synchronous process.
+It is faster when candidate files are in ramdisk."
+ (split-string
+ (shell-command-to-string
+ (agp-command-line-2 filter (helm-attr-defined 'search-from-end)))
+ "\n"))
+
+(defun agp-candidates-synchronous-grep--direct-insert-match (&optional filter)
+ "[EXPERIMENTAL]Fastest version of grep-candidates candidates function at the cost of absense of transformers.
+Grep is run by synchronous process.
+It is faster when candidate files are in ramdisk.
+
+If (direct-insert-match) is in the source, this function is used."
+ (with-current-buffer (helm-candidate-buffer 'global)
+ (call-process-shell-command
+ (agp-command-line-2 filter (helm-attr-defined 'search-from-end))
+ nil t)))
+
+(defun agp-command-line (query files &optional limit filter search-from-end)
+ "Build command line used by grep-candidates from QUERY, FILES, LIMIT, and FILTER."
+ (let ((allfiles (mapconcat (lambda (f) (shell-quote-argument (expand-file-name f)))
+ files " ")))
+ (with-temp-buffer
+ (when search-from-end
+ (insert "tac " allfiles))
+ (if (string= query "")
+ (unless search-from-end
+ (insert "cat " allfiles))
+ (when search-from-end (insert " | "))
+ (loop for (flag . re) in (helm-mp-3-get-patterns-internal query)
+ for i from 0
+ do
+ (setq re (replace-regexp-in-string "^-" "\\-" re))
+ (unless (zerop i) (insert " | "))
+ (insert "grep -ih "
+ (if (eq flag 'identity) "" "-v ")
+ (shell-quote-argument re))
+ (when (and (not search-from-end) (zerop i))
+ (insert " " allfiles))))
+
+ (when limit (insert (format " | head -n %d" limit)))
+ (when filter (insert " | " filter))
+ (buffer-string))))
+
+(defun agp-command-line-2 (filter &optional search-from-end)
+ "Build command line used by grep-candidates from FILTER and current source."
+ (agp-command-line
+ helm-pattern
+ (helm-mklist (helm-interpret-value (helm-attr 'grep-candidates)))
+ (helm-candidate-number-limit (helm-get-current-source))
+ filter search-from-end))
+
+(defun helm-compile-source--grep-candidates (source)
+ (helm-aif (assoc-default 'grep-candidates source)
+ (append
+ source
+ (let ((use-fast-directory
+ (and helm-grep-candidates-fast-directory-regexp
+ (string-match
+ helm-grep-candidates-fast-directory-regexp
+ (or (car (helm-mklist (helm-interpret-value it))) "")))))
+ (cond ((not (helm-interpret-value it)) nil)
+ ((and use-fast-directory (assq 'direct-insert-match source))
+ (helm-log "fastest version (use-fast-directory and direct-insert-match)")
+ `((candidates . agp-candidates-synchronous-grep--direct-insert-match)
+ (match identity)
+ (volatile)))
+ (use-fast-directory
+ (helm-log "faster version (use-fast-directory)")
+ `((candidates . agp-candidates-synchronous-grep)
+ (match identity)
+ (volatile)))
+ (t
+ (helm-log "normal version")
+ '((candidates . agp-candidates)
+ (delayed))))))
+ source))
+(add-to-list 'helm-compile-source-functions 'helm-compile-source--grep-candidates)
+
+(helm-document-attribute 'grep-candidates "grep-candidates plug-in"
+ "grep-candidates plug-in provides helm-match-plugin.el feature with grep and head program.
+It is MUCH FASTER than normal match-plugin to search from vary large (> 1MB) candidates.
+Make sure to install these programs.
+
+It expands `candidates' and `delayed' attributes.
+
+`grep-candidates' attribute accepts a filename or list of filename.
+It also accepts 0-argument function name or variable name.")
+
+;; (helm '(((name . "grep-test") (grep-candidates . "~/.emacs.el") (action . message))))
+;; (let ((a "~/.emacs.el")) (helm '(((name . "grep-test") (grep-candidates . a) (action . message) (delayed)))))
+;; (let ((a "~/.emacs.el")) (helm '(((name . "grep-test") (grep-candidates . (lambda () a)) (action . message) (delayed)))))
+;; (helm '(((name . "grep-test") (grep-candidates . "~/.emacs.el") (action . message) (delayed) (candidate-number-limit . 2))))
+;; (let ((helm-candidate-number-limit 2)) (helm '(((name . "grep-test") (grep-candidates . "~/.emacs.el") (action . message) (delayed)))))
+
+;;;; unit test
+;; unit test for match plugin are now in developper-tools/unit-test-match-plugin.el
+
+(provide 'helm-match-plugin)
+
+;;; helm-match-plugin.el ends here