;;; 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)
  ;; TODO: Prompt for path project root is not found.
  ;; TODO: Respect gitignore.
  (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
