branch: elpa/php-mode
commit af6913519cdd9781dab2ad655821dc99e75c8e4e
Author: USAMI Kenta <[email protected]>
Commit: USAMI Kenta <[email protected]>
Improve project.el and Projectile interoperability in php-project
`php-project-project-find-function` built a `(vc . ROOT)` cons, which is
only valid on Emacs 27. Since Emacs 28 the VC project is the 3-element
`(vc BACKEND ROOT)` form, so `project-root` (which reads `(nth 2 ...)`)
signalled `wrong-type-argument` on the returned value. Delegate to
`project-try-vc`, which yields the representation the running Emacs
expects, and fall back to a `transient` project otherwise.
Let `php-project-get-root-dir` fall back to `project-current` when no
PHP-specific marker is found. This lets any project.el backend
contribute detection -- Projectile 3 (which registers itself on
`project-find-functions'), `project-vc-extra-root-markers', and other
VCS -- while keeping the composer.json-first precedence that matters for
monorepos. `php-project-project-find-function' is removed from the hook
during the fallback to avoid recursing back into detection.
Deprecate `php-project-use-projectile-to-detect-root': Projectile 3 is
now consulted automatically through the project.el fallback, so the
dedicated option is redundant.
Drop the load-time `(require 'projectile nil t)'; Projectile is only
touched behind `fboundp'/`projectile-mode' guards, and an active
`projectile-mode' already implies the library is loaded.
Document the integration and the low-priority `project-find-functions'
registration in both READMEs.
---
CHANGELOG.md | 12 +++++++++++
README.ja.md | 18 +++++++++++++++++
README.md | 18 +++++++++++++++++
lisp/php-project.el | 57 ++++++++++++++++++++++++++++++++++++++++++++---------
4 files changed, 96 insertions(+), 9 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 115ef78d60..727c9f3186 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 2222cbb379..d9bc528f37 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 94421fb85b..178257b862 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 [...]
+
+* **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 e057b5fe6a..57e5ae33d9 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