;;; syd-project.el -*- lexical-binding: t; -*- (eval-when-compile (require 'cl-lib)) (require 'project) (cl-defun syd-project-root (&key (dir default-directory)) "Return the project root of DIR, or nil if DIR belongs to no project." (when-let* ((project (project-current nil dir))) (project-root project))) (defun syd-project-cd () "Change the working directory to the root of the current project." (cd (syd-project-root))) (define-obsolete-function-alias 'syd-cd-project 'syd-project-cd "2025-02-20") (defmacro syd-with-project-root (root &rest body) "Execute BODY with ROOT recognised as what project.el calls a \"transient project\"." (declare (indent defun)) (let ((root* (gensym "root")) (forget-after-p (gensym "forget-after-p"))) `(let* ((,root* ,root) (,forget-after-p (not (member ,root* (project-known-project-roots))))) (let ((project-find-functions (lambda (_) (cons 'transient ,root*)))) ,@body) (when ,forget-after-p (project-forget-project ,root*))))) (defun syd-project-search () (interactive) (require 'syd-file) ;; TODO: Prompt for path project root is not found. (syd-search-directory (syd-project-root))) (defun syd-project-root-find-file (file-name) "Just like `project-root-find-file', but allowing you to select the root directory itself." (declare (interactive-only find-file)) (interactive (list (let ((root (project-root (project-current t)))) (read-file-name "Find file in root: " root root (confirm-nonexistent-file-or-buffer))))) (find-file file-name t)) (provide 'syd-project) ;;; syd-project.el ends here