|
|
;;; projectile.el --- Manage and navigate projects in Emacs easily
|
|
|
|
|
|
;; Copyright © 2011-2013 Bozhidar Batsov
|
|
|
|
|
|
;; Author: Bozhidar Batsov
|
|
|
;; URL: https://github.com/bbatsov/projectile
|
|
|
;; Keywords: project, convenience
|
|
|
;; Version: 0.9.2
|
|
|
;; Package-Requires: ((s "1.0.0") (dash "1.0.0"))
|
|
|
|
|
|
;; This file is NOT part of GNU Emacs.
|
|
|
|
|
|
;;; License:
|
|
|
|
|
|
;; This program is free software; you can redistribute it and/or modify
|
|
|
;; it under the terms of the GNU General Public License as published by
|
|
|
;; the Free Software Foundation; either version 3, or (at your option)
|
|
|
;; any later version.
|
|
|
;;
|
|
|
;; This program is distributed in the hope that it will be useful,
|
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
;; GNU General Public License for more details.
|
|
|
;;
|
|
|
;; You should have received a copy of the GNU General Public License
|
|
|
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
|
|
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
;; Boston, MA 02110-1301, USA.
|
|
|
|
|
|
;;; Commentary:
|
|
|
;;
|
|
|
;; This library provides easy project management and navigation. The
|
|
|
;; concept of a project is pretty basic - just a folder containing
|
|
|
;; special file. Currently git, mercurial and bazaar repos are
|
|
|
;; considered projects by default. If you want to mark a folder
|
|
|
;; manually as a project just create an empty .projectile file in
|
|
|
;; it. See the README for more details.
|
|
|
;;
|
|
|
;;; Code:
|
|
|
|
|
|
;; requires
|
|
|
(require 'cl)
|
|
|
(require 'easymenu)
|
|
|
(require 'thingatpt)
|
|
|
(require 's)
|
|
|
(require 'dash)
|
|
|
(require 'grep)
|
|
|
|
|
|
(defgroup projectile nil
|
|
|
"Manage and navigate projects easily."
|
|
|
:group 'tools
|
|
|
:group 'convenience)
|
|
|
|
|
|
(defconst projectile-current-version "0.9.1"
|
|
|
"The current Projectile version.")
|
|
|
|
|
|
(defcustom projectile-use-native-indexing (eq system-type 'windows-nt)
|
|
|
"Use native Emacs Lisp project indexing."
|
|
|
:group 'projectile
|
|
|
:type 'boolean)
|
|
|
|
|
|
(defcustom projectile-enable-caching projectile-use-native-indexing
|
|
|
"Enable project files caching."
|
|
|
:group 'projectile
|
|
|
:type 'boolean)
|
|
|
|
|
|
(defcustom projectile-require-project-root t
|
|
|
"Require the presence of a project root to operate when true.
|
|
|
Otherwise consider the current directory the project root."
|
|
|
:group 'projectile
|
|
|
:type 'boolean)
|
|
|
|
|
|
(defcustom projectile-completion-system 'ido
|
|
|
"The completion system to be used by Projectile."
|
|
|
:group 'projectile
|
|
|
:type 'symbol
|
|
|
:options '(ido grizzl default))
|
|
|
|
|
|
(defcustom projectile-ack-function 'ack-and-a-half
|
|
|
"The ack function to use."
|
|
|
:group 'projectile
|
|
|
:type 'symbol
|
|
|
:options '(ack-and-a-half default))
|
|
|
|
|
|
(defcustom projectile-keymap-prefix (kbd "C-c p")
|
|
|
"Projectile keymap prefix."
|
|
|
:group 'projectile
|
|
|
:type 'string)
|
|
|
|
|
|
(defcustom projectile-cache-file
|
|
|
(expand-file-name "projectile.cache" user-emacs-directory)
|
|
|
"The name of Projectile's cache file."
|
|
|
:group 'projectile
|
|
|
:type 'string)
|
|
|
|
|
|
(defcustom projectile-tags-command "ctags -Re %s %s"
|
|
|
"The command Projectile's going to use to generate a TAGS file."
|
|
|
:group 'projectile
|
|
|
:type 'string)
|
|
|
|
|
|
;; variables
|
|
|
(defvar projectile-project-root-files
|
|
|
'(".projectile" "project.clj" ".git" ".hg" ".fslckout" ".bzr" "_darcs"
|
|
|
"rebar.config" "pom.xml" "build.sbt" "Gemfile" "Makefile")
|
|
|
"A list of files considered to mark the root of a project.")
|
|
|
|
|
|
(defvar projectile-globally-ignored-files
|
|
|
'("TAGS")
|
|
|
"A list of files globally ignored by projectile.")
|
|
|
|
|
|
(defvar projectile-globally-ignored-directories
|
|
|
'(".idea" ".eunit" ".git" ".hg" ".fslckout" ".bzr" "_darcs")
|
|
|
"A list of directories globally ignored by projectile.")
|
|
|
|
|
|
(defvar projectile-find-file-hook nil
|
|
|
"Hooks run when a file is opened with `projectile-find-file'.")
|
|
|
|
|
|
(defvar projectile-find-dir-hook nil
|
|
|
"Hooks run when a directory is opened with `projectile-find-dir'.")
|
|
|
|
|
|
(defun projectile-serialize (data filename)
|
|
|
"Serialize DATA to FILENAME.
|
|
|
|
|
|
The saved data can be restored with `projectile-unserialize'."
|
|
|
(when (file-writable-p filename)
|
|
|
(with-temp-file filename
|
|
|
(insert (prin1-to-string data)))))
|
|
|
|
|
|
(defun projectile-unserialize (filename)
|
|
|
"Read data serialized by `projectile-serialize' from FILENAME."
|
|
|
(when (file-exists-p filename)
|
|
|
(with-temp-buffer
|
|
|
(insert-file-contents filename)
|
|
|
(read (buffer-string)))))
|
|
|
|
|
|
(defvar projectile-projects-cache
|
|
|
(or (projectile-unserialize projectile-cache-file)
|
|
|
(make-hash-table :test 'equal))
|
|
|
"A hashmap used to cache project file names to speed up related operations.")
|
|
|
|
|
|
(defvar projectile-known-projects nil
|
|
|
"List of locations where we have previously seen projects.
|
|
|
The list of projects is ordered by the time they have been accessed.")
|
|
|
|
|
|
(defcustom projectile-known-projects-file
|
|
|
(expand-file-name "projectile-bookmarks.eld"
|
|
|
user-emacs-directory)
|
|
|
"Name and location of the Projectile's known projects file."
|
|
|
:group 'projectile
|
|
|
:type 'string)
|
|
|
|
|
|
(defun projectile-version ()
|
|
|
"Reports the version of Projectile in use."
|
|
|
(interactive)
|
|
|
(message "Projectile (version %s) 2011-2013 Bozhidar Batsov <bozhidar@batsov.com>"
|
|
|
projectile-current-version))
|
|
|
|
|
|
(defun projectile-invalidate-cache (arg)
|
|
|
"Remove the current project's files from `projectile-projects-cache'.
|
|
|
|
|
|
With a prefix argument ARG prompts for the name of the project whose cache
|
|
|
to invalidate."
|
|
|
(interactive "P")
|
|
|
(let ((project-root
|
|
|
(if arg
|
|
|
(completing-read "Remove cache for: "
|
|
|
(projectile-hash-keys projectile-projects-cache))
|
|
|
(projectile-project-root))))
|
|
|
(remhash project-root projectile-projects-cache)
|
|
|
(projectile-serialize-cache)
|
|
|
(message "Invalidated Projectile cache for %s."
|
|
|
(propertize project-root 'face 'font-lock-keyword-face))))
|
|
|
|
|
|
(defun projectile-cache-project (project files)
|
|
|
"Cache PROJECTs FILES.
|
|
|
The cache is created both in memory and on the hard drive."
|
|
|
(when projectile-enable-caching
|
|
|
(puthash project files projectile-projects-cache)
|
|
|
(projectile-serialize-cache)))
|
|
|
|
|
|
(defun projectile-project-root ()
|
|
|
"Retrieves the root directory of a project if available.
|
|
|
The current directory is assumed to be the project's root otherwise."
|
|
|
(let ((project-root
|
|
|
(or (->> projectile-project-root-files
|
|
|
(--map (locate-dominating-file default-directory it))
|
|
|
(-remove #'null)
|
|
|
(car)
|
|
|
(projectile-expand-file-name))
|
|
|
(if projectile-require-project-root
|
|
|
(error "You're not into a project")
|
|
|
default-directory))))
|
|
|
project-root))
|
|
|
|
|
|
(defun projectile-expand-file-name (file-name)
|
|
|
"A thin wrapper around `expand-file-name' that handles nil.
|
|
|
Expand FILE-NAME using `default-directory'."
|
|
|
(when file-name
|
|
|
(expand-file-name file-name)))
|
|
|
|
|
|
(defun projectile-project-p ()
|
|
|
"Check if we're in a project."
|
|
|
(condition-case nil
|
|
|
(projectile-project-root)
|
|
|
(error nil)))
|
|
|
|
|
|
(defun projectile-project-name ()
|
|
|
"Return project name."
|
|
|
(let ((project-root
|
|
|
(condition-case nil
|
|
|
(projectile-project-root)
|
|
|
(error default-directory))))
|
|
|
(file-name-nondirectory (directory-file-name project-root))))
|
|
|
|
|
|
(defun projectile-get-project-directories ()
|
|
|
"Get the list of project directories that are of interest to the user."
|
|
|
(-map (lambda (subdir) (concat (projectile-project-root) subdir))
|
|
|
(or (car (projectile-parse-dirconfig-file)) '(""))))
|
|
|
|
|
|
(defun projectile-dir-files (directory)
|
|
|
"List the files in DIRECTORY and in its sub-directories.
|
|
|
Files are returned as relative paths to the project root."
|
|
|
;; check for a cache hit first if caching is enabled
|
|
|
(let ((files-list (and projectile-enable-caching
|
|
|
(gethash directory projectile-projects-cache)))
|
|
|
(root (projectile-project-root)))
|
|
|
;; cache disabled or cache miss
|
|
|
(or files-list
|
|
|
(if projectile-use-native-indexing
|
|
|
(projectile-dir-files-native root directory)
|
|
|
;; use external tools to get the project files
|
|
|
(projectile-dir-files-external root directory)))))
|
|
|
|
|
|
(defun projectile-dir-files-native (root directory)
|
|
|
"Get the files for ROOT under DIRECTORY using just Emacs Lisp."
|
|
|
(message "Projectile is indexing %s. This may take a while."
|
|
|
(propertize directory 'face 'font-lock-keyword-face))
|
|
|
;; we need the files with paths relative to the project root
|
|
|
(-map (lambda (file) (s-chop-prefix root file))
|
|
|
(projectile-index-directory directory (projectile-patterns-to-ignore))))
|
|
|
|
|
|
(defun projectile-dir-files-external (root directory)
|
|
|
"Get the files for ROOT under DIRECTORY using external tools."
|
|
|
(let ((default-directory directory)
|
|
|
(files-list nil))
|
|
|
(setq files-list (-map (lambda (f)
|
|
|
(s-chop-prefix root (expand-file-name f directory)))
|
|
|
(projectile-get-repo-files)))
|
|
|
files-list))
|
|
|
|
|
|
(defun projectile-file-cached-p (file project)
|
|
|
"Check if FILE is already in PROJECT cache."
|
|
|
(member file (gethash project projectile-projects-cache)))
|
|
|
|
|
|
(defun projectile-cache-current-file ()
|
|
|
"Add the currently visited file to the cache."
|
|
|
(interactive)
|
|
|
(let* ((current-project (projectile-project-root))
|
|
|
(current-file (file-relative-name (buffer-file-name (current-buffer)) current-project)))
|
|
|
(unless (projectile-file-cached-p current-file current-project)
|
|
|
(puthash current-project
|
|
|
(cons current-file (gethash current-project projectile-projects-cache))
|
|
|
projectile-projects-cache)
|
|
|
(projectile-serialize-cache)
|
|
|
(message "File %s added to project %s cache." current-file current-project))))
|
|
|
|
|
|
;; cache opened files automatically to reduce the need for cache invalidation
|
|
|
(defun projectile-cache-files-find-file-hook ()
|
|
|
"Function for caching files with `find-file-hook'."
|
|
|
(when (and (projectile-project-p) projectile-enable-caching)
|
|
|
(projectile-cache-current-file)))
|
|
|
|
|
|
(defun projectile-cache-projects-find-file-hook ()
|
|
|
"Function for caching projects with `find-file-hook'."
|
|
|
(when (projectile-project-p)
|
|
|
(projectile-add-known-project (projectile-project-root))
|
|
|
(projectile-save-known-projects)))
|
|
|
|
|
|
(defcustom projectile-git-command "git ls-files -zco --exclude-standard"
|
|
|
"Command used by projectile to get the files in a git project."
|
|
|
:group 'projectile
|
|
|
:type 'string)
|
|
|
|
|
|
(defcustom projectile-hg-command "hg locate -0 -I ."
|
|
|
"Command used by projectile to get the files in a hg project."
|
|
|
:group 'projectile
|
|
|
:type 'string)
|
|
|
|
|
|
(defcustom projectile-fossil-command "fossil ls"
|
|
|
"Command used by projectile to get the files in a fossil project."
|
|
|
:group 'projectile
|
|
|
:type 'string)
|
|
|
|
|
|
(defcustom projectile-bzr-command "bzr ls --versioned -0"
|
|
|
"Command used by projectile to get the files in a bazaar project."
|
|
|
:group 'projectile
|
|
|
:type 'string)
|
|
|
|
|
|
(defcustom projectile-darcs-command "darcs show files -0 . "
|
|
|
"Command used by projectile to get the files in a darcs project."
|
|
|
:group 'projectile
|
|
|
:type 'string)
|
|
|
|
|
|
(defcustom projectile-svn-command "find . -type f -print0"
|
|
|
"Command used by projectile to get the files in a svn project."
|
|
|
:group 'projectile
|
|
|
:type 'string)
|
|
|
|
|
|
(defcustom projectile-generic-command "find . -type f -print0"
|
|
|
"Command used by projectile to get the files in a generic project."
|
|
|
:group 'projectile
|
|
|
:type 'string)
|
|
|
|
|
|
(defun projectile-get-ext-command ()
|
|
|
"Determine which external command to invoke based on the project's VCS."
|
|
|
(let ((vcs (projectile-project-vcs)))
|
|
|
(cond
|
|
|
((eq vcs 'git) projectile-git-command)
|
|
|
((eq vcs 'hg) projectile-hg-command)
|
|
|
((eq vcs 'fossil) projectile-fossil-command)
|
|
|
((eq vcs 'bzr) projectile-bzr-command)
|
|
|
((eq vcs 'darcs) projectile-darcs-command)
|
|
|
((eq vcs 'svn) projectile-svn-command)
|
|
|
(t projectile-generic-command))))
|
|
|
|
|
|
(defun projectile-get-repo-files ()
|
|
|
"Get a list of the files in the project."
|
|
|
(projectile-files-via-ext-command (projectile-get-ext-command)))
|
|
|
|
|
|
(defun projectile-files-via-ext-command (command)
|
|
|
"Get a list of relative file names in the project root by executing COMMAND."
|
|
|
(split-string (shell-command-to-string command) "\0" t))
|
|
|
|
|
|
(defun projectile-index-directory (directory patterns)
|
|
|
"Index DIRECTORY taking into account PATTERNS.
|
|
|
The function calls itself recursively until all sub-directories
|
|
|
have been indexed."
|
|
|
(let (files-list)
|
|
|
(dolist (current-file (file-name-all-completions "" directory) files-list)
|
|
|
(let ((absolute-file (expand-file-name current-file directory)))
|
|
|
(cond
|
|
|
;; check for directories that are not ignored
|
|
|
((and (s-ends-with-p "/" current-file)
|
|
|
;; avoid loops & ignore some well known directories
|
|
|
(not (-any? (lambda (file)
|
|
|
(string= (s-chop-suffix "/" current-file) file))
|
|
|
'("." ".." ".svn" ".cvs")))
|
|
|
(not (projectile-ignored-directory-p absolute-file))
|
|
|
(not (and patterns
|
|
|
(projectile-ignored-rel-p directory
|
|
|
absolute-file patterns))))
|
|
|
(setq files-list (append files-list
|
|
|
(projectile-index-directory
|
|
|
(expand-file-name current-file directory)
|
|
|
patterns))))
|
|
|
;; check for regular files that are not ignored
|
|
|
((and (not (s-ends-with-p "/" current-file))
|
|
|
(not (projectile-ignored-file-p absolute-file))
|
|
|
(not (and patterns
|
|
|
(projectile-ignored-rel-p directory
|
|
|
absolute-file patterns))))
|
|
|
(setq files-list (cons
|
|
|
(expand-file-name current-file directory)
|
|
|
files-list))))))))
|
|
|
|
|
|
(defun projectile-project-buffers ()
|
|
|
"Get a list of project buffers."
|
|
|
(let ((project-root (projectile-project-root)))
|
|
|
(-filter (lambda (buffer)
|
|
|
(projectile-project-buffer-p buffer project-root))
|
|
|
(buffer-list))))
|
|
|
|
|
|
(defun projectile-project-buffer-p (buffer project-root)
|
|
|
"Check if BUFFER is under PROJECT-ROOT."
|
|
|
(with-current-buffer buffer
|
|
|
(and (s-starts-with? project-root
|
|
|
(expand-file-name default-directory))
|
|
|
;; ignore hidden buffers
|
|
|
(not (s-starts-with? " " (buffer-name buffer))))))
|
|
|
|
|
|
(defun projectile-project-buffer-names ()
|
|
|
"Get a list of project buffer names."
|
|
|
(-map 'buffer-name (projectile-project-buffers)))
|
|
|
|
|
|
(defun projectile-prepend-project-name (string)
|
|
|
"Prepend the current project's name to STRING."
|
|
|
(format "[%s] %s" (projectile-project-name) string))
|
|
|
|
|
|
(defun projectile-switch-to-buffer ()
|
|
|
"Switch to a project buffer."
|
|
|
(interactive)
|
|
|
(switch-to-buffer
|
|
|
(projectile-completing-read
|
|
|
"Switch to buffer: "
|
|
|
(projectile-project-buffer-names))))
|
|
|
|
|
|
(defun projectile-multi-occur ()
|
|
|
"Do a `multi-occur' in the project's buffers."
|
|
|
(interactive)
|
|
|
(multi-occur (projectile-project-buffers)
|
|
|
(car (occur-read-primary-args))))
|
|
|
|
|
|
(defun projectile-ignored-directory-p (directory)
|
|
|
"Check if DIRECTORY should be ignored."
|
|
|
(member directory (projectile-ignored-directories)))
|
|
|
|
|
|
(defun projectile-ignored-file-p (file)
|
|
|
"Check if FILE should be ignored."
|
|
|
(member file (projectile-ignored-files)))
|
|
|
|
|
|
(defun projectile-ignored-rel-p (directory file patterns)
|
|
|
"Check if FILE should be ignored relative to DIRECTORY according to PATTERNS."
|
|
|
(let ((default-directory directory))
|
|
|
(-any? (lambda (pattern)
|
|
|
(or (s-ends-with? (s-chop-suffix "/" pattern)
|
|
|
(s-chop-suffix "/" file))
|
|
|
(member file (file-expand-wildcards pattern t))))
|
|
|
patterns)))
|
|
|
|
|
|
(defun projectile-ignored-files ()
|
|
|
"Return list of ignored files."
|
|
|
(-map
|
|
|
'projectile-expand-root
|
|
|
(append
|
|
|
projectile-globally-ignored-files
|
|
|
(projectile-project-ignored-files))))
|
|
|
|
|
|
(defun projectile-ignored-directories ()
|
|
|
"Return list of ignored directories."
|
|
|
(-map
|
|
|
'file-name-as-directory
|
|
|
(-map
|
|
|
'projectile-expand-root
|
|
|
(append
|
|
|
projectile-globally-ignored-directories
|
|
|
(projectile-project-ignored-directories)))))
|
|
|
|
|
|
(defun projectile-project-ignored-files ()
|
|
|
"Return list of project ignored files."
|
|
|
(-remove 'file-directory-p (projectile-project-ignored)))
|
|
|
|
|
|
(defun projectile-project-ignored-directories ()
|
|
|
"Return list of project ignored directories."
|
|
|
(-filter 'file-directory-p (projectile-project-ignored)))
|
|
|
|
|
|
(defun projectile-paths-to-ignore ()
|
|
|
"Return a list of ignored project paths."
|
|
|
(-map (lambda (pattern)
|
|
|
(s-chop-prefix "/" pattern))
|
|
|
(-filter (lambda (pattern)
|
|
|
(s-starts-with? "/" pattern))
|
|
|
(cdr (projectile-parse-dirconfig-file)))))
|
|
|
|
|
|
(defun projectile-patterns-to-ignore ()
|
|
|
"Return a list of relative file patterns."
|
|
|
(-remove (lambda (pattern)
|
|
|
(s-starts-with? "/" pattern))
|
|
|
(cdr (projectile-parse-dirconfig-file))))
|
|
|
|
|
|
(defun projectile-project-ignored ()
|
|
|
"Return list of project ignored files/directories."
|
|
|
(let ((paths (projectile-paths-to-ignore))
|
|
|
(default-directory (projectile-project-root)))
|
|
|
(apply 'append
|
|
|
(-map
|
|
|
(lambda (pattern)
|
|
|
(file-expand-wildcards pattern t))
|
|
|
paths))))
|
|
|
|
|
|
(defun projectile-dirconfig-file ()
|
|
|
"Return the absolute path to the project's dirconfig file."
|
|
|
(expand-file-name ".projectile" (projectile-project-root)))
|
|
|
|
|
|
(defun projectile-parse-dirconfig-file ()
|
|
|
"Parse project ignore file and return directories to ignore and keep.
|
|
|
|
|
|
The return value will be a cons, the car being the list of
|
|
|
directories to keep, and the cdr being the list of files or
|
|
|
directories to ignore.
|
|
|
|
|
|
Strings starting with + will be added to the list of directories
|
|
|
to keep, and strings starting with - will be added to the list of
|
|
|
directories to ignore. For backward compatibility, without a
|
|
|
prefix the string will be assumed to be an ignore string."
|
|
|
(let ((dirconfig-file (projectile-dirconfig-file)))
|
|
|
(when (file-exists-p dirconfig-file)
|
|
|
(with-temp-buffer
|
|
|
(insert-file-contents-literally dirconfig-file)
|
|
|
(let* ((split-string-default-separators "[\r\n]")
|
|
|
(strings (-map 's-trim (delete "" (split-string (buffer-string)))))
|
|
|
(separated-vals (--separate (s-starts-with? "+" it) strings)))
|
|
|
(flet ((strip-prefix (s) (s-chop-prefixes '("-" "+") s)))
|
|
|
(cons (-map 'strip-prefix (first separated-vals))
|
|
|
(-map 'strip-prefix (second separated-vals)))))))))
|
|
|
|
|
|
(defun projectile-expand-root (name)
|
|
|
"Expand NAME to project root.
|
|
|
|
|
|
Never use on many files since it's going to recalculate the
|
|
|
project-root for every file."
|
|
|
(expand-file-name name (projectile-project-root)))
|
|
|
|
|
|
(defun projectile-completing-read (prompt choices)
|
|
|
"Present a project tailored PROMPT with CHOICES."
|
|
|
(let ((prompt (projectile-prepend-project-name prompt)))
|
|
|
(cond
|
|
|
((eq projectile-completion-system 'ido)
|
|
|
(ido-completing-read prompt choices))
|
|
|
((eq projectile-completion-system 'default)
|
|
|
(completing-read prompt choices))
|
|
|
((eq projectile-completion-system 'grizzl)
|
|
|
(if (and (fboundp 'grizzl-completing-read)
|
|
|
(fboundp 'grizzl-make-index))
|
|
|
(grizzl-completing-read prompt (grizzl-make-index choices))
|
|
|
(user-error "Please install grizzl from \
|
|
|
https://github.com/d11wtq/grizzl")))
|
|
|
(t (funcall projectile-completion-system prompt choices)))))
|
|
|
|
|
|
(defun projectile-current-project-files ()
|
|
|
"Return a list of files for the current project."
|
|
|
(let ((files (and projectile-enable-caching
|
|
|
(gethash (projectile-project-root) projectile-projects-cache))))
|
|
|
;; nothing is cached
|
|
|
(unless files
|
|
|
(setq files (-mapcat 'projectile-dir-files
|
|
|
(projectile-get-project-directories)))
|
|
|
;; cache the resulting list of files
|
|
|
(when projectile-enable-caching
|
|
|
(projectile-cache-project (projectile-project-root) files)))
|
|
|
files))
|
|
|
|
|
|
(defun projectile-current-project-dirs ()
|
|
|
"Return a list of dirs for the current project."
|
|
|
(-remove 'null (-distinct
|
|
|
(-map 'file-name-directory
|
|
|
(projectile-current-project-files)))))
|
|
|
|
|
|
(defun projectile-hash-keys (hash)
|
|
|
"Return a list of all HASH keys."
|
|
|
(let (allkeys)
|
|
|
(maphash (lambda (k v) (setq allkeys (cons k allkeys))) hash)
|
|
|
allkeys))
|
|
|
|
|
|
(defun projectile-find-file (arg)
|
|
|
"Jump to a project's file using completion.
|
|
|
|
|
|
With a prefix ARG invalidates the cache first."
|
|
|
(interactive "P")
|
|
|
(when arg
|
|
|
(projectile-invalidate-cache nil))
|
|
|
(let ((file (projectile-completing-read "Find file: "
|
|
|
(projectile-current-project-files))))
|
|
|
(find-file (expand-file-name file (projectile-project-root)))
|
|
|
(run-hooks 'projectile-find-file-hook)))
|
|
|
|
|
|
(defun projectile-find-dir (arg)
|
|
|
"Jump to a project's file using completion.
|
|
|
|
|
|
With a prefix ARG invalidates the cache first."
|
|
|
(interactive "P")
|
|
|
(when arg
|
|
|
(projectile-invalidate-cache nil))
|
|
|
(let ((dir (projectile-completing-read "Find dir: "
|
|
|
(projectile-current-project-dirs))))
|
|
|
(dired (expand-file-name dir (projectile-project-root)))
|
|
|
(run-hooks 'projectile-find-dir-hook)))
|
|
|
|
|
|
(defun projectile-find-test-file (arg)
|
|
|
"Jump to a project's test file using completion.
|
|
|
|
|
|
With a prefix ARG invalidates the cache first."
|
|
|
(interactive "P")
|
|
|
(when arg
|
|
|
(projectile-invalidate-cache nil))
|
|
|
(let ((file (projectile-completing-read "Find test file: "
|
|
|
(projectile-current-project-files))))
|
|
|
(find-file (expand-file-name file (projectile-project-root)))))
|
|
|
|
|
|
(defvar projectile-test-files-suffices '("_test" "_spec" "Test" "-test")
|
|
|
"Some common suffices of test files.")
|
|
|
|
|
|
(defun projectile-test-files (files)
|
|
|
"Return only the test FILES."
|
|
|
(-filter 'projectile-test-file-p files))
|
|
|
|
|
|
(defun projectile-test-file-p (file)
|
|
|
"Check if FILE is a test file."
|
|
|
(-any? (lambda (suffix)
|
|
|
(s-ends-with? suffix (file-name-sans-extension file)))
|
|
|
projectile-test-files-suffices))
|
|
|
|
|
|
(defvar projectile-rails-rspec '("Gemfile" "app" "lib" "db" "config" "spec"))
|
|
|
(defvar projectile-rails-test '("Gemfile" "app" "lib" "db" "config" "test"))
|
|
|
(defvar projectile-symfony '("composer.json" "app" "src" "vendor"))
|
|
|
(defvar projectile-ruby-rspec '("Gemfile" "lib" "spec"))
|
|
|
(defvar projectile-ruby-test '("Gemfile" "lib" "test"))
|
|
|
(defvar projectile-maven '("pom.xml"))
|
|
|
(defvar projectile-lein '("project.clj"))
|
|
|
(defvar projectile-rebar '("rebar"))
|
|
|
(defvar projectile-make '("Makefile"))
|
|
|
|
|
|
(defun projectile-project-type ()
|
|
|
"Determine the project's type based on its structure."
|
|
|
(let ((project-root (projectile-project-root)))
|
|
|
(cond
|
|
|
((projectile-verify-files projectile-rails-rspec) 'rails-rspec)
|
|
|
((projectile-verify-files projectile-rails-test) 'rails-test)
|
|
|
((projectile-verify-files projectile-ruby-rspec) 'ruby-rspec)
|
|
|
((projectile-verify-files projectile-ruby-test) 'ruby-test)
|
|
|
((projectile-verify-files projectile-symfony) 'symfony)
|
|
|
((projectile-verify-files projectile-maven) 'maven)
|
|
|
((projectile-verify-files projectile-lein) 'lein)
|
|
|
((projectile-verify-files projectile-rebar) 'rebar)
|
|
|
((projectile-verify-files projectile-make) 'make)
|
|
|
(t 'generic))))
|
|
|
|
|
|
(defun projectile-verify-files (files)
|
|
|
"Check whether all FILES exist in the current project."
|
|
|
(-all? 'projectile-verify-file files))
|
|
|
|
|
|
(defun projectile-verify-file (file)
|
|
|
"Check whether FILE exists in the current project."
|
|
|
(file-exists-p (projectile-expand-root file)))
|
|
|
|
|
|
(defun projectile-project-vcs ()
|
|
|
"Determine the VCS used by the project if any."
|
|
|
(let ((project-root (projectile-project-root)))
|
|
|
(cond
|
|
|
((file-exists-p (expand-file-name ".git" project-root)) 'git)
|
|
|
((file-exists-p (expand-file-name ".hg" project-root)) 'hg)
|
|
|
((file-exists-p (expand-file-name ".fossil" project-root)) 'fossil)
|
|
|
((file-exists-p (expand-file-name ".bzr" project-root)) 'bzr)
|
|
|
((file-exists-p (expand-file-name "_darcs" project-root)) 'darcs)
|
|
|
((file-exists-p (expand-file-name ".svn" project-root)) 'svn)
|
|
|
((locate-dominating-file project-root ".git") 'git)
|
|
|
((locate-dominating-file project-root ".hg") 'hg)
|
|
|
((locate-dominating-file project-root ".fossil") 'fossil)
|
|
|
((locate-dominating-file project-root ".bzr") 'bzr)
|
|
|
((locate-dominating-file project-root "_darcs") 'darcs)
|
|
|
((locate-dominating-file project-root ".svn") 'svn)
|
|
|
(t 'none))))
|
|
|
|
|
|
(defun projectile-toggle-between-implemenation-and-test ()
|
|
|
"Toggle between an implementation file and its test file."
|
|
|
(interactive)
|
|
|
(if (projectile-test-file-p (buffer-file-name))
|
|
|
;; find the matching impl file
|
|
|
(let ((impl-file (projectile-find-matching-file (buffer-file-name))))
|
|
|
(if impl-file
|
|
|
(find-file (projectile-expand-root impl-file))
|
|
|
(error "No matching source file found")))
|
|
|
;; find the matching test file
|
|
|
(let ((test-file (projectile-find-matching-test (buffer-file-name))))
|
|
|
(if test-file
|
|
|
(find-file (projectile-expand-root test-file))
|
|
|
(error "No matching test file found")))))
|
|
|
|
|
|
(defun projectile-test-suffix (project-type)
|
|
|
"Find test files suffix based on PROJECT-TYPE."
|
|
|
(cond
|
|
|
((member project-type '(rails-rspec ruby-rspec)) "_spec")
|
|
|
((member project-type '(rails-test ruby-test lein)) "_test")
|
|
|
((member project-type '(maven symfony)) "Test")
|
|
|
(t (error "Project type not supported!"))))
|
|
|
|
|
|
(defun projectile-find-matching-test (file)
|
|
|
"Compute the name of the test matching FILE."
|
|
|
(let ((basename (file-name-nondirectory (file-name-sans-extension file)))
|
|
|
(extension (file-name-extension file))
|
|
|
(test-suffix (projectile-test-suffix (projectile-project-type))))
|
|
|
(-first (lambda (current-file)
|
|
|
(s-equals? (file-name-nondirectory (file-name-sans-extension current-file))
|
|
|
(concat basename test-suffix)))
|
|
|
(projectile-current-project-files))))
|
|
|
|
|
|
(defun projectile-find-matching-file (test-file)
|
|
|
"Compute the name of a file matching TEST-FILE."
|
|
|
(let ((basename (file-name-nondirectory (file-name-sans-extension test-file)))
|
|
|
(extension (file-name-extension test-file))
|
|
|
(test-suffix (projectile-test-suffix (projectile-project-type))))
|
|
|
(-first (lambda (current-file)
|
|
|
(s-equals? (concat (file-name-nondirectory (file-name-sans-extension current-file)) test-suffix)
|
|
|
basename))
|
|
|
(projectile-current-project-files))))
|
|
|
|
|
|
(defun projectile-grep ()
|
|
|
"Perform rgrep in the project."
|
|
|
(interactive)
|
|
|
(let ((roots (projectile-get-project-directories))
|
|
|
(search-regexp (if (and transient-mark-mode mark-active)
|
|
|
(buffer-substring (region-beginning) (region-end))
|
|
|
(read-string (projectile-prepend-project-name "Grep for: ")
|
|
|
(projectile-symbol-at-point)))))
|
|
|
(dolist (root-dir roots)
|
|
|
(require 'grep)
|
|
|
;; paths for find-grep should relative and without trailing /
|
|
|
(let ((grep-find-ignored-directories (union (-map (lambda (dir) (s-chop-suffix "/" (s-chop-prefix root-dir dir)))
|
|
|
(cdr (projectile-ignored-directories))) grep-find-ignored-directories))
|
|
|
(grep-find-ignored-files (union (-map (lambda (file) (s-chop-prefix root-dir file)) (projectile-ignored-files)) grep-find-ignored-files)))
|
|
|
(grep-compute-defaults)
|
|
|
(rgrep search-regexp "* .*" root-dir)))))
|
|
|
|
|
|
(defun projectile-ack ()
|
|
|
"Run an `ack-and-a-half' search in the project."
|
|
|
(interactive)
|
|
|
(let ((ack-and-a-half-arguments
|
|
|
(-map
|
|
|
(lambda (path)
|
|
|
(concat "--ignore-dir=" (file-name-nondirectory (directory-file-name path))))
|
|
|
(projectile-ignored-directories))))
|
|
|
(call-interactively projectile-ack-function)))
|
|
|
|
|
|
(defun projectile-tags-exclude-patterns ()
|
|
|
"Return a string with exclude patterns for ctags."
|
|
|
(mapconcat (lambda (pattern) (format "--exclude=%s" pattern))
|
|
|
(projectile-project-ignored-directories) " "))
|
|
|
|
|
|
(defun projectile-regenerate-tags ()
|
|
|
"Regenerate the project's etags."
|
|
|
(interactive)
|
|
|
(let* ((project-root (projectile-project-root))
|
|
|
(tags-exclude (projectile-tags-exclude-patterns))
|
|
|
(default-directory project-root))
|
|
|
(shell-command (format projectile-tags-command tags-exclude project-root))
|
|
|
(visit-tags-table project-root)))
|
|
|
|
|
|
(defun projectile-replace ()
|
|
|
"Replace a string in the project using `tags-query-replace'."
|
|
|
(interactive)
|
|
|
(let* ((old-text (read-string
|
|
|
(projectile-prepend-project-name "Replace: ")
|
|
|
(projectile-symbol-at-point)))
|
|
|
(new-text (read-string
|
|
|
(projectile-prepend-project-name
|
|
|
(format "Replace %s with: " old-text)))))
|
|
|
(tags-query-replace old-text new-text nil '(-map 'projectile-expand-root (projectile-current-project-files)))))
|
|
|
|
|
|
(defun projectile-symbol-at-point ()
|
|
|
"Get the symbol at point and strip its properties."
|
|
|
(substring-no-properties (or (thing-at-point 'symbol) "")))
|
|
|
|
|
|
(defun projectile-kill-buffers ()
|
|
|
"Kill all project buffers."
|
|
|
(interactive)
|
|
|
(let* ((buffers (projectile-project-buffer-names))
|
|
|
(question
|
|
|
(format
|
|
|
"Are you sure you want to kill %d buffer(s) for '%s'? "
|
|
|
(length buffers)
|
|
|
(projectile-project-name))))
|
|
|
(if (yes-or-no-p question)
|
|
|
(mapc 'kill-buffer buffers))))
|
|
|
|
|
|
(defun projectile-dired ()
|
|
|
"Opens dired at the root of the project."
|
|
|
(interactive)
|
|
|
(dired (projectile-project-root)))
|
|
|
|
|
|
(defun projectile-recentf ()
|
|
|
"Show a list of recently visited files in a project."
|
|
|
(interactive)
|
|
|
(if (boundp 'recentf-list)
|
|
|
(find-file (projectile-expand-root (projectile-completing-read "Recently visited files: " (projectile-recentf-files)))))
|
|
|
(message "recentf is not enabled"))
|
|
|
|
|
|
(defun projectile-recentf-files ()
|
|
|
"Return a list of recently visited files in a project."
|
|
|
(if (boundp 'recentf-list)
|
|
|
(let ((project-root (projectile-project-root)))
|
|
|
(->> recentf-list
|
|
|
(-filter (lambda (file) (s-starts-with-p project-root file)))
|
|
|
(-map (lambda (file) (s-chop-prefix project-root file)))))
|
|
|
nil))
|
|
|
|
|
|
(defun projectile-serialize-cache ()
|
|
|
"Serializes the memory cache to the hard drive."
|
|
|
(projectile-serialize projectile-projects-cache projectile-cache-file))
|
|
|
|
|
|
(defvar projectile-rails-compile-cmd "bundle exec rails server")
|
|
|
(defvar projectile-ruby-compile-cmd "bundle exec rake build")
|
|
|
(defvar projectile-ruby-test-cmd "bundle exec rake test")
|
|
|
(defvar projectile-ruby-rspec-cmd "bundle exec rspec")
|
|
|
(defvar projectile-symfony-compile-cmd "app/console server:run")
|
|
|
(defvar projectile-symfony-test-cmd "phpunit -c app ")
|
|
|
(defvar projectile-maven-compile-cmd "mvn clean install")
|
|
|
(defvar projectile-maven-test-cmd "mvn test")
|
|
|
(defvar projectile-lein-compile-cmd "lein compile")
|
|
|
(defvar projectile-lein-test-cmd "lein test")
|
|
|
(defvar projectile-rebar-compile-cmd "rebar")
|
|
|
(defvar projectile-rebar-test-cmd "rebar eunit")
|
|
|
(defvar projectile-make-compile-cmd "make")
|
|
|
(defvar projectile-make-test-cmd "make test")
|
|
|
|
|
|
(defvar projectile-compilation-cmd-map
|
|
|
(make-hash-table :test 'equal)
|
|
|
"A mapping between projects and the last compilation command used on them.")
|
|
|
(defvar projectile-test-cmd-map
|
|
|
(make-hash-table :test 'equal)
|
|
|
"A mapping between projects and the last test command used on them.")
|
|
|
|
|
|
(defun projectile-default-compilation-command (project-type)
|
|
|
"Retrieve default compilation command for PROJECT-TYPE."
|
|
|
(cond
|
|
|
((member project-type '(rails-rspec rails-test)) projectile-rails-compile-cmd)
|
|
|
((member project-type '(ruby-rspec ruby-test)) projectile-ruby-compile-cmd)
|
|
|
((eq project-type 'symfony) projectile-symfony-compile-cmd)
|
|
|
((eq project-type 'lein) projectile-lein-compile-cmd)
|
|
|
((eq project-type 'make) projectile-make-compile-cmd)
|
|
|
((eq project-type 'rebar) projectile-rebar-compile-cmd)
|
|
|
((eq project-type 'maven) projectile-maven-compile-cmd)
|
|
|
(t projectile-make-compile-cmd)))
|
|
|
|
|
|
(defun projectile-default-test-command (project-type)
|
|
|
"Retrieve default test command for PROJECT-TYPE."
|
|
|
(cond
|
|
|
((member project-type '(rails-rspec ruby-rspec)) projectile-ruby-rspec-cmd)
|
|
|
((member project-type '(rails-test ruby-test)) projectile-ruby-test-cmd)
|
|
|
((eq project-type 'symfony) projectile-symfony-test-cmd)
|
|
|
((eq project-type 'lein) projectile-lein-test-cmd)
|
|
|
((eq project-type 'make) projectile-make-test-cmd)
|
|
|
((eq project-type 'rebar) projectile-rebar-test-cmd)
|
|
|
((eq project-type 'maven) projectile-maven-test-cmd)
|
|
|
(t projectile-make-test-cmd)))
|
|
|
|
|
|
(defun projectile-compilation-command (project)
|
|
|
"Retrieve the compilation command for PROJECT."
|
|
|
(or (gethash project projectile-compilation-cmd-map)
|
|
|
(projectile-default-compilation-command (projectile-project-type))))
|
|
|
|
|
|
(defun projectile-test-command (project)
|
|
|
"Retrieve the compilation command for PROJECT."
|
|
|
(or (gethash project projectile-test-cmd-map)
|
|
|
(projectile-default-test-command (projectile-project-type))))
|
|
|
|
|
|
(defun projectile-compile-project ()
|
|
|
"Run project compilation command."
|
|
|
(interactive)
|
|
|
(let* ((project-root (projectile-project-root))
|
|
|
(compilation-cmd (compilation-read-command (projectile-compilation-command project-root)))
|
|
|
(default-directory project-root))
|
|
|
(puthash project-root compilation-cmd projectile-compilation-cmd-map)
|
|
|
(compilation-start compilation-cmd)))
|
|
|
|
|
|
;; TODO - factor this duplication out
|
|
|
(defun projectile-test-project ()
|
|
|
"Run project test command."
|
|
|
(interactive)
|
|
|
(let* ((project-root (projectile-project-root))
|
|
|
(test-cmd (compilation-read-command (projectile-test-command project-root)))
|
|
|
(default-directory project-root))
|
|
|
(puthash project-root test-cmd projectile-test-cmd-map)
|
|
|
(compilation-start test-cmd)))
|
|
|
|
|
|
(defun projectile-switch-project ()
|
|
|
"Switch to a project we have seen before."
|
|
|
(interactive)
|
|
|
(let* ((project-to-switch
|
|
|
(projectile-completing-read "Switch to which project: "
|
|
|
projectile-known-projects))
|
|
|
(default-directory project-to-switch))
|
|
|
(projectile-find-file nil)
|
|
|
(let ((project-switched project-to-switch))
|
|
|
(run-hooks 'projectile-switch-project-hook))))
|
|
|
|
|
|
(defvar projectile-switch-project-hook nil
|
|
|
"Hooks run when project is switched.
|
|
|
|
|
|
The path to the opened project is available as PROJECT-SWITCHED")
|
|
|
|
|
|
(defun projectile-clear-known-projects ()
|
|
|
"Clear both `projectile-known-projects' and `projectile-known-projects-file'."
|
|
|
(interactive)
|
|
|
(setq projectile-known-projects nil)
|
|
|
(projectile-save-known-projects))
|
|
|
|
|
|
(defun projectile-remove-known-project ()
|
|
|
"Remove a projected from the list of known projects."
|
|
|
(interactive)
|
|
|
(let ((project-to-remove
|
|
|
(projectile-completing-read "Switch to which project: "
|
|
|
projectile-known-projects)))
|
|
|
(setq projectile-known-projects
|
|
|
(--reject (string= project-to-remove it) projectile-known-projects))
|
|
|
(projectile-save-known-projects)
|
|
|
(message "Project %s removed from the list of known projects." project-to-remove)))
|
|
|
|
|
|
(defun projectile-add-known-project (project-root)
|
|
|
"Add PROJECT-ROOT to the list of known projects."
|
|
|
(setq projectile-known-projects
|
|
|
(-distinct
|
|
|
(cons (abbreviate-file-name project-root) projectile-known-projects))))
|
|
|
|
|
|
(defun projectile-load-known-projects ()
|
|
|
"Load saved projects from `projectile-known-projects-file'.
|
|
|
Also set `projectile-known-projects'."
|
|
|
(setq projectile-known-projects
|
|
|
(projectile-unserialize projectile-known-projects-file)))
|
|
|
|
|
|
;; load the known projects
|
|
|
(projectile-load-known-projects)
|
|
|
|
|
|
(defun projectile-save-known-projects ()
|
|
|
"Save PROJECTILE-KNOWN-PROJECTS to PROJECTILE-KNOWN-PROJECTS-FILE."
|
|
|
(projectile-serialize projectile-known-projects projectile-known-projects-file))
|
|
|
|
|
|
(defvar projectile-mode-map
|
|
|
(let ((map (make-sparse-keymap)))
|
|
|
(let ((prefix-map (make-sparse-keymap)))
|
|
|
(define-key prefix-map (kbd "f") 'projectile-find-file)
|
|
|
(define-key prefix-map (kbd "T") 'projectile-find-test-file)
|
|
|
(define-key prefix-map (kbd "t") 'projectile-toggle-between-implemenation-and-test)
|
|
|
(define-key prefix-map (kbd "g") 'projectile-grep)
|
|
|
(define-key prefix-map (kbd "b") 'projectile-switch-to-buffer)
|
|
|
(define-key prefix-map (kbd "o") 'projectile-multi-occur)
|
|
|
(define-key prefix-map (kbd "r") 'projectile-replace)
|
|
|
(define-key prefix-map (kbd "i") 'projectile-invalidate-cache)
|
|
|
(define-key prefix-map (kbd "R") 'projectile-regenerate-tags)
|
|
|
(define-key prefix-map (kbd "k") 'projectile-kill-buffers)
|
|
|
(define-key prefix-map (kbd "d") 'projectile-find-dir)
|
|
|
(define-key prefix-map (kbd "D") 'projectile-dired)
|
|
|
(define-key prefix-map (kbd "e") 'projectile-recentf)
|
|
|
(define-key prefix-map (kbd "a") 'projectile-ack)
|
|
|
(define-key prefix-map (kbd "c") 'projectile-compile-project)
|
|
|
(define-key prefix-map (kbd "p") 'projectile-test-project)
|
|
|
(define-key prefix-map (kbd "z") 'projectile-cache-current-file)
|
|
|
(define-key prefix-map (kbd "s") 'projectile-switch-project)
|
|
|
|
|
|
(define-key map projectile-keymap-prefix prefix-map))
|
|
|
map)
|
|
|
"Keymap for Projectile mode.")
|
|
|
|
|
|
(defun projectile-add-menu ()
|
|
|
"Add Projectile's menu under Tools."
|
|
|
(easy-menu-add-item nil '("Tools")
|
|
|
'("Projectile"
|
|
|
["Find file" projectile-find-file]
|
|
|
["Switch to buffer" projectile-switch-to-buffer]
|
|
|
["Kill project buffers" projectile-kill-buffers]
|
|
|
["Recent files" projectile-recentf]
|
|
|
"--"
|
|
|
["Open project in dired" projectile-dired]
|
|
|
["Find in project (grep)" projectile-grep]
|
|
|
["Find in project (ack)" projectile-ack]
|
|
|
["Replace in project" projectile-replace]
|
|
|
["Multi-occur in project" projectile-multi-occur]
|
|
|
"--"
|
|
|
["Invalidate cache" projectile-invalidate-cache]
|
|
|
["Regenerate etags" projectile-regenerate-tags]
|
|
|
"--"
|
|
|
["Compile project" projectile-compile-project]
|
|
|
["Test project" projectile-test-project]
|
|
|
"--"
|
|
|
["About" projectile-version])
|
|
|
"Search Files (Grep)...")
|
|
|
|
|
|
(easy-menu-add-item nil '("Tools") '("--") "Search Files (Grep)..."))
|
|
|
|
|
|
(defun projectile-remove-menu ()
|
|
|
"Remove Projectile's menu."
|
|
|
(easy-menu-remove-item nil '("Tools") "Projectile")
|
|
|
(easy-menu-remove-item nil '("Tools") "--"))
|
|
|
|
|
|
;;; define minor mode
|
|
|
|
|
|
;;;###autoload
|
|
|
(define-minor-mode projectile-mode
|
|
|
"Minor mode to assist project management and navigation.
|
|
|
|
|
|
\\{projectile-mode-map}"
|
|
|
:lighter " Projectile"
|
|
|
:keymap projectile-mode-map
|
|
|
:group 'projectile
|
|
|
(if projectile-mode
|
|
|
;; on start
|
|
|
(projectile-add-menu)
|
|
|
;; on stop
|
|
|
(projectile-remove-menu)))
|
|
|
|
|
|
;;;###autoload
|
|
|
(define-globalized-minor-mode projectile-global-mode
|
|
|
projectile-mode
|
|
|
projectile-on)
|
|
|
|
|
|
(defun projectile-on ()
|
|
|
"Enable Projectile minor mode."
|
|
|
(projectile-mode 1))
|
|
|
|
|
|
(defun projectile-off ()
|
|
|
"Disable Projectile minor mode."
|
|
|
(projectile-mode -1))
|
|
|
|
|
|
(defun projectile-global-on ()
|
|
|
"Enable Projectile global minor mode."
|
|
|
(add-hook 'find-file-hook 'projectile-cache-files-find-file-hook)
|
|
|
(add-hook 'find-file-hook 'projectile-cache-projects-find-file-hook)
|
|
|
(add-hook 'projectile-find-dir-hook 'projectile-cache-projects-find-file-hook))
|
|
|
|
|
|
(defun projectile-global-off ()
|
|
|
"Disable Projectile global minor mode."
|
|
|
(remove-hook 'find-file-hook 'projectile-cache-files-find-file-hook)
|
|
|
(remove-hook 'find-file-hook 'projectile-cache-projects-find-file-hook))
|
|
|
|
|
|
(defadvice projectile-global-mode (after projectile-setup-hooks activate)
|
|
|
"Add/remove `find-file-hook' functions within `projectile-global-mode'."
|
|
|
(if projectile-global-mode
|
|
|
(projectile-global-on)
|
|
|
(projectile-global-off)))
|
|
|
|
|
|
(provide 'projectile)
|
|
|
|
|
|
;; Local Variables:
|
|
|
;; byte-compile-warnings: (not cl-functions)
|
|
|
;; End:
|
|
|
|
|
|
;;; projectile.el ends here
|