Exploring large projects with Projectile and Helm Projectile (original) (raw)

Table of Contents

Let's start with a few demos.

Demos

Select and open multiple files

Demo (begins when START DEMO appears in minibuffer):

helm-projectile-find-files-1.gif

In the demo, a few files are narrowed and marked, then opened. I ranhelm-mini to display a list of opened txt files.

Open file at point anywhere

Demo (begins when START DEMO appears in minibuffer):

helm-projectile-find-files-dwim-1.gif

As you can see in the demo, Projectile can jump anywhere, even if there's only a filename without path. It works everywhere, even in text file.

Copy files anywhere

Demo (begins when START DEMO appears in minibuffer):

helm-projectile-find-file-copy.gif

In the demo, I marked a few files with helm-projectile-find-file, then switched to action menu and selected Copy file(s) action (it can be executed with a key binding, but I want to show you the action as a proof). Then, I copy to the directory ~/test. Notice that I got prompted to that directory immediately, because another Dired buffer is below. After I confirmed the action, 4 files got copied and displayed at the lower Dired buffer of ~/test.

Delete files anywhere

Demo (begins when START DEMO appears in minibuffer):

helm-projectile-find-file-delete.gif

As you see, you don't have to navigate to a file to delete it. All project files are always at your finger tip for you to delete!

Switch between current file and other files with same names but different extensions

Demo (begins when START DEMO appears in minibuffer):

helm-projectile-find-other-file.gif

NOTE: In the demo, you can see me narrow to a small set of files, then mark them and perform some action (such as copy or delete). However, if you want to continue to mark other files, you can delete the previous search pattern and enter another search pattern to narrow to another set of files and continue marking those files without losing your old markings. For example, at the beginning I want to mark files with the string "aaaa" in it; after done marking those files, I delete "aaaa" and enter "bbbb" to narrow to this set of files and mark the needed files, without losing the markings on the files contains "aaaa".

What is Projectile?

From the homepage:

Projectile is a project interaction library for Emacs. Its goal is to provide a nice set of features operating on a project level without introducing external dependencies(when feasible). For instance - finding project files has a portable implementation written in pure Emacs Lisp without the use of GNU find (but for performance sake an indexing mechanism backed by external commands exists as well).

Projectile tries to be practical - portability is great, but if some external tools could speed up some task substantially and the tools are available, Projectile will leverage them.

By default, git, mercurial, darcs and bazaar are considered projects. So are lein, maven, sbt, scons, rebar andbundler. If you want to mark a folder manually as a project just create an empty .projectile file in it.

I will help you quickly benefit from Projectile andhelm-projectile - an extension of Projectile that uses helminterface for many Projectile commands that provides even more features not available in stock Projectile - in a practical task like exploring the Linux kernel.

This tutorial is about how to use Projectile along with Helm. There are other completion systems such as grizzl or ido with flx, both use fuzzy matching. Helm also does almost the same thing, except that if you insert a space anywhere in the prompt, Helm reverts back to its old matching behavior: exact matching. Otherwise, Helm uses fuzzy-matching by default, and it works with many tens of thousands of files.

Installation and configuration

You also install Projectile via MELPA and setup:

