+(defun counsel--gg-candidates (regex)
+ "Return git grep candidates for REGEX."
+ (setq counsel-gg-state -2)
+ (counsel--gg-count regex)
+ (let* ((default-directory counsel--git-grep-dir)
+ (counsel-gg-process " *counsel-gg*")
+ (proc (get-process counsel-gg-process))
+ (buff (get-buffer counsel-gg-process)))
+ (when proc
+ (delete-process proc))
+ (when buff
+ (kill-buffer buff))
+ (setq proc (start-process-shell-command
+ counsel-gg-process
+ counsel-gg-process
+ (concat
+ (format counsel-git-grep-cmd regex)
+ " | head -n 200")))
+ (set-process-sentinel
+ proc
+ #'counsel--gg-sentinel)))
+
+(defun counsel--gg-sentinel (process event)
+ (if (string= event "finished\n")
+ (progn
+ (with-current-buffer (process-buffer process)
+ (setq ivy--all-candidates
+ (or (split-string (buffer-string) "\n" t)
+ '("")))
+ (setq ivy--old-cands ivy--all-candidates))
+ (when (= 0 (cl-incf counsel-gg-state))
+ (ivy--exhibit)))
+ (if (string= event "exited abnormally with code 1\n")
+ (progn
+ (setq ivy--all-candidates '("Error"))
+ (setq ivy--old-cands ivy--all-candidates)
+ (ivy--exhibit)))))
+
+(defun counsel--gg-count (regex &optional no-async)
+ "Quickly and asynchronously count the amount of git grep REGEX matches.
+When NO-ASYNC is non-nil, do it synchronously."
+ (let ((default-directory counsel--git-grep-dir)
+ (cmd
+ (concat
+ (format
+ (replace-regexp-in-string
+ "--full-name" "-c"
+ counsel-git-grep-cmd)
+ ;; "git grep -i -c '%s'"
+ (replace-regexp-in-string
+ "-" "\\\\-"
+ (replace-regexp-in-string "'" "''" regex)))
+ " | sed 's/.*:\\(.*\\)/\\1/g' | awk '{s+=$1} END {print s}'"))
+ (counsel-ggc-process " *counsel-gg-count*"))
+ (if no-async
+ (string-to-number (shell-command-to-string cmd))
+ (let ((proc (get-process counsel-ggc-process))
+ (buff (get-buffer counsel-ggc-process)))
+ (when proc
+ (delete-process proc))
+ (when buff
+ (kill-buffer buff))
+ (setq proc (start-process-shell-command
+ counsel-ggc-process
+ counsel-ggc-process
+ cmd))
+ (set-process-sentinel
+ proc
+ #'(lambda (process event)
+ (when (string= event "finished\n")
+ (with-current-buffer (process-buffer process)
+ (setq ivy--full-length (string-to-number (buffer-string))))
+ (when (= 0 (cl-incf counsel-gg-state))
+ (ivy--exhibit)))))))))
+
+(defun counsel-git-grep-occur ()
+ "Generate a custom occur buffer for `counsel-git-grep'.
+When REVERT is non-nil, regenerate the current *ivy-occur* buffer."
+ (unless (eq major-mode 'ivy-occur-grep-mode)
+ (ivy-occur-grep-mode)
+ (setq default-directory counsel--git-grep-dir))
+ (let ((cands (split-string
+ (shell-command-to-string
+ (format counsel-git-grep-cmd
+ (setq ivy--old-re (ivy--regex ivy-text t))))
+ "\n"
+ t)))
+ ;; Need precise number of header lines for `wgrep' to work.
+ (insert (format "-*- mode:grep; default-directory: %S -*-\n\n\n"
+ default-directory))
+ (insert (format "%d candidates:\n" (length cands)))
+ (ivy--occur-insert-lines
+ (mapcar
+ (lambda (cand) (concat "./" cand))
+ cands))))
+
+(defun counsel-git-grep-query-replace ()
+ "Start `query-replace' with string to replace from last search string."
+ (interactive)
+ (if (null (window-minibuffer-p))
+ (user-error
+ "Should only be called in the minibuffer through `counsel-git-grep-map'")
+ (let* ((enable-recursive-minibuffers t)
+ (from (ivy--regex ivy-text))
+ (to (query-replace-read-to from "Query replace" t)))
+ (ivy-exit-with-action
+ (lambda (_)
+ (let (done-buffers)
+ (dolist (cand ivy--old-cands)
+ (when (string-match "\\`\\(.*?\\):\\([0-9]+\\):\\(.*\\)\\'" cand)
+ (with-ivy-window
+ (let ((file-name (match-string-no-properties 1 cand)))
+ (setq file-name (expand-file-name file-name counsel--git-grep-dir))
+ (unless (member file-name done-buffers)
+ (push file-name done-buffers)
+ (find-file file-name)
+ (goto-char (point-min)))
+ (perform-replace from to t t nil)))))))))))
+
+(defun counsel-git-grep-recenter ()
+ (interactive)
+ (with-ivy-window
+ (counsel-git-grep-action ivy--current)
+ (recenter-top-bottom)))
+
+;;** `counsel-git-stash'
+(defun counsel-git-stash-kill-action (x)
+ (when (string-match "\\([^:]+\\):" x)
+ (kill-new (message (format "git stash apply %s" (match-string 1 x))))))
+
+;;;###autoload
+(defun counsel-git-stash ()
+ "Search through all available git stashes."
+ (interactive)
+ (let ((dir (locate-dominating-file default-directory ".git")))
+ (if (null dir)
+ (error "Not in a git repository")
+ (let ((cands (split-string (shell-command-to-string
+ "IFS=$'\n'
+for i in `git stash list --format=\"%gd\"`; do
+ git stash show -p $i | grep -H --label=\"$i\" \"$1\"
+done") "\n" t)))
+ (ivy-read "git stash: " cands
+ :action 'counsel-git-stash-kill-action
+ :caller 'counsel-git-stash)))))
+;;** `counsel-git-log'
+(defun counsel-git-log-function (input)
+ (if (< (length input) 3)
+ (counsel-more-chars 3)
+ ;; `counsel--yank-pop-format-function' uses this
+ (setq ivy--old-re (funcall ivy--regex-function input))
+ (counsel--async-command
+ ;; "git log --grep" likes to have groups quoted e.g. \(foo\).
+ ;; But it doesn't like the non-greedy ".*?".
+ (format "GIT_PAGER=cat git log --grep '%s'"
+ (replace-regexp-in-string
+ "\\.\\*\\?" ".*"
+ ivy--old-re)))
+ nil))
+
+(defun counsel-git-log-action (x)
+ (message "%S" (kill-new x)))
+
+(defcustom counsel-yank-pop-truncate-radius 2
+ "When non-nil, truncate the display of long strings."
+ :type 'integer
+ :group 'ivy)
+
+;;;###autoload
+(defun counsel-git-log ()
+ "Call the \"git log --grep\" shell command."
+ (interactive)
+ (let ((counsel-async-split-string-re "\ncommit ")
+ (counsel-yank-pop-truncate-radius 5)
+ (ivy-format-function #'counsel--yank-pop-format-function)
+ (ivy-height 4))
+ (ivy-read "Grep log: " #'counsel-git-log-function
+ :dynamic-collection t
+ :action #'counsel-git-log-action
+ :unwind #'counsel-delete-process
+ :caller 'counsel-git-log)))
+
+;;* File
+;;** `counsel-find-file'
+(defvar counsel-find-file-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map (kbd "C-DEL") 'counsel-up-directory)
+ (define-key map (kbd "C-<backspace>") 'counsel-up-directory)
+ map))
+
+(add-to-list 'ivy-ffap-url-functions 'counsel-github-url-p)
+(add-to-list 'ivy-ffap-url-functions 'counsel-emacs-url-p)
+(ivy-set-actions
+ 'counsel-find-file
+ '(("f" find-file-other-window "other window")))
+
+(defcustom counsel-find-file-at-point nil
+ "When non-nil, add file-at-point to the list of candidates."
+ :type 'boolean
+ :group 'ivy)
+
+(defcustom counsel-find-file-ignore-regexp nil
+ "A regexp of files to ignore while in `counsel-find-file'.
+These files are un-ignored if `ivy-text' matches them. The
+common way to show all files is to start `ivy-text' with a dot.
+
+Example value: \"\\(?:\\`[#.]\\)\\|\\(?:[#~]\\'\\)\". This will hide
+temporary and lock files.
+\\<ivy-minibuffer-map>
+Choosing the dotfiles option, \"\\`\\.\", might be convenient,
+since you can still access the dotfiles if your input starts with
+a dot. The generic way to toggle ignored files is \\[ivy-toggle-ignore],
+but the leading dot is a lot faster."
+ :group 'ivy
+ :type '(choice
+ (const :tag "None" nil)
+ (const :tag "Dotfiles" "\\`\\.")
+ (regexp :tag "Regex")))
+
+(defun counsel--find-file-matcher (regexp candidates)
+ "Return REGEXP-matching CANDIDATES.
+Skip some dotfiles unless `ivy-text' requires them."
+ (let ((res (ivy--re-filter regexp candidates)))
+ (if (or (null ivy-use-ignore)
+ (null counsel-find-file-ignore-regexp)
+ (string-match "\\`\\." ivy-text))
+ res
+ (or (cl-remove-if
+ (lambda (x)
+ (and
+ (string-match counsel-find-file-ignore-regexp x)
+ (not (member x ivy-extra-directories))))
+ res)
+ res))))
+
+(declare-function ffap-guesser "ffap")