diff options
Diffstat (limited to 'content')
-rw-r--r-- | content/post/opening-projects-with-projectile.md | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/content/post/opening-projects-with-projectile.md b/content/post/opening-projects-with-projectile.md new file mode 100644 index 0000000..43ee6f1 --- /dev/null +++ b/content/post/opening-projects-with-projectile.md | |||
@@ -0,0 +1,80 @@ | |||
1 | +++ | ||
2 | Categories = ["Development", "Emacs"] | ||
3 | Description = "" | ||
4 | Tags = ["Development", "emacs", "lisp"] | ||
5 | title = "Opening Projects with Projectile" | ||
6 | date = 2014-07-12T09:12:34Z | ||
7 | +++ | ||
8 | |||
9 | I use [Projectile] for working with projects in Emacs. It's really good at finding files in projects, working with source code indexes (I use [Global]), and with its [perspective] support, it's also great at separating projects into workspaces. However, I've always felt it lacking in actually opening projects. I tend to work on different projects all the time and `projectile-switch-project` only tracks projects once they've been opened initially (despite the name, it works across Emacs sessions). | ||
10 | |||
11 | With this in mind, I decided to try to add support for opening projects under a given subdirectory, e.g. `~/projects`, regardless of whether or not I've visited them before. | ||
12 | |||
13 | I saw that projectile uses [Dash.el] in some places, and after reading about [anaphoric macros], I decided that I'd try to use them to aid me. | ||
14 | |||
15 | {{% highlight cl %}} | ||
16 | (defun ap/subfolder-projects (dir) | ||
17 | (--map (file-relative-name it dir) | ||
18 | (-filter (lambda (subdir) | ||
19 | (--reduce-from (or acc (funcall it subdir)) nil | ||
20 | projectile-project-root-files-functions)) | ||
21 | (-filter #'file-directory-p (directory-files dir t "\\<"))))) | ||
22 | {{% /highlight %}} | ||
23 | |||
24 | First, this filters the non-special files under `dir`, filtering non-directories. Then it runs the list of `projectile-project-root-files-functions` on it to determine if it looks like a projectile project. To make the list more readable, it makes the filenames relative to the passed-in directory. It runs like this: | ||
25 | |||
26 | {{% highlight cl %}} | ||
27 | (ap/subfolder-projects "~/projects") => | ||
28 | ("dotfiles" "ggtags" …) | ||
29 | {{% /highlight %}} | ||
30 | |||
31 | So, we've got ourselves a list, but now we need to be able to open the project that's there, even though the folders are relative. | ||
32 | |||
33 | {{% highlight cl %}} | ||
34 | (defun ap/open-subfolder-project (from-dir &optional arg) | ||
35 | (let ((project-dir (projectile-completing-read "Open project: " | ||
36 | (ap/subfolder-projects from-dir)))) | ||
37 | (projectile-switch-project-by-name (expand-file-name project-dir from-dir) arg))) | ||
38 | {{% /highlight %}} | ||
39 | |||
40 | By wrapping the call to `ap/subfolder-projects` in another function that takes the same directory argument, we can re-use the project parent directory and expand the selected project name into an absolute path before passing it to `projectile-switch-project-by-name`. | ||
41 | |||
42 | We get support for multiple completion systems for free, since projectile has a wrapper function that works with the default system, ido, [grizzl] and recently, [helm]. | ||
43 | |||
44 | Then I defined some helper functions to make it easy to open work and home projects. | ||
45 | |||
46 | {{% highlight cl %}} | ||
47 | (defvar work-project-directory "~/work") | ||
48 | (defvar home-project-directory "~/projects") | ||
49 | |||
50 | (defun ap/open-work-project (&optional arg) | ||
51 | (interactive "P") | ||
52 | (ap/open-subfolder-project work-project-directory arg)) | ||
53 | |||
54 | (defun ap/open-home-project (&optional arg) | ||
55 | (interactive "P") | ||
56 | (ap/open-subfolder-project home-project-directory arg)) | ||
57 | {{% /highlight %}} | ||
58 | |||
59 | I could probably simplify this with a macro, but I'm not sure that there's much advantage in it. I only have two project types right now, after all. | ||
60 | |||
61 | With this all set up, whenever I want to start working on a project I just type `M-x home RET` to call up the list. | ||
62 | |||
63 | I also considered trying to add all the projects under a directory to the projectile known project list. I didn't find it quite as easy to use, but it's available below if anyone would prefer that style. | ||
64 | |||
65 | {{% highlight cl %}} | ||
66 | (defun ap/-add-known-subfolder-projects (dir) | ||
67 | (-map #'projectile-add-known-project (--map (concat (file-name-as-directory dir) it) (ap/subfolder-projects dir)))) | ||
68 | |||
69 | (defun ap/add-known-subfolder-projects () | ||
70 | (interactive) | ||
71 | (ap/-add-known-subfolder-projects (ido-read-directory-name "Add projects under: "))) | ||
72 | {{% /highlight %}} | ||
73 | |||
74 | [Projectile]: https://github.com/bbatsov/projectile | ||
75 | [Dash.el]: https://github.com/magnars/dash.el | ||
76 | [Helm]: https://github.com/emacs-helm/helm | ||
77 | [Global]: https://www.gnu.org/software/global/ | ||
78 | [Anaphoric macros]: https://en.wikipedia.org/wiki/Anaphoric_macro | ||
79 | [Perspective]: https://github.com/nex3/perspective-el | ||
80 | [Grizzl]: https://github.com/d11wtq/grizzl | ||