= Configuration In the typical style of Emacs, Projectile is *extremely* configurable. Pretty much every aspect of its behaviour can be tweaked or extended. In this section we'll go over some of the most common things you might want to fine-tune to make Projectile fit your workflow better. == Project indexing method Projectile has three 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 two (`hybrid` and `alien`) rely on external commands like `find`, `git`, etc to obtain the list of files in a project. The `alien` indexing method maximizes the speed of the `hybrid` indexing method. This means that Projectile will not do any processing or sorting of the files returned by the external commands and you're going to get the maximum performance possible. This behaviour makes a lot of sense for most people, as they'd typically be putting ignores in their VCS config (e.g. `.gitignore`) and won't care about any additional ignores/unignores/sorting that Projectile might also provide. NOTE: By default the `alien` method is used on all operating systems except Windows. Prior to Projectile 2.0 `hybrid` used to be the default (but to make things confusing `hybrid` used to be known as `alien` back then). To force the use of native indexing in all operating systems: [source,elisp] ---- (setq projectile-indexing-method 'native) ---- To force the use of hybrid indexing in all operating systems: [source,elisp] ---- (setq projectile-indexing-method 'hybrid) ---- To force the use of alien indexing in all operating systems: [source,elisp] ---- (setq projectile-indexing-method 'alien) ---- This can speed up Projectile in Windows significantly (especially on big projects). The disadvantage of this method is that it's not well supported on Windows systems, as it requires setting up some Unix utilities there. If there's a problem, you can always use `native` indexing mode. === Alien indexing The alien indexing works in a pretty simple manner - it simply shells out to a command that returns the list of files within a project. For version-controlled projects by default Projectile will use the VCS itself to obtain the list of files. As an example, here is the command that Projectile uses for Git projects: ---- git ls-files -zco --exclude-standard ---- For every supported VCS there's a matching Projectile defcustom holding the command to invoke for it: |=== | Variable | VCS | `projectile-git-command` | Git | `projectile-hg-command` | Mercurial | `projectile-svn-command` | Subversion | `projectile-bzr-command` | Bazaar | `projectile-darcs-command` | Darcs | `projectile-fossil-command` | Fossil | `projectile-pijul-command` | Pijul | `projectile-sapling-command` | Sapling | `projectile-jj-command` | Jujutsu |=== There are also two Git-specific commands for listing submodules and ignored files: [source,elisp] ---- ;; Command to list git submodules (set to nil to disable) (setq projectile-git-submodule-command "git submodule --quiet foreach 'echo $displaypath' | tr '\\n' '\\0'") ;; Command to get git-ignored files (setq projectile-git-ignored-command "git ls-files -zcoi --exclude-standard") ---- WARNING: If you ever decide to tweak those keep in mind that the command should always be returning the list of files **relative** to the project root and the resulting file list should be 0-delimited (as opposed to newline delimited). For non-VCS projects Projectile will invoke whatever is in `projectile-generic-command`. The default chooses `fd` when it's installed and falls back to `find`: [source,elisp] ---- ;; Effective default value of projectile-generic-command, picked at load time: ;; when fd is on PATH: "fd . -0 --type f --color=never --strip-cwd-prefix" ;; otherwise: "find . -type f | cut -c3- | tr '\\n' '\\0'" ---- TIP: It's a great idea to install https://github.com/sharkdp/fd[fd] which is much faster than `find`. If `fd` is found, projectile will use it as a replacement for `find` for non-VCS projects. WARNING: The `find` fallback does *not* exclude common build/cache directories (`.git`, `node_modules`, `target`, `build`, …); a non-VCS project under `alien` indexing on a host without `fd` will list everything. Either install `fd`, switch to `hybrid` indexing so `projectile-globally-ignored-directories` applies, or override `projectile-generic-command` with a tighter recipe. By default, `fd` is also used inside Git repositories (instead of `git ls-files`), because `git ls-files` has the limitation that it lists deleted files until the deletions are staged. With `fd`, deleted files disappear from the listing immediately; with `git ls-files`, Projectile post-filters the listing against `git ls-files -zd` to hide deletions until they're staged. You can control this with `projectile-git-use-fd`: [source,elisp] ---- ;; Disable fd in git repos (use git ls-files instead) (setq projectile-git-use-fd nil) ---- You can also customize the path to the `fd` executable and the arguments passed to it: [source,elisp] ---- ;; Use a custom fd path (setq projectile-fd-executable "/usr/local/bin/fd") ;; Customize the fd arguments used in git repos (setq projectile-git-fd-args "-H -0 -E .git -tf --strip-cwd-prefix -c never") ---- === Hybrid indexing The `hybrid` method runs the same external command as `alien` and then post-processes the result with Projectile's filtering rules. In other words, it's `alien` plus a second pass that applies: * `.projectile` (dirconfig) ignore/keep/ensure entries * `projectile-globally-ignored-files`, `projectile-globally-ignored-directories`, `projectile-globally-ignored-file-suffixes`, and `projectile-global-ignore-file-patterns` * `projectile-globally-unignored-files` and `projectile-globally-unignored-directories` * The configured sort order from `projectile-sort-order` This is the right choice when your VCS lists more files than you want Projectile to surface (e.g. you want to drop `*.elc` even though they're checked in), or when you rely on `.projectile` to keep/ignore subtrees inside an otherwise large repository. It's slower than `alien` because of the extra pass, but it remains substantially faster than `native` for big projects since the file list itself is still produced by the external tool. === Indexing method comparison Not all Projectile features apply to every indexing method. The table below summarises which configuration knobs take effect under which mode: [cols="3,1,1,1", options="header"] |=== | Feature | `native` | `hybrid` | `alien` | Project file listing speed | Slow (Elisp) | Fast | Fastest | Honors `.projectile` (dirconfig) `+`/`-`/`!` rules | Yes | Yes | No | Honors `projectile-globally-ignored-files` / `-directories` / `-file-suffixes` / `projectile-global-ignore-file-patterns` | Yes | Yes | No (rely on the VCS / `fd` / `find` ignores instead) | Honors `projectile-globally-unignored-*` | Yes | Yes | No | Sorts via `projectile-sort-order` | Yes | Yes | No (returned in the external tool's order) | File caching enabled by default | Yes | No | No | Works on Windows out of the box | Yes | Requires Unix utilities | Requires Unix utilities |=== NOTE: When `alien` is in use and a non-empty `.projectile` file is present, Projectile emits a one-time warning so the silent bypass doesn't catch you off guard. Switch to `hybrid` (or `native`) if you need those rules applied; set `projectile-warn-when-dirconfig-is-ignored` to nil to silence. == Sorting You can choose how Projectile sorts files by customizing `projectile-sort-order`. NOTE: If Alien indexing is set, files are not sorted by Projectile at all. The default is to not sort files: [source,elisp] ---- (setq projectile-sort-order 'default) ---- To sort files by recently opened: [source,elisp] ---- (setq projectile-sort-order 'recentf) ---- To sort files by recently active buffers and then recently opened files: [source,elisp] ---- (setq projectile-sort-order 'recently-active) ---- // These URLs below are in HTML so that the parentheses in the URL fragments are properly recognised. To sort files by https://en.wikipedia.org/wiki/MAC_times#Modification_time_(mtime)[modification time] (mtime): [source,elisp] ---- (setq projectile-sort-order 'modification-time) ---- To sort files by https://en.wikipedia.org/wiki/MAC_times#Access_time_(atime)[access time] (atime): [source,elisp] ---- (setq projectile-sort-order 'access-time) ---- == Caching === Project files Since indexing a big project is not exactly quick (especially in Emacs Lisp), Projectile supports caching of the project's files. The caching is enabled by default whenever native indexing is enabled. To enable caching unconditionally use this snippet of code: [source,elisp] ---- (setq projectile-enable-caching t) ---- At this point you can try out a Projectile command such as kbd:[s-p f] (kbd:[M-x] `projectile-find-file` kbd:[RET]). Running kbd:[C-u s-p f] will invalidate the cache prior to prompting you for a file to jump to. Pressing kbd:[s-p z] will add the currently visited file to the cache for current project. Generally files created outside Emacs will be added to the cache automatically the first time you open them. Normally the cache lasts for the duration of your Emacs session. If you want to cache to persist between Emacs sessions you should set this option to `'persistent`. [source,elisp] ---- (setq projectile-enable-caching 'persistent) ---- Now the project cache is persistent and will be preserved during Emacs restarts. Each project gets its own cache file, that will be placed in the root folder of the project. The name of the cache file is `.projectile-cache.eld` by default, but you can tweak it if you want to: [source,elisp] ---- (setq projectile-cache-file "foo.eld") ---- The cache file will be loaded automatically in memory the first time you trigger a "find file" operation for the project it belongs to. You can purge an individual file from the cache with `M-x projectile-purge-file-from-cache` or an entire directory with `M-x projectile-purge-dir-from-cache`. NOTE: Prior to Projectile 2.9 the cache for all projects was serialized to the same file. In Projectile 2.9 this was changed and now each project has its own cache file relative to the project's root directory. When `projectile-mode` is enabled Projectile will auto-update the project cache when files within are added or deleted from within Emacs. (this is achieved by file hooks) This behavior can be disabled like this: [source,elisp] ---- (setq projectile-auto-update-cache nil) ---- You can also set the project files cache to expire after a given number of seconds: [source,elisp] ---- ;; Expire the project files cache after 5 minutes (setq projectile-files-cache-expire (* 5 60)) ---- By default `projectile-files-cache-expire` is `nil`, meaning the cache never expires automatically. One last thing - the project cache will be auto-invalidated if you're using `.projectile` and it's last modification time is more recent than the time at which the cache file was last updated. === File exists cache Projectile does many file existence checks since that is how it identifies a project root. Normally this is fine, however in some situations the file system speed is much slower than usual and can make emacs "freeze" for extended periods of time when opening files and browsing directories. The most common example would be interfacing with remote systems using TRAMP/ssh. By default all remote file existence checks are cached To disable remote file exists cache that use this snippet of code: [source,elisp] ---- (setq projectile-file-exists-remote-cache-expire nil) ---- To change the remote file exists cache expire to 10 minutes use this snippet of code: [source,elisp] ---- (setq projectile-file-exists-remote-cache-expire (* 10 60)) ---- You can also enable the cache for local file systems, that is normally not needed but possible: [source,elisp] ---- (setq projectile-file-exists-local-cache-expire (* 5 60)) ---- == Using Projectile Commands Outside of Projects Directories Normally, you'd be using Projectile's commands from within some project directory. If, however, you invoke a command outside of a project, by default you'll be prompted for a project to switch to. That behavior is controlled by `projectile-require-project-root`. You can make Projectile simply raise an error outside of Project folders like this: [source,elisp] ---- (setq projectile-require-project-root t) ---- If you want Projectile to be usable in every directory (even without the presence of project file): [source,elisp] ---- (setq projectile-require-project-root nil) ---- With this setting if you invoke Projectile outside of a project, the current directory will be considered by Projectile the project root. TIP: This might not be a great idea if you start Projectile in your home folder for instance. :-) == Switching projects By default, projectile does not include the current project in the list when switching projects. If you want to include the current project, customize variable `projectile-current-project-on-switch`. When running `projectile-switch-project` (kbd:[s-p p]) and `projectile-switch-open-project` (kbd:[s-p q]) Projectile invokes the command specified in `projectile-switch-project-action` (by default it is `projectile-find-file`). TIP: Invoking the command with a prefix argument (kbd:[C-u s-p p] or kbd:[C-u s-p q]) will trigger the Projectile Commander, which gives you quick access to most common commands you might want to invoke on a project. Depending on your personal workflow and habits, you may prefer to alter the value of `projectile-switch-project-action`: === `projectile-find-file` NOTE: This is the default. With this setting, once you have selected your project via Projectile's completion system (see below), you will remain in the completion system to select a file to visit. `projectile-find-file` is capable of retrieving files in all sub-projects under the project root, such as Git submodules. Currently, only Git is supported. Support for other VCS will be added in the future. === `projectile-commander` NOTE: This is the recommended option for people who find themselves often needing to invoke a different action on project switch. With this setting, after selecting a project to switch to, you'll be prompted to specify the action to take with a 1-character mnemonic. |=== | Keybinding | Description | kbd:[?] | Commander help buffer. | kbd:[D] | Open project root in dired. | kbd:[R] | Regenerate the project's etags/gtags. | kbd:[T] | Find test file in project. | kbd:[V] | Browse dirty projects | kbd:[a] | Run ag on project. | kbd:[b] | Switch to project buffer. | kbd:[d] | Find directory in project. | kbd:[e] | Find recently visited file in project. | kbd:[f] | Find file in project. | kbd:[g] | Run grep on project. | kbd:[j] | Find tag in project. | kbd:[k] | Kill all project buffers. | kbd:[o] | Run multi-occur on project buffers. | kbd:[r] | Replace a string in the project (running with kbd:[C-u] will allow users to select file name patterns and extensions). | kbd:[s] | Switch project. | kbd:[v] | Open project root in vc-dir or magit. |=== === `projectile-find-file-in-known-projects` Similar to `projectile-find-file` but lists all files in all known projects. Since the total number of files could be huge, it is beneficial to enable caching for subsequent usages. === `projectile-find-file-dwim` If point is on a filepath, Projectile first tries to search for that file in project: * If it finds just a file, it switches to that file instantly. This works even if the filename is incomplete, but there's only a single file in the current project that matches the filename at point. For example, if there's only a single file named "projectile/projectile.el" but the current filename is "projectile/proj" (incomplete), projectile-find-file still switches to "projectile/projectile.el" immediately because this is the only filename that matches. * If it finds a list of files, the list is displayed for selecting. A list of files is displayed when a filename appears more than one in the project or the filename at point is a prefix of more than two files in a project. For example, if `projectile-find-file' is executed on a filepath like "projectile/", it lists the content of that directory. If it is executed on a partial filename like "projectile/a", a list of files with character 'a' in that directory is presented. * If it finds nothing, display a list of all files in project for selecting. === `projectile-dired` [source,elisp] ---- (setq projectile-switch-project-action #'projectile-dired) ---- With this setting, once you have selected your project, the top-level directory of the project is immediately opened for you in a dired buffer. === `projectile-find-dir` [source,elisp] ---- (setq projectile-switch-project-action #'projectile-find-dir) ---- With this setting, once you have selected your project, you will remain in Projectile's completion system to select a sub-directory of your project, and then _that_ sub-directory is opened for you in a dired buffer. If you use this setting, then you will probably also want to set [source,elisp] ---- (setq projectile-find-dir-includes-top-level t) ---- in order to allow for the occasions where you want to select the top-level directory. == Known Projects Projectile stores the list of known projects in a file on disk. You can customize its location: [source,elisp] ---- (setq projectile-known-projects-file "~/.emacs.d/projectile-bookmarks.eld") ---- === Ignoring projects You can prevent certain projects from being added to the known projects list: [source,elisp] ---- ;; A list of projects to ignore (setq projectile-ignored-projects '("/tmp/" "~/.emacs.d/")) ---- For more flexible filtering, set `projectile-ignored-project-function` to a predicate that receives a project root and returns non-nil if the project should be ignored: [source,elisp] ---- ;; Ignore all remote (TRAMP) projects (setq projectile-ignored-project-function #'file-remote-p) ---- === Automatic tracking By default, Projectile automatically adds projects to the known projects list whenever you visit a file in a project. You can disable this: [source,elisp] ---- (setq projectile-track-known-projects-automatically nil) ---- == Completion Options Projectile supports all major minibuffer completion packages that exist today. Normally it will just detect what you're using (e.g. `ivy`), but you can force a particular completion system via the variable `projectile-completion-system`. NOTE: Historically `projectile-completion-system` defaulted to `ido`, but this was changed in version 2.3. You may need to enable `ido-mode` in your Emacs configuration if updating from an older version of Projectile. === Auto (default) By default Projectile detects the completion system in use, based on the mode variables `ido-mode`, `ivy-mode` and `helm-mode`. If none of those is activated, the `default` completion system is used. Unless for some reason you want to use a different completion system for Projectile than for the rest of Emacs (e.g. you normally use `icomplete-mode`, but want to use `ido-mode` with Projectile), you'll probably don't want to select a particular completion system manually. === Basic (Emacs's default) Select this option if you want to use Emacs's standard completion (based on `completing-read`): [source,elisp] ---- (setq projectile-completion-system 'default) ---- TIP: You might want to combine default completion with `icomplete-mode` for optimum results. Emacs 27 added `fido-mode` to `icomplete`. If you are using `fido-mode`, Projectile will use the `default` completion system. The same holds for `vertico` which also rely on the `default` completion system. === Ido The `ido` completion system is extremely popular and it is built into Emacs. [source,elisp] ---- (setq projectile-completion-system 'ido) ---- As noted above, Projectile will auto-detect `ido-mode` if enabled, so the above configuration is not needed most of the time. TIP: As already noted above if you're going to use the `ido` completion it's **extremely highly** recommended that you install the optional https://github.com/lewang/flx[flx-ido package], which provides a much more powerful alternative to ``ido``'s built-in `flex` matching. === Ivy (recommended) Another completion option is https://github.com/abo-abo/swiper[ivy]: [source,elisp] ---- (setq projectile-completion-system 'ivy) ---- As noted above, Projectile will auto-detect `ivy-mode` if enabled, so the above configuration is not needed most of the time. === Custom Completion Function You can also set `projectile-completion-system` to a function: [source,elisp] ---- (setq projectile-completion-system #'my-custom-completion-fn) (setq projectile-completion-system (lambda (prompt choices) ;; ... )) ---- An example of a custom completion function is https://gist.github.com/rejeep/5933343[this one], which only show the file name (not including path) and if the file selected is not unique, another completion with names relative to project root appears. == Project-specific Compilation Buffers This affects all commands built on top of `projectile--run-project-cmd` like: - `projectile-configure-project` - `projectile-run-project` - `projectile-test-project` - `projectile-install-project` - `projectile-package-project` Normally, the buffers created by those commands would be shared (overwritten) between projects, but it's also possible to make the compilation buffer names project-specific. This requires that the user set: [source,elisp] ---- (setq projectile-per-project-compilation-buffer t) ---- Both of these degrade properly when not inside a project. == Limit the number of project file buffers Projectile can be configured to keep a maximum number of file buffers of a project that are opened at one point. The custom variable `projectile-max-buffer-count` can be set to an integer that will be the buffer count cap. If this limit is reached, by opening a new file, Projectile will close the least recent buffer of the current project. If the variable is `nil`, there will be no cap on the buffer count. [source,elisp] ---- (setq projectile-max-file-buffer-count 10) ---- Note that special project buffers (e.g. compilation, `dired`, etc) are not affected by this setting. == Tags To be able to regenerate a project's tags via `projectile-tags-command`, you should install and add to the PATH http://ctags.sourceforge.net/[Exuberant Ctags] instead of a plain ctags, which ships with Emacs distribution. You can customize the tags filename and the backend used for tag navigation: [source,elisp] ---- ;; The name of the tags file (default: "TAGS") (setq projectile-tags-file-name "TAGS") ;; Tag backend for projectile-find-tag ;; Options: 'auto, 'xref, 'ggtags, 'etags-select, 'find-tag (setq projectile-tags-backend 'auto) ---- When set to `auto` (the default), `projectile-find-tag` will choose a backend automatically with the following preference order: ggtags -> xref -> etags-select -> `find-tag`. == Idle Timer Projectile can be configured to run the hook `projectile-idle-timer-hook` every time Emacs is in a project and has been idle for `projectile-idle-timer-seconds` seconds (default is 30 seconds). To enable this feature, run: ---- M-x customize-group RET projectile RET ---- and set `projectile-enable-idle-timer` to non-nil. By default, `projectile-idle-timer-hook` runs `projectile-regenerate-tags`. Add additional functions to the hook using `add-hook`: [source,elisp] ---- (add-hook 'projectile-idle-timer-hook #'my-projectile-idle-timer-function) ---- == Mode line indicator By default the minor mode indicator of Projectile appears in the form " Projectile[ProjectName:ProjectType]". This is configurable via several custom variables: * `projectile-mode-line-prefix` (by default " Projectile") controls the static part of the mode-line * `projectile-dynamic-mode-line` (by default `t`) controls whether to display the project name & type part of the mode-line * `projectile-mode-line-function` (by default `projectile-default-mode-line`) controls the actual function to be invoked to generate the mode-line. If you'd like to show different info you should supply a custom function to replace the default, for example `(setq projectile-mode-line-function '(lambda () (format " Proj[%s]" (projectile-project-name))))` NOTE: The project name & type will not appear when editing remote files (via TRAMP), as recalculating the project name is a fairly slow operation there and would slow down a bit opening the files. They will also not appear for non-file buffers, as they get updated via `find-file-hook`. == Searching By default, Projectile uses Emacs's built-in `grep` for searching. In Git projects you can use `vc-git-grep` instead, which tends to be faster as it respects `.gitignore`: [source,elisp] ---- (setq projectile-use-git-grep t) ---- == Project name The project name displayed in the mode line and used in buffer names is determined by `projectile-project-name-function`. The default implementation uses the project root directory name, but you can provide your own: [source,elisp] ---- (setq projectile-project-name-function (lambda (project-root) (file-name-nondirectory (directory-file-name project-root)))) ---- NOTE: If the variable `projectile-project-name` is set (e.g. via `.dir-locals.el`), it takes precedence over the function. == Dirty projects `projectile-browse-dirty-projects` (kbd:[s-p V]) shows projects with uncommitted VCS changes. The variable `projectile-vcs-dirty-state` controls which VC states are considered dirty: [source,elisp] ---- ;; Default value — all states (setq projectile-vcs-dirty-state '("edited" "unregistered" "needs-update" "needs-merge" "unlocked-changes" "conflict")) ;; Only consider edited and unregistered files as dirty (setq projectile-vcs-dirty-state '("edited" "unregistered")) ---- The possible states are those defined in `vc.el`. == Hooks Projectile provides several hooks for integrating with your workflow: [source,elisp] ---- ;; Run after a file is opened via projectile-find-file (add-hook 'projectile-find-file-hook #'my-find-file-setup) ;; Run after a directory is opened via projectile-find-dir (add-hook 'projectile-find-dir-hook #'my-find-dir-setup) ;; Run right before switching projects (add-hook 'projectile-before-switch-project-hook #'my-before-switch-setup) ---- == Miscellaneous === Verbose messages By default Projectile echoes informational messages (not just errors). To suppress them: [source,elisp] ---- (setq projectile-verbose nil) ---- === Menu bar Projectile adds a menu to the Emacs menu bar by default. To disable it: [source,elisp] ---- (setq projectile-show-menu nil) ---- == Project-type-specific Configuration === CMake Projectile supports https://cmake.org/cmake/help/git-stage/manual/cmake-presets.7.html[CMake presets]. Preset support is disabled by default, but can be enabled by setting `projectile-enable-cmake-presets` to non-nil. With preset-support enabled Projectile will parse the preset files and present the command-specific presets when executing a lifecycle command. In addition a `*no preset*` option is included for entering the command manually. NOTE: Preset support requires a CMake version that supports preset and for `json-parse-buffer` to be available.