mirror of https://github.com/Chizi123/.emacs.d.git

Chizi123
2018-11-19 a4b9172aefa91861b587831e06f55b1e19f3f3be
commit | author | age
5cb5f7 1 ;;; helm-projectile.el --- Helm integration for Projectile         -*- lexical-binding: t; -*-
C 2
3 ;; Copyright (C) 2011-2016 Bozhidar Batsov
4
5 ;; Author: Bozhidar Batsov
6 ;; URL: https://github.com/bbatsov/helm-projectile
7 ;; Package-Version: 20180815.1514
8 ;; Created: 2011-31-07
9 ;; Keywords: project, convenience
10 ;; Version: 0.14.0
11 ;; Package-Requires: ((helm "1.9.9") (projectile "0.14.0") (cl-lib "0.3"))
12
13 ;; This file is NOT part of GNU Emacs.
14
15 ;;; License:
16
17 ;; This program is free software; you can redistribute it and/or modify
18 ;; it under the terms of the GNU General Public License as published by
19 ;; the Free Software Foundation; either version 3, or (at your option)
20 ;; any later version.
21 ;;
22 ;; This program is distributed in the hope that it will be useful,
23 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
24 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 ;; GNU General Public License for more details.
26 ;;
27 ;; You should have received a copy of the GNU General Public License
28 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
29 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
30 ;; Boston, MA 02110-1301, USA.
31
32 ;;; Commentary:
33 ;;
34 ;; This library provides easy project management and navigation.  The
35 ;; concept of a project is pretty basic - just a folder containing
36 ;; special file.  Currently git, mercurial and bazaar repos are
37 ;; considered projects by default.  If you want to mark a folder
38 ;; manually as a project just create an empty .projectile file in
39 ;; it.  See the README for more details.
40 ;;
41 ;;; Code:
42
43 (require 'subr-x)
44 (require 'projectile)
45 (require 'cl-lib)
46 (require 'grep)
47 (require 'helm)
48 (require 'helm-types)
49 (require 'helm-locate)
50 (require 'helm-buffers)
51 (require 'helm-files)
52
53 (declare-function eshell "eshell")
54 (declare-function helm-do-ag "helm-ag")
55 (declare-function dired-get-filename "dired")
56 (defvar helm-ag-base-command)
57
58 (defvar grep-find-ignored-directories)
59 (defvar grep-find-ignored-files)
60
61 (defgroup helm-projectile nil
62   "Helm support for projectile."
63   :prefix "helm-projectile-"
64   :group 'projectile
65   :group 'helm
66   :link `(url-link :tag "GitHub" "https://github.com/bbatsov/helm-projectile"))
67
68 (defvar helm-projectile-current-project-root)
69
70 (defcustom helm-projectile-truncate-lines nil
71   "Truncate lines in helm projectile commands when non--nil.
72
73 Some `helm-projectile' commands have similar behavior with existing
74 Helms.  In these cases their respective custom var for truncation
75 of lines will be honored.  E.g. `helm-buffers-truncate-lines'
76 dictates the truncation in `helm-projectile-switch-to-buffer'."
77   :group 'helm-projectile
78   :type 'boolean)
79
80 ;;;###autoload
81 (defcustom helm-projectile-fuzzy-match t
82   "Enable fuzzy matching for Helm Projectile commands.
83 This needs to be set before loading helm-projectile.el."
84   :group 'helm-projectile
85   :type 'boolean)
86
87 (defmacro helm-projectile-define-key (keymap key def &rest bindings)
88   "In KEYMAP, define KEY - DEF sequence KEY1 as DEF1, KEY2 as DEF2 ..."
89   (declare (indent defun))
90   (let ((ret '(progn)))
91     (while key
92       (push
93        `(define-key ,keymap ,key
94           (lambda ()
95             (interactive)
96             (helm-exit-and-execute-action ,def)))
97        ret)
98       (setq key (pop bindings)
99             def (pop bindings)))
100     (reverse ret)))
101
102 (defun helm-projectile-hack-actions (actions &rest prescription)
103   "Given a Helm action list and a prescription, return a hacked Helm action list, after applying the PRESCRIPTION.
104
105 The Helm action list ACTIONS is of the form:
106
107 \(\(DESCRIPTION1 . FUNCTION1\)
108  \(DESCRIPTION2 . FUNCTION2\)
109  ...
110  \(DESCRIPTIONn . FUNCTIONn\)\)
111
112 PRESCRIPTION is in the form:
113
114 \(INSTRUCTION1 INSTRUCTION2 ... INSTRUCTIONn\)
115
116 If an INSTRUCTION is a symbol, the action with function name
117 INSTRUCTION is deleted.
118
119 If an INSTRUCTION is of the form \(FUNCTION1 . FUNCTION2\), the
120 action with function name FUNCTION1 will change it's function to
121 FUNCTION2.
122
123 If an INSTRUCTION is of the form \(FUNCTION . DESCRIPTION\), and
124 if an action with function name FUNCTION exists in the original
125 Helm action list, the action in the Helm action list, with
126 function name FUNCTION will change it's description to
127 DESCRIPTION.  Otherwise, (FUNCTION . DESCRIPTION) will be added to
128 the action list.
129
130 Please check out how `helm-projectile-file-actions' is defined
131 for an example of how this function is being used."
132   (let* ((to-delete (cl-remove-if (lambda (entry) (listp entry)) prescription))
133          (actions (cl-delete-if (lambda (action) (memq (cdr action) to-delete))
134                                 (copy-alist actions)))
135          new)
136     (cl-dolist (action actions)
137       (when (setq new (cdr (assq (cdr action) prescription)))
138         (if (stringp new)
139             (setcar action new)
140           (setcdr action new))))
141     ;; Add new actions from PRESCRIPTION
142     (setq new nil)
143     (cl-dolist (instruction prescription)
144       (when (and (listp instruction)
145                  (null (rassq (car instruction) actions))
146                  (symbolp (car instruction)) (stringp (cdr instruction)))
147         (push (cons (cdr instruction) (car instruction)) new)))
148     (append actions (nreverse new))))
149
150 (defun helm-projectile-vc (dir)
151   "A Helm action for jumping to project root using `vc-dir' or Magit.
152 DIR is a directory to be switched"
153   (let ((projectile-require-project-root nil))
154     (projectile-vc dir)))
155
156 (defun helm-projectile-compile-project (dir)
157   "A Helm action for compile a project.
158 DIR is the project root."
159   (let ((helm--reading-passwd-or-string t)
160         (default-directory dir))
161     (projectile-compile-project helm-current-prefix-arg)))
162
163 (defun helm-projectile-test-project (dir)
164   "A Helm action for test a project.
165 DIR is the project root."
166   (let ((helm--reading-passwd-or-string t)
167         (default-directory dir))
168     (projectile-test-project helm-current-prefix-arg)))
169
170 (defun helm-projectile-run-project (dir)
171   "A Helm action for run a project.
172 DIR is the project root."
173   (let ((helm--reading-passwd-or-string t)
174         (default-directory dir))
175     (projectile-run-project helm-current-prefix-arg)))
176
177 (defun helm-projectile-remove-known-project (_ignore)
178   "Remove selected projects from projectile project list.
179 _IGNORE means the argument does not matter.
180 It is there because Helm requires it."
181   (let* ((projects (helm-marked-candidates :with-wildcard t))
182          (len (length projects)))
183     (with-helm-display-marked-candidates
184       helm-marked-buffer-name
185       projects
186       (if (not (y-or-n-p (format "Remove *%s projects(s)? " len)))
187           (message "(No removal performed)")
188         (progn
189           (mapc (lambda (p)
190                   (delete p projectile-known-projects))
191                 projects)
192           (projectile-save-known-projects))
193         (message "%s projects(s) removed" len)))))
194
195 (defvar helm-projectile-projects-map
196   (let ((map (make-sparse-keymap)))
197     (set-keymap-parent map helm-map)
198     (helm-projectile-define-key map
199       (kbd "C-d") #'dired
200       (kbd "M-g") #'helm-projectile-vc
201       (kbd "M-e") #'helm-projectile-switch-to-eshell
202       (kbd "C-s") #'helm-projectile-grep
203       (kbd "M-c") #'helm-projectile-compile-project
204       (kbd "M-t") #'helm-projectile-test-project
205       (kbd "M-r") #'helm-projectile-run-project
206       (kbd "M-D") #'helm-projectile-remove-known-project)
207     map)
208   "Mapping for known projectile projects.")
209
210 (defcustom helm-source-projectile-projects-actions
211   (helm-make-actions
212    "Switch to project" (lambda (project)
213                          (let ((projectile-completion-system 'helm))
214                            (projectile-switch-project-by-name project)))
215    "Open Dired in project's directory `C-d'" #'dired
216    "Open project root in vc-dir or magit `M-g'" #'helm-projectile-vc
217    "Switch to Eshell `M-e'" #'helm-projectile-switch-to-eshell
218    "Grep in projects `C-s'" #'helm-projectile-grep
219    "Compile project `M-c'. With C-u, new compile command" #'helm-projectile-compile-project
220    "Remove project(s) from project list `M-D'" #'helm-projectile-remove-known-project)
221   "Actions for `helm-source-projectile-projects'."
222   :group 'helm-projectile
223   :type '(alist :key-type string :value-type function))
224
225 (defvar helm-source-projectile-projects
226   (helm-build-sync-source "Projectile projects"
227     :candidates (lambda () (with-helm-current-buffer projectile-known-projects))
228     :fuzzy-match helm-projectile-fuzzy-match
229     :keymap helm-projectile-projects-map
230     :mode-line helm-read-file-name-mode-line-string
231     :action 'helm-source-projectile-projects-actions)
232   "Helm source for known projectile projects.")
233
234 (defvar helm-projectile-dirty-projects-map
235   (let ((map (make-sparse-keymap)))
236     (set-keymap-parent map helm-map)
237     (helm-projectile-define-key map
238       (kbd "C-d") #'dired
239       (kbd "M-o") #'(lambda (project)
240                       (let ((projectile-completion-system 'helm))
241                         (projectile-switch-project-by-name project)))
242       (kbd "M-e") #'helm-projectile-switch-to-eshell
243       (kbd "C-s") #'helm-projectile-grep
244       (kbd "M-c") #'helm-projectile-compile-project
245       (kbd "M-t") #'helm-projectile-test-project
246       (kbd "M-r") #'helm-projectile-run-project
247       (kbd "M-D") #'helm-projectile-remove-known-project)
248     map)
249   "Mapping for dirty projectile projects.")
250
251 (defvar helm-source-projectile-dirty-projects
252   (helm-build-sync-source "Projectile dirty projects"
253     :candidates (lambda () (with-helm-current-buffer (helm-projectile-get-dirty-projects)))
254     :fuzzy-match helm-projectile-fuzzy-match
255     :keymap helm-projectile-dirty-projects-map
256     :mode-line helm-read-file-name-mode-line-string
257     :action '(("Open project root in vc-dir or magit" . helm-projectile-vc)
258               ("Switch to project `M-o'" .
259                (lambda (project)
260                  (let ((projectile-completion-system 'helm))
261                    (projectile-switch-project-by-name project))))
262               ("Open Dired in project's directory `C-d'" . dired)
263               ("Switch to Eshell `M-e'" . helm-projectile-switch-to-eshell)
264               ("Grep in projects `C-s'" . helm-projectile-grep)
265               ("Compile project `M-c'. With C-u, new compile command"
266                . helm-projectile-compile-project)))
267     "Helm source for dirty version controlled projectile projects.")
268
269 (defun helm-projectile-get-dirty-projects ()
270   "Return dirty version controlled known projects as an alist to
271 have a nice display in Helm."
272   (message "Checking for dirty known projects...")
273   (let* ((status (projectile-check-vcs-status-of-known-projects))
274          (proj-dir (cl-loop for stat in status
275                             collect (car stat)))
276          (status-display (cl-loop for stat in status collect
277                                   (propertize (format "[%s]" (mapconcat 'identity (car (cdr stat)) ", ")) 'face 'helm-match)))
278          (max-status-display-length (cl-loop for sd in status-display
279                                              maximize (length sd)))
280          (status-display (cl-loop for sd in status-display collect
281                                   (format "%s%s    " sd (make-string (- max-status-display-length (length sd)) ? ))))
282          (full-display (cl-mapcar 'concat status-display proj-dir))
283          (helm-list (cl-pairlis full-display proj-dir)))
284     helm-list))
285
286 (define-key helm-etags-map (kbd "C-c p f")
287   (lambda ()
288     (interactive)
289     (helm-run-after-exit 'helm-projectile-find-file nil)))
290
291 (defun helm-projectile-file-persistent-action (candidate)
292   "Persistent action for file-related functionality.
293
294 Previews the contents of a file in a temporary buffer."
295   (let ((buf (get-buffer-create " *helm-projectile persistent*")))
296     (cl-flet ((preview (candidate)
297                        (switch-to-buffer buf)
298                        (setq inhibit-read-only t)
299                        (erase-buffer)
300                        (insert-file-contents candidate)
301                        (let ((buffer-file-name candidate))
302                          (set-auto-mode))
303                        (font-lock-ensure)
304                        (setq inhibit-read-only nil)))
305       (if (and (helm-attr 'previewp)
306                (string= candidate (helm-attr 'current-candidate)))
307           (progn
308             (kill-buffer buf)
309             (helm-attrset 'previewp nil))
310         (preview candidate)
311         (helm-attrset 'previewp t)))
312     (helm-attrset 'current-candidate candidate)))
313
314 (defun helm-projectile-find-files-eshell-command-on-file-action (candidate)
315   (interactive)
316   (let* ((helm-ff-default-directory (file-name-directory candidate)))
317     (helm-find-files-eshell-command-on-file candidate)))
318
319 (defun helm-projectile-ff-etags-select-action (candidate)
320   (interactive)
321   (let* ((helm-ff-default-directory (file-name-directory candidate)))
322     (helm-ff-etags-select candidate)))
323
324 (defun helm-projectile-switch-to-eshell (dir)
325   (interactive)
326   (let* ((projectile-require-project-root nil)
327          (helm-ff-default-directory (file-name-directory (projectile-expand-root dir))))
328     (helm-ff-switch-to-eshell dir)))
329
330 (defun helm-projectile-files-in-current-dired-buffer ()
331   "Return a list of files (only) in the current dired buffer."
332   (let (flist)
333     (cl-flet ((fpush (fname) (push fname flist)))
334       (save-excursion
335         (let (file buffer-read-only)
336           (goto-char (point-min))
337           (while (not (eobp))
338             (save-excursion
339               (and (not (eolp))
340                    (setq file (dired-get-filename t t)) ; nil on non-file
341                    (progn (end-of-line)
342                           (funcall #'fpush file))))
343             (forward-line 1)))))
344     (mapcar 'file-truename (nreverse flist))))
345
346 (defun helm-projectile-all-dired-buffers ()
347   "Get all current Dired buffers."
348   (mapcar (lambda (b)
349             (with-current-buffer b (buffer-name)))
350           (cl-remove-if-not
351            (lambda (b)
352              (with-current-buffer b
353                (and (eq major-mode 'dired-mode)
354                     (buffer-name))))
355            (buffer-list))))
356
357 (defvar helm-projectile-virtual-dired-remote-enable nil
358   "Enable virtual Dired manager on remote host.
359 Disabled by default.")
360
361 (defun helm-projectile-dired-files-new-action (candidate)
362   "Create a Dired buffer from chosen files.
363 CANDIDATE is the selected file, but choose the marked files if available."
364   (if (and (file-remote-p (projectile-project-root))
365            (not helm-projectile-virtual-dired-remote-enable))
366       (message "Virtual Dired manager is disabled in remote host. Enable with %s."
367                (propertize "helm-projectile-virtual-dired-remote-enable" 'face 'font-lock-keyword-face))
368     (let ((files (cl-remove-if-not
369                   (lambda (f)
370                     (not (string= f "")))
371                   (mapcar (lambda (file)
372                             (replace-regexp-in-string (projectile-project-root) "" file))
373                           (helm-marked-candidates :with-wildcard t))))
374           (new-name (completing-read "Select or enter a new buffer name: "
375                                      (helm-projectile-all-dired-buffers)))
376           (helm--reading-passwd-or-string t)
377           (default-directory (projectile-project-root)))
378       ;; create a unique buffer that is unique to any directory in default-directory
379       ;; or opened buffer; when Dired is passed with a non-existence directory name,
380       ;; it only creates a buffer and insert everything. If a new name user supplied
381       ;; exists as default-directory, Dired throws error when insert anything that
382       ;; does not exist in current directory.
383       (with-current-buffer (dired (cons (make-temp-name new-name)
384                                         (if files
385                                             files
386                                           (list candidate))))
387         (when (get-buffer new-name)
388           (kill-buffer new-name))
389         (rename-buffer new-name)))))
390
391 (defun helm-projectile-dired-files-add-action (candidate)
392   "Add files to a Dired buffer.
393 CANDIDATE is the selected file.  Used when no file is explicitly marked."
394   (if (and (file-remote-p (projectile-project-root))
395            (not helm-projectile-virtual-dired-remote-enable))
396       (message "Virtual Dired manager is disabled in remote host. Enable with %s."
397                (propertize "helm-projectile-virtual-dired-remote-enable" 'face 'font-lock-keyword-face))
398     (if (eq (with-helm-current-buffer major-mode) 'dired-mode)
399         (let* ((marked-files (helm-marked-candidates :with-wildcard t))
400                (helm--reading-passwd-or-string t)
401                (root (projectile-project-root)) ; store root for later use
402                (dired-buffer-name (or (and (eq major-mode 'dired-mode) (buffer-name))
403                                       (completing-read "Select a Dired buffer:"
404                                                        (helm-projectile-all-dired-buffers))))
405                (dired-files (with-current-buffer dired-buffer-name
406                               (helm-projectile-files-in-current-dired-buffer)))
407                (files (sort (mapcar (lambda (file)
408                                       (replace-regexp-in-string (projectile-project-root) "" file))
409                                     (cl-nunion (if marked-files
410                                                    marked-files
411                                                  (list candidate))
412                                                dired-files
413                                                :test #'string-equal))
414                             'string-lessp)))
415           (kill-buffer dired-buffer-name)
416           ;; Rebind default-directory because after killing a buffer, we
417           ;; could be in any buffer and default-directory is set to that
418           ;; random buffer
419           ;;
420           ;; Also use saved root directory, because after killing a buffer,
421           ;; we could be outside of current project
422           (let ((default-directory root))
423             (with-current-buffer (dired (cons (make-temp-name dired-buffer-name)
424                                               (if files
425                                                   (mapcar (lambda (file)
426                                                             (replace-regexp-in-string root "" file))
427                                                           files)
428                                                 (list candidate))))
429               (rename-buffer dired-buffer-name))))
430       (error "You're not in a Dired buffer to add"))))
431
432 (defun helm-projectile-dired-files-delete-action (candidate)
433   "Delete selected entries from a Dired buffer.
434 CANDIDATE is the selected file.  Used when no file is explicitly marked."
435   (if (and (file-remote-p (projectile-project-root))
436            (not helm-projectile-virtual-dired-remote-enable))
437       (message "Virtual Dired manager is disabled in remote host. Enable with %s."
438                (propertize "helm-projectile-virtual-dired-remote-enable" 'face 'font-lock-keyword-face))
439     (let* ((helm--reading-passwd-or-string t)
440            (root (projectile-project-root))
441            (dired-buffer-name (with-helm-current-buffer (buffer-name)))
442            (dired-files (with-current-buffer dired-buffer-name
443                           (helm-projectile-files-in-current-dired-buffer)))
444            (files (sort (cl-set-exclusive-or (helm-marked-candidates :with-wildcard t)
445                                              dired-files
446                                              :test #'string-equal) #'string-lessp)))
447       (kill-buffer dired-buffer-name)
448       ;; similar reason to `helm-projectile-dired-files-add-action'
449       (let ((default-directory root))
450         (with-current-buffer (dired (cons (make-temp-name dired-buffer-name)
451                                           (if files
452                                               (mapcar (lambda (file)
453                                                         (replace-regexp-in-string root "" file))
454                                                       files)
455                                             (list candidate))))
456           (rename-buffer dired-buffer-name))))))
457
458 (defun helm-projectile-run-projectile-hooks-after-find-file (_orig-fun &rest _args)
459   "Run `projectile-find-file-hook' if using projectile."
460   (when (and projectile-mode (projectile-project-p))
461     (run-hooks 'projectile-find-file-hook)))
462
463 (advice-add 'helm-find-file-or-marked
464             :after #'helm-projectile-run-projectile-hooks-after-find-file)
465
466 (defvar helm-projectile-find-file-map
467   (let ((map (copy-keymap helm-find-files-map)))
468     (helm-projectile-define-key map
469       (kbd "C-c f") #'helm-projectile-dired-files-new-action
470       (kbd "C-c a") #'helm-projectile-dired-files-add-action
471       (kbd "M-e") #'helm-projectile-switch-to-eshell
472       (kbd "M-.") #'helm-projectile-ff-etags-select-action
473       (kbd "M-!") #'helm-projectile-find-files-eshell-command-on-file-action)
474     (define-key map (kbd "<left>") #'helm-previous-source)
475     (define-key map (kbd "<right>") #'helm-next-source)
476     (dolist (cmd '(helm-find-files-up-one-level
477                    helm-find-files-down-last-level))
478       (substitute-key-definition cmd nil map))
479     map)
480   "Mapping for file commands in Helm Projectile.")
481
482 (defvar helm-projectile-file-actions
483   (helm-projectile-hack-actions
484    helm-find-files-actions
485    ;; Delete these actions
486    'helm-ff-browse-project
487    'helm-insert-file-name-completion-at-point
488    'helm-ff-find-sh-command
489    'helm-ff-cache-add-file
490    ;; Substitute these actions
491    '(helm-ff-switch-to-eshell . helm-projectile-switch-to-eshell)
492    '(helm-ff-etags-select     . helm-projectile-ff-etags-select-action)
493    '(helm-find-files-eshell-command-on-file
494      . helm-projectile-find-files-eshell-command-on-file-action)
495    ;; Change action descriptions
496    '(helm-find-file-as-root . "Find file as root `C-c r'")
497    ;; New actions
498    '(helm-projectile-dired-files-new-action
499      . "Create Dired buffer from files `C-c f'")
500    '(helm-projectile-dired-files-add-action
501      . "Add files to Dired buffer `C-c a'"))
502   "Action for files.")
503
504 (defun helm-projectile--move-selection-p (selection)
505   "Return non-nil if should move Helm selector after SELECTION.
506
507 SELECTION should be moved unless it's one of:
508
509 - Non-string
510 - Existing file
511 - Non-remote file that matches `helm-tramp-file-name-regexp'"
512   (not (or (not (stringp selection))
513          (file-exists-p selection)
514          (and (string-match helm-tramp-file-name-regexp selection)
515               (not (file-remote-p selection nil t))))))
516
517 (defun helm-projectile--move-to-real ()
518   "Move to first real candidate.
519
520 Similar to `helm-ff--move-to-first-real-candidate', but without
521 unnecessary complexity."
522   (while (let* ((src (helm-get-current-source))
523                 (selection (and (not (helm-empty-source-p))
524                                 (helm-get-selection nil nil src))))
525            (and (not (helm-end-of-source-p))
526                 (helm-projectile--move-selection-p selection)))
527     (helm-next-line)))
528
529 (defun helm-projectile--remove-move-to-real ()
530   "Hook function to remove `helm-projectile--move-to-real'.
531
532 Meant to be added to `helm-cleanup-hook', from which it removes
533  itself at the end."
534   (remove-hook 'helm-after-update-hook #'helm-projectile--move-to-real)
535   (remove-hook 'helm-cleanup-hook #'helm-projectile--remove-move-to-real))
536
537 (defvar helm-source-projectile-files-list
538   (helm-build-sync-source "Projectile files"
539     :before-init-hook (lambda ()
540                         (add-hook 'helm-after-update-hook #'helm-projectile--move-to-real)
541                         (add-hook 'helm-cleanup-hook #'helm-projectile--remove-move-to-real))
542     :candidates (lambda ()
543                   (when (projectile-project-p)
544                     (with-helm-current-buffer
545                       (cl-loop with root = (projectile-project-root)
546                                for display in (projectile-current-project-files)
547                                collect (cons display (expand-file-name display root))))))
548     :filtered-candidate-transformer
549     (lambda (files _source)
550       (with-helm-current-buffer
551         (let* ((root (projectile-project-root))
552                (file-at-root (file-relative-name (expand-file-name helm-pattern root))))
553           (if (or (string-empty-p helm-pattern)
554                   (assoc helm-pattern files))
555               files
556             (if (equal helm-pattern file-at-root)
557                 (cl-acons (helm-ff-prefix-filename helm-pattern nil t)
558                           (expand-file-name helm-pattern)
559                           files)
560               (cl-pairlis (list (helm-ff-prefix-filename helm-pattern nil t)
561                                 (helm-ff-prefix-filename file-at-root nil t))
562                           (list (expand-file-name helm-pattern)
563                                 (expand-file-name helm-pattern root))
564                           files))))))
565     :fuzzy-match helm-projectile-fuzzy-match
566     :keymap helm-projectile-find-file-map
567     :help-message 'helm-ff-help-message
568     :mode-line helm-read-file-name-mode-line-string
569     :action helm-projectile-file-actions
570     :persistent-action #'helm-projectile-file-persistent-action
571     :persistent-help "Preview file")
572   "Helm source definition for Projectile files.")
573
574 (defvar helm-source-projectile-files-in-all-projects-list
575   (helm-build-sync-source "Projectile files in all Projects"
576     :candidates (lambda ()
577                   (with-helm-current-buffer
578                     (let ((projectile-require-project-root nil))
579                       (projectile-all-project-files))))
580     :keymap helm-projectile-find-file-map
581     :help-message 'helm-ff-help-message
582     :mode-line helm-read-file-name-mode-line-string
583     :action helm-projectile-file-actions
584     :persistent-action #'helm-projectile-file-persistent-action
585     :persistent-help "Preview file")
586   "Helm source definition for all Projectile files in all projects.")
587
588 (defvar helm-projectile-dired-file-actions
589   (helm-projectile-hack-actions
590    helm-projectile-file-actions
591    ;; New actions
592    '(helm-projectile-dired-files-delete-action . "Remove entry(s) from Dired buffer `C-c d'")))
593
594 (defvar helm-source-projectile-dired-files-list
595   (helm-build-in-buffer-source "Projectile files in current Dired buffer"
596     :data (lambda ()
597             (if (and (file-remote-p (projectile-project-root))
598                      (not helm-projectile-virtual-dired-remote-enable))
599                 nil
600               (when (eq major-mode 'dired-mode)
601                 (helm-projectile-files-in-current-dired-buffer))))
602     :filter-one-by-one (lambda (file)
603                          (let ((helm-ff-transformer-show-only-basename t))
604                            (helm-ff-filter-candidate-one-by-one file)))
605     :action-transformer 'helm-find-files-action-transformer
606     :keymap (let ((map (copy-keymap helm-projectile-find-file-map)))
607               (helm-projectile-define-key map
608                 (kbd "C-c d") 'helm-projectile-dired-files-delete-action)
609               map)
610     :help-message 'helm-ff-help-message
611     :mode-line helm-read-file-name-mode-line-string
612     :action helm-projectile-dired-file-actions)
613   "Helm source definition for Projectile delete files.")
614
615 (defun helm-projectile-dired-find-dir (dir)
616   "Jump to a selected directory DIR from `helm-projectile'."
617   (dired (expand-file-name dir (projectile-project-root)))
618   (run-hooks 'projectile-find-dir-hook))
619
620 (defun helm-projectile-dired-find-dir-other-window (dir)
621   "Jump to a selected directory DIR from `helm-projectile'."
622   (dired-other-window (expand-file-name dir (projectile-project-root)))
623   (run-hooks 'projectile-find-dir-hook))
624
625 (defvar helm-source-projectile-directories-list
626   (helm-build-sync-source "Projectile directories"
627     :candidates (lambda ()
628                   (when (projectile-project-p)
629                     (with-helm-current-buffer
630                       (let ((dirs (if projectile-find-dir-includes-top-level
631                                       (append '("./") (projectile-current-project-dirs))
632                                     (projectile-current-project-dirs))))
633                         (helm-projectile--files-display-real dirs (projectile-project-root))))))
634     :fuzzy-match helm-projectile-fuzzy-match
635     :action-transformer 'helm-find-files-action-transformer
636     :keymap (let ((map (make-sparse-keymap)))
637               (set-keymap-parent map helm-map)
638               (helm-projectile-define-key map
639                 (kbd "<left>") #'helm-previous-source
640                 (kbd "<right>") #'helm-next-source
641                 (kbd "C-c o") #'helm-projectile-dired-find-dir-other-window
642                 (kbd "M-e")   #'helm-projectile-switch-to-eshell
643                 (kbd "C-c f") #'helm-projectile-dired-files-new-action
644                 (kbd "C-c a") #'helm-projectile-dired-files-add-action
645                 (kbd "C-s")   #'helm-projectile-grep)
646               map)
647     :help-message 'helm-ff-help-message
648     :mode-line helm-read-file-name-mode-line-string
649     :action '(("Open Dired" . helm-projectile-dired-find-dir)
650               ("Open Dired in other window `C-c o'" . helm-projectile-dired-find-dir)
651               ("Switch to Eshell `M-e'" . helm-projectile-switch-to-eshell)
652               ("Grep in projects `C-s'" . helm-projectile-grep)
653               ("Create Dired buffer from files `C-c f'" . helm-projectile-dired-files-new-action)
654               ("Add files to Dired buffer `C-c a'" . helm-projectile-dired-files-add-action)))
655   "Helm source for listing project directories.")
656
657 (defvar helm-projectile-buffers-list-cache nil)
658
659 (defclass helm-source-projectile-buffer (helm-source-sync helm-type-buffer)
660   ((init :initform (lambda ()
661                      ;; Issue #51 Create the list before `helm-buffer' creation.
662                      (setq helm-projectile-buffers-list-cache
663                            (ignore-errors (cdr (projectile-project-buffer-names))))
664                      (let ((result (cl-loop for b in helm-projectile-buffers-list-cache
665                                             maximize (length b) into len-buf
666                                             maximize (length (with-current-buffer b
667                                                                (symbol-name major-mode)))
668                                             into len-mode
669                                             finally return (cons len-buf len-mode))))
670                        (unless helm-buffer-max-length
671                          (setq helm-buffer-max-length (car result)))
672                        (unless helm-buffer-max-len-mode
673                          ;; If a new buffer is longer that this value
674                          ;; this value will be updated
675                          (setq helm-buffer-max-len-mode (cdr result))))))
676    (candidates :initform helm-projectile-buffers-list-cache)
677    (matchplugin :initform nil)
678    (match :initform 'helm-buffers-match-function)
679    (persistent-action :initform 'helm-buffers-list-persistent-action)
680    (keymap :initform helm-buffer-map)
681    (volatile :initform t)
682    (persistent-help
683     :initform
684     "Show this buffer / C-u \\[helm-execute-persistent-action]: Kill this buffer")))
685
686 (defvar helm-source-projectile-buffers-list (helm-make-source "Project buffers" 'helm-source-projectile-buffer))
687
688 (defvar helm-source-projectile-recentf-list
689   (helm-build-sync-source "Projectile recent files"
690     :candidates (lambda ()
691                   (when (projectile-project-p)
692                    (with-helm-current-buffer
693                      (helm-projectile--files-display-real (projectile-recentf-files)
694                                                           (projectile-project-root)))))
695     :fuzzy-match helm-projectile-fuzzy-match
696     :keymap helm-projectile-find-file-map
697     :help-message 'helm-ff-help-message
698     :mode-line helm-read-file-name-mode-line-string
699     :action helm-projectile-file-actions
700     :persistent-action #'helm-projectile-file-persistent-action
701     :persistent-help "Preview file")
702   "Helm source definition for recent files in current project.")
703
704 (defvar helm-source-projectile-files-and-dired-list
705   '(helm-source-projectile-dired-files-list
706     helm-source-projectile-files-list))
707
708 (defvar helm-source-projectile-directories-and-dired-list
709   '(helm-source-projectile-dired-files-list
710     helm-source-projectile-directories-list))
711
712 (defcustom helm-projectile-sources-list
713   '(helm-source-projectile-buffers-list
714     helm-source-projectile-files-list
715     helm-source-projectile-projects)
716   "Default sources for `helm-projectile'."
717   :type 'list
718   :group 'helm-projectile)
719
720 (defmacro helm-projectile-command (command source prompt &optional not-require-root truncate-lines-var)
721   "Template for generic `helm-projectile' commands.
722 COMMAND is a command name to be appended with \"helm-projectile\" prefix.
723 SOURCE is a Helm source that should be Projectile specific.
724 PROMPT is a string for displaying as a prompt.
725 NOT-REQUIRE-ROOT specifies the command doesn't need to be used in a
726 project root.
727 TRUNCATE-LINES-VAR is the symbol used dictate truncation of lines.
728 Defaults is `helm-projectile-truncate-lines'."
729   (unless truncate-lines-var (setq truncate-lines-var 'helm-projectile-truncate-lines))
730   `(defun ,(intern (concat "helm-projectile-" command)) (&optional arg)
731      "Use projectile with Helm for finding files in project
732
733 With a prefix ARG invalidates the cache first."
734      (interactive "P")
735      (if (projectile-project-p)
736          (projectile-maybe-invalidate-cache arg)
737        (unless ,not-require-root
738          (error "You're not in a project")))
739      (let ((helm-ff-transformer-show-only-basename nil)
740            ;; for consistency, we should just let Projectile take care of ignored files
741            (helm-boring-file-regexp-list nil))
742        (helm :sources ,source
743              :buffer (concat "*helm projectile: " (projectile-project-name) "*")
744              :truncate-lines ,truncate-lines-var
745              :prompt (projectile-prepend-project-name ,prompt)))))
746
747 (helm-projectile-command "switch-project" 'helm-source-projectile-projects "Switch to project: " t)
748 (helm-projectile-command "find-file" helm-source-projectile-files-and-dired-list "Find file: ")
749 (helm-projectile-command "find-file-in-known-projects" 'helm-source-projectile-files-in-all-projects-list "Find file in projects: " t)
750 (helm-projectile-command "find-dir" helm-source-projectile-directories-and-dired-list "Find dir: ")
751 (helm-projectile-command "recentf" 'helm-source-projectile-recentf-list "Recently visited file: ")
752 (helm-projectile-command "switch-to-buffer" 'helm-source-projectile-buffers-list "Switch to buffer: " nil helm-buffers-truncate-lines)
753 (helm-projectile-command "browse-dirty-projects" 'helm-source-projectile-dirty-projects "Select a project: " t)
754
755 (defun helm-projectile--files-display-real (files root)
756   "Create (DISPLAY . REAL) pairs with FILES and ROOT.
757
758   DISPLAY is the short file name.  REAL is the full path."
759   (cl-loop for display in files
760            collect (cons display (expand-file-name display root))))
761
762 ;;;###autoload
763 (defun helm-projectile-find-file-dwim ()
764   "Find file at point based on context."
765   (interactive)
766   (let* ((project-root (projectile-project-root))
767          (project-files (projectile-current-project-files))
768          (files (projectile-select-files project-files)))
769     (if (= (length files) 1)
770         (find-file (expand-file-name (car files) (projectile-project-root)))
771       (helm :sources (helm-build-sync-source "Projectile files"
772                        :candidates (if (> (length files) 1)
773                                        (helm-projectile--files-display-real files project-root)
774                                      (helm-projectile--files-display-real project-files project-root))
775                        :fuzzy-match helm-projectile-fuzzy-match
776                        :action-transformer 'helm-find-files-action-transformer
777                        :keymap helm-projectile-find-file-map
778                        :help-message helm-ff-help-message
779                        :mode-line helm-read-file-name-mode-line-string
780                        :action helm-projectile-file-actions
781                        :persistent-action #'helm-projectile-file-persistent-action
782                        :persistent-help "Preview file")
783             :buffer "*helm projectile*"
784             :truncate-lines helm-projectile-truncate-lines
785             :prompt (projectile-prepend-project-name "Find file: ")))))
786
787 ;;;###autoload
788 (defun helm-projectile-find-other-file (&optional flex-matching)
789   "Switch between files with the same name but different extensions using Helm.
790 With FLEX-MATCHING, match any file that contains the base name of current file.
791 Other file extensions can be customized with the variable `projectile-other-file-alist'."
792   (interactive "P")
793   (let* ((project-root (projectile-project-root))
794          (other-files (projectile-get-other-files (buffer-file-name)
795                                                   (projectile-current-project-files)
796                                                   flex-matching)))
797     (if other-files
798         (if (= (length other-files) 1)
799             (find-file (expand-file-name (car other-files) project-root))
800           (progn
801             (let* ((helm-ff-transformer-show-only-basename nil))
802               (helm :sources (helm-build-sync-source "Projectile other files"
803                                :candidates (helm-projectile--files-display-real other-files project-root)
804                                :keymap helm-projectile-find-file-map
805                                :help-message helm-ff-help-message
806                                :mode-line helm-read-file-name-mode-line-string
807                                :action helm-projectile-file-actions
808                                :persistent-action #'helm-projectile-file-persistent-action
809                                :persistent-help "Preview file")
810                     :buffer "*helm projectile*"
811                     :truncate-lines helm-projectile-truncate-lines
812                     :prompt (projectile-prepend-project-name "Find other file: ")))))
813       (error "No other file found"))))
814
815 (defcustom helm-projectile-grep-or-ack-actions
816   '("Find file" helm-grep-action
817     "Find file other frame" helm-grep-other-frame
818     (lambda () (and (locate-library "elscreen")
819                "Find file in Elscreen"))
820     helm-grep-jump-elscreen
821     "Save results in grep buffer" helm-grep-save-results
822     "Find file other window" helm-grep-other-window)
823   "Available actions for `helm-projectile-grep-or-ack'.
824 The contents of this list are passed as the arguments to `helm-make-actions'."
825   :type 'symbol
826   :group 'helm-projectile)
827
828 (defcustom helm-projectile-set-input-automatically t
829   "If non-nil, attempt to set search input automatically.
830 Automatic input selection uses the region (if there is an active
831 region), otherwise it uses the current symbol at point (if there
832 is one).  Applies to `helm-projectile-grep' and
833 `helm-projectile-ack' only.  If the `helm-ag' package is
834 installed, then automatic input behavior for `helm-projectile-ag'
835 can be customized using `helm-ag-insert-at-point'."
836   :group 'helm-projectile
837   :type 'boolean)
838
839 (defun helm-projectile-grep-or-ack (&optional dir use-ack-p ack-ignored-pattern ack-executable)
840   "Perform helm-grep at project root.
841 DIR directory where to search
842 USE-ACK-P indicates whether to use ack or not.
843 ACK-IGNORED-PATTERN is a file regex to exclude from searching.
844 ACK-EXECUTABLE is the actual ack binary name.
845 It is usually \"ack\" or \"ack-grep\".
846 If it is nil, or ack/ack-grep not found then use default grep command."
847   (let* ((default-directory (or dir (projectile-project-root)))
848          (helm-ff-default-directory default-directory)
849          (helm-grep-in-recurse t)
850          (helm-grep-ignored-files (cl-union (projectile-ignored-files-rel)  grep-find-ignored-files))
851          (helm-grep-ignored-directories
852           (cl-union (mapcar 'directory-file-name (projectile-ignored-directories-rel))
853                     grep-find-ignored-directories))
854          (helm-grep-default-command (if use-ack-p
855                                         (concat ack-executable " -H --no-group --no-color " ack-ignored-pattern " %p %f")
856                                       (if (and projectile-use-git-grep (eq (projectile-project-vcs) 'git))
857                                           "git --no-pager grep --no-color -n%c -e %p -- %f"
858                                         "grep -a -r %e -n%cH -e %p %f .")))
859          (helm-grep-default-recurse-command helm-grep-default-command))
860
861     (setq helm-source-grep
862           (helm-build-async-source
863               (capitalize (helm-grep-command t))
864             :header-name (lambda (_name)
865                            (let ((name (if use-ack-p
866                                            "Helm Projectile Ack"
867                                          "Helm Projectile Grep")))
868                              (concat name " " "(C-c ? Help)")))
869             :candidates-process 'helm-grep-collect-candidates
870             :filter-one-by-one 'helm-grep-filter-one-by-one
871             :candidate-number-limit 9999
872             :nohighlight t
873             ;; We need to specify keymap here and as :keymap arg [1]
874             ;; to make it available in further resuming.
875             :keymap helm-grep-map
876             :history 'helm-grep-history
877             :action (apply #'helm-make-actions helm-projectile-grep-or-ack-actions)
878             :persistent-action 'helm-grep-persistent-action
879             :persistent-help "Jump to line (`C-u' Record in mark ring)"
880             :requires-pattern 2))
881     (helm
882      :sources 'helm-source-grep
883      :input (when helm-projectile-set-input-automatically
884               (if (region-active-p)
885                   (buffer-substring-no-properties (region-beginning) (region-end))
886                 (thing-at-point 'symbol)))
887      :buffer (format "*helm %s*" (if use-ack-p
888                                      "ack"
889                                    "grep"))
890      :default-directory default-directory
891      :keymap helm-grep-map
892      :history 'helm-grep-history
893      :truncate-lines helm-grep-truncate-lines)))
894
895 ;;;###autoload
896 (defun helm-projectile-on ()
897   "Turn on `helm-projectile' key bindings."
898   (interactive)
899   (message "Turn on helm-projectile key bindings")
900   (helm-projectile-toggle 1))
901
902 ;;;###autoload
903 (defun helm-projectile-off ()
904   "Turn off `helm-projectile' key bindings."
905   (interactive)
906   (message "Turn off helm-projectile key bindings")
907   (helm-projectile-toggle -1))
908
909 ;;;###autoload
910 (defun helm-projectile-grep (&optional dir)
911   "Helm version of `projectile-grep'.
912 DIR is the project root, if not set then current directory is used"
913   (interactive)
914   (let ((project-root (or dir (projectile-project-root) (error "You're not in a project"))))
915     (funcall 'run-with-timer 0.01 nil
916              #'helm-projectile-grep-or-ack project-root nil)))
917
918 ;;;###autoload
919 (defun helm-projectile-ack (&optional dir)
920   "Helm version of projectile-ack."
921   (interactive)
922   (let ((project-root (or dir (projectile-project-root) (error "You're not in a project"))))
923     (let ((ack-ignored (mapconcat
924                         'identity
925                         (cl-union (mapcar (lambda (path)
926                                             (concat "--ignore-dir=" (file-name-nondirectory (directory-file-name path))))
927                                           (projectile-ignored-directories))
928                                   (mapcar (lambda (path)
929                                             (concat "--ignore-file=match:" (shell-quote-argument path)))
930                                           (append (projectile-ignored-files) (projectile-patterns-to-ignore)))) " "))
931           (helm-ack-grep-executable (cond
932                                      ((executable-find "ack") "ack")
933                                      ((executable-find "ack-grep") "ack-grep")
934                                      (t (error "ack or ack-grep is not available")))))
935       (funcall 'run-with-timer 0.01 nil
936                #'helm-projectile-grep-or-ack project-root t ack-ignored helm-ack-grep-executable))))
937
938 ;;;###autoload
939 (defun helm-projectile-ag (&optional options)
940   "Helm version of `projectile-ag'."
941   (interactive (if current-prefix-arg (list (helm-read-string "option: " "" 'helm-ag--extra-options-history))))
942   (if (require 'helm-ag nil t)
943       (if (projectile-project-p)
944           (let* ((grep-find-ignored-files (cl-union (projectile-ignored-files-rel) grep-find-ignored-files))
945                  (grep-find-ignored-directories (cl-union (projectile-ignored-directories-rel) grep-find-ignored-directories))
946                  (ignored (mapconcat (lambda (i)
947                                        (concat "--ignore " i))
948                                      (append grep-find-ignored-files grep-find-ignored-directories (cadr (projectile-parse-dirconfig-file)))
949                                      " "))
950                  (helm-ag-base-command (concat helm-ag-base-command " " ignored " " options))
951                  (current-prefix-arg nil))
952             (helm-do-ag (projectile-project-root) (car (projectile-parse-dirconfig-file))))
953         (error "You're not in a project"))
954     (when (yes-or-no-p "`helm-ag' is not installed. Install? ")
955       (condition-case nil
956           (progn
957             (package-install 'helm-ag)
958             (helm-projectile-ag options))
959         (error (error "`helm-ag' is not available.  Is MELPA in your `package-archives'?"))))))
960
961 ;; Declare/define these to satisfy the byte compiler
962 (defvar helm-rg-prepend-file-name-line-at-top-of-matches)
963 (defvar helm-rg-include-file-on-every-match-line)
964 (declare-function helm-rg "helm-rg")
965
966 (defun helm-projectile-rg--region-selection ()
967   (when (use-region-p)
968     (buffer-substring-no-properties (region-beginning) (region-end))))
969
970 ;;;###autoload
971 (defun helm-projectile-rg ()
972   "Projectile version of `helm-rg'."
973   (interactive)
974   (if (require 'helm-rg nil t)
975       (if (projectile-project-p)
976           (let ((helm-rg-prepend-file-name-line-at-top-of-matches nil)
977                 (helm-rg-include-file-on-every-match-line t))
978             (helm-rg (or (helm-projectile-rg--region-selection) "") nil (list (projectile-project-root))))
979         (error "You're not in a project"))
980     (when (yes-or-no-p "`helm-rg' is not installed. Install? ")
981       (condition-case nil
982           (progn
983             (package-install 'helm-rg)
984             (helm-projectile-rg))
985         (error "`helm-rg' is not available.  Is MELPA in your `package-archives'?")))))
986
987 (defun helm-projectile-commander-bindings ()
988   (def-projectile-commander-method ?a
989     "Run ack on project."
990     (call-interactively 'helm-projectile-ack))
991
992   (def-projectile-commander-method ?A
993     "Find ag on project."
994     (call-interactively 'helm-projectile-ag))
995
996   (def-projectile-commander-method ?f
997     "Find file in project."
998     (helm-projectile-find-file))
999
1000   (def-projectile-commander-method ?b
1001     "Switch to project buffer."
1002     (helm-projectile-switch-to-buffer))
1003
1004   (def-projectile-commander-method ?d
1005     "Find directory in project."
1006     (helm-projectile-find-dir))
1007
1008   (def-projectile-commander-method ?g
1009     "Run grep on project."
1010     (helm-projectile-grep))
1011
1012   (def-projectile-commander-method ?s
1013     "Switch project."
1014     (helm-projectile-switch-project))
1015
1016   (def-projectile-commander-method ?e
1017     "Find recently visited file in project."
1018     (helm-projectile-recentf))
1019
1020   (def-projectile-commander-method ?V
1021     "Find dirty projects."
1022     (helm-projectile-browse-dirty-projects)))
1023
1024 ;;;###autoload
1025 (defun helm-projectile-toggle (toggle)
1026   "Toggle Helm version of Projectile commands."
1027   (if (> toggle 0)
1028       (progn
1029         (when (eq projectile-switch-project-action #'projectile-find-file)
1030           (setq projectile-switch-project-action #'helm-projectile-find-file))
1031         (define-key projectile-mode-map [remap projectile-find-other-file] #'helm-projectile-find-other-file)
1032         (define-key projectile-mode-map [remap projectile-find-file] #'helm-projectile-find-file)
1033         (define-key projectile-mode-map [remap projectile-find-file-in-known-projects] #'helm-projectile-find-file-in-known-projects)
1034         (define-key projectile-mode-map [remap projectile-find-file-dwim] #'helm-projectile-find-file-dwim)
1035         (define-key projectile-mode-map [remap projectile-find-dir] #'helm-projectile-find-dir)
1036         (define-key projectile-mode-map [remap projectile-switch-project] #'helm-projectile-switch-project)
1037         (define-key projectile-mode-map [remap projectile-recentf] #'helm-projectile-recentf)
1038         (define-key projectile-mode-map [remap projectile-switch-to-buffer] #'helm-projectile-switch-to-buffer)
1039         (define-key projectile-mode-map [remap projectile-grep] #'helm-projectile-grep)
1040         (define-key projectile-mode-map [remap projectile-ack] #'helm-projectile-ack)
1041         (define-key projectile-mode-map [remap projectile-ag] #'helm-projectile-ag)
1042         (define-key projectile-mode-map [remap projectile-ripgrep] #'helm-projectile-rg)
1043         (define-key projectile-mode-map [remap projectile-browse-dirty-projects] #'helm-projectile-browse-dirty-projects)
1044         (helm-projectile-commander-bindings))
1045     (progn
1046       (when (eq projectile-switch-project-action #'helm-projectile-find-file)
1047         (setq projectile-switch-project-action #'projectile-find-file))
1048       (define-key projectile-mode-map [remap projectile-find-other-file] nil)
1049       (define-key projectile-mode-map [remap projectile-find-file] nil)
1050       (define-key projectile-mode-map [remap projectile-find-file-in-known-projects] nil)
1051       (define-key projectile-mode-map [remap projectile-find-file-dwim] nil)
1052       (define-key projectile-mode-map [remap projectile-find-dir] nil)
1053       (define-key projectile-mode-map [remap projectile-switch-project] nil)
1054       (define-key projectile-mode-map [remap projectile-recentf] nil)
1055       (define-key projectile-mode-map [remap projectile-switch-to-buffer] nil)
1056       (define-key projectile-mode-map [remap projectile-grep] nil)
1057       (define-key projectile-mode-map [remap projectile-ag] nil)
1058       (define-key projectile-mode-map [remap projectile-ripgrep] nil)
1059       (define-key projectile-mode-map [remap projectile-browse-dirty-projects] nil)
1060       (projectile-commander-bindings))))
1061
1062 ;;;###autoload
1063 (defun helm-projectile (&optional arg)
1064   "Use projectile with Helm instead of ido.
1065
1066 With a prefix ARG invalidates the cache first.
1067 If invoked outside of a project, displays a list of known projects to jump."
1068   (interactive "P")
1069   (if (not (projectile-project-p))
1070       (helm-projectile-switch-project arg)
1071     (projectile-maybe-invalidate-cache arg)
1072     (let ((helm-ff-transformer-show-only-basename nil))
1073       (helm :sources helm-projectile-sources-list
1074             :buffer "*helm projectile*"
1075             :truncate-lines helm-projectile-truncate-lines
1076             :prompt (projectile-prepend-project-name (if (projectile-project-p)
1077                                                          "pattern: "
1078                                                        "Switch to project: "))))))
1079
1080 ;;;###autoload
1081 (eval-after-load 'projectile
1082   '(progn
1083      (define-key projectile-command-map (kbd "h") #'helm-projectile)))
1084
1085 (provide 'helm-projectile)
1086
1087 ;;; helm-projectile.el ends here