From 11d1c65dd397220c8cb5f47182c0e3b1915e067c Mon Sep 17 00:00:00 2001
From: USAMI Kenta <tadsan@zonu.me>
Date: Mon, 10 May 2021 04:34:21 +0900
Subject: [PATCH 1/4] Improve php-imenu-generic-expression

The old list had duplicate items and lacked an index of modern syntax
elements.
Modern PHP code focuses on building one class per file. (PSR-4)
---
 CHANGELOG.md           |  11 ++++
 lisp/php.el            | 113 +++++++++++++++++++++++++++--------------
 tests/php-mode-test.el |   9 ++--
 3 files changed, 90 insertions(+), 43 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2f2a6446..27450d1f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,17 @@ All notable changes of the PHP Mode 1.19.1 release series are documented in this
 
  * Support new PHP 8.0 and 8.1 syntax hilighting and indentation
     * [8.0] `#[Attributes]`
+ * Add `php-imenu-generic-expression-default` for default value or `php-imenu-generic-expression`
+
+### Changed
+
+ * Re-organized `php-imenu-generic-expression`
+   * Added `Import`, `Constants` and `Properties`
+   * Removed `Anonymous Functions`
+   * Renamed `Named Functions` to `Functions`
+   * Renamed `All Methods` to `Methods`
+   * Removed `Public Methods`, `Protected Methods` and `Provate Methods`
+   * Unified `Classes`, `Traits`, `Interfaces` into `Classes`
 
 ## [1.24.0] - 2021-03-07
 
