|
|
;;; php-project.el --- Project support for PHP application -*- lexical-binding: t; -*- |
|
|
|
|
|
;; Copyright (C) 2020 Friends of Emacs-PHP development |
|
|
|
|
|
;; Author: USAMI Kenta <tadsan@zonu.me> |
|
|
;; Keywords: tools, files |
|
|
;; URL: https://github.com/emacs-php/php-mode |
|
|
;; Version: 1.24.0 |
|
|
;; License: GPL-3.0-or-later |
|
|
|
|
|
;; This program is free software; you can redistribute it and/or modify |
|
|
;; it under the terms of the GNU General Public License as published by |
|
|
;; the Free Software Foundation, either version 3 of the License, or |
|
|
;; (at your option) any later version. |
|
|
|
|
|
;; This program is distributed in the hope that it will be useful, |
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
;; GNU General Public License for more details. |
|
|
|
|
|
;; You should have received a copy of the GNU General Public License |
|
|
;; along with this program. If not, see <https://www.gnu.org/licenses/>. |
|
|
|
|
|
;;; Commentary: |
|
|
|
|
|
;; Define project specific functions and variables for PHP application. |
|
|
;; |
|
|
;; ## API |
|
|
;; |
|
|
;; ### `php-project-get-root-dir()' |
|
|
;; |
|
|
;; Return root directory of current buffer file. The root directory is |
|
|
;; determined by several marker file or directory. |
|
|
;; |
|
|
;; ### `php-project-get-bootstrap-scripts()' |
|
|
;; |
|
|
;; Return list of path to bootstrap script file. |
|
|
;; |
|
|
;; ### `php-project-get-php-executable()' |
|
|
;; |
|
|
;; Return path to PHP executable file with the project settings overriding. |
|
|
;; |
|
|
;; ### `php-project-get-phan-executable()' |
|
|
;; |
|
|
;; Return path to Phan executable file with the project settings overriding. |
|
|
;; Phan is a static analyzer and LSP server implementation for PHP. |
|
|
;; See https://github.com/phan/phan |
|
|
;; |
|
|
;; ## `.dir-locals.el' support |
|
|
;; |
|
|
;; - `php-project-coding-style' |
|
|
;; - Symbol value of the coding style. (ex. `pear', `psr2') |
|
|
;; - `php-project-root' |
|
|
;; - Symbol of marker file of project root. (ex. `git', `composer') |
|
|
;; - Full path to project root directory. (ex. "/path/to/your-project") |
|
|
;; - `php-project-bootstrap-scripts' |
|
|
;; - List of path to bootstrap file of project. |
|
|
;; (ex. (((root . "vendor/autoload.php") (root . "inc/bootstrap.php"))) |
|
|
;; - `php-project-php-executable' |
|
|
;; - Path to project specific PHP executable file. |
|
|
;; - If you want to use a file different from the system wide `php' command. |
|
|
;; - `php-project-phan-executable' |
|
|
;; - Path to project specific Phan executable file. |
|
|
;; - When not specified explicitly, it is automatically searched from |
|
|
;; Composer's dependency of the project and `exec-path'. |
|
|
;; |
|
|
|
|
|
;;; Code: |
|
|
(eval-when-compile |
|
|
(require 'cl-lib)) |
|
|
(require 'projectile nil t) |
|
|
|
|
|
;; Constants |
|
|
(defconst php-project-composer-autoloader "vendor/autoload.php") |
|
|
|
|
|
;; Custom variables |
|
|
(defgroup php-project nil |
|
|
"Major mode for editing PHP code." |
|
|
:tag "PHP Project" |
|
|
:prefix "php-project-" |
|
|
:group 'php) |
|
|
|
|
|
(defcustom php-project-auto-detect-etags-file nil |
|
|
"If `T', automatically detect etags file when file is opened." |
|
|
:tag "PHP Project Auto Detect Etags File" |
|
|
:group 'php-project |
|
|
:type 'boolean) |
|
|
|
|
|
(defcustom php-project-use-projectile-to-detect-root nil |
|
|
"If `T' and projectile-mode is activated, use Projectile for root detection." |
|
|
:tag "PHP Project Use Projectile To Detect Root" |
|
|
:group 'php-project |
|
|
:type 'boolean) |
|
|
|
|
|
;; Variables |
|
|
(defvar php-project-available-root-files |
|
|
'((projectile ".projectile") |
|
|
(composer "composer.json" "composer.lock") |
|
|
(git ".git") |
|
|
(mercurial ".hg") |
|
|
(subversion ".svn") |
|
|
;; NOTICE: This method does not detect the top level of .editorconfig |
|
|
;; However, we can integrate it by adding the editorconfig.el's API. |
|
|
;;(editorconfig . ".editorconfig") |
|
|
)) |
|
|
|
|
|
;; Buffer local variables |
|
|
|
|
|
;;;###autoload |
|
|
(progn |
|
|
(defvar-local php-project-root 'auto |
|
|
"Method of searching for the top level directory. |
|
|
|
|
|
`auto' (default) |
|
|
Try to search file in order of `php-project-available-root-files'. |
|
|
|
|
|
SYMBOL |
|
|
Key of `php-project-available-root-files'. |
|
|
|
|
|
STRING |
|
|
A file/directory name of top level marker. |
|
|
If the string is an actual directory path, it is set as the absolute path |
|
|
of the root directory, not the marker.") |
|
|
(put 'php-project-root 'safe-local-variable |
|
|
#'(lambda (v) (or (stringp v) (assq v php-project-available-root-files)))) |
|
|
|
|
|
(defvar-local php-project-etags-file nil) |
|
|
(put 'php-project-etags-file 'safe-local-variable |
|
|
#'(lambda (v) (or (functionp v) |
|
|
(eq v t) |
|
|
(php-project--eval-bootstrap-scripts v)))) |
|
|
|
|
|
(defvar-local php-project-bootstrap-scripts nil |
|
|
"List of path to bootstrap php script file. |
|
|
|
|
|
The ideal bootstrap file is silent, it only includes dependent files, |
|
|
defines constants, and sets the class loaders.") |
|
|
(put 'php-project-bootstrap-scripts 'safe-local-variable #'php-project--eval-bootstrap-scripts) |
|
|
|
|
|
(defvar-local php-project-php-executable nil |
|
|
"Path to php executable file.") |
|
|
(put 'php-project-php-executable 'safe-local-variable |
|
|
#'(lambda (v) (and (stringp v) (file-executable-p v)))) |
|
|
|
|
|
(defvar-local php-project-phan-executable nil |
|
|
"Path to phan executable file.") |
|
|
(put 'php-project-phan-executable 'safe-local-variable #'php-project--eval-bootstrap-scripts) |
|
|
|
|
|
(defvar-local php-project-coding-style nil |
|
|
"Symbol value of the coding style of the project that PHP major mode refers to. |
|
|
|
|
|
Typically it is `pear', `drupal', `wordpress', `symfony2' and `psr2'.") |
|
|
(put 'php-project-coding-style 'safe-local-variable #'symbolp) |
|
|
|
|
|
(defvar-local php-project-align-lines t |
|
|
"If T, automatically turn on `php-align-mode' by `php-align-setup'.") |
|
|
(put 'php-project-align-lines 'safe-local-variable #'booleanp) |
|
|
|
|
|
(defvar-local php-project-php-file-as-template 'auto |
|
|
" |
|
|
`auto' (default) |
|
|
Automatically switch to mode for template when HTML tag detected in file. |
|
|
|
|
|
`t' |
|
|
Switch all PHP files in that directory to mode for HTML template. |
|
|
|
|
|
`nil' |
|
|
Any .php in that directory is just a PHP script. |
|
|
|
|
|
\(\(PATTERN . SYMBOL)) |
|
|
Alist of file name pattern regular expressions and the above symbol pairs. |
|
|
PATTERN is regexp pattern. |
|
|
") |
|
|
(put 'php-project-php-file-as-template 'safe-local-variable #'php-project--validate-php-file-as-template) |
|
|
|
|
|
(defvar-local php-project-repl nil |
|
|
"Function name or path to REPL (interactive shell) script.") |
|
|
(put 'php-project-repl 'safe-local-variable |
|
|
#'(lambda (v) (or (functionp v) |
|
|
(php-project--eval-bootstrap-scripts v)))) |
|
|
|
|
|
(defvar-local php-project-unit-test nil |
|
|
"Function name or path to unit test script.") |
|
|
(put 'php-project-unit-test 'safe-local-variable |
|
|
#'(lambda (v) (or (functionp v) |
|
|
(php-project--eval-bootstrap-scripts v)))) |
|
|
|
|
|
(defvar-local php-project-deploy nil |
|
|
"Function name or path to deploy script.") |
|
|
(put 'php-project-deploy 'safe-local-variable |
|
|
#'(lambda (v) (or (functionp v) |
|
|
(php-project--eval-bootstrap-scripts v)))) |
|
|
|
|
|
(defvar-local php-project-build nil |
|
|
"Function name or path to build script.") |
|
|
(put 'php-project-build 'safe-local-variable |
|
|
#'(lambda (v) (or (functionp v) |
|
|
(php-project--eval-bootstrap-scripts v)))) |
|
|
|
|
|
(defvar-local php-project-server-start nil |
|
|
"Function name or path to server-start script.") |
|
|
(put 'php-project-server-start 'safe-local-variable |
|
|
#'(lambda (v) (or (functionp v) |
|
|
(php-project--eval-bootstrap-scripts v))))) |
|
|
|
|
|
;; Functions |
|
|
(defun php-project--validate-php-file-as-template (val) |
|
|
"Return T when `VAL' is valid list of safe ." |
|
|
(cond |
|
|
((null val) t) |
|
|
((memq val '(t auto)) t) |
|
|
((listp val) |
|
|
(cl-loop for v in val |
|
|
always (and (consp v) |
|
|
(stringp (car v)) |
|
|
(php-project--validate-php-file-as-template (cdr v))))) |
|
|
(t nil))) |
|
|
|
|
|
(defun php-project--eval-bootstrap-scripts (val) |
|
|
"Return T when `VAL' is valid list of safe bootstrap php script." |
|
|
(cond |
|
|
((stringp val) (and (file-exists-p val) val)) |
|
|
((eq 'composer val) |
|
|
(let ((path (expand-file-name php-project-composer-autoloader (php-project-get-root-dir)))) |
|
|
(and (file-exists-p path) path))) |
|
|
((and (consp val) (eq 'root (car val)) (stringp (cdr val))) |
|
|
(let ((path (expand-file-name (cdr val) (php-project-get-root-dir)))) |
|
|
(and (file-exists-p path) path))) |
|
|
((null val) nil) |
|
|
((listp val) |
|
|
(cl-loop for v in val collect (php-project--eval-bootstrap-scripts v))) |
|
|
(t nil))) |
|
|
|
|
|
(defun php-project-get-php-executable () |
|
|
"Return path to PHP executable file." |
|
|
(cond |
|
|
((and (stringp php-project-php-executable) |
|
|
(file-executable-p php-project-php-executable)) |
|
|
php-project-php-executable) |
|
|
((boundp 'php-executable) php-executable) |
|
|
(t (executable-find "php")))) |
|
|
|
|
|
(defun php-project-get-phan-executable () |
|
|
"Return path to phan executable file." |
|
|
(or (car-safe (php-project--eval-bootstrap-scripts |
|
|
(list php-project-phan-executable |
|
|
(cons 'root "vendor/bin/phan")))) |
|
|
(executable-find "phan"))) |
|
|
|
|
|
(defun php-project-get-file-html-template-type (filename) |
|
|
"Return symbol T, NIL or `auto' by `FILENAME'." |
|
|
(cond |
|
|
((not php-project-php-file-as-template) nil) |
|
|
((eq t php-project-php-file-as-template) t) |
|
|
((eq 'auto php-project-php-file-as-template) 'auto) |
|
|
((listp php-project-php-file-as-template) |
|
|
(assoc-default filename php-project-php-file-as-template #'string-match-p)) |
|
|
(t (prog1 nil |
|
|
(warn "php-project-php-file-as-template is unexpected format"))))) |
|
|
|
|
|
(defun php-project-apply-local-variables () |
|
|
"Apply php-project variables to local variables." |
|
|
(when (null tags-file-name) |
|
|
(when (or (and php-project-auto-detect-etags-file |
|
|
(null php-project-etags-file)) |
|
|
(eq php-project-etags-file t)) |
|
|
(let ((tags-file (expand-file-name "TAGS" (php-project-get-root-dir)))) |
|
|
(when (file-exists-p tags-file) |
|
|
(setq-local php-project-etags-file tags-file)))) |
|
|
(when php-project-etags-file |
|
|
(setq-local tags-file-name (php-project--eval-bootstrap-scripts php-project-etags-file))))) |
|
|
;;;###autoload |
|
|
(defun php-project-get-bootstrap-scripts () |
|
|
"Return list of bootstrap script." |
|
|
(let ((scripts (php-project--eval-bootstrap-scripts php-project-bootstrap-scripts))) |
|
|
(if (stringp scripts) (list scripts) scripts))) |
|
|
|
|
|
;;;###autoload |
|
|
(defun php-project-get-root-dir () |
|
|
"Return path to current PHP project." |
|
|
(if (and (stringp php-project-root) (file-directory-p php-project-root)) |
|
|
php-project-root |
|
|
(php-project--detect-root-dir))) |
|
|
|
|
|
(defun php-project--detect-root-dir () |
|
|
"Return detected project root." |
|
|
(if (and php-project-use-projectile-to-detect-root |
|
|
(bound-and-true-p projectile-mode) |
|
|
(fboundp 'projectile-project-root)) |
|
|
(projectile-project-root default-directory) |
|
|
(let ((detect-method |
|
|
(cond |
|
|
((stringp php-project-root) (list php-project-root)) |
|
|
((eq php-project-root 'auto) |
|
|
(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))))) |
|
|
|
|
|
(provide 'php-project) |
|
|
;;; php-project.el ends here
|
|
|
|