1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
|
= Projects
== Supported Project Types
One of the main goals of Projectile is to operate on a wide range of project types
without the need for any configuration. To achieve this it contains a lot of
project detection logic and project type specific logic.
=== Version Control Systems
Projectile considers most version-controlled repos to be
a project. Out of the box Projectile supports:
* Git
* Mercurial
* Bazaar
* Subversion
* CVS
* Fossil
* Darcs
=== File markers
Projectile considers many files to denote the root of a project. Usually those files
are the configuration files of various build tools. Out of the box the following are supported:
|===
| File | Project Type
| rebar.config
| Rebar project file
| project.clj
| Leiningen project file
| build.boot
| Boot-clj project file
| deps.edn
| Clojure CLI project file
| SConstruct
| Scons project file
| pom.xml
| Maven project file
| build.sbt
| SBT project file
| build.sc
| Mill project file
| gradlew
| Gradle wrapper script
| build.gradle
| Gradle project file
| .ensime
| Ensime configuration file
| Gemfile
| Bundler file
| requirements.txt
| Pip file
| setup.py
| Setuptools file
| tox.ini
| Tox file
| composer.json
| Composer project file
| Cargo.toml
| Cargo project file
| mix.exs
| Elixir mix project file
| stack.yaml
| Haskell's stack tool based project
| dune-project
| OCaml Dune project file
| info.rkt
| Racket package description file
| DESCRIPTION
| R package description file
| TAGS
| etags/ctags are usually in the root of project
| GTAGS
| GNU Global tags
| configure.in
| autoconf old style
| configure.ac
| autoconf new style
| cscope.out
| cscope
| Makefile
| Make
| WORKSPACE
| Bazel workspace file
|===
There's also Projectile's own `.projectile` which serves both as a project marker
and a configuration file. We'll talk more about later in this section.
== Adding Custom Project Types
If a project you are working on is recognized incorrectly or you want
to add your own type of projects you can add following to your Emacs
initialization code
[source,elisp]
----
(projectile-register-project-type 'npm '("package.json")
:project-file "package.json"
:compile "npm install"
:test "npm test"
:run "npm start"
:test-suffix ".spec")
----
What this does is:
. add your own type of project, in this case `npm` package.
. add a list of files and/or folders in a root of the project that helps to identify the type, in this case it is only `package.json`.
. add _project-file_, which is typically the primary project configuration file. In this case that's `package.json`.
. add _compile-command_, in this case it is `npm install`.
. add _test-command_, in this case it is `npm test`.
. add _run-command_, in this case it is `npm start`.
. add test files suffix for toggling between implementation/test files, in this case it is `.spec`, so the implementation/test file pair could be `service.js`/`service.spec.js` for example.
Let's see a couple of more complex examples.
[source,elisp]
----
;; Ruby + RSpec
(projectile-register-project-type 'ruby-rspec '("Gemfile" "lib" "spec")
:project-file "Gemfile"
:compile "bundle exec rake"
:src-dir "lib/"
:test "bundle exec rspec"
:test-dir "spec/"
:test-suffix "_spec")
;; Ruby + Minitest
(projectile-register-project-type 'ruby-test '("Gemfile" "lib" "test")
:project-file "Gemfile"
:compile"bundle exec rake"
:src-dir "lib/"
:test "bundle exec rake test"
:test-suffix "_test")
;; Rails + Minitest
(projectile-register-project-type 'rails-test '("Gemfile" "app" "lib" "db" "config" "test")
:project-file "Gemfile"
:compile "bundle exec rails server"
:src-dir "lib/"
:test "bundle exec rake test"
:test-suffix "_test")
;; Rails + RSpec
(projectile-register-project-type 'rails-rspec '("Gemfile" "app" "lib" "db" "config" "spec")
:project-file "Gemfile"
:compile "bundle exec rails server"
:src-dir "lib/"
:test "bundle exec rspec"
:test-dir "spec/"
:test-suffix "_spec")
----
All those projects are using `Gemfile` (``bundler``'s project file), but they have different directory structures.
Bellow is a listing of all the available options for `projectile-register-project-type`:
|===
| Option | Documentation
| :project-file
| A file, relative to the project root, typically the main project file (e.g. `pom.xml` for Maven projects).
| :compilation-dir
| A path, relative to the project root, from where to run the tests and compilation commands.
| :compile
| A command to compile the project.
| :configure
| A command to configure the project. `%s` will be substituted with the project root.
| :install
| A function to install the project.
| :package
| A function to package the project.
| :run
| A command to run the project.
| :src-dir
| A path, relative to the project root, where the source code lives.
| :test
| A command to test the project.
| :test-dir
| A path, relative to the project root, where the test code lives.
| :test-prefix
| A prefix to generate test files names.
| :test-suffix
| A suffix to generate test files names.
| :related-files-fn
| A function to specify test/impl/other files in a more flexible way.
|===
[discrete]
==== Returning Projectile Commands from a function
You can also pass a symbolic reference to a function into your project type definition if you wish to define the compile command dynamically:
[source,elisp]
----
(defun my/compile-command ()
"Returns a String representing the compile command to run for the given context"
(cond
((and (eq major-mode 'java-mode)
(not (string-match-p (regexp-quote "\\.*/test/\\.*") (buffer-file-name (current-buffer)))))
"./gradlew build")
((eq major-mode 'web-mode)
"./gradlew compile-templates")
))
(defun my/test-command ()
"Returns a String representing the test command to run for the given context"
(cond
((eq major-mode 'js-mode) "grunt test") ;; Test the JS of the project
((eq major-mode 'java-mode) "./gradlew test") ;; Test the Java code of the project
((eq major-mode 'my-mode) "special-command.sh") ;; Even Special conditions/test-sets can be covered
))
(projectile-register-project-type 'has-command-at-point '("file.txt")
:compile 'my/compile-command
:test 'my/test-command)
----
If you would now navigate to a file that has the `*.java` extension under the `./tests/` directory and hit `C-c c p` you
will see `./gradlew build` as the suggestion. If you were to navigate to a HTML file the compile command will have switched
to `./gradlew compile-templates`.
This works for:
* `:configure`
* `:compile`
* `:compilation-dir`
* `:run`
Note that your function has to return a string to work properly.
=== Related file location
For simple projects, `:test-prefix` and `:test-suffix` option with string will
be enough to specify test prefix/suffix applicable regardless of file extensions
on any directory path. `projectile-other-file-alist` variable can be also set to
find other files based on the extension.
For the full control of finding related files, `:related-files-fn` option with a
custom function or a list of custom functions can be used. The custom function
accepts the relative file name from the project root and it should return the
related file information as plist with the following optional key/value pairs:
|===
| Key | Value | Command applicable
| :impl
| matching implementation file if the given file is a test file
| projectile-toggle-between-implementation-and-test, projectile-find-related-file
| :test
| matching test file if the given file has test files.
| projectile-toggle-between-implementation-and-test, projectile-find-related-file
| :other
| any other files if the given file has them.
| projectile-find-other-file, projectile-find-related-file
| :foo
| any key other than above
| projectile-find-related-file
|===
For each value, following type can be used:
|===
| Type | Meaning
| string / a list of strings
| Relative paths from the project root. The paths which actually exist on the file system will be matched.
| a function
| A predicate which accepts a relative path as the input and return t if it matches.
| nil
| No match exists.
|===
Notes:
. For a big project consisting of many source files, returning strings instead
of a function can be fast as it does not iterate over each source file.
. There is a difference in behaviour between no key and `nil` value for the
key. Only when the key does not exist, other project options such as
`:test_prefix` or `projectile-other-file-alist` mechanism is tried.
==== Example - Same source file name for test and impl
[source,elisp]
----
(defun my/related-files (path)
(if (string-match (rx (group (or "src" "test")) (group "/" (1+ anything) ".cpp")) path)
(let ((dir (match-string 1 path))
(file-name (match-string 2 path)))
(if (equal dir "test")
(list :impl (concat "src" file-name))
(list :test (concat "test" file-name)
:other (concat "src" file-name ".def"))))))
(projectile-register-project-type
;; ...
:related-files-fn #'my/related-files)
----
With the above example, src/test directory can contain the same name file for test and its implementation file.
For example, "src/foo/abc.cpp" will match to "test/foo/abc.cpp" as test file and "src/foo/abc.cpp.def" as other file.
==== Example - Different test prefix per extension
A custom function for the project using multiple programming languages with different test prefixes.
[source,elisp]
----
(defun my/related-files(file)
(let ((ext-to-test-prefix '(("cpp" . "Test")
("py" . "test_"))))
(if-let ((ext (file-name-extension file))
(test-prefix (assoc-default ext ext-to-test-prefix))
(file-name (file-name-nondirectory file)))
(if (string-prefix-p test-prefix file-name)
(let ((suffix (concat "/" (substring file-name (length test-prefix)))))
(list :impl (lambda (other-file)
(string-suffix-p suffix other-file))))
(let ((suffix (concat "/" test-prefix file-name)))
(list :test (lambda (other-file)
(string-suffix-p suffix other-file))))))))
----
`projectile-find-related-file` command is also available to find and choose
related files of any kinds. For example, the custom function can specify the
related documents with ':doc' key. Note that `projectile-find-related-file` only
relies on `:related-files-fn` for now.
=== Related file custom function helper
`:related-files-fn` can accept a list of custom functions to combine the result
of each custom function. This allows users to write several custom functions
and apply them differently to projects.
Projectile includes a couple of helpers to generate commonly used custom functions.
|===
| Helper name and params | Purpose
| groups KIND GROUPS
| Relates files in each group as the specified kind.
| extensions KIND EXTENSIONS
| Relates files with extensions as the specified kind.
| tests-with-prefix EXTENSION PREFIX
| Relates files with prefix and extension as :test and :impl.
| tests-with-suffix EXTENSION SUFFIX
| Relates files with suffix and extension as :test and :impl.
|===
Each helper means `projectile-related-files-fn-helper-name` function.
==== Example usage of projectile-related-files-fn-helpers
[source,elisp]
----
(setq my/related-files
(list
(projectile-related-files-fn-extensions :other '("cpp" "h" "hpp"))
(projectile-related-files-fn-test-with-prefix "cpp" "Test")
(projectile-related-files-fn-test-with-suffix "el" "_test")
(projectile-related-files-fn-groups
:doc
'(("doc/common.txt"
"src/foo.h"
"src/bar.h")))))
(projectile-register-project-type
;; ...
:related-files-fn #'my/related-files)
----
== Customizing project root files
You can set the values of `projectile-project-root-files`,
`projectile-project-root-files-top-down-recurring`,
`projectile-project-root-files-bottom-up` and
`projectile-project-root-files-functions` to customize how project roots are
identified.
To customize project root files settings:
----
M-x customize-group RET projectile RET
----
== Ignoring files
WARNING: The contents of `.projectile` are ignored when using the
`alien` project indexing method.
If you'd like to instruct Projectile to ignore certain files in a
project, when indexing it you can do so in the `.projectile` file by
adding each path to ignore, where the paths all are relative to the
root directory and start with a slash. Everything ignored should be
preceded with a `-` sign. Alternatively, not having any prefix at all
also means to ignore the directory or file pattern that follows.
Here's an example for a typical Rails application:
----
-/log
-/tmp
-/vendor
-/public/uploads
----
This would ignore the folders only at the root of the project.
Projectile also supports relative pathname ignores:
----
-tmp
-*.rb
-*.yml
-models
----
You can also ignore everything except certain subdirectories. This is
useful when selecting the directories to keep is easier than selecting
the directories to ignore, although you can do both. To select
directories to keep, that means everything else will be ignored.
Example:
----
+/src/foo
+/tests/foo
----
Keep in mind that you can only include subdirectories, not file
patterns.
If both directories to keep and ignore are specified, the directories
to keep first apply, restricting what files are considered. The paths
and patterns to ignore are then applied to that set.
Finally, you can override ignored files. This is especially useful
when some files ignored by your VCS should be considered as part of
your project by projectile:
----
!/src/foo
!*.yml
----
When a path is overridden, its contents are still subject to ignore
patterns. To override those files as well, specify their full path
with a bang prefix.
If you would like to include comment lines in your .projectile file,
you can customize the variable `projectile-dirconfig-comment-prefix`.
Assigning it a non-nil character value, e.g. `#`, will cause lines in
the .projectile file starting with that character to be treated as
comments instead of patterns.
=== File-local project root definitions
If you want to override the projectile project root for a specific
file, you can set the file-local variable `projectile-project-root`. This
can be useful if you have files within one project that are related to
a different project (for instance, Org files in one git repo that
correspond to other projects).
== Storing project settings
From project to project, some things may differ even in the same
language - coding styles, auto-completion sources, etc. If you need
to set some variables according to the selected project, you can use a
standard Emacs feature called
http://www.gnu.org/software/emacs/manual/html_node/emacs/Directory-Variables.html[Per-directory Local Variables].
To use it you must create a file named `.dir-locals.el` (as specified
by the constant `dir-locals-file`) inside the project directory. This
file should contain something like this:
[source,elisp]
----
((nil . ((secret-ftp-password . "secret")
(compile-command . "make target-x")
(eval . (progn
(defun my-project-specific-function ()
;; ...
)))))
(c-mode . ((c-file-style . "BSD"))))
----
The top-level alist member referenced with the key `nil` applies to
the entire project. A key with the name `eval` will evaluate its
corresponding value. In the example above, this is used to create a
function. It could also be used to e.g. add such a function to a key
map.
You can also quickly visit or create the `dir-locals-file` with
kbd:[s-p E] (kbd:[M-x] `projectile-edit-dir-locals` kbd:[RET]).
Here are a few examples of how to use this feature with Projectile.
== Configuring Projectile's Behavior
Projectile exposes many variables (via `defcustom`) which allow users
to customize its behavior. Directory variables can be used to set
these customizations on a per-project basis.
You could enable caching for a project in this way:
[source,elisp]
----
((nil . ((projectile-enable-caching . t))))
----
If one of your projects had a file that you wanted Projectile to
ignore, you would customize Projectile by:
[source,elisp]
----
((nil . ((projectile-globally-ignored-files . ("MyBinaryFile")))))
----
If you wanted to wrap the git command that Projectile uses to list
the files in you repository, you could do:
[source,elisp]
----
((nil . ((projectile-git-command . "/path/to/other/git ls-files -zco --exclude-standard"))))
----
If you want to use a different project name than how Projectile named
your project, you could customize it with the following:
[source,elisp]
----
((nil . ((projectile-project-name . "your-project-name-here"))))
----
== Configure a Project's Compilation, Test and Run commands
There are a few variables that are intended to be customized via `.dir-locals.el`.
* for compilation - `projectile-project-compilation-cmd`
* for testing - `projectile-project-test-cmd`
* for running - `projectile-project-run-cmd`
When these variables have their default value of `nil`, Projectile
runs the default command for the current project type. You can
override this behavior by setting them to either a string to run an
external command or an Emacs Lisp function:
[source,elisp]
----
(setq projectile-test-cmd #'custom-test-function)
----
|