diff --git a/lisp/php.el b/lisp/php.el
index 3cc23cfe..fcb82868 100644
--- a/lisp/php.el
+++ b/lisp/php.el
@@ -227,7 +227,7 @@ it is the character that will terminate the string, or t if the string should be
   "Regular expression for a PHP function.")
 
 (eval-when-compile
-  (defun php-create-regexp-for-method (&optional visibility)
+  (cl-defun php-create-regexp-for-method (&optional visibility &key include-args)
     "Make a regular expression for methods with the given VISIBILITY.
 
 VISIBILITY must be a string that names the visibility for a PHP
@@ -242,22 +242,25 @@ which will be the name of the method."
       (setq visibility (list visibility)))
     (rx-to-string `(: line-start
                       (* (syntax whitespace))
-                      ,@(if visibility
-                            `((* (or "abstract" "final" "static")
-                                 (+ (syntax whitespace)))
-                              (or ,@visibility)
-                              (+ (syntax whitespace))
-                              (* (or "abstract" "final" "static")
-                                 (+ (syntax whitespace))))
-                          '((* (* (or "abstract" "final" "static"
-                                      "private" "protected" "public")
-                                  (+ (syntax whitespace))))))
-                      "function"
-                      (+ (syntax whitespace))
-                      (? "&" (* (syntax whitespace)))
-                      (group (+ (or (syntax word) (syntax symbol))))
-                      (* (syntax whitespace))
-                      "(")))
+                      (group
+                       ,@(if visibility
+                             `((* (or "abstract" "final" "static")
+                                  (+ (syntax whitespace)))
+                               (or ,@visibility)
+                               (+ (syntax whitespace))
+                               (* (or "abstract" "final" "static")
+                                  (+ (syntax whitespace))))
+                           '((* (* (or "abstract" "final" "static"
+                                       "private" "protected" "public")
+                                   (+ (syntax whitespace))))))
+                       "function"
+                       (+ (syntax whitespace))
+                       (? "&" (* (syntax whitespace)))
+                       (group (+ (or (syntax word) (syntax symbol))))
+                       (* (syntax whitespace))
+                       "("
+                       ,@(when include-args
+                           '((* any) line-end))))))
 
   (defun php-create-regexp-for-classlike (type)
     "Accepts a `TYPE' of a 'classlike' object as a string, such as
@@ -275,30 +278,66 @@ can be used to match against definitions for that classlike."
      ;; this is not necessarily correct for all values of `type'.
      "\\s-+\\(\\(?:\\sw\\|\\\\\\|\\s_\\)+\\)")))
 
-(defconst php-imenu-generic-expression
+(defconst php-imenu-generic-expression-default
   (eval-when-compile
-    `(("Namespaces"
-       ,(php-create-regexp-for-classlike "namespace") 1)
+    `(("Methods"
+       ,(php-create-regexp-for-method nil :include-args t) 1)
+      ("Properties"
+       ,(rx line-start
+            (* (syntax whitespace))
+            (group
+             (+ (or "public" "protected" "private" "static" "var")
+                (+ (syntax whitespace)))
+             (* (? (? (or "|" "?"))
+                   (or "\\" (syntax word) (syntax symbol))
+                   (+ (syntax whitespace))))
+             "$" (+ (or (syntax word) (syntax symbol)))
+             word-boundary))
+       1)
+      ("Constants"
+       ,(rx line-start
+            (* (syntax whitespace))
+            (group
+             (* (or "public" "protected" "private")
+                (+ (syntax whitespace)))
+             "const"
+             (+ (syntax whitespace))
+             (+ (or (syntax word) (syntax symbol)))
+             (* (syntax whitespace))
+             (? "=" (* (syntax whitespace))
+                (repeat 0 40 any))))
+       1)
+      ("Functions"
+       ,(rx line-start
+            (* (syntax whitespace))
+            (group
+             "function"
+             (+ (syntax whitespace))
+             (+ (or (syntax word) (syntax symbol)))
+             (* (syntax whitespace))
+             "("
+             (repeat 0 100 any)))
+       1)
+      ("Import"
+       ,(rx line-start
+            ;; (* (syntax whitespace))
+            (group
+             "use"
+             (+ (syntax whitespace))
+             (repeat 0 100 any)))
+       1)
       ("Classes"
-       ,(php-create-regexp-for-classlike "class") 1)
-      ("Interfaces"
-       ,(php-create-regexp-for-classlike "interface") 1)
-      ("Traits"
-       ,(php-create-regexp-for-classlike "trait") 1)
-      ("All Methods"
-       ,(php-create-regexp-for-method) 1)
-      ("Private Methods"
-       ,(php-create-regexp-for-method '("private")) 1)
-      ("Protected Methods"
-       ,(php-create-regexp-for-method '("protected"))  1)
-      ("Public Methods"
-       ,(php-create-regexp-for-method '("public")) 1)
-      ("Anonymous Functions"
-       "\\<\\(\\(?:\\sw\\|\\s_\\)+\\)\\s-*=\\s-*f\\(unctio\\)?n\\s-*(" 1)
-      ("Named Functions"
-       "^\\s-*function\\s-+\\(\\(?:\\sw\\|\\s_\\)+\\)\\s-*(" 1)))
+       ,(php-create-regexp-for-classlike "\\(?:class\\|interface\\|trait\\|enum\\)") 0)
+      ("Namespace"
+       ,(php-create-regexp-for-classlike "namespace") 1)))
   "Imenu generic expression for PHP Mode.  See `imenu-generic-expression'.")
 
+(defcustom php-imenu-generic-expression php-imenu-generic-expression-default
+  "Default Imenu generic expression for PHP Mode.  See `imenu-generic-expression'."
+  :type '(alist :key-type string
+                :value-type list)
+  :group 'php)
+
 (defconst php--re-namespace-pattern
   (eval-when-compile
     (php-create-regexp-for-classlike "namespace")))
diff --git a/tests/php-mode-test.el b/tests/php-mode-test.el
index c53da223..ebce565f 100644
--- a/tests/php-mode-test.el
+++ b/tests/php-mode-test.el
@@ -319,12 +319,9 @@ style from Drupal."
   "All static method should appear on imenu whether 'static' keyword is placed before or after visibility"
   (with-php-mode-test ("issue-83.php")
     (let* ((index-alist (imenu--make-index-alist))
-           (public-methods (mapcar 'car (cdr (assoc "Public Methods" index-alist))))
-           (all-methods (mapcar 'car (cdr (assoc "All Methods" index-alist)))))
-      (should (member "staticBeforeVisibility" public-methods))
-      (should (member "staticBeforeVisibility" all-methods))
-      (should (member "staticAfterVisibility" public-methods))
-      (should (member "staticAfterVisibility" all-methods)))))
+           (all-methods (mapcar 'car (cdr (assoc "Methods" index-alist)))))
+      (should (member "static public function staticBeforeVisibility()" all-methods))
+      (should (member "public static function staticAfterVisibility()" all-methods)))))
 
 (ert-deftest php-mode-test-issue-99 ()
   "Proper indentation for 'foreach' statements without braces."

From d4b7076ad5ccf026a7342cbfe0c6bcb14d663e39 Mon Sep 17 00:00:00 2001
From: USAMI Kenta <tadsan@zonu.me>
Date: Mon, 10 May 2021 05:11:13 +0900
Subject: [PATCH 2/4] Add php-imenu-generic-expression-simple and
 php-imenu-generic-expression-legacy

---
 CHANGELOG.md |  2 ++
 lisp/php.el  | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 66 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 27450d1f..d7779fe4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,8 @@ All notable changes of the PHP Mode 1.19.1 release series are documented in this
  * Support new PHP 8.0 and 8.1 syntax hilighting and indentation
     * [8.0] `#[Attributes]`
  * Add `php-imenu-generic-expression-default` for default value or `php-imenu-generic-expression`
+   * Add `php-imenu-generic-expression-legacy` for compatibility
+   * Add `php-imenu-generic-expression-simple` for simple display
 
 ### Changed
 
diff --git a/lisp/php.el b/lisp/php.el
index fcb82868..46ac047f 100644
--- a/lisp/php.el
+++ b/lisp/php.el
@@ -332,6 +332,70 @@ can be used to match against definitions for that classlike."
        ,(php-create-regexp-for-classlike "namespace") 1)))
   "Imenu generic expression for PHP Mode.  See `imenu-generic-expression'.")
 
+(defconst php-imenu-generic-expression-simple
+  (eval-when-compile
+    `(("Methods"
+       ,(php-create-regexp-for-method nil) 2)
+      ("Properties"
+       ,(rx line-start
+            (* (syntax whitespace))
+            (+ (or "public" "protected" "private" "static" "var")
+               (+ (syntax whitespace)))
+            (* (? (? (or "|" "?"))
+                  (or "\\" (syntax word) (syntax symbol))
+                  (+ (syntax whitespace))))
+            (group
+             "$" (+ (or (syntax word) (syntax symbol))))
+            word-boundary)
+       1)
+      ("Constants"
+       ,(rx line-start
+            (* (syntax whitespace))
+            (group
+             (* (or "public" "protected" "private")
+                (+ (syntax whitespace)))
+             "const"
+             (+ (syntax whitespace))
+             (+ (or (syntax word) (syntax symbol)))))
+       1)
+      ("Functions"
+       ,(rx line-start
+            (* (syntax whitespace))
+            "function"
+            (+ (syntax whitespace))
+            (group
+             (+ (or (syntax word) (syntax symbol)))))
+       1)
+      ("Classes"
+       ,(php-create-regexp-for-classlike "\\(?:class\\|interface\\|trait\\|enum\\)") 1)
+      ("Namespace"
+       ,(php-create-regexp-for-classlike "namespace") 1)))
+  "Imenu generic expression for PHP Mode.  See `imenu-generic-expression'.")
+
+(defconst php-imenu-generic-expression-legacy
+  (eval-when-compile
+    `(("Namespaces"
+       ,(php-create-regexp-for-classlike "namespace") 1)
+      ("Classes"
+       ,(php-create-regexp-for-classlike "class") 1)
+      ("Interfaces"
+       ,(php-create-regexp-for-classlike "interface") 1)
+      ("Traits"
+       ,(php-create-regexp-for-classlike "trait") 1)
+      ("All Methods"
+       ,(php-create-regexp-for-method) 1)
+      ("Private Methods"
+       ,(php-create-regexp-for-method '("private")) 2)
+      ("Protected Methods"
+       ,(php-create-regexp-for-method '("protected"))  2)
+      ("Public Methods"
+       ,(php-create-regexp-for-method '("public")) 2)
+      ("Anonymous Functions"
+       "\\<\\(\\(?:\\sw\\|\\s_\\)+\\)\\s-*=\\s-*f\\(unctio\\)?n\\s-*(" 1)
+      ("Named Functions"
+       "^\\s-*function\\s-+\\(\\(?:\\sw\\|\\s_\\)+\\)\\s-*(" 1)))
+  "Imenu generic expression for PHP Mode.  See `imenu-generic-expression'.")
+
 (defcustom php-imenu-generic-expression php-imenu-generic-expression-default
   "Default Imenu generic expression for PHP Mode.  See `imenu-generic-expression'."
   :type '(alist :key-type string

From c1843b1463bd7ad93f2402be5a8420c054055a7b Mon Sep 17 00:00:00 2001
From: USAMI Kenta <tadsan@zonu.me>
Date: Mon, 10 May 2021 05:24:16 +0900
Subject: [PATCH 3/4] Separate the dependency of make test on .cask

---
 .github/workflows/test.yml | 2 +-
 Makefile                   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 25d4be7d..4eb8d06e 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -37,7 +37,7 @@ jobs:
     - uses: actions/checkout@v2
     - name: Run tests
       if: matrix.allow_failure != true
-      run: 'make test'
+      run: 'make .cask test'
     - name: Run tests (allow failure)
       if: matrix.allow_failure == true
       run: 'make test || true'
diff --git a/Makefile b/Makefile
index c495e2d6..e27eeca3 100644
--- a/Makefile
+++ b/Makefile
@@ -54,7 +54,7 @@ dev:
 #
 # for an example of using a script like this with the 'git bisect run'
 # command.
-test: .cask clean all
+test: clean all
 	touch tests/project/1/.git
 	$(EMACS) -Q -batch -L lisp/ --eval \
 	"(let ((default-directory (expand-file-name \".cask\" default-directory))) \

From 0817e36fb4153f8f3cc7311b5b1c4d452758b688 Mon Sep 17 00:00:00 2001
From: USAMI Kenta <tadsan@zonu.me>
Date: Mon, 10 May 2021 21:46:48 +0900
Subject: [PATCH 4/4] Make php-imenu-generic-expression accept symbol of
 variable name

---
 lisp/php-mode.el       | 4 +++-
 lisp/php.el            | 8 +++++---
 tests/php-mode-test.el | 6 ++++--
 3 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/lisp/php-mode.el b/lisp/php-mode.el
index e35b688f..4a53a960 100644
--- a/lisp/php-mode.el
+++ b/lisp/php-mode.el
@@ -1195,7 +1195,9 @@ After setting the stylevars run hooks according to STYLENAME
   (add-hook 'syntax-propertize-extend-region-functions
             #'php-syntax-propertize-extend-region t t)
 
-  (setq imenu-generic-expression php-imenu-generic-expression)
+  (setq imenu-generic-expression (if (symbolp php-imenu-generic-expression)
+                                     (symbol-value php-imenu-generic-expression)
+                                   php-imenu-generic-expression))
 
   ;; PHP vars are case-sensitive
   (setq case-fold-search t)
diff --git a/lisp/php.el b/lisp/php.el
index 46ac047f..0ae6d342 100644
--- a/lisp/php.el
+++ b/lisp/php.el
@@ -396,10 +396,12 @@ can be used to match against definitions for that classlike."
        "^\\s-*function\\s-+\\(\\(?:\\sw\\|\\s_\\)+\\)\\s-*(" 1)))
   "Imenu generic expression for PHP Mode.  See `imenu-generic-expression'.")
 
-(defcustom php-imenu-generic-expression php-imenu-generic-expression-default
+(defcustom php-imenu-generic-expression 'php-imenu-generic-expression-default
   "Default Imenu generic expression for PHP Mode.  See `imenu-generic-expression'."
-  :type '(alist :key-type string
-                :value-type list)
+  :type '(choice (alist :key-type string :value-type list)
+                 (const 'php-imenu-generic-expression-legacy)
+                 (const 'php-imenu-generic-expression-simple)
+                 variable)
   :group 'php)
 
 (defconst php--re-namespace-pattern
diff --git a/tests/php-mode-test.el b/tests/php-mode-test.el
index ebce565f..9ee41647 100644
--- a/tests/php-mode-test.el
+++ b/tests/php-mode-test.el
@@ -320,8 +320,10 @@ style from Drupal."
   (with-php-mode-test ("issue-83.php")
     (let* ((index-alist (imenu--make-index-alist))
            (all-methods (mapcar 'car (cdr (assoc "Methods" index-alist)))))
-      (should (member "static public function staticBeforeVisibility()" all-methods))
-      (should (member "public static function staticAfterVisibility()" all-methods)))))
+      (should (equal all-methods
+                     (list
+                      "static public function staticBeforeVisibility()"
+                      "public static function staticAfterVisibility()"))))))
 
 (ert-deftest php-mode-test-issue-99 ()
   "Proper indentation for 'foreach' statements without braces."