├── screenshots
└── helm-projectile.png
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE.md
└── PULL_REQUEST_TEMPLATE.md
├── .dir-locals.el
├── CHANGELOG.md
├── README.md
└── helm-projectile.el
/screenshots/helm-projectile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bbatsov/helm-projectile/HEAD/screenshots/helm-projectile.png
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: bbatsov
4 | patreon: bbatsov
5 | custom: https://www.paypal.me/bbatsov
6 |
--------------------------------------------------------------------------------
/.dir-locals.el:
--------------------------------------------------------------------------------
1 | ((emacs-lisp-mode . ((flycheck-emacs-lisp-load-path . inherit)
2 | (eval . (progn
3 | (setq-local package-lint--sane-prefixes
4 | (rx (or (regexp package-lint--sane-prefixes)
5 | (seq string-start "helm-source-")))))))))
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Expected behavior
2 |
3 |
4 |
5 | ## Actual behavior
6 |
7 |
8 |
9 | ## Steps to reproduce the problem
10 |
11 |
12 |
13 | ## Backtraces if necessary (`M-x toggle-debug-on-error`)
14 |
15 |
16 |
17 | ## Environment & version information
18 |
19 | - `helm-projectile` version:
20 | - `helm` version (in `helm-pkg.el`):
21 | - `projectile` version (`M-x projectile-version`):
22 | - Emacs version (`M-x emacs-version`):
23 | - OS:
24 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | **Replace this placeholder text with a summary of the changes in your PR.
2 | The more detailed you are, the better.**
3 |
4 | -----------------
5 |
6 | Before submitting a PR make sure the following things have been done (and denote this
7 | by checking the relevant checkboxes):
8 |
9 | - [ ] The commits are consistent with our [contribution guidelines](../blob/master/CONTRIBUTING.md)
10 | - [ ] The new code is not generating bytecode or `M-x checkdoc` warnings
11 | - [ ] You've updated the [changelog](../blob/master/CHANGELOG.md) (if adding/changing user-visible functionality)
12 | - [ ] You've updated the readme (if adding/changing user-visible functionality)
13 |
14 | Thanks!
15 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## master (unreleased)
4 |
5 | ## 1.4.0 (2025-09-02)
6 |
7 | ### New features
8 |
9 | * [#155](https://github.com/bbatsov/helm-projectile/issues/155): Allow to specify files/types to include when searching with `grep`
10 | and `ack`.
11 | * [#175](https://github.com/bbatsov/helm-projectile/issues/175): Add a setting to allow the current buffer
12 | in `helm-source-projectile-buffers-list`.
13 |
14 | ### Fixes
15 |
16 | * [#168](https://github.com/bbatsov/helm-projectile/pull/168): `helm-source-projectile-files-list` collects candidates `with-temp-buffer`.
17 | * Ensure ignored directories elements are unique.
18 | * Ensure `helm-source-grep` is not set to a `helm-projectile` specific one.
19 |
20 | ### Changes
21 |
22 | * [#179](https://github.com/bbatsov/helm-projectile/issues/179) Only install package `helm-rg` when it is really needed.
23 | * Make `helm-projectile-ag` to use common ignore list.
24 | * Don't add ** to ignored globs in helm-projectile-rg.
25 |
26 | ## 1.3.2 (2025-08-14)
27 |
28 | ### Fixes
29 |
30 | * [#196](https://github.com/bbatsov/helm-projectile/issues/196): Opening project no longer respects `projectile-switch-project-action`.
31 |
32 | ## 1.3.1 (2025-08-07)
33 |
34 | Snap release - bump version only.
35 |
36 | ## 1.3.0 (2025-08-07)
37 |
38 | ### New features
39 |
40 | * Add `helm-projectile` actions to search projects with rg and ag. The new
41 | actions are bound to `C-S-a` and `C-S-d` respectively and they are available
42 | in all sources that complete projects and directories.
43 | * Add `helm-projectile` specific commands to switch projects, to switch to
44 | buffers and to find files in other window and other frame. Most of new
45 | commands is bound after `C-c p 4` and `C-c p 5`.
46 |
47 | ### Fixes
48 |
49 | * When searching with rg, ensure the directory is chosen based on projectile.
50 |
51 | ### Changes
52 |
53 | * Add faces to sources that use files.
54 | * Add faces to `helm-projectile-browse-dirty-projects`
55 | * Add autoloads to `helm-projectile` commands defined
56 | with `helm-projectile-command`.
57 |
58 | ## 1.2.0 (2025-07-21)
59 |
60 | ### Changes
61 |
62 | * [#193](https://github.com/bbatsov/helm-projectile/issues/193), [#195](https://github.com/bbatsov/helm-projectile/pull/195): Switch to use built-in `helm-grep-ag`.
63 |
64 | ### Bugs fixed
65 |
66 | * Fix `checkdoc` and (some of) `package-lint` diagnostics.
67 | * Fix side effects in `helm-projectile-grep-or-ack` and `helm-projectile-ag`
68 | * [#173](https://github.com/bbatsov/helm-projectile/pull/191): Fix `helm-rg--extra-args` losing dynamic scope due to use of setq
69 | * [#173](https://github.com/bbatsov/helm-projectile/pull/173), [#194](https://github.com/bbatsov/helm-projectile/pull/194): Respect `helm-buffer-max-length` if it's `nil`.
70 | * [#189](https://github.com/bbatsov/helm-projectile/pull/192): Fix `helm-projectile-rg` specifying incorrect extra args.
71 | * [#188](https://github.com/bbatsov/helm-projectile/pull/178): Fix `helm-projectile-projects-source` slots.
72 |
73 | ## 1.1.0 (2025-02-14)
74 |
75 | ### New features
76 |
77 | * Improve `helm-source-proctile-project-list` by also inheriting the
78 | `helm-type-file` class to benefit of it's functionality such as candidate
79 | transformer or file cache.
80 | * [#180](https://github.com/bbatsov/helm-projectile/pull/180): Introduce `helm-projectile-ignore-strategy` defcustom.
81 |
82 | ### Changes
83 |
84 | * [#151](https://github.com/bbatsov/helm-projectile/pull/157): Teach `helm-projectile-rg` to respect ignored files and directories.
85 | * [#151](https://github.com/bbatsov/helm-projectile/issues/151): Rename `helm-projectile-switch-to-eshell` -> `helm-projectile-switch-to-shell`.
86 |
87 | ### Bugs fixed
88 |
89 | * [#176](https://github.com/bbatsov/helm-projectile/pull/178): Correctly remove current buffer from `helm-source-projectile-buffers-list`.
90 | * [#143](https://github.com/bbatsov/helm-projectile/issues/143): Fix rg command for helm-ag arity.
91 | * [#145](https://github.com/bbatsov/helm-projectile/issues/145): Fix bug in `M-D` / remove from project list action for first project in the list.
92 | * [#143](https://github.com/bbatsov/helm-projectile/issues/143): Fix `rg` command for `helm-ag` arity.
93 | * [#140](https://github.com/bbatsov/helm-projectile/pull/140): Fix interactive options for `helm-projectile-grep` and `helm-projectile-ack`.
94 |
95 | ## 1.0.0 (2020-05-18)
96 |
97 | Initial stable release.
98 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [![License GPL 3][badge-license]](http://www.gnu.org/licenses/gpl-3.0.txt)
2 | [](http://melpa.org/#/helm-projectile)
3 | [](http://stable.melpa.org/#/helm-projectile)
4 | [](https://www.patreon.com/bbatsov)
5 |
6 | ## Helm Projectile
7 |
8 | [Projectile](https://github.com/bbatsov/projectile) can be integrated
9 | with [Helm](https://github.com/emacs-helm/helm) via
10 | `helm-source-projectile-projects`,
11 | `helm-source-projectile-files-list`,
12 | `helm-source-projectile-buffers-list` and
13 | `helm-source-projectile-recentf-list` sources (available in
14 | `helm-projectile.el`). There is also an example function for calling
15 | Helm with the Projectile file source. You can call it like this:
16 |
17 | ```
18 | M-x helm-projectile
19 | ```
20 |
21 | or even better - invoke the key binding h in Projectile's
22 | keymap. Assuming you've opted for one of the recommended keymap prefixes (`C-c p`
23 | or `s-p`), that would mean C-c p h or s-p h.
24 |
25 | See Projectile's [installation docs](https://docs.projectile.mx/projectile/installation.html#installation-via-package-el) for more details.
26 |
27 | ## Project Status
28 |
29 | The project is currently looking for more maintainers. I (Bozhidar) don't use Helm myself and
30 | don't have much time for the project, so I would definitely appreciate some help with it.
31 |
32 | Contact me via e-mail or GitHub if you'd like to become a co-maintainer.
33 |
34 | ## Installation
35 |
36 | The recommended way to install helm-projectile is via `package.el`.
37 |
38 | ### package.el
39 |
40 | #### MELPA
41 |
42 | You can install a snapshot version of helm-projectile from the
43 | [MELPA](http://melpa.org) repository. The version of
44 | Projectile there will always be up-to-date, but it might be unstable
45 | (albeit rarely).
46 |
47 | #### MELPA Stable
48 |
49 | You can install the last stable version of helm-projectile from the
50 | [MELPA Stable](http://stable.melpa.org) repository.
51 |
52 | ### el-get
53 |
54 | helm-projectile is also available for installation from the
55 | [el-get](https://github.com/dimitri/el-get) package manager.
56 |
57 | ### Emacs Prelude
58 |
59 | helm-projectile is naturally part of the
60 | [Emacs Prelude](https://github.com/bbatsov/prelude). If you're a Prelude
61 | user - helm-projectile is already properly configured and ready for
62 | action.
63 |
64 | ### Debian and Ubuntu
65 |
66 | Users of Debian 9 or later or Ubuntu 16.04 or later may `apt
67 | install elpa-helm-projectile`.
68 |
69 | ## Usage
70 |
71 | For those who prefer helm to ido/ivy, the command `helm-projectile-switch-project`
72 | can be used to replace `projectile-switch-project` to switch project. Please
73 | note that this is different from simply setting `projectile-completion-system`
74 | to `helm`, which just enables projectile to use the Helm completion to complete
75 | a project name. The benefit of using `helm-projectile-switch-project` is that on
76 | any selected project we can fire many actions, not limited to just the "switch
77 | to project" action, as in the case of using helm completion by setting
78 | `projectile-completion-system` to `helm`. Currently, there are five actions:
79 | "Switch to project", "Open Dired in project's directory", "Open project root in
80 | vc-dir or magit", "Switch to Eshell" and "Grep project files". We will add more
81 | and more actions in the future.
82 |
83 | `helm-projectile` is capable of opening multiple files by marking the files with
84 | C-SPC or mark all files with M-a. Then, press RET,
85 | all the selected files will be opened.
86 |
87 | Note that the helm grep is different from `projectile-grep` because the helm
88 | grep is incremental. To use it, select your projects (select multiple projects
89 | by pressing C-SPC), press "C-s" (or "C-u C-s" for recursive grep), and type your
90 | regexp. As you type the regexp in the mini buffer, the live grep results are
91 | displayed incrementally.
92 |
93 | `helm-projectile` also provides Helm versions of common Projectile commands. Currently,
94 | these are the supported commands:
95 |
96 | * `helm-projectile-switch-project`
97 | * `helm-projectile-find-file`
98 | * `helm-projectile-find-file-in-known-projects`
99 | * `helm-projectile-find-file-dwim`
100 | * `helm-projectile-find-dir`
101 | * `helm-projectile-recentf`
102 | * `helm-projectile-switch-to-buffer`
103 | * `helm-projectile-grep` (can be used for both grep or ack)
104 | * `helm-projectile-ag`
105 | * `helm-projectile-rg`
106 | * Replace Helm equivalent commands in `projectile-commander`
107 | * A virtual directory manager that is unique to Helm Projectile
108 |
109 | Why should you use these commands compared with the normal Projectile
110 | commands, even if the normal commands use `helm` as
111 | `projectile-completion-system`? The answer is, Helm specific commands
112 | give more useful features. For example,
113 | `helm-projectile-switch-project` allows opening a project in Dired,
114 | Magit or Eshell. `helm-projectile-find-file` reuses actions in
115 | `helm-find-files` (which is plenty) and able to open multiple
116 | files. Another reason is that in a large source tree, helm-projectile
117 | could be slow because it has to open all available sources.
118 |
119 | If you want to use these commands, you have to activate it to replace
120 | the normal Projectile commands:
121 |
122 | ```el
123 | ;; (setq helm-projectile-fuzzy-match nil)
124 | (require 'helm-projectile)
125 | (helm-projectile-on)
126 | ```
127 |
128 | If you already activate helm-projectile key bindings and you don't
129 | like it, you can turn it off and use the normal Projectile bindings
130 | with command `helm-projectile-off`. Similarly, if you want to disable
131 | fuzzy matching in Helm Projectile (it is enabled by default), you must
132 | set `helm-projectile-fuzzy-match` to nil before loading
133 | `helm-projectile`.
134 |
135 | To fully learn Helm Projectile and see what it is capable of, you
136 | should refer to this guide:
137 | [Exploring large projects with Projectile and Helm Projectile](http://tuhdo.github.io/helm-projectile.html).
138 |
139 | Obviously you need to have Helm installed for this to work. :-)
140 |
141 | 
142 |
143 | ## Known issues
144 |
145 | Check out the project's
146 | [issue list](https://github.com/bbatsov/helm-projectile/issues?sort=created&direction=desc&state=open)
147 | a list of unresolved issues. By the way - feel free to fix any of them
148 | and sent me a pull request. :-)
149 |
150 | ## Contributors
151 |
152 | Here's a [list](https://github.com/bbatsov/helm-projectile/contributors) of all the people who have contributed to the
153 | development of Projectile.
154 |
155 | ## Changelog
156 |
157 | A fairly extensive changelog is available [here](CHANGELOG.md).
158 |
159 | **Note:** We started keeping track of changes there after version 1.0.
160 |
161 | ## License
162 |
163 | Copyright © 2011-2025 Bozhidar Batsov and
164 | [contributors](https://github.com/bbatsov/helm-projectile/contributors).
165 |
166 | Distributed under the GNU General Public License, version 3
167 |
168 | [badge-license]: https://img.shields.io/badge/license-GPLv3-blue.svg
169 |
--------------------------------------------------------------------------------
/helm-projectile.el:
--------------------------------------------------------------------------------
1 | ;;; helm-projectile.el --- Helm integration for Projectile -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2011-2025 Bozhidar Batsov
4 |
5 | ;; Author: Bozhidar Batsov
6 | ;; URL: https://github.com/bbatsov/helm-projectile
7 | ;; Maintainer: Przemysław Kryger
8 | ;; Created: 2011-31-07
9 | ;; Keywords: project, convenience
10 | ;; Version: 1.4.0
11 | ;; Package-Requires: ((emacs "26.1") (helm "3.0") (projectile "2.9"))
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 | ;; built-in libraries
44 | (require 'subr-x)
45 | (require 'cl-lib)
46 | (require 'grep) ;; TODO: Probably we should defer this require
47 |
48 | (require 'helm-core)
49 | (require 'helm-global-bindings)
50 | (require 'helm-types)
51 | (require 'helm-locate)
52 | (require 'helm-buffers)
53 | (require 'helm-files)
54 | (require 'helm-grep)
55 |
56 | (require 'projectile)
57 |
58 | (declare-function eshell "eshell")
59 | (declare-function dired-get-filename "dired")
60 |
61 | (defvar grep-find-ignored-directories)
62 | (defvar grep-find-ignored-files)
63 |
64 | (defgroup helm-projectile nil
65 | "Helm support for projectile."
66 | :prefix "helm-projectile-"
67 | :group 'projectile
68 | :group 'helm
69 | :link `(url-link :tag "GitHub" "https://github.com/bbatsov/helm-projectile"))
70 |
71 | (defvar helm-projectile-current-project-root)
72 |
73 | (defcustom helm-projectile-truncate-lines nil
74 | "Truncate lines in helm projectile commands when non--nil.
75 |
76 | Some `helm-projectile' commands have similar behavior with existing
77 | Helms. In these cases their respective custom var for truncation
78 | of lines will be honored. E.g. `helm-buffers-truncate-lines'
79 | dictates the truncation in `helm-projectile-switch-to-buffer'."
80 | :group 'helm-projectile
81 | :type 'boolean)
82 |
83 | (defcustom helm-projectile-remove-current-buffer nil
84 | "Non-nil if current buffer should be removed from buffer sources.
85 | For example from `helm-projectile' (when invoked from a project) as well
86 | as from `helm-projectile-switch-to-buffer',
87 | `helm-projectile-switch-to-buffer-other-window', and
88 | `helm-projectile-switch-to-buffer-other-frame'."
89 | :type 'boolean
90 | :group 'helm-projectile)
91 |
92 | ;;;###autoload
93 | (defcustom helm-projectile-fuzzy-match t
94 | "Enable fuzzy matching for Helm Projectile commands.
95 | This needs to be set before loading helm-projectile.el."
96 | :group 'helm-projectile
97 | :type 'boolean)
98 |
99 | (defmacro helm-projectile-define-key (keymap &rest bindings)
100 | "In KEYMAP, define BINDINGS.
101 | BINDINS is a list in a form of (KEY1 DEF1 KEY2 DEF2 ...)."
102 | (declare (indent defun))
103 | (when (or (< (length bindings) 2)
104 | (= 1 (% 2 (length bindings))))
105 | (error "Expected BINDINGS to be KEY1 DEF1 KEY2 DEF2 ... "))
106 | (let ((ret '(progn)))
107 | (while-let ((key (car bindings))
108 | (def (cadr bindings)))
109 | (push
110 | `(define-key ,keymap ,key
111 | (lambda ()
112 | (interactive)
113 | (helm-exit-and-execute-action ,def)))
114 | ret)
115 | (setq bindings (cddr bindings)))
116 | (reverse ret)))
117 |
118 | (defun helm-projectile-hack-actions (actions &rest prescription)
119 | "Given a Helm action list and a prescription, return a hacked Helm action list.
120 | Optionally applies the PRESCRIPTION beforehand.
121 |
122 | The Helm action list ACTIONS is of the form:
123 |
124 | \(\(DESCRIPTION1 . FUNCTION1\)
125 | \(DESCRIPTION2 . FUNCTION2\)
126 | ...
127 | \(DESCRIPTIONn . FUNCTIONn\)\)
128 |
129 | PRESCRIPTION is in the form:
130 |
131 | \(INSTRUCTION1 INSTRUCTION2 ... INSTRUCTIONn\)
132 |
133 | If an INSTRUCTION is a symbol, the action with function name
134 | INSTRUCTION is deleted.
135 |
136 | If an INSTRUCTION is of the form \(FUNCTION1 . FUNCTION2\), the
137 | action with function name FUNCTION1 will change it's function to
138 | FUNCTION2.
139 |
140 | If an INSTRUCTION is of the form \(FUNCTION . DESCRIPTION\), and
141 | if an action with function name FUNCTION exists in the original
142 | Helm action list, the action in the Helm action list, with
143 | function name FUNCTION will change it's description to
144 | DESCRIPTION. Otherwise, (FUNCTION . DESCRIPTION) will be added to
145 | the action list.
146 |
147 | If an INSTRUCTION is of the form \(FUNCTION . :make-first\), and if the
148 | an action with function name FUNCTION exists in the th Helm action list
149 | concatenated with new actions from PRESCRIPTION, the action will become
150 | the first (default) action.
151 |
152 | Please check out how `helm-projectile-file-actions' is defined
153 | for an example of how this function is being used."
154 | (let* ((to-delete (cl-remove-if (lambda (entry) (listp entry)) prescription))
155 | (actions (cl-delete-if (lambda (action) (memq (cdr action) to-delete))
156 | (copy-alist actions)))
157 | new)
158 | (cl-dolist (action actions)
159 | (when (setq new (cdr (assq (cdr action) prescription)))
160 | (cond
161 | ((stringp new) (setcar action new))
162 | ((functionp new) (setcdr action new)))))
163 | ;; Add new actions from PRESCRIPTION
164 | (setq new nil)
165 | (cl-dolist (instruction prescription)
166 | (when (and (listp instruction)
167 | (null (rassq (car instruction) actions))
168 | (symbolp (car instruction)) (stringp (cdr instruction)))
169 | (push (cons (cdr instruction) (car instruction)) new)))
170 | ;; Push to front the desired action
171 | (let ((actions (append actions (nreverse new))))
172 | (if-let* ((first-function (car (rassq :make-first prescription)))
173 | (first-action-p (lambda (action)
174 | (eq (cdr action)
175 | first-function)))
176 | (first-action (cl-find-if first-action-p actions)))
177 | (cons first-action
178 | (cl-remove-if first-action-p actions))
179 | actions))))
180 |
181 | (defun helm-projectile-vc (dir)
182 | "A Helm action for jumping to project root using `vc-dir' or Magit.
183 | DIR is a directory to be switched"
184 | (let ((projectile-require-project-root nil))
185 | (projectile-vc dir)))
186 |
187 | (defun helm-projectile-compile-project (dir)
188 | "A Helm action for compile a project.
189 | DIR is the project root."
190 | (let ((helm--reading-passwd-or-string t)
191 | (default-directory dir))
192 | (projectile-compile-project helm-current-prefix-arg)))
193 |
194 | (defun helm-projectile-test-project (dir)
195 | "A Helm action for test a project.
196 | DIR is the project root."
197 | (let ((helm--reading-passwd-or-string t)
198 | (default-directory dir))
199 | (projectile-test-project helm-current-prefix-arg)))
200 |
201 | (defun helm-projectile-run-project (dir)
202 | "A Helm action for run a project.
203 | DIR is the project root."
204 | (let ((helm--reading-passwd-or-string t)
205 | (default-directory dir))
206 | (projectile-run-project helm-current-prefix-arg)))
207 |
208 | (defun helm-projectile-remove-known-project (_ignore)
209 | "Remove selected projects from projectile project list.
210 | _IGNORE means the argument does not matter.
211 | It is there because Helm requires it."
212 | (let* ((projects (helm-marked-candidates :with-wildcard t))
213 | (len (length projects)))
214 | (with-helm-display-marked-candidates
215 | helm-marked-buffer-name
216 | projects
217 | (if (not (y-or-n-p (format "Remove *%s projects(s)? " len)))
218 | (message "(No removal performed)")
219 | (progn
220 | (mapc (lambda (p)
221 | (setq projectile-known-projects (delete p projectile-known-projects)))
222 | projects)
223 | (projectile-save-known-projects))
224 | (message "%s projects(s) removed" len)))))
225 |
226 | (defun helm-projectile-switch-project-by-name (project)
227 | "Switch to PROJECT and execute `projectile-switch-project-action' in it.
228 | When `projectile-switch-project-action' is `projectile-find-file' a
229 | `helm-projectile-find-file' will be used instead."
230 | (let ((projectile-completion-system 'helm)
231 | (projectile-switch-project-action
232 | (if (eq projectile-switch-project-action 'projectile-find-file)
233 | #'helm-projectile-find-file
234 | projectile-switch-project-action)))
235 | (projectile-switch-project-by-name project)))
236 |
237 | (defun helm-projectile-switch-project-by-name-other-window (project)
238 | "Switch to PROJECT and find file in it in other window."
239 | (let ((projectile-completion-system 'helm)
240 | (projectile-switch-project-action #'helm-projectile-find-file-other-window))
241 | (projectile-switch-project-by-name project)))
242 |
243 | (defun helm-projectile-switch-project-by-name-other-frame (project)
244 | "Switch to PROJECT and find file in it in other frame."
245 | (let ((projectile-completion-system 'helm)
246 | (projectile-switch-project-action #'helm-projectile-find-file-other-frame))
247 | (projectile-switch-project-by-name project)))
248 |
249 | (defvar helm-projectile-projects-map
250 | (let ((map (make-sparse-keymap)))
251 | (set-keymap-parent map helm-map)
252 | (helm-projectile-define-key map
253 | (kbd "C-d") #'dired
254 | (kbd "C-c o") #'helm-projectile-switch-project-by-name-other-window
255 | (kbd "C-c C-o") #'helm-projectile-switch-project-by-name-other-frame
256 | (kbd "M-g") #'helm-projectile-vc
257 | (kbd "M-e") #'helm-projectile-switch-to-shell
258 | (kbd "C-s") #'helm-projectile-grep
259 | (kbd "M-c") #'helm-projectile-compile-project
260 | (kbd "M-t") #'helm-projectile-test-project
261 | (kbd "M-r") #'helm-projectile-run-project
262 | (kbd "M-D") #'helm-projectile-remove-known-project
263 | (kbd "C-S-a") #'helm-projectile--switch-project-and-ag-action
264 | (kbd "C-S-r") #'helm-projectile--switch-project-and-rg-action)
265 | map)
266 | "Mapping for known projectile projects.")
267 |
268 | (defcustom helm-source-projectile-projects-actions
269 | (helm-make-actions
270 | "Switch to project" #'helm-projectile-switch-project-by-name
271 | "Switch to project other window `C-c o'" #'helm-projectile-switch-project-by-name-other-window
272 | "Switch to project other frame `C-c C-o'" #'helm-projectile-switch-project-by-name-other-frame
273 | "Open Dired in project's directory `C-d'" #'dired
274 | "Open project root in vc-dir or magit `M-g'" #'helm-projectile-vc
275 | "Switch to Eshell `M-e'" #'helm-projectile-switch-to-shell
276 | "Grep in projects `C-s'" #'helm-projectile-grep
277 | "Compile project `M-c'. With C-u, new compile command" #'helm-projectile-compile-project
278 | "Remove project(s) from project list `M-D'" #'helm-projectile-remove-known-project
279 | "Silver searcher (ag) in project `C-S-a'" #'helm-projectile--switch-project-and-ag-action
280 | "Ripgrep (rg) in project `C-S-r'" #'helm-projectile--switch-project-and-rg-action)
281 | "Actions for `helm-source-projectile-projects'."
282 | :group 'helm-projectile
283 | :type '(alist :key-type string :value-type function))
284 |
285 | (defclass helm-projectile-projects-source (helm-source-sync helm-type-file)
286 | ((candidates :initform (lambda () (with-helm-current-buffer
287 | (mapcar #'copy-sequence
288 | (projectile-known-projects)))))
289 | (fuzzy-match :initform 'helm-projectile-fuzzy-match)
290 | (keymap :initform 'helm-projectile-projects-map)
291 | (mode-line :initform 'helm-read-file-name-mode-line-string)
292 | (action :initform 'helm-source-projectile-projects-actions))
293 | "Helm source for known projectile projects.")
294 |
295 | (cl-defmethod helm-setup-user-source ((source helm-projectile-projects-source))
296 | "Make SOURCE specific to project switching.
297 | The `helm-projectile-projects-source` inherits from
298 | `helm-type-file` (which see), which sets up actions, keymap, and
299 | help message slots to file specific ones. Override these slots
300 | to be specific to `helm-projectile-projects-source'."
301 | (setf (slot-value source 'action) 'helm-source-projectile-projects-actions)
302 | (setf (slot-value source 'keymap) helm-projectile-projects-map)
303 | ;; Use `ignore' as a persistent action, to actually keep `helm' session
304 | ;; when `helm-execute-persistent-action' is executed.
305 | (setf (slot-value source 'persistent-action) #'ignore)
306 | (let ((persistent-help "Do Nothing"))
307 | (setf (slot-value source 'persistent-help) persistent-help)
308 | (setf (slot-value source 'header-line)
309 | (helm-source--persistent-help-string
310 | persistent-help
311 | source)))
312 | (setf (slot-value source 'mode-line)
313 | (list "Project(s)" helm-mode-line-string)))
314 |
315 | (defvar helm-source-projectile-projects
316 | (helm-make-source "Projectile projects" 'helm-projectile-projects-source))
317 |
318 | (defclass helm-projectile-projects-other-window-source (helm-projectile-projects-source)
319 | ())
320 |
321 | (cl-defmethod helm-setup-user-source :after ((source helm-projectile-projects-other-window-source))
322 | "Set `helm-projectile-switch-project-by-name-other-window' as the first action."
323 | (setf (slot-value source 'action)
324 | (helm-projectile-hack-actions
325 | helm-source-projectile-projects-actions
326 | '(helm-projectile-switch-project-by-name-other-window . :make-first))))
327 |
328 | (defclass helm-projectile-projects-other-frame-source (helm-projectile-projects-source)
329 | ())
330 |
331 | (cl-defmethod helm-setup-user-source :after ((source helm-projectile-projects-other-frame-source))
332 | "Set `helm-projectile-switch-project-by-name-other-frame' as the first action."
333 | (setf (slot-value source 'action)
334 | (helm-projectile-hack-actions
335 | helm-source-projectile-projects-actions
336 | '(helm-projectile-switch-project-by-name-other-frame . :make-first))))
337 |
338 | (defvar helm-projectile-dirty-projects-map
339 | (let ((map (make-sparse-keymap)))
340 | (set-keymap-parent map helm-map)
341 | (helm-projectile-define-key map
342 | (kbd "C-d") #'dired
343 | (kbd "M-o") #'helm-projectile-switch-project-by-name
344 | (kbd "C-c o") #'helm-projectile-switch-project-by-name-other-window
345 | (kbd "C-c C-o") #'helm-projectile-switch-project-by-name-other-frame
346 | (kbd "M-e") #'helm-projectile-switch-to-shell
347 | (kbd "C-s") #'helm-projectile-grep
348 | (kbd "M-c") #'helm-projectile-compile-project
349 | (kbd "M-t") #'helm-projectile-test-project
350 | (kbd "M-r") #'helm-projectile-run-project
351 | (kbd "M-D") #'helm-projectile-remove-known-project
352 | (kbd "C-S-a") #'helm-projectile--switch-project-and-ag-action
353 | (kbd "C-S-r") #'helm-projectile--switch-project-and-rg-action)
354 | map)
355 | "Mapping for dirty projectile projects.")
356 |
357 | (defvar helm-source-projectile-dirty-projects
358 | (helm-build-sync-source "Projectile dirty projects"
359 | :candidates (lambda () (with-helm-current-buffer (helm-projectile-get-dirty-projects)))
360 | :fuzzy-match helm-projectile-fuzzy-match
361 | :keymap helm-projectile-dirty-projects-map
362 | :mode-line helm-read-file-name-mode-line-string
363 | :action '(("Open project root in vc-dir or magit" . helm-projectile-vc)
364 | ("Switch to project `M-o'" . helm-projectile-switch-project-by-name)
365 | ("Switch to project other window `C-c o'" . helm-projectile-switch-project-by-name-other-window)
366 | ("Switch to project other frame `C-c C-o'" . helm-projectile-switch-project-by-name-other-frame)
367 | ("Open Dired in project's directory `C-d'" . dired)
368 | ("Switch to Eshell `M-e'" . helm-projectile-switch-to-shell)
369 | ("Grep in projects `C-s'" . helm-projectile-grep)
370 | ("Compile project `M-c'. With C-u, new compile command"
371 | . helm-projectile-compile-project)
372 | ("Silver searcher (ag) in project `C-S-a'" . helm-projectile--switch-project-and-ag-action)
373 | ("Ripgrep (rg) in project `C-S-r'" . helm-projectile--switch-project-and-rg-action)))
374 | "Helm source for dirty version controlled projectile projects.")
375 |
376 | (defun helm-projectile-get-dirty-projects ()
377 | "Return dirty version controlled known projects.
378 | The value is returned as an alist to have a nice display in Helm."
379 | (message "Checking for dirty known projects...")
380 | (let* ((status (projectile-check-vcs-status-of-known-projects))
381 | (proj-dir (cl-loop for stat in status
382 | collect (car stat)))
383 | (status-display (cl-loop for stat in status collect
384 | (propertize (format "[%s]"
385 | (mapconcat 'identity
386 | (car (cdr stat)) ", "))
387 | 'face 'helm-match)))
388 | (max-status-display-length (cl-loop for sd in status-display
389 | maximize (length sd)))
390 | (status-display (cl-loop for sd in status-display collect
391 | (format "%s%s "
392 | sd
393 | (make-string
394 | (- max-status-display-length (length sd)) ? ))))
395 | (full-display (cl-mapcar 'concat
396 | status-display
397 | (mapcar (lambda (dir)
398 | (propertize dir 'face 'helm-ff-directory))
399 | proj-dir))))
400 | (cl-pairlis full-display proj-dir)))
401 |
402 | (define-key helm-etags-map (kbd "C-c p f")
403 | (lambda ()
404 | (interactive)
405 | (helm-run-after-exit 'helm-projectile-find-file nil)))
406 |
407 | (defun helm-projectile-file-persistent-action (candidate)
408 | "Previews the contents of a file CANDIDATE in a temporary buffer.
409 | This is a persistent action for file-related functionality."
410 | (let ((buf (get-buffer-create " *helm-projectile persistent*")))
411 | (cl-flet ((preview (candidate)
412 | (switch-to-buffer buf)
413 | (setq inhibit-read-only t)
414 | (erase-buffer)
415 | (insert-file-contents candidate)
416 | (let ((buffer-file-name candidate))
417 | (set-auto-mode))
418 | (font-lock-ensure)
419 | (setq inhibit-read-only nil)))
420 | (if (and (helm-get-attr 'previewp)
421 | (string= candidate (helm-get-attr 'current-candidate)))
422 | (progn
423 | (kill-buffer buf)
424 | (helm-set-attr 'previewp nil))
425 | (preview candidate)
426 | (helm-set-attr 'previewp t)))
427 | (helm-set-attr 'current-candidate candidate)))
428 |
429 | (defun helm-projectile-find-files-eshell-command-on-file-action (candidate)
430 | "Execute an eshell command on a file CANDIDATE."
431 | (interactive)
432 | (let* ((helm-ff-default-directory (file-name-directory candidate)))
433 | (helm-find-files-eshell-command-on-file candidate)))
434 |
435 | (defun helm-projectile-ff-etags-select-action (candidate)
436 | "Jump to etags for file CANDIDATE.
437 | See also `helm-etags-select'."
438 | (interactive)
439 | (let* ((helm-ff-default-directory (file-name-directory candidate)))
440 | (helm-ff-etags-select candidate)))
441 |
442 | (defun helm-projectile-switch-to-shell (dir)
443 | "Within DIR, switch to a shell corresponding to `helm-ff-preferred-shell-mode'."
444 | (interactive)
445 | (let* ((projectile-require-project-root nil)
446 | (helm-ff-default-directory (file-name-directory (projectile-expand-root dir))))
447 | (helm-ff-switch-to-shell dir)))
448 |
449 | (defun helm-projectile-files-in-current-dired-buffer ()
450 | "Return a list of files (only) in the current Dired buffer."
451 | (let (flist)
452 | (cl-flet ((fpush (fname) (push fname flist)))
453 | (save-excursion
454 | (let (file buffer-read-only)
455 | (goto-char (point-min))
456 | (while (not (eobp))
457 | (save-excursion
458 | (and (not (eolp))
459 | (setq file (dired-get-filename t t)) ; nil on non-file
460 | (progn (end-of-line)
461 | (funcall #'fpush file))))
462 | (forward-line 1)))))
463 | (mapcar 'file-truename (nreverse flist))))
464 |
465 | (defun helm-projectile-all-dired-buffers ()
466 | "Get all current Dired buffers."
467 | (mapcar (lambda (b)
468 | (with-current-buffer b (buffer-name)))
469 | (cl-remove-if-not
470 | (lambda (b)
471 | (with-current-buffer b
472 | (and (eq major-mode 'dired-mode)
473 | (buffer-name))))
474 | (buffer-list))))
475 |
476 | (defvar helm-projectile-virtual-dired-remote-enable nil
477 | "Enable virtual Dired manager on remote host.
478 | Disabled by default.")
479 |
480 | (defun helm-projectile-dired-files-new-action (candidate)
481 | "Create a Dired buffer from chosen files.
482 | CANDIDATE is the selected file, but choose the marked files if available."
483 | (if (and (file-remote-p (projectile-project-root))
484 | (not helm-projectile-virtual-dired-remote-enable))
485 | (message "Virtual Dired manager is disabled in remote host. Enable with %s."
486 | (propertize "helm-projectile-virtual-dired-remote-enable" 'face 'font-lock-keyword-face))
487 | (let ((files (cl-remove-if-not
488 | (lambda (f)
489 | (not (string= f "")))
490 | (mapcar (lambda (file)
491 | (replace-regexp-in-string (projectile-project-root) "" file))
492 | (helm-marked-candidates :with-wildcard t))))
493 | (new-name (completing-read "Select or enter a new buffer name: "
494 | (helm-projectile-all-dired-buffers)))
495 | (helm--reading-passwd-or-string t)
496 | (default-directory (projectile-project-root)))
497 | ;; create a unique buffer that is unique to any directory in default-directory
498 | ;; or opened buffer; when Dired is passed with a non-existence directory name,
499 | ;; it only creates a buffer and insert everything. If a new name user supplied
500 | ;; exists as default-directory, Dired throws error when insert anything that
501 | ;; does not exist in current directory.
502 | (with-current-buffer (dired (cons (make-temp-name new-name)
503 | (if files
504 | files
505 | (list candidate))))
506 | (when (get-buffer new-name)
507 | (kill-buffer new-name))
508 | (rename-buffer new-name)))))
509 |
510 | (defun helm-projectile-dired-files-add-action (candidate)
511 | "Add files to a Dired buffer.
512 | CANDIDATE is the selected file. Used when no file is explicitly marked."
513 | (if (and (file-remote-p (projectile-project-root))
514 | (not helm-projectile-virtual-dired-remote-enable))
515 | (message "Virtual Dired manager is disabled in remote host. Enable with %s."
516 | (propertize "helm-projectile-virtual-dired-remote-enable" 'face 'font-lock-keyword-face))
517 | (if (eq (with-helm-current-buffer major-mode) 'dired-mode)
518 | (let* ((marked-files (helm-marked-candidates :with-wildcard t))
519 | (helm--reading-passwd-or-string t)
520 | (root (projectile-project-root)) ; store root for later use
521 | (dired-buffer-name (or (and (eq major-mode 'dired-mode) (buffer-name))
522 | (completing-read "Select a Dired buffer:"
523 | (helm-projectile-all-dired-buffers))))
524 | (dired-files (with-current-buffer dired-buffer-name
525 | (helm-projectile-files-in-current-dired-buffer)))
526 | (files (sort (mapcar (lambda (file)
527 | (replace-regexp-in-string (projectile-project-root) "" file))
528 | (cl-nunion (if marked-files
529 | marked-files
530 | (list candidate))
531 | dired-files
532 | :test #'string-equal))
533 | 'string-lessp)))
534 | (kill-buffer dired-buffer-name)
535 | ;; Rebind default-directory because after killing a buffer, we
536 | ;; could be in any buffer and default-directory is set to that
537 | ;; random buffer
538 | ;;
539 | ;; Also use saved root directory, because after killing a buffer,
540 | ;; we could be outside of current project
541 | (let ((default-directory root))
542 | (with-current-buffer (dired (cons (make-temp-name dired-buffer-name)
543 | (if files
544 | (mapcar (lambda (file)
545 | (replace-regexp-in-string root "" file))
546 | files)
547 | (list candidate))))
548 | (rename-buffer dired-buffer-name))))
549 | (error "You're not in a Dired buffer to add"))))
550 |
551 | (defun helm-projectile-dired-files-delete-action (candidate)
552 | "Delete selected entries from a Dired buffer.
553 | CANDIDATE is the selected file. Used when no file is explicitly marked."
554 | (if (and (file-remote-p (projectile-project-root))
555 | (not helm-projectile-virtual-dired-remote-enable))
556 | (message "Virtual Dired manager is disabled in remote host. Enable with %s."
557 | (propertize "helm-projectile-virtual-dired-remote-enable" 'face 'font-lock-keyword-face))
558 | (let* ((helm--reading-passwd-or-string t)
559 | (root (projectile-project-root))
560 | (dired-buffer-name (with-helm-current-buffer (buffer-name)))
561 | (dired-files (with-current-buffer dired-buffer-name
562 | (helm-projectile-files-in-current-dired-buffer)))
563 | (files (sort (cl-set-exclusive-or (helm-marked-candidates :with-wildcard t)
564 | dired-files
565 | :test #'string-equal) #'string-lessp)))
566 | (kill-buffer dired-buffer-name)
567 | ;; similar reason to `helm-projectile-dired-files-add-action'
568 | (let ((default-directory root))
569 | (with-current-buffer (dired (cons (make-temp-name dired-buffer-name)
570 | (if files
571 | (mapcar (lambda (file)
572 | (replace-regexp-in-string root "" file))
573 | files)
574 | (list candidate))))
575 | (rename-buffer dired-buffer-name))))))
576 |
577 | (defun helm-projectile-run-projectile-hooks-after-find-file (_orig-fun &rest _args)
578 | "Run `projectile-find-file-hook' if using projectile."
579 | (when (and projectile-mode (projectile-project-p))
580 | (run-hooks 'projectile-find-file-hook)))
581 |
582 | (advice-add 'helm-find-file-or-marked
583 | :after #'helm-projectile-run-projectile-hooks-after-find-file)
584 |
585 | (defvar helm-projectile-find-file-map
586 | (let ((map (copy-keymap helm-find-files-map)))
587 | (helm-projectile-define-key map
588 | (kbd "C-c f") #'helm-projectile-dired-files-new-action
589 | (kbd "C-c a") #'helm-projectile-dired-files-add-action
590 | (kbd "M-e") #'helm-projectile-switch-to-shell
591 | (kbd "M-.") #'helm-projectile-ff-etags-select-action
592 | (kbd "M-!") #'helm-projectile-find-files-eshell-command-on-file-action)
593 | (define-key map (kbd "") #'helm-previous-source)
594 | (define-key map (kbd "") #'helm-next-source)
595 | (dolist (cmd '(helm-find-files-up-one-level
596 | helm-find-files-down-last-level))
597 | (substitute-key-definition cmd nil map))
598 | map)
599 | "Mapping for file commands in Helm Projectile.")
600 |
601 | (defvar helm-projectile-file-actions
602 | (helm-projectile-hack-actions
603 | helm-find-files-actions
604 | ;; Delete these actions
605 | 'helm-ff-browse-project
606 | 'helm-insert-file-name-completion-at-point
607 | 'helm-ff-find-sh-command
608 | 'helm-ff-cache-add-file
609 | ;; Substitute these actions
610 | '(helm-ff-switch-to-shell . helm-projectile-switch-to-shell)
611 | '(helm-ff-etags-select . helm-projectile-ff-etags-select-action)
612 | '(helm-find-files-eshell-command-on-file
613 | . helm-projectile-find-files-eshell-command-on-file-action)
614 | ;; Change action descriptions
615 | '(helm-find-file-as-root . "Find file as root `C-c r'")
616 | ;; New actions
617 | '(helm-projectile-dired-files-new-action
618 | . "Create Dired buffer from files `C-c f'")
619 | '(helm-projectile-dired-files-add-action
620 | . "Add files to Dired buffer `C-c a'"))
621 | "Action for files.")
622 |
623 | (defun helm-projectile--move-selection-p (selection)
624 | "Return non-nil if should move Helm selector after SELECTION.
625 |
626 | SELECTION should be moved unless it's one of:
627 |
628 | - Non-string
629 | - Existing file
630 | - Non-remote file that matches `helm-tramp-file-name-regexp'"
631 | (not (or (not (stringp selection))
632 | (file-exists-p selection)
633 | (and (string-match helm-tramp-file-name-regexp selection)
634 | (not (file-remote-p selection nil t))))))
635 |
636 | (defun helm-projectile--move-to-real ()
637 | "Move to first real candidate.
638 |
639 | Similar to `helm-ff--move-to-first-real-candidate', but without
640 | unnecessary complexity."
641 | (while (let* ((src (helm-get-current-source))
642 | (selection (and (not (helm-empty-source-p))
643 | (helm-get-selection nil nil src))))
644 | (and (not (helm-end-of-source-p))
645 | (helm-projectile--move-selection-p selection)))
646 | (helm-next-line)))
647 |
648 | (defun helm-projectile--remove-move-to-real ()
649 | "Hook function to remove `helm-projectile--move-to-real'.
650 |
651 | Meant to be added to `helm-cleanup-hook', from which it removes
652 | itself at the end."
653 | (remove-hook 'helm-after-update-hook #'helm-projectile--move-to-real)
654 | (remove-hook 'helm-cleanup-hook #'helm-projectile--remove-move-to-real))
655 |
656 | (defvar helm-source-projectile-files-list-before-init-hook
657 | (lambda ()
658 | (add-hook 'helm-after-update-hook #'helm-projectile--move-to-real)
659 | (add-hook 'helm-cleanup-hook #'helm-projectile--remove-move-to-real)))
660 |
661 | (defclass helm-source-projectile-file (helm-source-sync)
662 | ((before-init-hook :initform 'helm-source-projectile-files-list-before-init-hook)
663 | (candidates
664 | :initform (lambda ()
665 | (when (projectile-project-p)
666 | (with-temp-buffer
667 | (hack-dir-local-variables-non-file-buffer)
668 | (helm-projectile--files-display-real (projectile-current-project-files)
669 | (projectile-project-root))))))
670 | (filtered-candidate-transformer
671 | :initform (lambda (files _source)
672 | (with-temp-buffer
673 | (hack-dir-local-variables-non-file-buffer)
674 | (let* ((root (projectile-project-root))
675 | (file-at-root (file-relative-name (expand-file-name helm-pattern root))))
676 | (if (or (string-empty-p helm-pattern)
677 | (assoc helm-pattern files))
678 | files
679 | (if (equal helm-pattern file-at-root)
680 | (cl-acons (helm-ff-prefix-filename helm-pattern nil t)
681 | (expand-file-name helm-pattern)
682 | files)
683 | (cl-pairlis (list (helm-ff-prefix-filename helm-pattern nil t)
684 | (helm-ff-prefix-filename file-at-root nil t))
685 | (list (expand-file-name helm-pattern)
686 | (expand-file-name helm-pattern root))
687 | files)))))))
688 | (fuzzy-match :initform 'helm-projectile-fuzzy-match)
689 | (keymap :initform 'helm-projectile-find-file-map)
690 | (help-message :initform 'helm-ff-help-message)
691 | (mode-line :initform 'helm-read-file-name-mode-line-string)
692 | (action :initform 'helm-projectile-file-actions)
693 | (persistent-action :initform #'helm-projectile-file-persistent-action)
694 | (persistent-help :initform "Preview file")))
695 |
696 | (defvar helm-source-projectile-files-list
697 | (helm-make-source "Projectile files" 'helm-source-projectile-file)
698 | "Helm source definition for Projectile files.")
699 |
700 | (defclass helm-source-projectile-file-other-window (helm-source-projectile-file)
701 | ())
702 |
703 | (cl-defmethod helm-setup-user-source ((source helm-source-projectile-file-other-window))
704 | "Make `helm-find-files-other-window' the first action in SOURCE."
705 | (setf (slot-value source 'action)
706 | (helm-projectile-hack-actions
707 | helm-projectile-file-actions
708 | '(helm-find-files-other-window . :make-first))))
709 |
710 | (defvar helm-source-projectile-files-other-window-list
711 | (helm-make-source "Projectile files" 'helm-source-projectile-file-other-window)
712 | "Helm source definition for Projectile files.")
713 |
714 | (defclass helm-source-projectile-file-other-frame (helm-source-projectile-file)
715 | ())
716 |
717 | (cl-defmethod helm-setup-user-source ((source helm-source-projectile-file-other-frame))
718 | "Make `find-file-other-frame' the first action in SOURCE."
719 | (setf (slot-value source 'action)
720 | (helm-projectile-hack-actions
721 | helm-projectile-file-actions
722 | '(find-file-other-frame . :make-first))))
723 |
724 | (defvar helm-source-projectile-files-other-frame-list
725 | (helm-make-source "Projectile files" 'helm-source-projectile-file-other-frame)
726 | "Helm source definition for Projectile files.")
727 |
728 | (defvar helm-source-projectile-files-in-all-projects-list
729 | (helm-build-sync-source "Projectile files in all Projects"
730 | :candidates (lambda ()
731 | (with-helm-current-buffer
732 | (let ((projectile-require-project-root nil))
733 | (projectile-all-project-files))))
734 | :keymap helm-projectile-find-file-map
735 | :help-message 'helm-ff-help-message
736 | :mode-line helm-read-file-name-mode-line-string
737 | :action helm-projectile-file-actions
738 | :persistent-action #'helm-projectile-file-persistent-action
739 | :persistent-help "Preview file")
740 | "Helm source definition for all Projectile files in all projects.")
741 |
742 | (defvar helm-projectile-dired-file-actions
743 | (helm-projectile-hack-actions
744 | helm-projectile-file-actions
745 | ;; New actions
746 | '(helm-projectile-dired-files-delete-action . "Remove entry(s) from Dired buffer `C-c d'")))
747 |
748 | (defclass helm-source-projectile-dired-file (helm-source-in-buffer)
749 | ((data :initform (lambda ()
750 | (if (and (file-remote-p (projectile-project-root))
751 | (not helm-projectile-virtual-dired-remote-enable))
752 | nil
753 | (when (eq major-mode 'dired-mode)
754 | (helm-projectile-files-in-current-dired-buffer)))))
755 | (filter-one-by-one :initform (lambda (file)
756 | (let ((helm-ff-transformer-show-only-basename t))
757 | (helm-ff-filter-candidate-one-by-one file))))
758 | (action-transformer :initform (lambda (actions candidate)
759 | (let ((actions (helm-find-files-action-transformer actions candidate)))
760 | (if (file-directory-p candidate)
761 | (append
762 | actions
763 | '(("Silver searcher (ag) in directory `C-S-a'" . helm-projectile--switch-project-and-ag-action)
764 | ("Ripgrep (rg) in directory `C-S-r'" . helm-projectile--switch-project-and-rg-action)))
765 | actions))))
766 | (keymap :initform (let ((map (copy-keymap helm-projectile-find-file-map)))
767 | (helm-projectile-define-key map
768 | (kbd "C-c d") #'helm-projectile-dired-files-delete-action
769 | (kbd "C-S-a") #'helm-projectile--switch-project-and-ag-action
770 | (kbd "C-S-r") #'helm-projectile--switch-project-and-rg-action)
771 | map))
772 | (help-message :initform 'helm-ff-help-message)
773 | (mode-line :initform 'helm-read-file-name-mode-line-string)
774 | (action :initform 'helm-projectile-dired-file-actions)))
775 |
776 | (defvar helm-source-projectile-dired-files-list
777 | (helm-make-source "Projectile files in current Dired buffer"
778 | 'helm-source-projectile-dired-file)
779 | "Helm source definition for Projectile delete files.")
780 |
781 | (defclass helm-source-projectile-dired-file-other-window (helm-source-projectile-dired-file)
782 | ())
783 |
784 | (cl-defmethod helm-setup-user-source ((source helm-source-projectile-dired-file-other-window))
785 | "Make `helm-find-files-other-window' the first action in SOURCE."
786 | (setf (slot-value source 'action)
787 | (helm-projectile-hack-actions
788 | helm-projectile-dired-file-actions
789 | '(helm-find-files-other-window . :make-first))))
790 |
791 | (defvar helm-source-projectile-dired-files-other-window-list
792 | (helm-make-source "Projectile files in current Dired buffer"
793 | 'helm-source-projectile-dired-file-other-window)
794 | "Helm source definition for Projectile delete files.")
795 |
796 | (defclass helm-source-projectile-dired-file-other-frame (helm-source-projectile-dired-file)
797 | ())
798 |
799 | (cl-defmethod helm-setup-user-source ((source helm-source-projectile-dired-file-other-frame))
800 | "Make `find-file-other-frame' the first action in SOURCE."
801 | (setf (slot-value source 'action)
802 | (helm-projectile-hack-actions
803 | helm-projectile-dired-file-actions
804 | '(find-file-other-frame . :make-first))))
805 |
806 | (defvar helm-source-projectile-dired-files-other-frame-list
807 | (helm-make-source "Projectile files in current Dired buffer"
808 | 'helm-source-projectile-dired-file-other-frame)
809 | "Helm source definition for Projectile delete files.")
810 |
811 | (defun helm-projectile-dired-find-dir (dir)
812 | "Jump to a selected directory DIR from `helm-projectile'."
813 | (dired (expand-file-name dir (projectile-project-root)))
814 | (run-hooks 'projectile-find-dir-hook))
815 |
816 | (defun helm-projectile-dired-find-dir-other-window (dir)
817 | "Jump to a selected directory DIR from `helm-projectile' (in other window)."
818 | (dired-other-window (expand-file-name dir (projectile-project-root)))
819 | (run-hooks 'projectile-find-dir-hook))
820 |
821 | (defun helm-projectile-dired-find-dir-other-frame (dir)
822 | "Jump to a selected directory DIR from `helm-projectile' (in other frame)."
823 | (dired-other-frame (expand-file-name dir (projectile-project-root)))
824 | (run-hooks 'projectile-find-dir-hook))
825 |
826 | (defvar helm-source-projectile-directory-actions
827 | '(("Open Dired" . helm-projectile-dired-find-dir)
828 | ("Open Dired in other window `C-c o'" . helm-projectile-dired-find-dir-other-window)
829 | ("Open Dired in other frame `C-c C-o'" . helm-projectile-dired-find-dir-other-frame)
830 | ("Switch to Eshell `M-e'" . helm-projectile-switch-to-shell)
831 | ("Grep in projects `C-s'" . helm-projectile-grep)
832 | ("Create Dired buffer from files `C-c f'" . helm-projectile-dired-files-new-action)
833 | ("Add files to Dired buffer `C-c a'" . helm-projectile-dired-files-add-action)
834 | ("Silver searcher (ag) in directory `C-S-a'" . helm-projectile--switch-project-and-ag-action)
835 | ("Ripgrep (rg) in directory `C-S-r'" . helm-projectile--switch-project-and-rg-action)))
836 |
837 | (defclass helm-source-projectile-directory (helm-source-sync)
838 | ((candidates :initform (lambda ()
839 | (when (projectile-project-p)
840 | (with-temp-buffer
841 | (hack-dir-local-variables-non-file-buffer)
842 | (let ((dirs (if projectile-find-dir-includes-top-level
843 | (append '("./") (projectile-current-project-dirs))
844 | (projectile-current-project-dirs))))
845 | (helm-projectile--files-display-real dirs (projectile-project-root)))))))
846 | (fuzzy-match :initform 'helm-projectile-fuzzy-match)
847 | (action-transformer :initform 'helm-find-files-action-transformer)
848 | (keymap :initform (let ((map (make-sparse-keymap)))
849 | (set-keymap-parent map helm-map)
850 | (helm-projectile-define-key map
851 | (kbd "") #'helm-previous-source
852 | (kbd "") #'helm-next-source
853 | (kbd "C-c o") #'helm-projectile-dired-find-dir-other-window
854 | (kbd "C-c C-o") #'helm-projectile-dired-find-dir-other-frame
855 | (kbd "M-e") #'helm-projectile-switch-to-shell
856 | (kbd "C-c f") #'helm-projectile-dired-files-new-action
857 | (kbd "C-c a") #'helm-projectile-dired-files-add-action
858 | (kbd "C-s") #'helm-projectile-grep
859 | (kbd "C-S-a") #'helm-projectile--switch-project-and-ag-action
860 | (kbd "C-S-r") #'helm-projectile--switch-project-and-rg-action)
861 | map))
862 | (help-message :initform 'helm-ff-help-message)
863 | (mode-line :initform 'helm-read-file-name-mode-line-string)
864 | (action :initform 'helm-source-projectile-directory-actions)))
865 |
866 | (defvar helm-source-projectile-directories-list
867 | (helm-make-source "Projectile directories" 'helm-source-projectile-directory)
868 | "Helm source for listing project directories.")
869 |
870 | (defclass helm-source-projectile-directory-other-window (helm-source-projectile-directory)
871 | ())
872 |
873 | (cl-defmethod helm-setup-user-source ((source helm-source-projectile-directory-other-window))
874 | "Make `helm-projectile-dired-find-dir-other-window' the first action in SOURCE."
875 | (setf (slot-value source 'action)
876 | (helm-projectile-hack-actions
877 | helm-source-projectile-directory-actions
878 | '(helm-projectile-dired-find-dir-other-window . :make-first))))
879 |
880 | (defvar helm-source-projectile-directories-other-window-list
881 | (helm-make-source "Projectile directories" 'helm-source-projectile-directory-other-window)
882 | "Helm source for listing project directories.")
883 |
884 | (defclass helm-source-projectile-directory-other-frame (helm-source-projectile-directory)
885 | ())
886 |
887 | (cl-defmethod helm-setup-user-source ((source helm-source-projectile-directory-other-frame))
888 | "Make `helm-projectile-dired-find-dir-other-frame' the first action in SOURCE."
889 | (setf (slot-value source 'action)
890 | (helm-projectile-hack-actions
891 | helm-source-projectile-directory-actions
892 | '(helm-projectile-dired-find-dir-other-frame . :make-first))))
893 |
894 | (defvar helm-source-projectile-directories-other-frame-list
895 | (helm-make-source "Projectile directories" 'helm-source-projectile-directory-other-frame)
896 | "Helm source for listing project directories.")
897 |
898 | (defvar helm-projectile-buffers-list-cache nil)
899 |
900 | (defclass helm-source-projectile-buffer (helm-source-sync helm-type-buffer)
901 | ((init :initform (lambda ()
902 | ;; Issue #51 Create the list before `helm-buffer' creation.
903 | (setq helm-projectile-buffers-list-cache
904 | (ignore-errors
905 | (let* ((buffers (projectile-project-buffer-names))
906 | (current-buffer (buffer-name)))
907 | (append (remove current-buffer buffers)
908 | (unless helm-projectile-remove-current-buffer
909 | ;; Allow current buffer to be shown,
910 | ;; but push it to the end.
911 | (list current-buffer))))))
912 | (let ((result (cl-loop for b in helm-projectile-buffers-list-cache
913 | maximize (length b) into len-buf
914 | maximize (length (with-current-buffer b
915 | (symbol-name major-mode)))
916 | into len-mode
917 | finally return (cons len-buf len-mode))))
918 | (unless (default-value 'helm-buffer-max-length)
919 | (helm-set-local-variable 'helm-buffer-max-length (car result)))
920 | (unless (default-value 'helm-buffer-max-len-mode)
921 | ;; If a new buffer is longer that this value
922 | ;; this value will be updated
923 | (helm-set-local-variable 'helm-buffer-max-len-mode (cdr result))))))
924 | (candidates :initform 'helm-projectile-buffers-list-cache)
925 | (matchplugin :initform nil)
926 | (match :initform 'helm-buffers-match-function)
927 | (persistent-action :initform 'helm-buffers-list-persistent-action)
928 | (keymap :initform 'helm-buffer-map)
929 | (volatile :initform t)
930 | (persistent-help
931 | :initform
932 | "Show this buffer / C-u \\[helm-execute-persistent-action]: Kill this buffer")))
933 |
934 | (defvar helm-source-projectile-buffers-list
935 | (helm-make-source "Project buffers" 'helm-source-projectile-buffer))
936 |
937 | (defclass helm-source-projectile-buffer-other-window (helm-source-projectile-buffer)
938 | ())
939 |
940 | (cl-defmethod helm-setup-user-source ((source helm-source-projectile-buffer-other-window))
941 | "Make `helm-buffer-switch-buffers-other-window' first action in SOURCE."
942 | (setf (slot-value source 'action)
943 | (helm-projectile-hack-actions
944 | helm-type-buffer-actions
945 | '(helm-buffer-switch-buffers-other-window . :make-first))))
946 |
947 | (defvar helm-source-projectile-buffers-other-window-list
948 | (helm-make-source "Project buffers" 'helm-source-projectile-buffer-other-window))
949 |
950 | (defclass helm-source-projectile-buffer-other-frame (helm-source-projectile-buffer)
951 | ())
952 |
953 | (cl-defmethod helm-setup-user-source ((source helm-source-projectile-buffer-other-frame))
954 | "Make `helm-buffer-switch-to-buffer-other-frame' first action in SOURCE."
955 | (setf
956 | (slot-value source 'action)
957 | (helm-projectile-hack-actions
958 | helm-type-buffer-actions
959 | '(helm-buffer-switch-to-buffer-other-frame . :make-first))))
960 |
961 | (defvar helm-source-projectile-buffers-other-frame-list
962 | (helm-make-source "Project buffers" 'helm-source-projectile-buffer-other-frame))
963 |
964 | (defvar helm-source-projectile-recentf-list
965 | (helm-build-sync-source "Projectile recent files"
966 | :candidates (lambda ()
967 | (when (projectile-project-p)
968 | (with-temp-buffer
969 | (hack-dir-local-variables-non-file-buffer)
970 | (helm-projectile--files-display-real (projectile-recentf-files)
971 | (projectile-project-root)))))
972 | :fuzzy-match helm-projectile-fuzzy-match
973 | :keymap helm-projectile-find-file-map
974 | :help-message 'helm-ff-help-message
975 | :mode-line helm-read-file-name-mode-line-string
976 | :action helm-projectile-file-actions
977 | :persistent-action #'helm-projectile-file-persistent-action
978 | :persistent-help "Preview file")
979 | "Helm source definition for recent files in current project.")
980 |
981 | (defcustom helm-projectile-git-grep-command
982 | "git --no-pager grep --no-color -n%c -e %p -- %f"
983 | "Command to execute when performing `helm-grep' inside a projectile git project.
984 | See documentation of `helm-grep-default-command' for the format."
985 | :type 'string
986 | :group 'helm-projectile)
987 |
988 | (defcustom helm-projectile-grep-command
989 | "grep -a -r %e -n%cH -e %p %f ."
990 | "Command to execute when performing `helm-grep' outside a projectile git project.
991 | See documentation of `helm-grep-default-command' for the format."
992 | :type 'string
993 | :group 'helm-projectile)
994 |
995 |
996 | (defcustom helm-projectile-sources-list
997 | '(helm-source-projectile-buffers-list
998 | helm-source-projectile-files-list
999 | helm-source-projectile-projects)
1000 | "Default sources for `helm-projectile'."
1001 | :type '(repeat symbol)
1002 | :group 'helm-projectile)
1003 |
1004 | (defmacro helm-projectile-command (command source prompt &optional not-require-root truncate-lines-var)
1005 | "Template for generic `helm-projectile' commands.
1006 | COMMAND is a command name to be appended with \"helm-projectile\" prefix.
1007 | SOURCE is a Helm source that should be Projectile specific.
1008 | PROMPT is a string for displaying as a prompt.
1009 | NOT-REQUIRE-ROOT specifies the command doesn't need to be used in a
1010 | project root.
1011 | TRUNCATE-LINES-VAR is the symbol used dictate truncation of lines.
1012 | Defaults is `helm-projectile-truncate-lines'."
1013 | (unless truncate-lines-var (setq truncate-lines-var 'helm-projectile-truncate-lines))
1014 | `(defun ,(intern (concat "helm-projectile-" command)) (&optional arg)
1015 | "Use projectile with Helm for finding files in project
1016 |
1017 | With a prefix ARG invalidates the cache first."
1018 | (interactive "P")
1019 | (if (projectile-project-p)
1020 | (projectile-maybe-invalidate-cache arg)
1021 | (unless ,not-require-root
1022 | (error "You're not in a project")))
1023 | (let ((helm-ff-transformer-show-only-basename nil)
1024 | ;; for consistency, we should just let Projectile take care of ignored files
1025 | (helm-boring-file-regexp-list nil))
1026 | (helm :sources ,source
1027 | :buffer (concat "*helm projectile: " (projectile-project-name) "*")
1028 | :truncate-lines ,truncate-lines-var
1029 | :prompt (projectile-prepend-project-name ,prompt)))))
1030 |
1031 | ;; You can evaluate the following command to help inserting autoloads for the
1032 | ;; `helm-projectile-command' macro. To use: move point to a form that calls
1033 | ;; `helm-projectile-command' and type:
1034 | ;;
1035 | ;; M-x helm-projectile-command-insert-autoload
1036 | ;;
1037 | ;; (defun helm-projectile-command-insert-autoload ()
1038 | ;; "Insert autoload for a helm-projectile-command at point."
1039 | ;; (interactive)
1040 | ;; (save-excursion
1041 | ;; (end-of-defun)
1042 | ;; (beginning-of-defun)
1043 | ;; (when-let* ((beg (point))
1044 | ;; (form (funcall load-read-function (current-buffer)))
1045 | ;; ((eq (car form) 'helm-projectile-command))
1046 | ;; (command (concat "helm-projectile-" (cadr form)))
1047 | ;; (file (file-name-sans-extension
1048 | ;; (file-name-nondirectory
1049 | ;; (buffer-file-name)))))
1050 | ;; (goto-char beg)
1051 | ;; (insert ";;;###autoload(autoload '" command " \"" file "\" nil t)\n"))))
1052 |
1053 | ;;;###autoload(autoload 'helm-projectile-switch-project "helm-projectile" nil t)
1054 | (helm-projectile-command "switch-project"
1055 | 'helm-source-projectile-projects
1056 | "Switch to project: " t)
1057 |
1058 | ;;;###autoload(autoload 'helm-projectile-switch-project-other-window "helm-projectile" nil t)
1059 | (helm-projectile-command "switch-project-other-window"
1060 | (helm-make-source
1061 | "Projectile projects"
1062 | 'helm-projectile-projects-other-window-source)
1063 | "Switch to project: " t)
1064 |
1065 | ;;;###autoload(autoload 'helm-projectile-switch-project-other-frame "helm-projectile" nil t)
1066 | (helm-projectile-command "switch-project-other-frame"
1067 | (helm-make-source
1068 | "Projectile projects"
1069 | 'helm-projectile-projects-other-frame-source)
1070 | "Switch to project: " t)
1071 |
1072 | ;;;###autoload(autoload 'helm-projectile-find-file "helm-projectile" nil t)
1073 | (helm-projectile-command "find-file"
1074 | '(helm-source-projectile-dired-files-list
1075 | helm-source-projectile-files-list)
1076 | "Find file: ")
1077 | ;;;###autoload(autoload 'helm-projectile-find-file-other-window "helm-projectile" nil t)
1078 | (helm-projectile-command "find-file-other-window"
1079 | '(helm-source-projectile-dired-files-other-window-list
1080 | helm-source-projectile-files-other-window-list)
1081 | "Find file (other window): ")
1082 |
1083 | ;;;###autoload(autoload 'helm-projectile-find-file-other-frame "helm-projectile" nil t)
1084 | (helm-projectile-command "find-file-other-frame"
1085 | '(helm-source-projectile-dired-files-other-frame-list
1086 | helm-source-projectile-files-other-frame-list)
1087 | "Find file (other frame): ")
1088 |
1089 | ;;;###autoload(autoload 'helm-projectile-find-file-in-known-projects "helm-projectile" nil t)
1090 | (helm-projectile-command "find-file-in-known-projects" 'helm-source-projectile-files-in-all-projects-list "Find file in projects: " t)
1091 |
1092 | ;;;###autoload(autoload 'helm-projectile-find-dir "helm-projectile" nil t)
1093 | (helm-projectile-command "find-dir"
1094 | '(helm-source-projectile-dired-files-list
1095 | helm-source-projectile-directories-list)
1096 | "Find dir: ")
1097 |
1098 | ;;;###autoload(autoload 'helm-projectile-find-dir-other-window "helm-projectile" nil t)
1099 | (helm-projectile-command "find-dir-other-window"
1100 | '(helm-source-projectile-dired-files-other-window-list
1101 | helm-source-projectile-directories-other-window-list)
1102 | "Find dir (other window): ")
1103 |
1104 | ;;;###autoload(autoload 'helm-projectile-find-dir-other-frame "helm-projectile" nil t)
1105 | (helm-projectile-command "find-dir-other-frame"
1106 | '(helm-source-projectile-dired-files-other-frame-list
1107 | helm-source-projectile-directories-other-frame-list)
1108 | "Find dir (other frame): ")
1109 |
1110 | ;;;###autoload(autoload 'helm-projectile-recentf "helm-projectile" nil t)
1111 | (helm-projectile-command "recentf" 'helm-source-projectile-recentf-list "Recently visited file: ")
1112 |
1113 | ;;;###autoload(autoload 'helm-projectile-switch-to-buffer "helm-projectile" nil t)
1114 | (helm-projectile-command "switch-to-buffer"
1115 | 'helm-source-projectile-buffers-list
1116 | "Switch to buffer: " nil helm-buffers-truncate-lines)
1117 |
1118 | ;;;###autoload(autoload 'helm-projectile-switch-to-buffer-other-window "helm-projectile" nil t)
1119 | (helm-projectile-command "switch-to-buffer-other-window"
1120 | 'helm-source-projectile-buffers-other-window-list
1121 | "Switch to buffer (other window): " nil helm-buffers-truncate-lines)
1122 |
1123 | ;;;###autoload(autoload 'helm-projectile-switch-to-buffer-other-frame "helm-projectile" nil t)
1124 | (helm-projectile-command "switch-to-buffer-other-frame"
1125 | 'helm-source-projectile-buffers-other-frame-list
1126 | "Switch to buffer (other frame): " nil helm-buffers-truncate-lines)
1127 |
1128 | ;;;###autoload(autoload 'helm-projectile-browse-dirty-projects "helm-projectile" nil t)
1129 | (helm-projectile-command "browse-dirty-projects" 'helm-source-projectile-dirty-projects "Select a project: " t)
1130 |
1131 | (defun helm-projectile--files-display-real (files root)
1132 | "Create (DISPLAY . REAL) pairs with FILES and ROOT.
1133 |
1134 | DISPLAY is the short file name. REAL is the full path."
1135 | ;; Use `helm-ff-filter-candidate-one-by-one' (just like `helm-find-files-get-candidates' does).
1136 | ;; With a twist that some of files may contain a directory component.
1137 | ;; In such a case `helm-ff-filter-candidate-one-by-one' just returns a file component,
1138 | ;; so we the do a concatenation of file and directory components manually.
1139 | (cl-loop with default-directory = root
1140 | for file in files
1141 | collect (let ((file-res (helm-ff-filter-candidate-one-by-one file nil t)))
1142 | (setcdr file-res (expand-file-name file root))
1143 | (if-let* ((directory (file-name-directory file)))
1144 | (cons (concat (if-let* ((face (get-text-property
1145 | 0 'face (car file-res))))
1146 | (propertize directory 'face face)
1147 | directory)
1148 | (unless (file-directory-p file)
1149 | (car file-res)))
1150 | (cdr file-res))
1151 | file-res))))
1152 |
1153 | (defun helm-projectile--find-file-dwim-1 (one-candidate-action actions prompt)
1154 | "Find file at point based on context.
1155 | Execute ONE-CANDIDATE-ACTION when there is a single file returned by
1156 | `projectile-select-files' (which see). Otherwise display a Helm with
1157 | ACTIONS and PROMPT with other selected files."
1158 | (let* ((project-root (projectile-project-root))
1159 | (project-files (projectile-current-project-files))
1160 | (files (projectile-select-files project-files)))
1161 | (if (= (length files) 1)
1162 | (funcall one-candidate-action (expand-file-name (car files) (projectile-project-root)))
1163 | (helm :sources (helm-build-sync-source "Projectile files"
1164 | :candidates (if (> (length files) 1)
1165 | (helm-projectile--files-display-real files project-root)
1166 | (helm-projectile--files-display-real project-files project-root))
1167 | :fuzzy-match helm-projectile-fuzzy-match
1168 | :action-transformer 'helm-find-files-action-transformer
1169 | :keymap helm-projectile-find-file-map
1170 | :help-message helm-ff-help-message
1171 | :mode-line helm-read-file-name-mode-line-string
1172 | :action actions
1173 | :persistent-action #'helm-projectile-file-persistent-action
1174 | :persistent-help "Preview file")
1175 | :buffer "*helm projectile*"
1176 | :truncate-lines helm-projectile-truncate-lines
1177 | :prompt (projectile-prepend-project-name prompt)))))
1178 |
1179 | ;;;###autoload
1180 | (defun helm-projectile-find-file-dwim ()
1181 | "Find file at point based on context."
1182 | (interactive)
1183 | (helm-projectile--find-file-dwim-1
1184 | #'find-file helm-projectile-file-actions "Find file: "))
1185 |
1186 | ;;;###autoload
1187 | (defun helm-projectile-find-file-dwim-other-window ()
1188 | "Find file at point based on context."
1189 | (interactive)
1190 | (helm-projectile--find-file-dwim-1
1191 | #'find-file-other-window
1192 | (helm-projectile-hack-actions
1193 | helm-projectile-file-actions
1194 | '(helm-find-files-other-window . :make-first))
1195 | "Find file (other window): "))
1196 |
1197 | ;;;###autoload
1198 | (defun helm-projectile-find-file-dwim-other-frame ()
1199 | "Find file at point based on context."
1200 | (interactive)
1201 | (helm-projectile--find-file-dwim-1
1202 | #'find-file-other-frame
1203 | (helm-projectile-hack-actions
1204 | helm-projectile-file-actions
1205 | '(find-file-other-frame . :make-first))
1206 | "Find file (other frame): "))
1207 |
1208 | (defun helm-projectile--find-other-file-1 (one-candidate-action actions prompt flex-matching)
1209 | "Switch between files with the same name but different extensions using Helm.
1210 | Execute ONE-CANDIDATE-ACTION when there is a single file returned by
1211 | `projectile-get-other-files' (which see). Otherwise display a Helm with
1212 | ACTIONS and PROMPT with other selected files.
1213 |
1214 | With FLEX-MATCHING, match any file that contains the base name of
1215 | current file. Other file extensions can be customized with the
1216 | variable `projectile-other-file-alist'."
1217 | (interactive "P")
1218 | (let* ((project-root (projectile-project-root))
1219 | (other-files (projectile-get-other-files (buffer-file-name)
1220 | flex-matching)))
1221 | (if other-files
1222 | (if (= (length other-files) 1)
1223 | (funcall one-candidate-action (expand-file-name (car other-files) project-root))
1224 | (progn
1225 | (let* ((helm-ff-transformer-show-only-basename nil))
1226 | (helm :sources (helm-build-sync-source "Projectile other files"
1227 | :candidates (helm-projectile--files-display-real other-files project-root)
1228 | :keymap helm-projectile-find-file-map
1229 | :help-message helm-ff-help-message
1230 | :mode-line helm-read-file-name-mode-line-string
1231 | :action actions
1232 | :persistent-action #'helm-projectile-file-persistent-action
1233 | :persistent-help "Preview file")
1234 | :buffer "*helm projectile*"
1235 | :truncate-lines helm-projectile-truncate-lines
1236 | :prompt (projectile-prepend-project-name prompt)))))
1237 | (error "No other file found"))))
1238 |
1239 | ;;;###autoload
1240 | (defun helm-projectile-find-other-file (&optional flex-matching)
1241 | "Switch between files with the same name but different extensions using Helm.
1242 | With FLEX-MATCHING, match any file that contains the base name of
1243 | current file. Other file extensions can be customized with the
1244 | variable `projectile-other-file-alist'."
1245 | (interactive "P")
1246 | (helm-projectile--find-other-file-1
1247 | #'find-file
1248 | helm-projectile-file-actions
1249 | "Find other file: "
1250 | flex-matching))
1251 |
1252 | ;;;###autoload
1253 | (defun helm-projectile-find-other-file-other-window (&optional flex-matching)
1254 | "Switch between files with the same name but different extensions using Helm.
1255 | With FLEX-MATCHING, match any file that contains the base name of
1256 | current file. Other file extensions can be customized with the
1257 | variable `projectile-other-file-alist'."
1258 | (interactive "P")
1259 | (helm-projectile--find-other-file-1
1260 | #'find-file-other-window
1261 | (helm-projectile-hack-actions
1262 | helm-projectile-file-actions
1263 | '(helm-find-files-other-window . :make-first))
1264 | "Find other file (other window): "
1265 | flex-matching))
1266 |
1267 | ;;;###autoload
1268 | (defun helm-projectile-find-other-file-other-frame (&optional flex-matching)
1269 | "Switch between files with the same name but different extensions using Helm.
1270 | With FLEX-MATCHING, match any file that contains the base name of
1271 | current file. Other file extensions can be customized with the
1272 | variable `projectile-other-file-alist'."
1273 | (interactive "P")
1274 | (helm-projectile--find-other-file-1
1275 | #'find-file-other-frame
1276 | (helm-projectile-hack-actions
1277 | helm-projectile-file-actions
1278 | '(find-file-other-frame . :make-first))
1279 | "Find other file (other frame): "
1280 | flex-matching))
1281 |
1282 | (defcustom helm-projectile-ignore-strategy 'projectile
1283 | "Allow projectile to compute ignored files and directories.
1284 |
1285 | When set to `projectile', the package will compute ignores and
1286 | explicitly add additionally command line arguments to the search
1287 | tool. Note that this might override search tool specific
1288 | behaviors (for instance ag would not use VCS ignore files).
1289 |
1290 | When set to `search-tool', the above does not happen."
1291 | :group 'helm-projectile
1292 | :type '(choice (const :tag "Allow projectile to compute ignores" projectile)
1293 | (const :tag "Let the search tool compute ignores" search-tool)))
1294 |
1295 | (defun helm-projectile--projectile-ignore-strategy ()
1296 | "True if the ignore strategy is `projectile'."
1297 | (eq 'projectile helm-projectile-ignore-strategy))
1298 |
1299 | (defun helm-projectile--ignored-files ()
1300 | "Compute ignored files."
1301 | (cl-union (projectile-ignored-files-rel) grep-find-ignored-files
1302 | :test #'equal))
1303 |
1304 | (defun helm-projectile--ignored-directories ()
1305 | "Compute ignored directories."
1306 | (cl-union (mapcar #'file-name-as-directory (projectile-ignored-directories-rel))
1307 | (mapcar #'file-name-as-directory grep-find-ignored-directories)
1308 | :test #'equal))
1309 |
1310 | (defcustom helm-projectile-grep-or-ack-actions
1311 | '("Find file" helm-grep-action
1312 | "Find file other frame" helm-grep-other-frame
1313 | (lambda () (and (locate-library "elscreen")
1314 | "Find file in Elscreen"))
1315 | helm-grep-jump-elscreen
1316 | "Save results in grep buffer" helm-grep-save-results
1317 | "Find file other window" helm-grep-other-window)
1318 | "Available actions for `helm-projectile-grep-or-ack'.
1319 | The contents of this list are passed as the arguments to `helm-make-actions'."
1320 | :type 'symbol
1321 | :group 'helm-projectile)
1322 |
1323 | (defcustom helm-projectile-set-input-automatically t
1324 | "If non-nil, attempt to set search input automatically.
1325 | Automatic input selection uses the region (if there is an active
1326 | region), otherwise it uses the current symbol at point (if there is
1327 | one). Applies to `helm-projectile-grep', `helm-projectile-ack', and
1328 | `helm-projectile-ag'."
1329 | :group 'helm-projectile
1330 | :type 'boolean)
1331 |
1332 | (defun helm-projectile-grep-or-ack (&optional dir use-ack-p ack-ignored-pattern ack-executable include)
1333 | "Perform helm-grep at project root.
1334 | DIR directory where to search. USE-ACK-P indicates whether to use ack
1335 | or not. ACK-IGNORED-PATTERN is a file regex to exclude from searching.
1336 | ACK-EXECUTABLE is the actual ack binary name. It is usually \"ack\" or
1337 | \"ack-grep\". If it is nil, or ack/ack-grep not found then use default
1338 | grep command. INCLUDE is a string with patterns (for \"grep\") or
1339 | types (for \"ack\") to include in search."
1340 | (let* ((default-directory (or dir (projectile-project-root)))
1341 | (helm-ff-default-directory default-directory)
1342 | (helm-grep-in-recurse t)
1343 | (helm-grep-ignored-files (if (helm-projectile--projectile-ignore-strategy)
1344 | (helm-projectile--ignored-files)
1345 | helm-grep-ignored-files))
1346 | (helm-grep-ignored-directories (if (helm-projectile--projectile-ignore-strategy)
1347 | (mapcar 'directory-file-name
1348 | (helm-projectile--ignored-directories))
1349 | helm-grep-ignored-directories))
1350 | (helm-grep-default-command
1351 | (if use-ack-p
1352 | (concat ack-executable " -H --no-group --no-color "
1353 | (when include "%e ")
1354 | (when ack-ignored-pattern (concat ack-ignored-pattern " "))
1355 | "%p %f")
1356 | (if (and projectile-use-git-grep (eq (projectile-project-vcs) 'git))
1357 | helm-projectile-git-grep-command
1358 | (if include
1359 | (replace-regexp-in-string (rx (one-or-more whitespace)
1360 | "."
1361 | (zero-or-more whitespace)
1362 | string-end)
1363 | ""
1364 | helm-projectile-grep-command)
1365 | helm-projectile-grep-command))))
1366 | (helm-grep-default-recurse-command helm-grep-default-command)
1367 | (helm-grep-include-files include)
1368 | (helm-source-grep
1369 | (helm-build-async-source
1370 | (capitalize (helm-grep-command t))
1371 | :header-name (lambda (_name)
1372 | (let ((name (if use-ack-p
1373 | "Helm Projectile Ack"
1374 | "Helm Projectile Grep")))
1375 | (concat name " " "(C-c ? Help)")))
1376 | :candidates-process 'helm-grep-collect-candidates
1377 | :filter-one-by-one 'helm-grep-filter-one-by-one
1378 | :candidate-number-limit 9999
1379 | :nohighlight t
1380 | ;; We need to specify keymap here and as :keymap arg [1]
1381 | ;; to make it available in further resuming.
1382 | :keymap helm-grep-map
1383 | :history 'helm-grep-history
1384 | :action (apply #'helm-make-actions helm-projectile-grep-or-ack-actions)
1385 | :persistent-action 'helm-grep-persistent-action
1386 | :persistent-help "Jump to line (`C-u' Record in mark ring)"
1387 | :requires-pattern 2)))
1388 | (helm
1389 | :sources 'helm-source-grep
1390 | :input (when helm-projectile-set-input-automatically
1391 | (if (region-active-p)
1392 | (buffer-substring-no-properties (region-beginning) (region-end))
1393 | (thing-at-point 'symbol)))
1394 | :buffer (format "*helm %s*" (if use-ack-p
1395 | "ack"
1396 | "grep"))
1397 | :default-directory default-directory
1398 | :keymap helm-grep-map
1399 | :history 'helm-grep-history
1400 | :truncate-lines helm-grep-truncate-lines)))
1401 |
1402 | ;;;###autoload
1403 | (defun helm-projectile-on ()
1404 | "Turn on `helm-projectile' key bindings."
1405 | (interactive)
1406 | (message "Turn on helm-projectile key bindings")
1407 | (helm-projectile-toggle 1))
1408 |
1409 | ;;;###autoload
1410 | (defun helm-projectile-off ()
1411 | "Turn off `helm-projectile' key bindings."
1412 | (interactive)
1413 | (message "Turn off helm-projectile key bindings")
1414 | (helm-projectile-toggle -1))
1415 |
1416 | ;;;###autoload
1417 | (defun helm-projectile-grep (&optional dir files)
1418 | "Helm version of `projectile-grep'.
1419 | DIR is the project root, if not set then current project root is used.
1420 | FILES is a list of file patterns to search in. When called with a
1421 | prefix argument then ask for FILES."
1422 | (interactive)
1423 | (let* ((project-root (or dir (projectile-project-root) (error "You're not in a project")))
1424 | (include (if (equal current-prefix-arg '(4))
1425 | (read-string (projectile-prepend-project-name "Grep in: "))
1426 | files)))
1427 | (funcall 'run-with-timer 0.01 nil
1428 | #'helm-projectile-grep-or-ack project-root nil nil nil include)))
1429 |
1430 | (defun helm-projectile--wildcard-to-ack-match (wildcard)
1431 | "Transform WILDCARD into a form expected by \"match:\" filter of \"ack\"."
1432 | ;; FIXME: Make the following more robust such that [...[!...] won't be
1433 | ;; changed.
1434 | (thread-last
1435 | wildcard
1436 | (replace-regexp-in-string (rx ".") ;; "." -> "\."
1437 | "\\\\.")
1438 | (replace-regexp-in-string (rx "?") ;; "?" -> "."
1439 | ".")
1440 | (replace-regexp-in-string (rx "*") ;; "*" -> ".*"
1441 | ".*")
1442 | (replace-regexp-in-string (rx (or string-start (not "[")) "[!") ;; [!...] -> [^...], but don't change [[!]
1443 | "[^")
1444 | (replace-regexp-in-string (rx (group (one-or-more any))) ;; Wrap everything in ^...$
1445 | "^\\1$")))
1446 |
1447 | ;;;###autoload
1448 | (defun helm-projectile-ack (&optional dir types)
1449 | "Helm version of `projectile-ack'.
1450 | DIR directory where to search, if not set then current project root is
1451 | used. TYPES is a list of types to include in search. When called with
1452 | a prefix argument, then ask for TYPES."
1453 | (interactive)
1454 | (let* ((project-root (or dir (projectile-project-root) (error "You're not in a project")))
1455 | (ignored (when (helm-projectile--projectile-ignore-strategy)
1456 | (mapconcat
1457 | 'identity
1458 | (cl-union (mapcar (lambda (path)
1459 | (concat "--ignore-dir="
1460 | (file-name-nondirectory
1461 | (directory-file-name path))))
1462 | (helm-projectile--ignored-directories))
1463 | (mapcar (lambda (path)
1464 | (concat "--ignore-file=match:"
1465 | (thread-last
1466 | path
1467 | (helm-projectile--wildcard-to-ack-match)
1468 | (shell-quote-argument))))
1469 | (append
1470 | (helm-projectile--ignored-files)
1471 | (projectile-patterns-to-ignore)))
1472 | :test #'equal)
1473 | " ")))
1474 | (helm-ack-grep-executable (cond
1475 | ((executable-find "ack") "ack")
1476 | ((executable-find "ack-grep") "ack-grep")
1477 | (t (error "Neither 'ack' nor 'ack-grep' is available"))))
1478 | (include (if (equal current-prefix-arg '(4))
1479 | (let ((helm-grep-default-recurse-command helm-ack-grep-executable)
1480 | (helm-grep-default-command helm-ack-grep-executable))
1481 | (helm-grep-read-ack-type))
1482 | types)))
1483 | (funcall 'run-with-timer 0.01 nil
1484 | #'helm-projectile-grep-or-ack project-root t ignored helm-ack-grep-executable include)))
1485 |
1486 | (defvar helm-projectile--ag-input nil
1487 | "The value of input to be used in a next `helm-projectile-ag' call.")
1488 |
1489 | (defun helm-projectile--ag-automatic-input (args)
1490 | "Use active region or a symbol at point as a third element in ARGS.
1491 | This function has been designed as an advice to `helm-grep-ag-1'. Do not
1492 | use directly."
1493 | (pcase-let ((`(,directory ,type ,input) args))
1494 | (list directory
1495 | type
1496 | (or input helm-projectile--ag-input))))
1497 |
1498 | ;; When calling `helm', the function `helm-grep-ag' uses symbol at point as an
1499 | ;; argument `:default-input' (via `helm-sources-using-default-as-input'). This
1500 | ;; however sets argument `:input' to an empty string. As a result the shell
1501 | ;; command `ag' (or `rg', or `pt') is being run with the active region or a
1502 | ;; symbol at point as a search pattern, but typing in minibuffer starts search
1503 | ;; from scratch. This advice will use symbol active region or a symbol point
1504 | ;; as an `input' argument to `helm-grep-ag-1', which will ensure both `helm'
1505 | ;; arguments `:default-input' and `:input' are populated.
1506 | (advice-add #'helm-grep-ag-1
1507 | :filter-args #'helm-projectile--ag-automatic-input)
1508 |
1509 | (defun helm-projectile--ag--region-selection ()
1510 | "Return a default input for `helm-grep-ag'."
1511 | (when helm-projectile-set-input-automatically
1512 | (if (region-active-p)
1513 | (buffer-substring-no-properties (region-beginning) (region-end))
1514 | (thing-at-point 'symbol))))
1515 |
1516 | (defun helm-projectile--ag-1 (directory input &optional options)
1517 | "Call `helm-grep-ag' with DIRECTORY.
1518 | DIRECTORY is the directory to search in. INPUT is the value of default
1519 | input to use for the search. OPTIONS are explicit command line
1520 | arguments to `helm-grep-ag-command'. When called with a double or a
1521 | triple prefix argument, ask for TYPES (see `helm-grep-ag')."
1522 | (let* ((ignored (when (helm-projectile--projectile-ignore-strategy)
1523 | (mapconcat (lambda (i)
1524 | (helm-acase (helm-grep--ag-command)
1525 | ;; `helm-grep-ag-command' suggests
1526 | ;; that PT is obsolete, but support
1527 | ;; still persist in Helm. Likely
1528 | ;; remove after Helm drops support.
1529 | (("ag" "pt")
1530 | (concat "--ignore " (shell-quote-argument i)))
1531 | ("rg"
1532 | (concat "--glob !" (shell-quote-argument i)))))
1533 | (append (helm-projectile--ignored-files)
1534 | (helm-projectile--ignored-directories))
1535 | " ")))
1536 | (helm-grep-ag-command (format helm-grep-ag-command
1537 | (mapconcat #'identity
1538 | (delq nil (list ignored options "%s"))
1539 | " ")
1540 | "%s" "%s"))
1541 | (with-types (member current-prefix-arg '((16) (64))))
1542 | (current-prefix-arg nil)
1543 | (helm-projectile--ag-input input))
1544 | (helm-grep-ag directory with-types)))
1545 |
1546 | ;;;###autoload
1547 | (defun helm-projectile-ag (&optional options)
1548 | "Helm version of `projectile-ag'.
1549 | When called with a single or a triple prefix argument, ask for OPTIONS.
1550 | When called with a double or a triple prefix argument, ask for
1551 | TYPES (see `helm-grep-ag').'
1552 |
1553 | This command uses `helm-grep-ag' to perform the search, so the actual
1554 | searcher used is determined by the value of `helm-grep-ag-command'."
1555 | (interactive (if (member current-prefix-arg '((4) (64)))
1556 | (list (helm-read-string "option: " ""
1557 | 'helm-ag--extra-options-history))))
1558 | (if (projectile-project-p)
1559 | (helm-projectile--ag-1 (projectile-project-root)
1560 | (helm-projectile--ag--region-selection)
1561 | options)
1562 | (error "You're not in a project")))
1563 |
1564 | (defun helm-projectile--switch-project-and-ag-action (directory)
1565 | "Switch to a project containing DIRECTORY then run ag in the DIRECTORY."
1566 | (if (and (file-directory-p directory)
1567 | (projectile-project-p directory))
1568 | (let* ((input (helm-projectile--ag--region-selection))
1569 | (projectile-switch-project-action
1570 | (lambda ()
1571 | (helm-projectile--ag-1 (file-truename directory) input))))
1572 | (projectile-switch-project-by-name (projectile-project-root directory)))
1573 | (error (if (file-directory-p directory)
1574 | (format "Directory %s is not in any project!" directory)
1575 | (format "Not a directory %s" directory)))))
1576 |
1577 | ;; Declare/define these to satisfy the byte compiler
1578 | (defvar helm-rg-prepend-file-name-line-at-top-of-matches)
1579 | (defvar helm-rg-include-file-on-every-match-line)
1580 | (defvar helm-rg-default-directory)
1581 | (defvar helm-rg--extra-args)
1582 | (declare-function helm-rg "ext:helm-rg")
1583 | (declare-function helm-rg--get-thing-at-pt "ext:helm-rg")
1584 |
1585 | (defcustom helm-projectile-ask-to-install-helm-rg t
1586 | "Whether to ask user to install `helm-rg' when searching with ripgrep (rg).
1587 | The question is presented to user when the `helm-grep-ag-command' is not
1588 | pointing to ripgrep (rg) executable and the package `helm-rg' is not
1589 | installed."
1590 | :type 'boolean
1591 | :group 'helm-projectile)
1592 |
1593 | (defun helm-projectile-rg--region-selection ()
1594 | "Return a default input for `helm-rg'."
1595 | (when helm-projectile-set-input-automatically
1596 | (if (region-active-p)
1597 | (buffer-substring-no-properties (region-beginning) (region-end))
1598 | (helm-rg--get-thing-at-pt))))
1599 |
1600 | (defmacro helm-projectile--with-helm-rg (fallback &rest body)
1601 | "Execute BODY if package `helm-rg'is installed.
1602 | Otherwise if `helm-grep-ag-command' is pointing to ripgrep (rg) call the
1603 | FALLBACK, which is expected to be some version of `helm-projectile-ag'.
1604 | Otherwise ask to install package`helm-rg' and execute BODY."
1605 | (declare (indent 1) (debug t))
1606 | `(cond ((require 'helm-rg nil t)
1607 | (progn ,@body))
1608 | ((equal "rg" (helm-grep--ag-command))
1609 | (funcall ,fallback))
1610 | ((and
1611 | helm-projectile-ask-to-install-helm-rg
1612 | (yes-or-no-p "`helm-rg' is not installed. Install it? "))
1613 | (condition-case nil
1614 | (progn
1615 | (package-refresh-contents)
1616 | (package-install 'helm-rg)
1617 | ,@body)
1618 | (error "`helm-rg' is not available. Is MELPA in your `package-archives'?")))
1619 | (t
1620 | (error "Cannot execute search with ripgrep (rg)"))))
1621 |
1622 | (defun helm-projectile--rg-1 (directory input)
1623 | "Call `helm-rg' with DIRECTORY.
1624 | DIRECTORY is the directory to search in. INPUT is the value of default
1625 | input to use for the search."
1626 | (let* ((helm-rg-prepend-file-name-line-at-top-of-matches nil)
1627 | (helm-rg-include-file-on-every-match-line t)
1628 | (helm-rg-default-directory 'default)
1629 | (default-directory directory)
1630 | (helm-rg--extra-args
1631 | (if (helm-projectile--projectile-ignore-strategy)
1632 | (mapcan (lambda (path) (list "--glob" path))
1633 | (mapcar (lambda (path)
1634 | (concat "!" path))
1635 | (append (helm-projectile--ignored-files)
1636 | (helm-projectile--ignored-directories))))
1637 | helm-rg--extra-args)))
1638 | (helm-rg input nil)))
1639 |
1640 | ;;;###autoload
1641 | (defun helm-projectile-rg ()
1642 | "Projectile version of `helm-rg'.
1643 | When package `helm-rg' is not installed and `helm-grep-ag-command' is
1644 | pointing to ripgrep (rg) then use `helm-projectile-ag'. Otherwise if
1645 | `helm-projectile-ask-to-install-helm-rg' is non nil then ask to install
1646 | package `helm-rg'."
1647 | (interactive)
1648 | (helm-projectile--with-helm-rg
1649 | #'helm-projectile-ag
1650 | (if (projectile-project-p)
1651 | (helm-projectile--rg-1 (projectile-project-root)
1652 | (helm-projectile-rg--region-selection))
1653 | (error "You're not in a project"))))
1654 |
1655 | (defun helm-projectile--switch-project-and-rg-action (directory)
1656 | "Switch to a project containing DIRECTORY then run rg in the DIRECTORY.
1657 | When package `helm-rg' is not installed and `helm-grep-ag-command' is
1658 | pointing to ripgrep (rg) then use
1659 | `helm-projectile--switch-project-and-ag-action'. Otherwise if
1660 | `helm-projectile-ask-to-install-helm-rg' is non nil then ask to install
1661 | package `helm-rg'."
1662 | (helm-projectile--with-helm-rg
1663 | (lambda ()
1664 | (helm-projectile--switch-project-and-ag-action directory))
1665 | (if (and (file-directory-p directory)
1666 | (projectile-project-p directory))
1667 | (let* ((input (helm-projectile-rg--region-selection))
1668 | (projectile-switch-project-action
1669 | (lambda ()
1670 | (helm-projectile--rg-1 (file-truename directory) input))))
1671 | (projectile-switch-project-by-name (projectile-project-root directory)))
1672 | (error (if (file-directory-p directory)
1673 | (format "Directory %s is not in any project!" directory)
1674 | (format "Not a directory %s" directory))))))
1675 |
1676 | (defun helm-projectile-commander-bindings ()
1677 | "Define Helm versions of Projectile commands in `projectile-commander'."
1678 | (def-projectile-commander-method ?a
1679 | "Run ack on project."
1680 | (call-interactively 'helm-projectile-ack))
1681 |
1682 | (def-projectile-commander-method ?A
1683 | "Find ag on project."
1684 | (call-interactively 'helm-projectile-ag))
1685 |
1686 | (def-projectile-commander-method ?f
1687 | "Find file in project."
1688 | (helm-projectile-find-file))
1689 |
1690 | (def-projectile-commander-method ?b
1691 | "Switch to project buffer."
1692 | (helm-projectile-switch-to-buffer))
1693 |
1694 | (def-projectile-commander-method ?d
1695 | "Find directory in project."
1696 | (helm-projectile-find-dir))
1697 |
1698 | (def-projectile-commander-method ?g
1699 | "Run grep on project."
1700 | (helm-projectile-grep))
1701 |
1702 | (def-projectile-commander-method ?s
1703 | "Switch project."
1704 | (helm-projectile-switch-project))
1705 |
1706 | (def-projectile-commander-method ?e
1707 | "Find recently visited file in project."
1708 | (helm-projectile-recentf))
1709 |
1710 | (def-projectile-commander-method ?V
1711 | "Find dirty projects."
1712 | (helm-projectile-browse-dirty-projects)))
1713 |
1714 | ;;;###autoload
1715 | (defun helm-projectile-toggle (toggle)
1716 | "Toggle Helm version of Projectile commands.
1717 | When TOGGLE is greater than 0 turn Helm version of Projectile commands
1718 | on. When TOGGLE is is less or equal to 0 turn Helm version of commands
1719 | off."
1720 | (if (> toggle 0)
1721 | (progn
1722 | (when (eq projectile-switch-project-action #'projectile-find-file)
1723 | (setq projectile-switch-project-action #'helm-projectile-find-file))
1724 | (define-key projectile-mode-map [remap projectile-find-other-file] #'helm-projectile-find-other-file)
1725 | (define-key projectile-mode-map [remap projectile-find-other-file-other-window] #'helm-projectile-find-other-file-other-window)
1726 | (define-key projectile-mode-map [remap projectile-find-other-file-other-frame] #'helm-projectile-find-other-file-other-frame)
1727 | (define-key projectile-mode-map [remap projectile-find-file] #'helm-projectile-find-file)
1728 | (define-key projectile-mode-map [remap projectile-find-file-other-window] #'helm-projectile-find-file-other-window)
1729 | (define-key projectile-mode-map [remap projectile-find-file-other-frame] #'helm-projectile-find-file-other-frame)
1730 | (define-key projectile-mode-map [remap projectile-find-file-in-known-projects] #'helm-projectile-find-file-in-known-projects)
1731 | (define-key projectile-mode-map [remap projectile-find-file-dwim] #'helm-projectile-find-file-dwim)
1732 | (define-key projectile-mode-map [remap projectile-find-file-dwim-other-window] #'helm-projectile-find-file-dwim-other-window)
1733 | (define-key projectile-mode-map [remap projectile-find-file-dwim-other-frame] #'helm-projectile-find-file-dwim-other-frame)
1734 | (define-key projectile-mode-map [remap projectile-find-dir] #'helm-projectile-find-dir)
1735 | (define-key projectile-mode-map [remap projectile-find-dir-other-window] #'helm-projectile-find-dir-other-window)
1736 | (define-key projectile-mode-map [remap projectile-find-dir-other-frame] #'helm-projectile-find-dir-other-frame)
1737 | (define-key projectile-mode-map [remap projectile-switch-project] #'helm-projectile-switch-project)
1738 | ;; At the time of writing projectile didn't have neither
1739 | ;; `projectile-switch-to-project-other-window' nor
1740 | ;; `projectile-switch-to-project-other-frame' (hopefully these will be
1741 | ;; names should they be added). Adding `helm-projectile' bindings in a
1742 | ;; - hopefully - backward compatible way, by setting up keys in
1743 | ;; `projectile-command-map'.
1744 | (if (where-is-internal 'projectile-switch-project-other-window projectile-mode-map nil t t)
1745 | (define-key projectile-mode-map [remap projectile-switch-project-other-window] #'helm-projectile-switch-project-other-window)
1746 | (define-key projectile-command-map (kbd "4 p") #'helm-projectile-switch-project-other-window))
1747 | (if (where-is-internal 'projectile-switch-project-other-frame projectile-mode-map nil t t)
1748 | (define-key projectile-mode-map [remap projectile-switch-project-other-frame] #'helm-projectile-switch-project-other-frame)
1749 | (define-key projectile-command-map (kbd "5 p") #'helm-projectile-switch-project-other-frame))
1750 | (define-key projectile-mode-map [remap projectile-recentf] #'helm-projectile-recentf)
1751 | (define-key projectile-mode-map [remap projectile-switch-to-buffer] #'helm-projectile-switch-to-buffer)
1752 | (define-key projectile-mode-map [remap projectile-switch-to-buffer-other-window] #'helm-projectile-switch-to-buffer-other-window)
1753 | (define-key projectile-mode-map [remap projectile-switch-to-buffer-other-frame] #'helm-projectile-switch-to-buffer-other-frame)
1754 | (define-key projectile-mode-map [remap projectile-grep] #'helm-projectile-grep)
1755 | (define-key projectile-mode-map [remap projectile-ack] #'helm-projectile-ack)
1756 | (define-key projectile-mode-map [remap projectile-ag] #'helm-projectile-ag)
1757 | (define-key projectile-mode-map [remap projectile-ripgrep] #'helm-projectile-rg)
1758 | (define-key projectile-mode-map [remap projectile-browse-dirty-projects] #'helm-projectile-browse-dirty-projects)
1759 | (helm-projectile-commander-bindings))
1760 | (progn
1761 | (when (eq projectile-switch-project-action #'helm-projectile-find-file)
1762 | (setq projectile-switch-project-action #'projectile-find-file))
1763 | (define-key projectile-mode-map [remap projectile-find-other-file] nil)
1764 | (define-key projectile-mode-map [remap projectile-find-other-file-other-window] nil)
1765 | (define-key projectile-mode-map [remap projectile-find-other-file-other-frame] nil)
1766 | (define-key projectile-mode-map [remap projectile-find-file] nil)
1767 | (define-key projectile-mode-map [remap projectile-find-file-other-window] nil)
1768 | (define-key projectile-mode-map [remap projectile-find-file-other-frame] nil)
1769 | (define-key projectile-mode-map [remap projectile-find-file-in-known-projects] nil)
1770 | (define-key projectile-mode-map [remap projectile-find-file-dwim] nil)
1771 | (define-key projectile-mode-map [remap projectile-find-file-dwim-other-window] nil)
1772 | (define-key projectile-mode-map [remap projectile-find-file-dwim-other-frame] nil)
1773 | (define-key projectile-mode-map [remap projectile-find-dir] nil)
1774 | (define-key projectile-mode-map [remap projectile-find-dir-other-window] nil)
1775 | (define-key projectile-mode-map [remap projectile-find-dir-other-frame] nil)
1776 | (define-key projectile-mode-map [remap projectile-switch-project] nil)
1777 | (if (where-is-internal 'helm-projectile-switch-project-other-window projectile-command-map nil t t)
1778 | (define-key projectile-mode-map (kbd "4 p") nil)
1779 | (define-key projectile-mode-map [remap projectile-switch-project-other-window] nil))
1780 | (if (where-is-internal 'helm-projectile-switch-project-other-frame projectile-command-map nil t t)
1781 | (define-key projectile-mode-map (kbd "5 p") nil)
1782 | (define-key projectile-mode-map [remap projectile-switch-project-other-frame] nil))
1783 | (define-key projectile-mode-map [remap projectile-recentf] nil)
1784 | (define-key projectile-mode-map [remap projectile-switch-to-buffer] nil)
1785 | (define-key projectile-mode-map [remap projectile-switch-to-buffer-other-window] nil)
1786 | (define-key projectile-mode-map [remap projectile-switch-to-buffer-other-frame] nil)
1787 | (define-key projectile-mode-map [remap projectile-grep] nil)
1788 | (define-key projectile-mode-map [remap projectile-ag] nil)
1789 | (define-key projectile-mode-map [remap projectile-ripgrep] nil)
1790 | (define-key projectile-mode-map [remap projectile-browse-dirty-projects] nil)
1791 | (projectile-commander-bindings))))
1792 |
1793 | ;;;###autoload
1794 | (defun helm-projectile (&optional arg)
1795 | "Use projectile with Helm instead of ido.
1796 |
1797 | With a prefix ARG invalidates the cache first.
1798 | If invoked outside of a project, displays a list of known projects to jump."
1799 | (interactive "P")
1800 | (if (not (projectile-project-p))
1801 | (helm-projectile-switch-project arg)
1802 | (projectile-maybe-invalidate-cache arg)
1803 | (let ((helm-ff-transformer-show-only-basename nil))
1804 | (helm :sources helm-projectile-sources-list
1805 | :buffer "*helm projectile*"
1806 | :truncate-lines helm-projectile-truncate-lines
1807 | :prompt (projectile-prepend-project-name (if (projectile-project-p)
1808 | "pattern: "
1809 | "Switch to project: "))))))
1810 |
1811 | ;;;###autoload
1812 | (eval-after-load 'projectile
1813 | '(progn
1814 | (define-key projectile-command-map (kbd "h") #'helm-projectile)))
1815 |
1816 | (provide 'helm-projectile)
1817 |
1818 | ;;; helm-projectile.el ends here
1819 |
--------------------------------------------------------------------------------