├── 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 | [![MELPA](http://melpa.org/packages/helm-projectile-badge.svg)](http://melpa.org/#/helm-projectile) 3 | [![MELPA Stable](http://stable.melpa.org/packages/helm-projectile-badge.svg)](http://stable.melpa.org/#/helm-projectile) 4 | [![Patreon](https://img.shields.io/badge/patreon-donate-orange.svg)](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 | ![Helm-Projectile Screenshot](screenshots/helm-projectile.png) 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 | --------------------------------------------------------------------------------