diff --git a/CHANGELOG.md b/CHANGELOG.md index 115ef78d..727c9f31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,24 @@ All notable changes of the PHP Mode 1.19.1 release series are documented in this ## Unreleased +### Added + + * `php-project-get-root-dir` falls back to `project-current` when no PHP-specific marker is found, so `project.el` backends (Projectile 3, `project-vc-extra-root-markers`, etc.) can contribute project detection + ### Changed * Add `readonly` class modifier to [Imenu] ([#802]) * Add `enum` support to `php-current-class` ([#802]) * Remove hardcoding of implicit paths in `php` that are not guaranteed to exist ([#803]) +### Fixed + + * `php-project-project-find-function` now returns a `project.el` value valid on Emacs 28+; it previously built a `(vc . ROOT)` cons that broke the 3-element `(vc BACKEND ROOT)` representation and made `project-root` signal an error + +### Deprecated + + * `php-project-use-projectile-to-detect-root` is obsolete; Projectile 3 registers itself on `project-find-functions`, which `php-project-get-root-dir` now consults automatically + [Imenu]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Imenu.html [#802]: https://github.com/emacs-php/php-mode/pull/802 [#803]: https://github.com/emacs-php/php-mode/pull/803 diff --git a/README.ja.md b/README.ja.md index 2222cbb3..d9bc528f 100644 --- a/README.ja.md +++ b/README.ja.md @@ -75,6 +75,24 @@ M-x package-install php-mode (php-project-coding-style . psr2))) ``` +### `project.el`・Projectileとの連携 + +`php-project-get-root-dir`は、まずPHP固有のマーカー(`.projectile`、`composer.json`/`composer.lock`、続いてVCSディレクトリ)を探索します。モノレポではパッケージ単位の`vendor/autoload.php`やコーディングスタイルが`php-mode`にとって重要なため、VCSルートより`composer.json`を優先します。これらのマーカーが見つからない場合は`project-current`にフォールバックするので、任意の[`project.el`](https://www.gnu.org/software/emacs/manual/html_node/emacs/Projects.html)バックエンドが検出に寄与できます。 + +* **Projectile 3**は`projectile-mode`有効時に自身を`project-find-functions`へ登録するため、その検出結果が自動的に利用されます。従来の`php-project-use-projectile-to-detect-root`オプションはこのため廃止予定です。 +* Projectileなしでも`project-vc-extra-root-markers`(Emacs 29以降)で追加のルートマーカーを宣言できます。例えば`.dir-locals.el`に次のように記述します。 + + ```lisp + ((nil + (project-vc-extra-root-markers . ("composer.json")))) + ``` + +逆に、PHP固有の検出(上記の`composer.json`優先ルール)をEglotや`project-find-file`などの`project.el`利用側に見せたい場合は、組み込みのVC検出を補完するだけになるよう、低い優先度で`php-project-project-find-function`を登録します。 + +```lisp +(add-hook 'project-find-functions #'php-project-project-find-function 90) +``` + ## HTMLとPHPが混在するファイルの編集 `php-mode`は純粋なPHPスクリプトのためのメジャーモードです。テンプレートのようにHTMLの中にPHPを埋め込んだファイルは、両方の言語を理解するメジャーモードで編集するほうが適しています。特にインデントは、HTML部分を素の`php-mode`で編集すると正しく動作しません。 diff --git a/README.md b/README.md index 94421fb8..178257b8 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,24 @@ You can add project-specific settings by creating a `.dir-locals.el` or `.dir-lo (php-project-coding-style . psr2))) ``` +### Integration with `project.el` and Projectile + +`php-project-get-root-dir` first looks for a PHP-specific marker (`.projectile`, `composer.json`/`composer.lock`, then a VCS directory). Preferring `composer.json` over the VCS root matters in monorepos, where per-package `vendor/autoload.php` and coding styles are what `php-mode` cares about. When none of those markers is found, it now falls back to `project-current`, so any [`project.el`](https://www.gnu.org/software/emacs/manual/html_node/emacs/Projects.html) backend can contribute detection: + +* **Projectile 3** registers itself on `project-find-functions` when `projectile-mode` is enabled, so its detection is picked up automatically. The old `php-project-use-projectile-to-detect-root` option is therefore obsolete. +* **Extra root markers** can be declared without Projectile via `project-vc-extra-root-markers` (Emacs 29+), for example in `.dir-locals.el`: + + ```lisp + ((nil + (project-vc-extra-root-markers . ("composer.json")))) + ``` + +Conversely, to expose PHP-specific detection (the `composer.json`-first precedence above) to `project.el` consumers such as Eglot or `project-find-file`, register `php-project-project-find-function` with a low priority so it only supplements the built-in VC detection: + +```lisp +(add-hook 'project-find-functions #'php-project-project-find-function 90) +``` + ## Editing files that mix HTML and PHP `php-mode` is designed for pure PHP scripts. Files that embed PHP inside HTML, such as templates, are better edited in a major mode that understands both languages. Indentation in particular is unreliable when the HTML part of a file is edited in plain `php-mode`. diff --git a/lisp/php-project.el b/lisp/php-project.el index e057b5fe..57e5ae33 100644 --- a/lisp/php-project.el +++ b/lisp/php-project.el @@ -59,7 +59,10 @@ (eval-when-compile (require 'cl-lib)) (require 'php-core) -(require 'projectile nil t) +(require 'project) +;; Projectile is optional and only used at runtime through `fboundp' guards. +;; `projectile-mode' being active already implies the library is loaded, so we +;; deliberately avoid pulling in the whole of Projectile just by requiring us. ;; Constants (defconst php-project-composer-autoloader "vendor/autoload.php") @@ -77,9 +80,18 @@ :type 'boolean) (defcustom php-project-use-projectile-to-detect-root nil - "If `T' and projectile-mode is activated, use Projectile for root detection." + "If `T' and projectile-mode is activated, use Projectile for root detection. + +This option predates Projectile 3, which registers itself on +`project-find-functions'. `php-project-get-root-dir' now falls back to +`project-current' when no PHP marker is found, so Projectile (and any other +`project.el' backend) is consulted automatically. Prefer that path instead." :tag "PHP Project Use Projectile To Detect Root" :type 'boolean) +(make-obsolete-variable 'php-project-use-projectile-to-detect-root + "rely on `project-find-functions'; \ +`php-project-get-root-dir' falls back to `project-current' automatically." + "1.28.0") ;; Variables (defvar php-project-available-root-files @@ -260,14 +272,24 @@ Typically it is `pear', `drupal', `wordpress', `symfony2' and `psr2'.") ;;;###autoload (defun php-project-project-find-function (dir) - "Return path to current PHP project from DIR. + "Return the PHP project containing DIR. + +This function is compatible with `project-find-functions'. Register it with a +low priority so that it only supplements the built-in detection, for example: + + (add-hook \\='project-find-functions #\\='php-project-project-find-function 90) -This function is compatible with `project-find-functions'." +When the detected root is under version control the result is delegated to +`project-try-vc', which yields the representation expected by the running +Emacs (a plain \\='(vc . ROOT) cons is only valid on Emacs 27 and breaks the +3-element \\='(vc BACKEND ROOT) form used since Emacs 28). Otherwise a +`transient' project rooted at the PHP project directory is returned." (let ((default-directory dir)) (when-let* ((root (php-project-get-root-dir))) - (if (file-exists-p (expand-file-name ".git" root)) - (cons 'vc root) - (cons 'transient root))))) + (or (and (file-exists-p (expand-file-name ".git" root)) + (fboundp 'project-try-vc) + (project-try-vc root)) + (cons 'transient root))))) (defun php-project--detect-root-dir () "Return detected project root." @@ -282,8 +304,25 @@ This function is compatible with `project-find-functions'." (cl-loop for m in php-project-available-root-files append (cdr m))) (t (cdr-safe (assq php-project-root php-project-available-root-files)))))) - (cl-loop for m in detect-method - thereis (locate-dominating-file default-directory m))))) + (or (cl-loop for m in detect-method + thereis (locate-dominating-file default-directory m)) + (php-project--detect-root-by-project-el))))) + +(defun php-project--detect-root-by-project-el () + "Return the root reported by `project.el', or nil. + +Used as a last resort when no PHP-specific marker is found. This lets +`project.el' backends contribute their own detection, including Projectile 3 +\(which registers itself on `project-find-functions') and +`project-vc-extra-root-markers'. `php-project-project-find-function' is +temporarily removed from the hook to avoid recursing back into this function." + ;; `project-root' only exists on Emacs 28.1+; on Emacs 27 there is no + ;; non-obsolete accessor, so the fallback simply does nothing there. + (when (fboundp 'project-root) + (let ((project-find-functions + (remq #'php-project-project-find-function project-find-functions))) + (when-let* ((project (project-current))) + (project-root project))))) (provide 'php-project) ;;; php-project.el ends here