(projectile-global-mode) (setq projectile-completion-system 'helm) (helm-projectile-on)

All Projectile commands has prefix C-c p.

FOR WINDOWS USERS:

According to Projectile homepage:

Projectile has two modes of operation - one is portable and is implemented in Emacs Lisp(therefore it's native to Emacs and is known as the native indexing method) and the other relies on external commands like find, git, etc to obtain the list of files in a project.

Since the native indexing mode is much slower, by default the second method is used on all operating systems except Windows.

Using Emacs Lisp for indexing files is really slow on Windows. To enable external indexing, add this setting:

(setq projectile-indexing-method 'alien)

The alien indexing method uses external tools (e.g. git, find, etc) to speed up the indexing process.

All-in-one command: helm-projectile, C-c p h

Usage: This command, by default, is the combination of these 5 commands:

Enter project portal: helm-projectile-switch-project, C-c p p

Usage: This is the very first command you need to use before using other commands, because it is the entrance to all of your projects and the only command that can be used outside of a project, aside fromhelm-projectile-find-file-in-known-projects. The command lists all visited projects. If you first use Projectile, you have to visit at least a project supported by Projectile to let it remember the location of this project. The next time you won't have to manually navigate to that project but jump to it instantly using helm-projectile-switch-project.

helm-projectile-switch-project.gif

Available actions:

File management

Command: helm-projectile-find-file, C-c p f

Usage: This command lists all files in a project for users to narrow down to wanted files. Some frequently used actions that cover open, rename, copy, delete,search and other miscelaneous operations. Once you mastered the actions of helm-projectile-find-file, you master the actions of other commands as well since the actions of other commands are just a subset of helm-projectile-find-file actions. All the key bindings associated with actions are only available while a Helm buffer is active. You can think of actions as an mini version ofM-x: only applicable commands are listed, and even those commands have key bindings. Prefix argument can be applied, when possible.

The same Helm interface can be used to search for an action. The first 12 actions are bound from to . You can type the index number to instantly narrow to that action, or simply press respective key.

Open

Move and Rename

Copy and Delete

Search and Replace

Miscelaneous

Command: helm-projectile-find-file-in-known-projects, C-c p F

This command is another one that can be used outside of any project. When executed, it lists all files in all known projects. Depends on your style, use this command orhelm-projectile-switch-project command, when you want to jump to a file. Note that this command could be slow to show you the list of files if there is a large number of files. To speed it up, it is beneficial to enable caching. You will learn about caching at near the end of this tutorial. With caching, Projectile won't have to build up a list of files again; it simply reuses, and show you the list instantly for selecting.

The action menu is the same as helm-projectile-find-file.

Command: helm-projectile-find-file-dwim, C-c p g

Usage: Find file based on context at point (do what you mean):

This command is demonstrated at the beginning: Open file at point anywhere.

Command: helm-projectile-find-dir, C-c p d

Usage: List available directories in the current project.

Available actions:

Command: helm-projectile-recentf, C-c p e

Usage: List recently visited files in current project. The command has a subset of actions in helm-projectile-find-file, so once you mastered the actions in helm-projectile-find-file, you can reuse your knowledge here.

Command: helm-projectile-find-other-file, C-c p a

Usage: Switch between files with the same name but different extensions. With prefix argument C-u, enable flex-matching that match any file that contains the name of current file. The command has a subset of actions in helm-projectile-find-file, so once you mastered the actions inhelm-projectile-find-file, you don't need to learn anything else.

Other file extensions can be customized with the variableprojectile-other-file-alist. The variable looks like this:

'(("cpp" "h" "hpp" "ipp") ("ipp" "h" "hpp" "cpp") ("hpp" "h" "ipp" "cpp") ("cxx" "hxx" "ixx") ("ixx" "cxx" "hxx") ("hxx" "ixx" "cxx") ("c" "h") ("m" "h") ("mm" "h") ("h" "c" "cpp" "ipp" "hpp" "m" "mm") ("cc" "hh") ("hh" "cc") ("vert" "frag") ("frag" "vert") (nil "lock" "gpg") ("lock" "") ("gpg" ""))

Basically just a list of lists. Each lists hold the current file extension as first element and other files' extensions to switch to. For example, the list ("cpp" "h" "hpp" "ipp") means that if your current file is foo.cpp, the command will search for other files with foo as exact name (add prefix C-u for any file that containsfoo) and with extensions .h, .hpp and .ipp; anything but .cpp.

If you want to add more, for example, to switch between html <->js, add to your init file like this:

(add-to-list 'projectile-other-file-alist '("html" "js")) ;; switch from html -> js (add-to-list 'projectile-other-file-alist '("js" "html")) ;; switch from js -> html

The command is already demonstrated in the section Switch between current file and other files with same names but different extensions.

Caching

Usage: In large projects, caching can significantly speedup file and directory listings, making it display instantly. Caching is enabled by:

(setq projectile-enable-caching t)

With caching enabled, even if you use Projectile on your home directory with 30GB, it lists files instantly. Cache is a way to speed up getting files because Projectile only needs to index your project once and reuses this result future usages. In case if your project has new files, you have to add C-u before executing any command to invalidate the cache (except for helm-projectile-find-other-file andprojectile-find-other-file, C-u is reserved for different behaviour), or using standalone command invalidate C-c p i to refresh the whole cache.

Command: projectile-invalidate-cache, C-c p i

Usage: As the command name suggests, it invalidates the current cache and retrieves everything as new.

Command: projectile-cache-current-file, C-c p z

Usage: Add the file of current selected buffer to cache.

Command: projectile-purge-file-from-cache

Usage: Remove a file from the cache. Once removed, you won't see it appear the next time using file related commands. If you delete a file, Projectile automatically removes the file from the cache.

Command: projectile-purge-dir-from-cache

Usage: Remove a directory from the cache.

Virtual directory manager

Now that you know how to manage your project files with Helm and Projectile, it's time to explore this cool feature that is exclusive to Helm Projectile: Virtual Directory. A virtual directory is just a Dired buffer but with files from different directory location assembled into one buffer.

Purpose: Projectile is excellent for file browsing, and can access files anywhere in project. Dired is excellent because it can be used as a file browser, as well as by being a normal buffer, it can be saved as an entry in Bookmark for future sessions. We can combine get the best of both: a logical list of files from anywhere that can be saved for use in the future.

The following are actions to be used for managing a virtual directory when you are in a helm-projectile-find-file session:

Store virtual directories with Bookmark (or Bookmark+)

Using Bookmark (or Bookmark+), you can actually save your virtual Dired buffers and preserve this knowledge for future reference, when you want to quickly review related files to improve or fix bug. You won't have to reconnect related files again, and often it takes quite some time since you could forget many things.

If you haven't learned how to use Bookmark, learn the basics of it with Xah Lee's "Emacs: Using Bookmark Feature" article. I recommend you to install Bookmark+ because it offers much more features. For example, you can write annotations for (C-u a on a bookmark) bookmarks in Org-mode and read the annotation with Org-mode (pressa to open the annotation of a bookmark for reading). To learn all about Bookmark+ features, please refer to Bookmark+ documentation.

Buffer management

Command: helm-projectile-switch-to-buffer, C-c p b

Usage: List all opened buffers in current project. The command has a similar subset of actions in helm-projectile-find-file, so once you mastered the actions in helm-projectile-find-file, except instead of opening files, you open buffers instead.

Search in project

Command: helm-projectile-grep, C-c p s g

This is a replacement command for projectile-grep that uses Helm interface. When a symbol is at point, this command uses that symbol and search at project root for every occurrence of this symbol in all non-ignored files in project. If a region is active, use the region instead.

Demo (begins when START DEMO appears in minibuffer):

helm-projectile-grep.gif

You can specify directory to exclude when searching by customize either one of these variables:

For example, if you have a directory like personal/backup and if you want to ignore directory backup, simply add backup to thegrep-find-ignored-directories orprojectile-globally-ignored-directories. For example, if I useprojectile-globally-ignored-directories, I would do it like this:

(add-to-list 'projectile-globally-ignored-directories "backup")

You can also use C-h v and select either of the above variables and use customize GUI to add your selections.

NOTE: For helm-projectile-grep, when specifying directories to ignore, you must enter only the names of sub-directories, not its full path, either absolute or relative to current project. In the example above, I only add backup, not personal/backup.

Finally, if you quit your current helm-projectile-grep session with current search results and don't want to search things all over again, you can save the result with the action Save results in grep buffer(bound to <f3>). Or you can simply resume the previous buffer withhelm-resume (you should bind it to a key. If your follow my Helm guide, it is bound to C-c h b); if you helm-projectile-grep buffer is not the most recently used Helm buffer, add C-u before runninghelm-resume to select it from the list of all previously Helm buffers of all ran Helm commands.

Command: helm-projectile-ack, C-c p s a

This is a replacement command for projectile-ack. Similar tohelm-projectile-grep, but use ack instead. You must have ackinstalled to be able to use this command. You can also customize these variables to ignore files or directories:

Similarly, you can save results in a grep buffer with the action Save results in grep buffer (bound to <f3>), and resume with C-c h b(add C-u to select from the list of all previous Helm sessions).

NOTE: if you want ack to ignore files/directories, you must use regex pattern. Otherwise it won't work.

Command: helm-projectile-ag, C-c p s s

This is a replacement command for projectile-ag. Similar tohelm-projectile-grep, but use ag insetad. You must have ag andhelm-ag installed to be able to use this command. This command only works with symbol at point, but not region. You can also customize these variables to ignore files or directories:

NOTE: Unlike helm-projectile-grep, you can specify directory with path to ignore. For example, you can only specify sub-directory names to ignore in helm-projectile-grep, i.e. backup notpersonal/backup, but with helm-projectile-ag, either backup orpersonal/backup works fine.

Summary of Keybindings

This chapter summarizes the key bindings introduced in the above chapters.

Key Binding Command Description
C-c p h helm-projectile Helm interface to projectile
C-c p p helm-projectile-switch-project Switches to another projectile project
C-c p f helm-projectile-find-file Lists all files in a project
C-c p F helm-projectile-find-file-in-known-projects Find file in all known projects
C-c p g helm-projectile-find-file-dwim Find file based on context at point
C-c p d helm-projectile-find-dir Lists available directories in current project
C-c p e helm-projectile-recentf Lists recently opened files in current project
C-c p a helm-projectile-find-other-file Switch between files with same name but different extensions
C-c p i projectile-invalidate-cache Invalidate cache
C-c p z projectile-cache-current-file Add the file of current selected buffer to cache
C-c p b helm-projectile-switch-to-buffer List all open buffers in current project
C-c p s g helm-projectile-grep Searches for symbol starting from project root
C-c p s a helm-projectile-ack Same as above but using ack
C-c p s s helm-projectile-ag Same as above but using ag