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 |