Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Projectile Project Loader
(with-eval-after-load 'projectile
(defconst fn/project-file ".project.el"
"Project configuration file")
(defconst fn/project-local-file ".project-locals.el"
"Project local setting file")
(defconst fn/project-init-files (list fn/project-file fn/project-local-file)
"Project init files")
(defun fn/load-project-file ()
"Loads the `fn/project-file' for a project.
This is run once after the project is loaded signifying project setup."
(interactive)
(when (projectile-project-p)
(lexical-let* ((current-project-root (projectile-project-root))
(project-init-file (expand-file-name fn/project-file current-project-root)))
(when (file-exists-p project-init-file)
(message "Loading project init file for %s" (projectile-project-name))
(condition-case ex
(load project-init-file t)
('error
(message
"There was an error loading %s: %s"
project-init-file
(error-message-string ex))))))))
(defun fn/load-project-local-file ()
"Loads the `fn/project-local-file' for a project.
This is run for every time a file in a project is opened signifying per-file setup."
(interactive)
(when (projectile-project-p)
(let* ((current-project-root (projectile-project-root))
(project-local-init-file (expand-file-name fn/project-local-file current-project-root)))
(when (and (file-exists-p project-local-init-file)
(not (member (buffer-file-name) fn/project-init-files)))
(message
"Loading project local file for %s on %s"
(projectile-project-name)
(buffer-name))
(condition-case ex
(load project-local-init-file t)
('error
(message
"There was an error loading %s: %s"
project-local-init-file
(error-message-string ex))))))))
;; Safety Loader
(defvar fn/checked-projects (list)
"Projects that are trusted with loading `fn/project-init-files' or not.")
(defvar fn/loaded-projects (list)
"Projects that have been loaded by `fn/load-project-file'.")
(defun fn/safe-project-p (project-root)
"Check if PROJECT-ROOT can be trusted."
(lexical-let* ((last-modification-time
(file-attribute-modification-time (file-attributes project-root)))
(safe-project-properties
(or (cdr (assoc project-root fn/checked-projects))
(lexical-let* ((base-properties
(list :last-modification-time last-modification-time
:project-root project-root))
(trusted
(yes-or-no-p
(format "Do you ultimately trust the project at %s?" project-root)))
(new-properties
(plist-put base-properties :trusted trusted)))
new-properties))))
(prog1
(plist-get safe-project-properties :trusted)
(add-to-list 'fn/checked-projects
(cons project-root safe-project-properties)))))
(defun fn/safe-load-project-file ()
"Safety wrapper around `fn/load-project-file' with `fn/safe-project-p'."
(when (projectile-project-p)
(lexical-let ((project-root (projectile-project-root))
(project-name (projectile-project-name)))
(when (not (member project-root fn/loaded-projects))
(if (not (fn/safe-project-p project-root))
(message "Project script for %s is not trusted." project-name)
(fn/load-project-file)
(add-to-list 'fn/loaded-projects project-root))))))
(defun fn/safe-load-project-local-file ()
"Safety wrapper around `fn/load-project-local-file' with `fn/safe-project-p'."
(when (projectile-project-p)
(lexical-let ((project-root (projectile-project-root))
(project-name (projectile-project-name)))
(if (not (fn/safe-project-p project-root))
(message "Project local script for %s is not trusted." project-name)
(fn/load-project-local-file)))))
;; Project root script should run before project file script
(add-hook 'find-file-hook #'fn/safe-load-project-file t)
(add-hook 'dired-mode-hook #'fn/safe-load-project-file t)
(add-hook 'find-file-hook #'fn/safe-load-project-local-file t)
(add-hook 'dired-mode-hook #'fn/safe-load-project-local-file t))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment