2017-06-08 11:47:56 +02:00
|
|
|
;;; core-projects.el -*- lexical-binding: t; -*-
|
2017-02-08 02:02:51 -05:00
|
|
|
|
2017-03-02 18:16:52 -05:00
|
|
|
(defvar doom-project-hook nil
|
|
|
|
"Hook run when a project is enabled. The name of the project's mode and its
|
|
|
|
state are passed in.")
|
|
|
|
|
2017-06-08 11:47:56 +02:00
|
|
|
(def-package! projectile
|
2017-10-08 13:09:57 +02:00
|
|
|
:init
|
2017-02-19 18:09:26 -05:00
|
|
|
(setq projectile-cache-file (concat doom-cache-dir "projectile.cache")
|
2017-01-31 19:50:11 -05:00
|
|
|
projectile-enable-caching (not noninteractive)
|
|
|
|
projectile-indexing-method 'alien
|
2017-02-19 18:09:26 -05:00
|
|
|
projectile-known-projects-file (concat doom-cache-dir "projectile.projects")
|
2017-03-27 13:05:30 -04:00
|
|
|
projectile-require-project-root nil
|
2017-09-20 01:39:15 +02:00
|
|
|
projectile-globally-ignored-files '(".DS_Store" "Icon
" "TAGS")
|
|
|
|
projectile-globally-ignored-file-suffixes '(".elc" ".pyc" ".o"))
|
|
|
|
|
2017-10-08 13:09:57 +02:00
|
|
|
(add-hook 'doom-init-hook #'projectile-mode)
|
|
|
|
:config
|
2017-12-31 11:09:36 -05:00
|
|
|
(add-hook 'dired-before-readin-hook #'projectile-track-known-projects-find-file-hook)
|
|
|
|
|
2017-09-20 01:39:15 +02:00
|
|
|
;; a more generic project root file
|
2017-09-19 15:08:24 +02:00
|
|
|
(push ".project" projectile-project-root-files-bottom-up)
|
|
|
|
|
2017-10-23 20:09:01 +02:00
|
|
|
(nconc projectile-globally-ignored-directories (list (abbreviate-file-name doom-local-dir) ".sync"))
|
2017-09-20 01:39:15 +02:00
|
|
|
(nconc projectile-other-file-alist '(("css" . ("scss" "sass" "less" "style"))
|
|
|
|
("scss" . ("css"))
|
|
|
|
("sass" . ("css"))
|
|
|
|
("less" . ("css"))
|
|
|
|
("styl" . ("css"))))
|
|
|
|
|
2017-07-26 18:42:33 +02:00
|
|
|
;; Projectile root-searching functions can cause an infinite loop on TRAMP
|
|
|
|
;; connections, so disable them.
|
2017-04-16 11:55:47 -04:00
|
|
|
(defun doom*projectile-locate-dominating-file (orig-fn &rest args)
|
2017-09-20 01:39:15 +02:00
|
|
|
"Don't traverse the file system if on a remote connection."
|
2017-04-16 11:55:47 -04:00
|
|
|
(unless (file-remote-p default-directory)
|
|
|
|
(apply orig-fn args)))
|
2017-04-17 02:17:10 -04:00
|
|
|
(advice-add #'projectile-locate-dominating-file :around #'doom*projectile-locate-dominating-file)
|
2017-04-16 11:55:47 -04:00
|
|
|
|
2017-01-31 19:50:11 -05:00
|
|
|
(defun doom*projectile-cache-current-file (orig-fun &rest args)
|
|
|
|
"Don't cache ignored files."
|
2017-09-20 01:39:15 +02:00
|
|
|
(unless (cl-loop for path in (projectile-ignored-directories)
|
|
|
|
if (string-prefix-p buffer-file-name (expand-file-name path))
|
|
|
|
return t)
|
2017-01-31 19:50:11 -05:00
|
|
|
(apply orig-fun args)))
|
2017-04-17 02:17:10 -04:00
|
|
|
(advice-add #'projectile-cache-current-file :around #'doom*projectile-cache-current-file))
|
2017-01-31 19:50:11 -05:00
|
|
|
|
|
|
|
|
|
|
|
;;
|
|
|
|
;; Library
|
|
|
|
;;
|
|
|
|
|
2017-11-05 01:23:36 +01:00
|
|
|
(defun doom//reload-project ()
|
2017-09-19 15:08:54 +02:00
|
|
|
"Reload the project root cache."
|
|
|
|
(interactive)
|
|
|
|
(projectile-invalidate-cache nil)
|
|
|
|
(projectile-reset-cached-project-root)
|
|
|
|
(dolist (fn projectile-project-root-files-functions)
|
|
|
|
(remhash (format "%s-%s" fn default-directory) projectile-project-root-cache)))
|
|
|
|
|
2017-09-20 01:33:25 +02:00
|
|
|
(defun doom-project-p ()
|
2017-01-31 19:50:11 -05:00
|
|
|
"Whether or not this buffer is currently in a project or not."
|
2017-09-20 01:33:25 +02:00
|
|
|
(let ((projectile-require-project-root t))
|
2017-01-31 19:50:11 -05:00
|
|
|
(projectile-project-p)))
|
|
|
|
|
2017-09-20 01:33:25 +02:00
|
|
|
(defun doom-project-root ()
|
2017-09-19 15:08:54 +02:00
|
|
|
"Get the path to the root of your project.
|
|
|
|
If STRICT-P, return nil if no project was found, otherwise return
|
|
|
|
`default-directory'."
|
2017-09-20 01:33:25 +02:00
|
|
|
(let (projectile-require-project-root)
|
|
|
|
(projectile-project-root)))
|
2017-06-19 00:03:37 +02:00
|
|
|
|
2017-10-04 17:42:37 +02:00
|
|
|
(defalias 'doom-project-expand #'projectile-expand-root)
|
|
|
|
|
2017-03-02 18:16:52 -05:00
|
|
|
(defmacro doom-project-has! (files)
|
2017-09-20 01:39:15 +02:00
|
|
|
"Checks if the project has the specified FILES.
|
|
|
|
Paths are relative to the project root, unless they start with ./ or ../ (in
|
|
|
|
which case they're relative to `default-directory'). If they start with a slash,
|
|
|
|
they are absolute."
|
2017-09-27 01:21:48 +02:00
|
|
|
(doom--resolve-path-forms files (doom-project-root)))
|
2017-03-02 18:16:52 -05:00
|
|
|
|
2017-12-23 02:09:40 -05:00
|
|
|
(defun doom-project-find-file (dir)
|
|
|
|
"Fuzzy-find a file in DIR."
|
|
|
|
(let ((default-directory dir)
|
|
|
|
;; Necessary to isolate this search from the current project
|
|
|
|
projectile-project-name
|
|
|
|
projectile-require-project-root
|
|
|
|
projectile-cached-buffer-file-name
|
|
|
|
projectile-cached-project-root)
|
|
|
|
(call-interactively
|
|
|
|
;; completion modules may remap this command
|
|
|
|
(or (command-remapping #'projectile-find-file)
|
|
|
|
#'projectile-find-file))))
|
|
|
|
|
|
|
|
(defun doom-project-browse (dir)
|
|
|
|
"TODO"
|
|
|
|
(let ((default-directory dir))
|
|
|
|
(call-interactively
|
|
|
|
;; completion modules may remap this command
|
|
|
|
(or (command-remapping #'find-file)
|
|
|
|
#'find-file))))
|
|
|
|
|
2017-03-02 18:16:52 -05:00
|
|
|
|
|
|
|
;;
|
|
|
|
;; Projects
|
|
|
|
;;
|
|
|
|
|
|
|
|
(defvar-local doom-project nil
|
2017-09-20 01:39:15 +02:00
|
|
|
"A list of project mode to enable. Used for .dir-locals.el.")
|
2017-03-02 18:16:52 -05:00
|
|
|
|
|
|
|
(defun doom|autoload-project-mode ()
|
|
|
|
"Auto-enable projects listed in `doom-project', which is meant to be set from
|
|
|
|
.dir-locals.el files."
|
2017-10-05 16:17:52 +02:00
|
|
|
(cl-loop for mode in doom-project
|
|
|
|
unless (symbol-value mode)
|
|
|
|
do (funcall mode)))
|
2017-04-17 02:17:10 -04:00
|
|
|
(add-hook 'after-change-major-mode-hook #'doom|autoload-project-mode)
|
2017-03-02 18:16:52 -05:00
|
|
|
|
|
|
|
(defmacro def-project-mode! (name &rest plist)
|
2017-09-20 01:39:15 +02:00
|
|
|
"Define a project minor-mode named NAME (a symbol) and declare where and how
|
|
|
|
it is activated. Project modes allow you to configure 'sub-modes' for
|
|
|
|
major-modes that are specific to a specific folder, certain project structure,
|
|
|
|
framework or arbitrary context you define. These project modes can have their
|
|
|
|
own settings, keymaps, hooks, snippets, etc.
|
2017-03-02 18:16:52 -05:00
|
|
|
|
|
|
|
This creates NAME-hook and NAME-map as well.
|
|
|
|
|
2017-06-16 14:34:28 +02:00
|
|
|
A project can be enabled through .dir-locals.el too, if `doom-project' is set to
|
|
|
|
the name (symbol) of the project mode(s) to enable.
|
2017-03-02 18:16:52 -05:00
|
|
|
|
2017-06-16 14:34:28 +02:00
|
|
|
PLIST may contain any of these properties, which are all checked to see if NAME
|
|
|
|
should be activated. If they are *all* true, NAME is activated.
|
2017-03-02 18:16:52 -05:00
|
|
|
|
|
|
|
:modes MODES -- if buffers are derived from MODES (one or a list of symbols).
|
|
|
|
|
2017-06-16 14:34:28 +02:00
|
|
|
:files FILES -- if project contains FILES; takes a string or a form comprised
|
|
|
|
of nested (and ...) and/or (or ...) forms. Each path is relative to the
|
2017-03-15 22:42:05 -04:00
|
|
|
project root, however, if prefixed with a '.' or '..', it is relative to the
|
|
|
|
current buffer.
|
2017-03-02 18:16:52 -05:00
|
|
|
|
|
|
|
:match REGEXP -- if file name matches REGEXP
|
|
|
|
|
|
|
|
:when PREDICATE -- if PREDICATE returns true (can be a form or the symbol of a
|
|
|
|
function)
|
|
|
|
|
2017-10-04 17:42:37 +02:00
|
|
|
:add-hooks HOOKS -- HOOKS is a list of hooks to add this mode's hook.
|
|
|
|
|
|
|
|
:on-load FORM -- FORM to run the first time this project mode is enabled.
|
|
|
|
|
|
|
|
:on-enter FORM -- FORM is run each time the mode is activated.
|
2017-06-16 14:34:28 +02:00
|
|
|
|
2017-10-04 17:42:37 +02:00
|
|
|
:on-exit FORM -- FORM is run each time the mode is disabled.
|
2017-10-03 03:02:53 +02:00
|
|
|
|
2017-06-16 14:34:28 +02:00
|
|
|
Relevant: `doom-project-hook'."
|
2017-10-04 17:42:37 +02:00
|
|
|
(declare (indent 1) (doc-string 2))
|
|
|
|
(let ((doc-string (if (stringp (car plist))
|
|
|
|
(prog1 (car plist)
|
|
|
|
(setq plist (cdr plist)))
|
|
|
|
"A project minor mode."))
|
|
|
|
(modes (plist-get plist :modes))
|
2017-03-02 18:16:52 -05:00
|
|
|
(files (plist-get plist :files))
|
|
|
|
(when (plist-get plist :when))
|
|
|
|
(match (plist-get plist :match))
|
2017-10-04 17:42:37 +02:00
|
|
|
(hooks (plist-get plist :add-hooks))
|
|
|
|
(load-form (plist-get plist :on-load))
|
|
|
|
(enter-form (plist-get plist :on-enter))
|
|
|
|
(exit-form (plist-get plist :on-exit))
|
|
|
|
(init-var (intern (format "%s-init" name))))
|
2017-03-02 18:16:52 -05:00
|
|
|
`(progn
|
2017-10-04 17:42:37 +02:00
|
|
|
,(if load-form `(defvar ,init-var nil))
|
2017-03-02 18:16:52 -05:00
|
|
|
(define-minor-mode ,name
|
2017-10-04 17:42:37 +02:00
|
|
|
,doc-string
|
2017-03-02 18:16:52 -05:00
|
|
|
:init-value nil
|
2017-10-04 17:42:37 +02:00
|
|
|
:lighter ""
|
|
|
|
:keymap (make-sparse-keymap)
|
|
|
|
(if (not ,name)
|
|
|
|
,exit-form
|
|
|
|
(run-hook-with-args 'doom-project-hook ',name)
|
|
|
|
,(when load-form
|
|
|
|
`(unless ,init-var
|
|
|
|
,load-form
|
|
|
|
(setq ,init-var t)))
|
|
|
|
,enter-form))
|
|
|
|
,(when hooks
|
|
|
|
`(setq ,(intern (format "%s-hook" name)) ',hooks))
|
2017-03-02 18:16:52 -05:00
|
|
|
,(when (or modes match files when)
|
|
|
|
`(associate! ,name
|
|
|
|
:modes ,modes
|
|
|
|
:match ,match
|
|
|
|
:files ,files
|
2017-10-04 17:42:37 +02:00
|
|
|
:when ,when)))))
|
2017-01-31 19:50:11 -05:00
|
|
|
|
|
|
|
(provide 'core-projects)
|
|
|
|
;;; core-projects.el ends here
|