projectile
Table of Contents
- 1. 概述
- 2. 安装
- 3. Usage
- 4. Projects
- 5. Configuration
- 6. Extensions
1 概述
Projectile 是 Emacs 的项目交互库。它的目标是在项目级别上提供一系列优秀的功能,而不会引入外部依赖。例如,有一个用纯 Emacs Lisp 编写的查找项目文件功能,而不使用 GNU find(但出于性能考虑,也存在由外部命令支持的索引机制)。
Projectile 以实用为目的:虽然考虑便携性,但是如果一些可用的外部工具可以大大加快某些任务,那么 Projectile 将利用它们。
该库提供简单的项目管理和导航。项目的概念非常简单:只是一个包含特殊文件的文件夹。目前包含 git,mercurial,darcs 和 bazaar repo的文件夹被视为项目。lein,maven,sbt,scons,rebar 和 bundler 项目也是如此。如果要将文件夹手动标记为项目,只需在其中创建一个空的 .projectile
文件。Projectile 的一些特点:
- 跳转到项目中的文件(c-p-f)。
- 跳转到项目中当前 point 处所指示的文件(c-p-g)。
- 跳转到项目中的目录(c-p-d)。
- 跳转到目录中的文件。
- 跳转到项目有关的 buffer(c-p-b)。
- 跳转到项目的测试(c-p-T)。
- 在同名但不同扩展名的文件间跳转(例如.h 和.cpp)(c-p-a)。
- 跳转到项目中最近浏览的文件(c-p-e)。
- 在工作的项目间切换。
- 关闭项目有关的 buffer(c-p-k)。
- 在项目范围内替换(c-p-r)。
- 在项目所有 buffer 中查找出现(c-p-o)。
- 在项目中查找(c-p-s-g)。
- 重新生成你项目的 etags 或 gtags。
- 在 dired 中访问项目(c-p-D)。
- 在项目上执行 make。
- 在项目的根目录执行命令(c-p-!)。
- 检查脏的存储库。
- 创建项目设置文件(dir-local-file)(c-p-E)
2 安装
2.1 Prerequisites
2.2 Installation via package.el
2.3 Installation via use-package
如果要安装在主分支中找到的 Projectile 版本,请在 Emacs 初始化文件(.emacs 或 init.el)中声明以下内容:
(use-package projectile :ensure t :config (define-key projectile-mode-map (kbd "s-p") 'projectile-command-map) (define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map) (projectile-mode +1))
但是,如果您想要更加保守并且只使用 Projectile 的稳定版本,需要声明以下内容:
(use-package projectile :ensure t :pin melpa-stable :config (define-key projectile-mode-map (kbd "s-p") 'projectile-command-map) (define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map) (projectile-mode +1))
2.4 Installation via el-get
2.5 Installation on Debian and Ubuntu
2.6 Emacs Prelude
Projectile 与 Emacs Prelude 捆绑在一起。如果是 Prelude 用户,Projectile 已经正确配置。
3 Usage
3.1 Basic Usage
3.2 Interactive Commands
4 Projects
4.1 Supported Project Types
Projectile 的主要目标之一是在不需要任何配置的情况下操作各种项目类型。为实现这一点,它包含许多项目检测逻辑和项目类型特定的逻辑。
4.2 Version Control Systems
Projectile 可讲大多数版本控制的仓库识别为一个项目。Projectile 支持:
- Git
- Mercurial
- Bazaar
- Subversion
- CVS
- Fossil
- Darcs
4.3 File markers
Projectile 将许多文件识别为表示项目的 root。通常这些文件是各种构建工具的配置文件。支持以下内容:
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 |
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 |
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 |
Projectile 自己的 .projectile
既可以作为项目标记,也可以作为配置文件。将在本节后面详细讨论。
4.4 Adding Custom Project Types
如果没有正确识别正在处理的项目,或者想定制项目类型,则可以在 Emacs 初始化代码中添加以下内容:
(projectile-register-project-type 'npm '("package.json") :compile "npm install" :test "npm test" :run "npm start" :test-suffix ".spec")
Option | Documentation |
---|---|
: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. |
: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. |
4.5 Returning Projectile Commands from a function
如果要动态定义编译命令,可以在项目类型定义中传递函数的符号引用:
(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)
如果现在导航到 ./tests/
目录下具有 *.java
扩展名的文件,并点击 C-c c p
,将看到 ./gradlew build
作为建议。 如果要导航到 HTML 文件,编译命令将切换到 ./gradlew compile-templates
。
可同样设置的还有:
:configure
:compile
:compilation-dir
:run
请注意,函数必须返回一个字符串才能正常工作。
4.6 Customizing project root files
可以设置 projectile-project-root-files, projectile-project-root-files-top-down-recurring, projectile-project-root-files-bottom-up and projectile-project-root-files-functions
的值来定制如何识别项目的 root。
4.7 Ignoring files
注意:使用 alien 方法索引项目时,忽略.projectile 的内容。
如果想 Projectile 在索引项目时忽略某些文件,可以在 .projectile
文件中添加要忽略的路径,其中所有相对于根目录的路径都以 /
开头。忽略的项目都应该以 -
符号开头。 或者,没有任何前缀也意味着忽略后面的目录或文件模式。 以下是典型 Rails 应用程序的示例:
-/log -/tmp -/vendor -/public/uploads
这将忽略仅在项目根目录下的文件夹。Projectile 还支持相对路径名忽略:
-tmp -*.rb -*.yml -models
也可以忽略除某些子目录之外的所有内容。 这在选择要保留的目录比选择要忽略的目录更容易时很有用,尽管您可以同时执行这两个操作。 选择要保留的目录,这意味着其他所有内容都将被忽略。
+/src/foo +/tests/foo
注意:此时只能包含子目录,而不能包含文件模式。
如果目录同时被指定为保留和忽略,则首先应用要保留,从而限制考虑的文件。然后将要忽略的路径和模式应用于该集合。
最后,可以覆盖被忽略的文件。当 VCS 忽略的某些文件应被视为项目的一部分时,这尤其有用:
!/src/foo !*.yml
4.8 File-local project root definitions
如果要用指定文件覆盖 projectile 项目的 root,可以设置文件局部变量 projectile-project-root。 如果一个项目中的文件与另一个项目相关(例如,一个 git 仓库中的 Org 文件与其他项目相对应),这将非常有用。
4.9 Storing project settings
即使相同的语言项目也有可能各不相同:编码样式、自动完成源等。如果需要根据所选项目设置一些变量,可以使用名为 Per-directory Local 的 Emacs 标准功能。要使用它,您必须在项目目录中创建名为 .dir-locals.el
的文件(由常量 dir-locals-file
指定)。 此文件包含内容与以下类似:
((nil . ((secret-ftp-password . "secret") (compile-command . "make target-x") (eval . (progn (defun my-project-specific-function () ;; ... ))))) (c-mode . ((c-file-style . "BSD"))))
使用键 nil 引用的顶级 alist 成员适用于整个项目。 名为 eval 的键将会求值。在上面的示例中,它用于创建函数。 它也可以用于将这样的函数添加到键映射中。
还可以使用 s-p E(M-x projectile-edit-dir-locals RET)
快速访问或创建 dir-locals-file
。
以下是如何在 Projectile 中使用此功能的几个示例。
4.9.1 Configuring Projectile's Behavior
Projectile 有许多变量(通过 defcustom),允许用户自定义其行为。目录变量可用来基于每个项目设置这些自定义。
可以通过以下方式为项目启用缓存:
((nil . ((projectile-enable-caching . t))))
如果某个项目有一个希望 Projectile 忽略的文件,可以通过以下方式自定义 Projectile:
((nil . ((projectile-globally-ignored-files . ("MyBinaryFile")))))
如果想包装 Projectile 用于列出存储库中文件的 git 命令,可以这样做
((nil . ((projectile-git-command . "/path/to/other/git ls-files -zco --exclude-standard"))))
如果要使用不同于 Projectile 命名项目的名称,可以使用以下内容对其进行自定义:
((nil . ((projectile-project-name . "your-project-name-here"))))
4.9.2 Configure a Project's Compilation, Test and Run commands
通过 .dir-locals.el
自定义命令:
- for compilation - projectile-project-compilation-cmd
- for testing - projectile-project-test-cmd
- for running - projectile-project-run-cmd
当这些变量的默认值为 nil 时,Projectile 将运行当前项目类型的默认命令。可以通过将它们设置为运行外部命令的字符串或 Emacs Lisp 函数来覆盖此行为:
(setq projectile-test-cmd #'custom-test-function)
5 Configuration
Projectile 非常易于配置。几乎可以调整或扩展其行为的每个方面。
在本节中,将介绍一些可能需要微调的常用配置,以使 Projectile 更好地适应工作流程。
5.1 Project indexing method
Projectile 有三种操作模式 - 一种是可移植的,使用 Emacs Lisp 实现(因此它是 Emacs 原生,被称为 native 索引方法),另外两种(hybrid 和 alien)依赖于外部命令,如 find,git 等 获取项目中的文件列表。
alien 索引方法优化了 hybrid 索引方法的速度。 这意味着 Projectile 不会对外部命令返回的文件进行任何处理,将获得最大的性能。这种行为适用于大多数人,因为他们通常会在他们的 VCS 配置中放弃忽略(例如.gitignore),并且不会关心 Projectile 可能提供的任何其他 ignores/unignores/sorting。
默认情况下,alien 方法用于除 Windows 之外的所有操作系统。Projectile 2.0 之前 hybrid 曾经是默认设置。
要在所有操作系统中强制使用 native 索引:
(setq projectile-indexing-method 'native)
强制在所有操作系统中使用 hybrid 索引:
(setq projectile-indexing-method 'hybrid)
强制在所有操作系统中使用 alien 索引:
(setq projectile-indexing-method 'alien)
Windows 中这可以显著加速 Projectile(特别是在大型项目中)。 这种方法的缺点是它在 Windows 系统上得不到很好的支持,因为它需要在那里设置一些 Unix 实用程序。 如果出现问题,可以使用 native 索引模式。
5.2 Alien indexing
alien 索引以一种非常简单的方式工作 - 它只是执行一个返回项目中文件列表的命令。 对于版本控制的项目,默认情况下,Projectile 将使用 VCS 本身来获取文件列表。例如,Projectile 用于 Git 项目的命令:
git ls-files -zco --exclude-standard
对于支持的 VCS,都有一个匹配的 Projectile 命令调用它(例如 projectile-git-command,projectile-hg-command 等)。
警告:如果决定调整这些命令,请记住命令应该总是返回相对于项目根目录的文件列表,结果文件列表应该是 0 分隔(而不是换行符分隔)。
对于非版本控制的项目,Projectile 将调用 projectile-generic-command 中的命令。默认情况下是:
find . -type f -print0
安装 fd 并用其替代 git ls-files
(fd 理解 .gitignore
)和 find
是个好主意。
5.3 Caching
5.3.1 Project files
由于索引一个大项目并不是很快(特别使用 Emacs Lisp),Projectile 支持缓存项目的文件。默认情况下,启用 native 索引就会启用缓存。
使用以下代码段无条件启用缓存:
(setq projectile-enable-caching t)
此时,可以尝试使用诸如 s-p f (M-x projectile-find-file RET)
之类的 Projectile 命令。
在提示跳转到文件之前,运行 C-u s-p f
将使缓存无效。
按 s-p z
会将当前访问的文件添加到当前项目的缓存中。 通常,在 Emacs 外部创建的文件将在第一次打开时自动添加到缓存中。
项目缓存是持久的,将在 Emacs 重新启动期间保留。
可以使用 M-x projectile-purge-file-from-cache
从缓存中清除单个文件, M-x projectile-purge-dir-from-cache
的从缓存中清除整个目录。
5.3.2 File exists cache
Projectile 执行许多文件存在性检查,以识别项目根目录。通常情况下这很好,但在某些情况下,文件系统速度比平常慢得多,这可能导致在打开文件和浏览目录时 Emacs “冻结” 很长一段时间。
最常见的情形是使用 TRAMP/ssh
连接远程系统。默认情况下,缓存所有远程文件的存在性检查。
不缓存远程文件存在性检查:
(setq projectile-file-exists-remote-cache-expire nil)
设置远程文件存在检查缓存 10 分钟后过期:
(setq projectile-file-exists-remote-cache-expire (* 10 60))
还可以为本地文件系统启用缓存,但通常没必要:
(setq projectile-file-exists-local-cache-expire (* 5 60))
5.4 Using Projectile everywhere
如果希望在每个目录中都使用 Projectile(即使没有项目文件):
(setq projectile-require-project-root nil)
5.5 Switching projects
当运行 projectile-switch-project(s-p p)时,Projectile 会调用 projectile-switch-project-action 中指定的命令(默认情况下它是 projectile-find-file)。
使用前缀参数(C-u s-p p)调用命令将触发 Projectile Commander,可以快速访问可能要在项目上调用的大多数常用命令。
根据您的个人工作流程和习惯,可能需要改变 projectile-switch-project-action
的值:
5.5.1 projectile-find-file
默认值。使用此设置,一旦通过 Projectile 的补全系统选择了项目(见下文),将在补全系统中继续选择要访问的文件。projectile-find-file 能够检索项目根目录下的所有子项目中的文件,例如 Git 子模块。目前仅支持 Git。将来会增加对其他 VCS 的支持。
5.5.2 projectile-find-file-in-known-projects
与 projectile-find-file 类似,但列出了所有已知项目中的所有文件。由于文件总数可能很大,启用缓存有助于后续使用。
5.5.3 projectile-find-file-dwim
如果 point 在文件路径上,Projectile 首先尝试在项目中搜索该文件:
- 如果它只找到一个文件,它会立即切换到该文件。即使文件名不完整,但当前项目中只有一个文件与 point 处的文件名匹配也会生效。例如,如果只有一个名为
projectile/projectile.el
的文件,但当前文件名是projectile/proj
(不完整),则 projectile-find-file 仍会立即切换到projectile/projectile.el
,因为这是唯一匹配的文件名。 - 如果找到文件列表,则会显示列表以供选择。当文件名在项目中出现多个文件名时,或者 point 上的文件名是项目中两个以上文件的前缀时,将显示文件列表。例如,如果在
projectile/
之类的文件路径上执行 projectile-find-file,它会列出该目录的内容。如果它在诸如projectile/a
的部分文件名上执行,则会显示该目录中具有字符a
的文件列表。 - 如果找不到任何内容,则显示项目中所有文件的列表以供选择。
5.5.4 projectile-dired
(setq projectile-switch-project-action#'projectile-dired)
使用此设置,选择项目后,项目的顶级目录将立即在 dired 缓冲区中打开。
5.5.5 projectile-find-dir
(setq projectile-switch-project-action#'projectile-find-dir)
使用此设置,选择项目后,将保留在 Projectile 的补全系统中以选择项目的子目录,然后在 dired 缓冲区中打开该子目录。如果使用此设置,可能还需要设置以下代码允许选择顶级目录:
(setq projectile-find-dir-includes-top-level t)
5.6 Completion Options
5.6.1 Ido
Projectile 默认使用 ido 作为其补全系统。ido 非常受欢迎,内置于 Emacs 中。
如果要使用 ido 补全,强烈建议安装可选的 flx-ido 软件包,它为 ido 内置的 flex 匹配提供了更强大的替代方案。
5.6.2 Ivy (recommended)
另外一个补全选项是ivy:
(setq projectile-completion-system 'ivy)
5.6.3 Basic (Emacs's default)
如果你不喜欢 ido 和常春藤你可以使用常规补全:
(setq projectile-completion-system 'default)
5.6.4 Custom Completion Function
还可以将 projectile-completion-system 设置为函数:
(setq projectile-completion-system #'my-custom-completion-fn) (setq projectile-completion-system (lambda (prompt choices) ;; ... ))
自定义完成功能的一个示例,它仅显示文件名(不包括路径),如果选择的文件不唯一,则会显示另一个名称相对于项目根目录的补全。
5.6.5 Regenerate tags
为了能够通过 projectile-tags-command 重新生成项目的标签,应该安装 Exuberant Ctags 并将其添加到 PATH,而不是与 Emacs 发行版一起提供的普通 ctags。
注意:Exuberant Ctags 已经停止开发,使用 Universal-ctags 替代。
5.7 Idle Timer
每当 Emacs 在该项目中空闲 projectile-idle-timer-seconds 配置时间(默认为 30 秒),可以运行配置 projectile-idle-timer-hook。要启用此功能,请运行:
(add-hook 'projectile-idle-timer-hook #'my-projectile-idle-timer-function)
5.8 Mode line indicator
默认情况下,Projectile 的 minor mode 指示器以 “Projectile [ProjectName:ProjectType]” 的形式出现。可以通过几个自定义变量进行配置:
projectile-mode-line-prefix
(默认为 “Projectile”)控制 mode-line 的静态部分。projectile-dynamic-mode-line
(默认为 t)控制是否显示 mode-line 的项目名称和类型部分。projectile-mode-line-function
(默认为 projectile-default-mode-line)控制要生成 mode-line 的实际调用的函数。如果想显示不同的信息,应该提供一个自定义函数来替换默认值,例如:(setq projectile-mode-line-function '(lambda () (format " Proj[%s]" (projectile-project-name))))
注意:编辑远程文件时(通过 TRAMP),项目名称和类型不会出现,因为重新计算项目名称是一个相当慢的操作,并且会减慢打开文件的速度。它们也不会出现在非文件缓冲区中,因为它们是通过 find-file-hook 更新的。
6 Extensions
有许多软件包构建在 Projectile 提供的基本功能之上:
- counsel-projectile provides Ivy integration
- helm-projectile provides Helm integration
- persp-projectile provides perspective.el integration
- projectile-rails provides extra functionality for Ruby on Rails projects