emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] scratch/javaimp-wip 5d00e71: wip


From: Filipp Gunbin
Subject: [elpa] scratch/javaimp-wip 5d00e71: wip
Date: Thu, 5 Aug 2021 16:27:22 -0400 (EDT)

branch: scratch/javaimp-wip
commit 5d00e712d18a585594b946eaa96d24bd5ef44757
Author: Filipp Gunbin <fgunbin@fastmail.fm>
Commit: Filipp Gunbin <fgunbin@fastmail.fm>

    wip
---
 javaimp-gradle.el |   7 --
 javaimp-maven.el  |  21 ++---
 javaimp-parse.el  | 180 ++++++++----------------------------
 javaimp-tests.el  | 161 +++++++++++++++++---------------
 javaimp-util.el   | 270 +++++++++++++++++++++++++++++++++++++-----------------
 javaimp.el        | 124 +++++++++++++++++--------
 6 files changed, 402 insertions(+), 361 deletions(-)

diff --git a/javaimp-gradle.el b/javaimp-gradle.el
index e99aab7..af3fd5a 100644
--- a/javaimp-gradle.el
+++ b/javaimp-gradle.el
@@ -20,13 +20,6 @@
 
 (require 'javaimp-util)
 
-(defcustom javaimp-gradle-program "gradle"
-  "Path to the `gradle' program.  Customize it if the program is
-not on `exec-path'.  If the visited project's directory contains
-gradlew program, it is used in preference."
-  :group 'javaimp
-  :type 'string)
-
 (defconst javaimp--gradle-task-body
   (with-temp-buffer
     (insert-file-contents (expand-file-name "gradleTaskBody.inc.kts" 
javaimp--basedir))
diff --git a/javaimp-maven.el b/javaimp-maven.el
index e2c2118..73d914d 100644
--- a/javaimp-maven.el
+++ b/javaimp-maven.el
@@ -23,13 +23,6 @@
 
 (require 'javaimp-util)
 
-(defcustom javaimp-mvn-program "mvn"
-  "Path to the `mvn' program.  Customize it if the program is not
-on `exec-path'."
-  :group 'javaimp
-  :type 'string)
-
-
 (defun javaimp--maven-visit (file)
   "Calls \"mvn help:effective-pom\" on FILE,
 reads project structure from the output and records which files
@@ -90,13 +83,13 @@ resulting module trees."
                 (message "Building tree for root: %s"
                          (javaimp-print-id (javaimp-module-id root)))
                 (javaimp--build-tree
-                              root modules
-                             ;; more or less reliable way to find
-                             ;; children is to look for modules with
-                             ;; "this" as the parent
-                              (lambda (el tested)
-                                (equal (javaimp-module-parent-id tested)
-                                       (javaimp-module-id el)))))
+                 root modules
+                ;; more or less reliable way to find
+                ;; children is to look for modules with
+                ;; "this" as the parent
+                 (lambda (el tested)
+                   (equal (javaimp-module-parent-id tested)
+                          (javaimp-module-id el)))))
               roots))))
 
 (defun javaimp--maven-effective-pom-handler ()
diff --git a/javaimp-parse.el b/javaimp-parse.el
index 76da646..0c31506 100644
--- a/javaimp-parse.el
+++ b/javaimp-parse.el
@@ -18,61 +18,23 @@
 ;; You should have received a copy of the GNU General Public License
 ;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-(require 'cl-lib)
-(require 'seq)
-(require 'cc-mode)                      ;for java-mode-syntax-table
 (require 'javaimp-util)
 
-(defcustom javaimp-parse-format-method-name
-  #'javaimp--parse-format-method-name-full
-  "Function to format method name, invoked with 3 arguments:
-NAME, ARGS and THROWS-ARGS.  The last two are lists with elements
-of the form (TYPE . NAME).  For THROWS-ARGS, only TYPE is
-present."
-  :group 'javaimp
-  :type 'function)
-
-(cl-defstruct javaimp-scope
-  type                            ; see javaimp--parse-all-scope-types
-  name
-  start
-  open-brace
-  parent)
-
-(defconst javaimp--parse-named-scope-types
-  '(class interface enum local-class method))
-
-(defconst javaimp--parse-all-scope-types
-  (append
-   '(anonymous-class statement simple-statement array unknown)
-   javaimp--parse-named-scope-types))
-
 (defconst javaimp--parse-classlike-keywords
-  '("class" "interface" "enum"))
+  (mapcar #'symbol-name
+          javaimp--classlike-scope-types))
+
 (defconst javaimp--parse-stmt-keywords
   '("if" "for" "while" "switch" "try" "catch" "finally"
-    "static"                            ;static initializer block
+    "static"                            ; static initializer block
     ))
 
-(defvar javaimp-syntax-table
-  (make-syntax-table java-mode-syntax-table) ;TODO don't depend
-  "Javaimp syntax table")
-
-(defvar javaimp--arglist-syntax-table
-  (let ((st (make-syntax-table javaimp-syntax-table)))
-    (modify-syntax-entry ?< "(>" st)
-    (modify-syntax-entry ?> ")<" st)
-    (modify-syntax-entry ?. "_" st) ; separates parts of fully-qualified type
-    st)
-  "Enables parsing angle brackets as lists")
-
 (defvar-local javaimp--parse-dirty-pos nil
   "Buffer position after which all parsed information should be
 considered as stale.  Usually set by modification change hooks.
 Should be set to (point-min) in major mode hook.")
 
 
-
 (defmacro javaimp--parse-with-syntax-table (syntax-table beg &rest body)
   (declare (debug t)
            (indent 2))
@@ -90,38 +52,6 @@ Should be set to (point-min) in major mode hook.")
         (string-trim (substring str 0 end))
       str)))
 
-(defsubst javaimp--parse-is-classlike (scope)
-  (and scope
-       (member (symbol-name (javaimp-scope-type scope))
-               javaimp--parse-classlike-keywords)))
-
-(defsubst javaimp--parse-is-named (scope)
-  (and scope
-       (memq (javaimp-scope-type scope)
-             javaimp--parse-named-scope-types)))
-
-(defsubst javaimp--parse-is-imenu-included-method (scope)
-  (and (eq (javaimp-scope-type scope) 'method)
-       (javaimp--parse-is-classlike (javaimp-scope-parent scope))))
-
-(defun javaimp--parse-copy-scope (scope)
-  "Recursively copies SCOPE and its parents."
-  (let* ((res (copy-javaimp-scope scope))
-         (tmp res)
-         orig-parent)
-    (while (setq orig-parent (javaimp-scope-parent tmp))
-      (setf (javaimp-scope-parent tmp) (copy-javaimp-scope orig-parent))
-      (setq tmp (javaimp-scope-parent tmp)))
-    res))
-
-(defun javaimp--parse-concat-parent-names (scope)
-  (let (parents)
-    (while (setq scope (javaimp-scope-parent scope))
-      (push scope parents))
-    (mapconcat #'javaimp-scope-name parents ".")))
-
-
-
 (defun javaimp--parse-rsb-keyword (regexp &optional bound noerror count)
   "Like `re-search-backward', but count only occurences outside
 syntactic context as given by `syntax-ppss-context'.  Assumes
@@ -270,32 +200,6 @@ is unchanged."
       (goto-char pos)
       nil)))
 
-
-
-;;; Formatting
-
-(defsubst javaimp--parse-format-method-name-full (name args throws-args)
-  "Outputs NAME, ARGS (name and type) and THROWS-ARGS (only type)."
-  (concat name
-          "("
-          (mapconcat (lambda (arg)
-                       (concat (car arg) " " (cdr arg)))
-                     args
-                     ", ")
-          ")"
-          (if throws-args
-              (concat " throws "
-                      (mapconcat #'car throws-args ", ")))
-          ))
-
-(defsubst javaimp--parse-format-method-name-types (name args _throws-args)
-  "Outputs NAME and ARGS (only type)."
-  (concat name
-          "("
-          (mapconcat #'car args ", ")
-          ")"
-          ))
-
 
 ;;; Scopes
 
@@ -405,7 +309,7 @@ those may later become 'local-class' (see 
`javaimp--parse-scopes')."
              :type type
              :name (if (eq type 'statement)
                        name
-                     (funcall javaimp-parse-format-method-name
+                     (funcall javaimp-format-method-name
                               name
                               (javaimp--parse-arglist (car arglist-region)
                                                       (cdr arglist-region))
@@ -458,7 +362,7 @@ nil then goes all the way up.  Examines and sets property
     (let ((tmp res)
           in-local parent)
       (while tmp
-        (if (javaimp--parse-is-classlike (car tmp))
+        (if (javaimp--is-classlike (car tmp))
             (when in-local
               (setf (javaimp-scope-type (car tmp)) 'local-class))
           (setq in-local t))
@@ -493,65 +397,55 @@ Resets this variable after parsing is done."
 
 (defun javaimp--parse-get-package ()
   (goto-char (point-max))
-  (when (javaimp--parse-rsb-keyword
-         "^[ \t]*package[ \t]+\\([^;\n]+\\)[ \t]*;" nil t 1)
-    (match-string 1)))
+  (javaimp--parse-with-syntax-table javaimp-syntax-table (point-min)
+    (when (javaimp--parse-rsb-keyword
+           "^[ \t]*package[ \t]+\\([^ \t;\n]+\\)[ \t]*;" nil t 1)
+      (match-string 1))))
 
-(defun javaimp--parse-get-all-classlikes (&optional reparse)
+(defun javaimp--parse-get-all-classlikes ()
   (mapcar (lambda (scope)
             (let ((name (javaimp-scope-name scope))
-                  (parent-names (javaimp--parse-concat-parent-names scope)))
+                  (parent-names (javaimp--concat-scope-parents scope)))
               (if (string-empty-p parent-names)
                   name
                 (concat parent-names "." name))))
-          (javaimp--parse-get-all-scopes reparse 
#'javaimp--parse-is-classlike)))
-
-(defun javaimp--parse-get-imenu-forest (&optional reparse)
-  (let* ((methods
-          (javaimp--parse-get-all-scopes
-           reparse #'javaimp-imenu--included-method))
-         (classes (javaimp--parse-get-all-scopes
-                   nil                  ; no need to reparse again
-                   #'javaimp--parse-is-classlike))
-         (top-classes (mapcar (lambda (s)
-                                (null (javaimp-scope-parent s)))
-                              classes)))
+          (javaimp--parse-get-all-scopes #'javaimp--is-classlike)))
+
+(defun javaimp--parse-get-imenu-forest ()
+  (let* ((methods (javaimp--parse-get-all-scopes
+                   #'javaimp--is-imenu-included-method 
#'javaimp--is-classlike))
+         (classes (javaimp--parse-get-all-scopes #'javaimp--is-classlike))
+         (top-classes (seq-filter (lambda (s)
+                                    (null (javaimp-scope-parent s)))
+                                  classes)))
     (mapcar
-     (lambda (class)
+     (lambda (top-class)
        (message "Building tree for top-level class-like scope: %s"
-                (javaimp-scope-name class))
-       (javaimp--build-tree class (concat methods classes)
+                (javaimp-scope-name top-class))
+       (javaimp--build-tree top-class (append methods classes)
                             (lambda (el tested)
-                              (equal el (javaimp-scope-parent tested)))))
+                              (equal el (javaimp-scope-parent tested)))
+                            nil
+                            (lambda (s1 s2)
+                              (< (javaimp-scope-start s1)
+                                 (javaimp-scope-start s2)))))
      top-classes)))
 
-(defun javaimp--parse-get-all-scopes (&optional reparse pred)
-  "Return all scopes in the current buffer for which PRED (if
-given) returns non-nil.  If REPARSE is non-nil then full
-reparsing is done."
-  (when reparse
-    (setq javaimp--parse-dirty-pos (point-min)))
+(defun javaimp--parse-get-all-scopes (&optional pred parent-pred)
+  "Return all scopes in the current buffer, optionally filtering
+them with PRED, and their parents with PARENT-PRED."
   (javaimp--parse-all-scopes)
   (let ((pos (point-max))
         scope res)
-    (while (setq pos (previous-single-property-change
-                      pos 'javaimp-parse-scope))
+    (while (setq pos (previous-single-property-change pos 
'javaimp-parse-scope))
       (setq scope (get-text-property pos 'javaimp-parse-scope))
       (when (and scope
                  (or (null pred)
                      (funcall pred scope)))
-        (let* ((tmp (javaimp--parse-copy-scope scope))
-               (parent (javaimp-scope-parent tmp)))
-          (while (and tmp parent)
-            (if (funcall pred parent)
-                ;; go up
-                (setq parent (javaimp-scope-parent parent)
-                      tmp parent)
-              ;; skip this parent
-              (setq parent (javaimp-scope-parent parent))
-              (setf (javaimp-scope-parent tmp) parent)
-              (setq tmp parent)))
-          (push tmp res))))
+        (setq scope (javaimp--copy-scope scope))
+        (when parent-pred
+          (javaimp--filter-scope-parents scope parent-pred))
+        (push scope res)))
     res))
 
 (defun javaimp--parse-update-dirty-pos (beg _end _old-len)
diff --git a/javaimp-tests.el b/javaimp-tests.el
index 62d96a2..755e644 100644
--- a/javaimp-tests.el
+++ b/javaimp-tests.el
@@ -95,7 +95,8 @@ extends Bar<? extends Baz<? extends Baz2>> {"
      '("interface Foo<Bar, Baz> {"
        interface "Foo")
      '("private enum Foo {"
-       enum "Foo"))))
+       enum "Foo")
+     )))
 
 (ert-deftest javaimp-test--parse-scope-anonymous-class ()
   (let ((javaimp--parse-scope-hook #'javaimp--parse-scope-anonymous-class))
@@ -109,12 +110,12 @@ extends Bar<? extends Baz<? extends Baz2>> {"
      '(" = (obj.getField()).new Object<Class1, Class2>(1, baz) {"
        anonymous-class "Object")
      '(" = obj.new Object<>(1, baz) {"
-       anonymous-class "Object"))))
+       anonymous-class "Object")
+     )))
 
 (ert-deftest javaimp-test--parse-scope-method-or-stmt ()
   (let ((javaimp--parse-scope-hook #'javaimp--parse-scope-method-or-stmt)
-        (javaimp-parse-format-method-name
-         #'javaimp--parse-format-method-name-full))
+        (javaimp-format-method-name #'javaimp-format-method-name-full))
     (javaimp-test--check-single-scope
      '("static void foo_bar ( String a , int b ) {"
        method "foo_bar(String a, int b)")
@@ -128,7 +129,8 @@ extends Bar<? extends Baz<? extends Baz2>> {"
 throws E1 {"
        method "foo_bar() throws E1")
      '("if (foo_bar(a, b) < 2) {"
-       statement "if"))))
+       statement "if")
+     )))
 
 (ert-deftest javaimp-test--parse-scope-simple-stmt ()
   (let ((javaimp--parse-scope-hook #'javaimp--parse-scope-simple-stmt))
@@ -152,35 +154,41 @@ throws E1 {"
     (javaimp-test--check-single-scope
      '("new String[] {"
        array "")
-     '("new Object[][] { {"
-       array "")
-     '("new int[] {{1, 2}, {"
-       array ""))))
+     ;; TODO fix
+     ;; '("new Object[][] { {"
+     ;;   array "")
+     ;; '("new int[] {{1, 2}, {"
+     ;;   array "")
+     )))
 
 (defun javaimp-test--check-single-scope (&rest test-items)
   (dolist (item test-items)
     (with-temp-buffer
       (insert (nth 0 item))
-      (let* ((scope (car (javaimp--parse-get-all-scopes t))))
-        (should-not (null scope))
-        (should (eq (javaimp-scope-type scope) (nth 1 item)))
-        (should (equal (javaimp-scope-name scope) (nth 2 item)))))))
+      (setq javaimp--parse-dirty-pos (point-min))
+      (let ((scopes (javaimp--parse-get-all-scopes)))
+        (should (= 1 (length scopes)))
+        (should (eq (javaimp-scope-type (car scopes)) (nth 1 item)))
+        (should (equal (javaimp-scope-name (car scopes)) (nth 2 item)))))))
 
 
 ;; Tests for javaimp-parse.el "package-private" API.
 
 (ert-deftest javaimp-test--parse-get-package ()
   (with-temp-buffer
-    (insert "//package org.commented1;
-/*package org.commented2;*/
-  package org.foo;")
-    (should (equal (javaimp--parse-get-package) "org.foo"))))
+    (insert "  package  foo.bar.baz  ;
+//package commented.line;
+/*
+package commented.block;
+*/")
+    (should (equal (javaimp--parse-get-package) "foo.bar.baz"))))
 
 (ert-deftest javaimp-test--parse-get-all-classlikes ()
   (with-temp-buffer
     (insert-file-contents
      (concat javaimp--basedir "testdata/test1-misc-classes.java"))
-    (should (equal (javaimp--parse-get-all-classlikes t)
+    (setq javaimp--parse-dirty-pos (point-min))
+    (should (equal (javaimp--parse-get-all-classlikes)
                    '("Top"
                      "Top.CInner1"
                      "Top.CInner1.CInner1_CInner1"
@@ -195,21 +203,24 @@ throws E1 {"
   (with-temp-buffer
     (insert-file-contents
      (concat javaimp--basedir "testdata/test1-misc-classes.java"))
-    (let ((javaimp-parse-format-method-name
-           #'javaimp--parse-format-method-name-types))
+    (let ((javaimp-format-method-name #'javaimp-format-method-name-types))
       ;;
       ;; parse full buffer
+      (setq javaimp--parse-dirty-pos (point-min))
       (javaimp-test--check-named-scopes
-       (javaimp--parse-get-all-scopes t #'javaimp--parse-is-named))
+       (javaimp--parse-get-all-scopes
+        #'javaimp--is-named #'javaimp--is-named))
       ;;
       ;; reparse half of buffer
       (setq javaimp--parse-dirty-pos (/ (- (point-max) (point-min)) 2))
       (javaimp-test--check-named-scopes
-       (javaimp--parse-get-all-scopes nil #'javaimp--parse-is-named))
+       (javaimp--parse-get-all-scopes
+        #'javaimp--is-named #'javaimp--is-named))
       ;;
-      ;; use cache
+      ;; don't reparse
       (javaimp-test--check-named-scopes
-       (javaimp--parse-get-all-scopes nil #'javaimp--parse-is-named)))))
+       (javaimp--parse-get-all-scopes
+        #'javaimp--is-named #'javaimp--is-named)))))
 
 (defun javaimp-test--check-named-scopes (scopes)
   (let ((actual
@@ -282,9 +293,9 @@ throws E1 {"
   ;;
   (let ((data
          `((,(nth 0 scopes) "Top" 26 36)
-           (,(nth 15 scopes) "Top.IInner1.IInner1_CInner1.foo" 1798 1804)
-           (,(nth 22 scopes) "Top.EnumInner1_EInner1" 24574 2498)
-           (,(nth 24 scopes) "ColocatedTop.foo" 2566 2572))))
+           (,(nth 16 scopes) "foo()" 1798 1804)
+           (,(nth 23 scopes) "EnumInner1_EInner1" 2462 2486)
+           (,(nth 25 scopes) "foo()" 2554 2560))))
     (dolist (elt data)
       (let ((scope (nth 0 elt)))
         (should (equal (nth 1 elt) (javaimp-scope-name scope)))
@@ -297,17 +308,15 @@ throws E1 {"
 
 (ert-deftest javaimp-test--imenu-group ()
   (let* ((javaimp-imenu-group-methods t)
-         (javaimp-parse-format-method-name
-          #'javaimp--parse-format-method-name-types)
-         (actual
-          (javaimp-test--imenu-simplify-entries
-           (with-temp-buffer
-             (insert-file-contents
-              (concat javaimp--basedir "testdata/test1-misc-classes.java"))
-             (javaimp-imenu-create-index)))))
+         (javaimp-format-method-name #'javaimp-format-method-name-types)
+         (actual (with-temp-buffer
+                   (insert-file-contents
+                    (concat javaimp--basedir 
"testdata/test1-misc-classes.java"))
+                   (setq javaimp--parse-dirty-pos (point-min))
+                   (javaimp-imenu-create-index))))
+    (javaimp-test--imenu-simplify-entries actual)
     (should
      (equal
-      actual
       '(("Top"
          ("CInner1"
           ("foo()" . 98)
@@ -317,18 +326,19 @@ throws E1 {"
          ("IInner1"
           ("foo()" . 1603)
           ("IInner1_CInner1"
-           ("foo()" . 1810))
-          ("defaultMethod(String)" . 1975)
+           ("foo()" . 1798))
+          ("defaultMethod(String)" . 1963)
           ("IInner1_IInner1"
-           ("defaultMethod(String)" . 2169)))
+           ("defaultMethod(String)" . 2157)))
          ("EnumInner1"
-          ("EnumInner1()" . 2365)
-          ("foo()" . 2411)
+          ("EnumInner1()" . 2353)
+          ("foo()" . 2399)
           ;; "EnumInner1_EInner1" omitted because no methods inside
           ))
         ("ColocatedTop"
-         ("foo()" . 2566)
-         ("bar(String, String)" . 2578)))))))
+         ("foo()" . 2554)
+         ("bar(String, String)" . 2578)))
+      actual))))
 
 (defun javaimp-test--imenu-simplify-entries (alist)
   (dolist (elt alist)
@@ -339,46 +349,47 @@ throws E1 {"
 
 
 (ert-deftest javaimp-test--imenu-simple ()
-  (let ((javaimp-parse-format-method-name
-         #'javaimp--parse-format-method-name-types)
+  (let ((javaimp-format-method-name #'javaimp-format-method-name-types)
         (javaimp-imenu-group-methods nil))
     (javaimp-test--imenu-method-list 0)))
 
 (ert-deftest javaimp-test--imenu-qualified ()
-  (let ((javaimp-parse-format-method-name
-         #'javaimp--parse-format-method-name-types)
+  (let ((javaimp-format-method-name #'javaimp-format-method-name-types)
         (javaimp-imenu-group-methods 'qualified))
     (javaimp-test--imenu-method-list 1)))
 
+(defconst javaimp-test--imenu-method-list-expected
+  '(("foo() [Top.CInner1]"
+     "Top.CInner1.foo()" 98)
+    ("foo() [Top.CInner1.CInner1_CInner1]"
+     "Top.CInner1.CInner1_CInner1.foo()" 1099)
+    ("bar()"
+     "Top.CInner1.CInner1_CInner1.bar()" 1192)
+    ("foo() [Top.IInner1]"
+     "Top.IInner1.foo()" 1603)
+    ("foo() [Top.IInner1.IInner1_CInner1]"
+     "Top.IInner1.IInner1_CInner1.foo()" 1798)
+    ("defaultMethod(String) [Top.IInner1]"
+     "Top.IInner1.defaultMethod(String)" 1963)
+    ("defaultMethod(String) [Top.IInner1.IInner1_IInner1]"
+     "Top.IInner1.IInner1_IInner1.defaultMethod(String)" 2157)
+    ("EnumInner1()"
+     "Top.EnumInner1.EnumInner1()" 2353)
+    ("foo() [Top.EnumInner1]"
+     "Top.EnumInner1.foo()" 2399)
+    ("foo() [ColocatedTop]"
+     "ColocatedTop.foo()" 2554)
+    ("bar(String, String)"
+     "ColocatedTop.bar(String, String)" 2578)))
+
 (defun javaimp-test--imenu-method-list (exp-name-idx)
-  (let* ((actual
-          (with-temp-buffer
-            (insert-file-contents
-             (concat javaimp--basedir "testdata/test1-misc-classes.java"))
-            (javaimp-imenu-create-index)))
-         (expected
-          '(("foo() [Top.CInner1]"
-             "Top.CInner1.foo()" 98)
-            ("foo() [Top.CInner1.CInner1_CInner1]"
-             "Top.CInner1.CInner1_CInner1.foo()" 1099)
-            ("bar()"
-             "Top.CInner1.CInner1_CInner1.bar()" 1192)
-            ("foo() [Top.IInner1]"
-             "Top.IInner1.foo()" 1603)
-            ("foo() [Top.IInner1.IInner1_CInner1]"
-             "Top.IInner1.IInner1_CInner1.foo()" 1810)
-            ("defaultMethod(String) [Top.IInner1]"
-             "Top.IInner1.defaultMethod(String)" 1975)
-            ("defaultMethod(String) [Top.IInner1.IInner1_IInner1]"
-             "Top.IInner1.IInner1_IInner1.defaultMethod(String)" 2169)
-            ("EnumInner1()"
-             "Top.EnumInner1.EnumInner1()" 2365)
-            ("foo() [Top.EnumInner1]"
-             "Top.EnumInner1.foo()" 2411)
-            ("foo() [ColocatedTop]"
-             "ColocatedTop.foo()" 2566)
-            ("bar(String, String)"
-             "ColocatedTop.bar(String, String)" 2578))))
+  (let ((actual
+         (with-temp-buffer
+           (insert-file-contents
+            (concat javaimp--basedir "testdata/test1-misc-classes.java"))
+           (setq javaimp--parse-dirty-pos (point-min))
+           (javaimp-imenu-create-index)))
+        (expected javaimp-test--imenu-method-list-expected))
     (should (= (length expected) (length actual)))
     (dotimes (i (length expected))
       (let ((exp (nth i expected))
diff --git a/javaimp-util.el b/javaimp-util.el
index 25dd12d..d058e82 100644
--- a/javaimp-util.el
+++ b/javaimp-util.el
@@ -25,17 +25,25 @@
 (require 'cl-lib)
 (require 'seq)
 
-(defcustom javaimp-cygpath-program
-  (if (eq system-type 'cygwin) "cygpath")
-  "Path to the `cygpath' program (Cygwin only).  Customize it if
-the program is not on `exec-path'."
-  :group 'javaimp
-  :type 'string)
-
 (defconst javaimp-debug-buf-name "*javaimp-debug*")
 
 (defconst javaimp--basedir (file-name-directory load-file-name))
 
+(defconst javaimp--classlike-scope-types
+  '(class interface enum))
+
+(defconst javaimp--named-scope-types
+  (append
+   '(local-class method)
+   javaimp--classlike-scope-types))
+
+(defconst javaimp--all-scope-types
+  (append
+   '(anonymous-class statement simple-statement array unknown)
+   javaimp--named-scope-types))
+
+
+
 ;; Structs
 
 (cl-defstruct javaimp-node
@@ -59,6 +67,16 @@ the program is not on `exec-path'."
 (cl-defstruct javaimp-cached-jar        ;jar or jmod
   file read-ts classes)
 
+(cl-defstruct javaimp-scope
+  type                                  ; see javaimp--all-scope-types
+  name
+  start
+  open-brace
+  parent)
+
+
+
+;; Xml
 
 (defun javaimp--xml-children (xml-tree child-name)
   "Returns list of children of XML-TREE filtered by CHILD-NAME"
@@ -76,83 +94,98 @@ the program is not on `exec-path'."
   (car (cddr el)))
 
 
-(defun javaimp--get-file-ts (file)
-  (nth 5 (file-attributes file)))
-
-(defun javaimp-print-id (id)
-  (format "%s:%s:%s"
-          (javaimp-id-artifact id)
-          (javaimp-id-group id)
-          (javaimp-id-version id)))
-
-
-;; TODO use functions `cygwin-convert-file-name-from-windows' and
-;; `cygwin-convert-file-name-to-windows' when they are available
-;; instead of calling `cygpath'.  See
-;; https://cygwin.com/ml/cygwin/2013-03/msg00228.html
-
-(defun javaimp-cygpath-convert-maybe (path &optional mode is-really-path)
-  "On Cygwin, converts PATH using cygpath according to MODE and
-IS-REALLY-PATH.  If MODE is `unix' (the default), adds -u switch.
-If MODE is `windows', adds -m switch.  If `is-really-path' is
-non-nil, adds `-p' switch.  On other systems, PATH is returned
-unchanged."
-  (if (and path (eq system-type 'cygwin))
-      (progn
-       (unless mode (setq mode 'unix))
-       (let (args)
-         (push (cond ((eq mode 'unix) "-u")
-                     ((eq mode 'windows) "-m")
-                     (t (error "Invalid mode: %s" mode)))
-               args)
-         (and is-really-path (push "-p" args))
-         (push path args)
-         (car (apply #'process-lines javaimp-cygpath-program args))))
-    path))
+
+;; Scopes
+
+(defsubst javaimp--is-classlike (scope)
+  (and scope
+       (memq (javaimp-scope-type scope)
+             javaimp--classlike-scope-types)))
+
+(defsubst javaimp--is-named (scope)
+  (and scope
+       (memq (javaimp-scope-type scope)
+             javaimp--named-scope-types)))
+
+(defsubst javaimp--is-imenu-included-method (scope)
+  (and (eq (javaimp-scope-type scope) 'method)
+       (javaimp--is-classlike (javaimp-scope-parent scope))))
+
+(defun javaimp--copy-scope (scope)
+  "Recursively copies SCOPE and its parents."
+  (let* ((res (copy-javaimp-scope scope))
+         (tmp res)
+         orig-parent)
+    (while (setq orig-parent (javaimp-scope-parent tmp))
+      (setf (javaimp-scope-parent tmp) (copy-javaimp-scope orig-parent))
+      (setq tmp (javaimp-scope-parent tmp)))
+    res))
+
+(defun javaimp--filter-scope-parents (scope pred)
+  "Rewrite SCOPE's parents so that only those matching PRED are
+left."
+  (while scope
+    (let ((parent (javaimp-scope-parent scope)))
+      (if (and parent
+               (not (funcall pred parent)))
+          ;; leave out this parent
+          (setf (javaimp-scope-parent scope) (javaimp-scope-parent parent))
+        (setq scope (javaimp-scope-parent scope))))))
+
+(defun javaimp--concat-scope-parents (scope)
+  (let (parents)
+    (while (setq scope (javaimp-scope-parent scope))
+      (push scope parents))
+    (mapconcat #'javaimp-scope-name parents ".")))
 
 
-(defun javaimp--call-build-tool (program handler &rest args)
-  "Runs PROGRAM with ARGS, then calls HANDLER in the temporary
-buffer and returns its result"
-  (message "Calling %s on args: %s" program args)
-  (with-temp-buffer
-    (let ((status (let ((coding-system-for-read
-                         (if (eq system-type 'cygwin) 'utf-8-dos)))
-                    ;; TODO check 
 in output on Gnu/Linux
-                    (apply #'process-file program nil t nil args)))
-         (buf (current-buffer)))
-      (with-current-buffer (get-buffer-create javaimp-debug-buf-name)
-       (erase-buffer)
-       (insert-buffer-substring buf))
-      (or (and (numberp status) (= status 0))
-         (error "\"%s\" failed with status \"%s\"" program status))
-      (goto-char (point-min))
-      (funcall handler))))
+
+;;; Formatting
+
+(defsubst javaimp-format-method-name-full (name args throws-args)
+  "Outputs NAME, ARGS (name and type) and THROWS-ARGS (only type)."
+  (concat name
+          "("
+          (mapconcat (lambda (arg)
+                       (concat (car arg) " " (cdr arg)))
+                     args
+                     ", ")
+          ")"
+          (if throws-args
+              (concat " throws "
+                      (mapconcat #'car throws-args ", ")))
+          ))
+
+(defsubst javaimp-format-method-name-types (name args _throws-args)
+  "Outputs NAME and ARGS (only type)."
+  (concat name
+          "("
+          (mapconcat #'car args ", ")
+          ")"
+          ))
 
-(defun javaimp--split-native-path (path)
-  (when path
-    ;; don't use parse-colon-path because it makes resulting elements
-    ;; to be directories
-    (split-string (javaimp-cygpath-convert-maybe path 'unix t)
-                  (concat "[" path-separator "\n]+")
-                  t)))
 
 
-;; Tree building & search
+;; Tree
 
-(defun javaimp--build-tree (this all child-p &optional parent-node)
+(defun javaimp--build-tree (this all child-p &optional parent-node sort-pred)
   "Recursively builds tree for element THIS and its children.
 Children are those elements from ALL for which CHILD-P invoked
-with this element and tested element returns non-nil.
-PARENT-NODE is indented for recursive calls."
-  (let ((children (seq-filter (apply-partially child-p this) all)))
+with this element and tested element returns non-nil.  Children
+are sorted by SORT-PRED, if given.  PARENT-NODE is indented for
+recursive calls."
+  (let ((children (seq-filter (apply-partially child-p this)
+                              all)))
+    (if sort-pred
+        (setq children (sort children sort-pred)))
     (let* ((this-node (make-javaimp-node
                       :parent parent-node
                       :children nil
                       :contents this))
           (child-nodes
            (mapcar (lambda (child)
-                     (javaimp--build-tree child this-node all))
+                     (javaimp--build-tree
+                       child all child-p this-node sort-pred))
                    children)))
       (setf (javaimp-node-children this-node) child-nodes)
       this-node)))
@@ -191,25 +224,31 @@ PARENT-NODE is indented for recursive calls."
                         (javaimp-node-children tree))))))
 
 
-(defun javaimp--map-nodes (mapper pred forest)
-  "Recursively applies MAPPER to each node's contents in FOREST.
-Returns new tree where each node is of the form (MAPPED-NODE
-. CHILDREN).  A node is included only if PRED applied to it
-returns non-nil."
+(defun javaimp--map-nodes (function pred forest)
+  "Recursively applies FUNCTION to each node's contents in FOREST
+and returns new tree.  FUNCTION should return (t . VALUE) if the
+result for this node should be made a list of the form (VALUE
+. CHILDREN), or (nil . VALUE) for plain VALUE as the result (in
+this case children are discarded).  The result for each node is
+additionally tested by PRED."
   (delq nil
         (mapcar (lambda (tree)
-                  (javaimp--map-nodes-from-tree tree mapper pred))
+                  (javaimp--map-nodes-from-tree tree function pred))
                 forest)))
 
-(defun javaimp--map-nodes-from-tree (tree mapper pred)
+(defun javaimp--map-nodes-from-tree (tree function pred)
   (when tree
-    (let* ((contents (funcall mapper (javaimp-node-contents tree)))
-           (children
-            (delq nil
-                  (mapcar (lambda (child)
-                            (javaimp--map-nodes-from-tree child mapper pred))
-                          (javaimp-node-children tree))))
-           (res (cons contents children)))
+    (let* ((cell (funcall function (javaimp-node-contents tree)))
+           (res
+            (if (car cell)
+                (let ((children
+                       (delq nil
+                             (mapcar (lambda (child)
+                                       (javaimp--map-nodes-from-tree
+                                        child function pred))
+                                     (javaimp-node-children tree)))))
+                  (cons (cdr cell) children))
+              (cdr cell))))
       (and (funcall pred res)
            res))))
 
@@ -218,4 +257,67 @@ returns non-nil."
     (setq node (javaimp-node-parent node)))
   node)
 
+
+
+;; Other
+
+(defsubst javaimp-print-id (id)
+  (format "%s:%s:%s"
+          (javaimp-id-artifact id)
+          (javaimp-id-group id)
+          (javaimp-id-version id)))
+
+(defsubst javaimp--get-file-ts (file)
+  (nth 5 (file-attributes file)))
+
+;; TODO use functions `cygwin-convert-file-name-from-windows' and
+;; `cygwin-convert-file-name-to-windows' when they are available
+;; instead of calling `cygpath'.  See
+;; https://cygwin.com/ml/cygwin/2013-03/msg00228.html
+
+(defun javaimp-cygpath-convert-maybe (path &optional mode is-really-path)
+  "On Cygwin, converts PATH using cygpath according to MODE and
+IS-REALLY-PATH.  If MODE is `unix' (the default), adds -u switch.
+If MODE is `windows', adds -m switch.  If `is-really-path' is
+non-nil, adds `-p' switch.  On other systems, PATH is returned
+unchanged."
+  (if (and path (eq system-type 'cygwin))
+      (progn
+       (unless mode (setq mode 'unix))
+       (let (args)
+         (push (cond ((eq mode 'unix) "-u")
+                     ((eq mode 'windows) "-m")
+                     (t (error "Invalid mode: %s" mode)))
+               args)
+         (and is-really-path (push "-p" args))
+         (push path args)
+         (car (apply #'process-lines javaimp-cygpath-program args))))
+    path))
+
+(defun javaimp--call-build-tool (program handler &rest args)
+  "Runs PROGRAM with ARGS, then calls HANDLER in the temporary
+buffer and returns its result"
+  (message "Calling %s on args: %s" program args)
+  (with-temp-buffer
+    (let ((status (let ((coding-system-for-read
+                         (if (eq system-type 'cygwin) 'utf-8-dos)))
+                    ;; TODO check 
 in output on Gnu/Linux
+                    (apply #'process-file program nil t nil args)))
+         (buf (current-buffer)))
+      (with-current-buffer (get-buffer-create javaimp-debug-buf-name)
+       (erase-buffer)
+       (insert-buffer-substring buf))
+      (or (and (numberp status) (= status 0))
+         (error "\"%s\" failed with status \"%s\"" program status))
+      (goto-char (point-min))
+      (funcall handler))))
+
+(defun javaimp--split-native-path (path)
+  (when path
+    ;; don't use parse-colon-path because it makes resulting elements
+    ;; to be directories
+    (split-string (javaimp-cygpath-convert-maybe path 'unix t)
+                  (concat "[" path-separator "\n]+")
+                  t)))
+
 (provide 'javaimp-util)
diff --git a/javaimp.el b/javaimp.el
index 2bd4d2f..c1ba563 100644
--- a/javaimp.el
+++ b/javaimp.el
@@ -78,6 +78,7 @@
 (require 'javaimp-maven)
 (require 'javaimp-gradle)
 (require 'javaimp-parse)
+(require 'cc-mode)                      ;for java-mode-syntax-table
 
 
 
@@ -154,12 +155,36 @@ If t (the default), methods are grouped in their enclosing
 scopes.  nil means use just flat list of simple method names.
 `qualified' means use flat list where each method name is
 prepended with nested scopes.  See also
-`javaimp-parse-format-method-name'."
+`javaimp-format-method-name'."
   :type '(choice :tag "Group methods"
                 (const :tag "Group into enclosing scopes" t)
                 (const :tag "Flat list of simple name" nil)
                 (const :tag "Flat list of qualified names" qualified)))
 
+(defcustom javaimp-cygpath-program
+  (if (eq system-type 'cygwin) "cygpath")
+  "Path to the `cygpath' program (Cygwin only).  Customize it if
+the program is not on `exec-path'."
+  :type 'string)
+
+(defcustom javaimp-format-method-name #'javaimp-format-method-name-full
+  "Function to format method name, invoked with 3 arguments:
+NAME, ARGS and THROWS-ARGS.  The last two are lists with elements
+of the form (TYPE . NAME).  For THROWS-ARGS, only TYPE is
+present."
+  :type 'function)
+
+(defcustom javaimp-mvn-program "mvn"
+  "Path to the `mvn' program.  Customize it if the program is not
+on `exec-path'."
+  :type 'string)
+
+(defcustom javaimp-gradle-program "gradle"
+  "Path to the `gradle' program.  Customize it if the program is
+not on `exec-path'.  If the visited project's directory contains
+gradlew program, it is used in preference."
+  :type 'string)
+
 
 ;; Variables
 
@@ -172,6 +197,17 @@ prepended with nested scopes.  See also
 
 (defvar javaimp--jdk-classes 'need-init)
 
+(defvar javaimp-syntax-table
+  (make-syntax-table java-mode-syntax-table) ;TODO don't depend
+  "Javaimp syntax table")
+
+(defvar javaimp--arglist-syntax-table
+  (let ((st (make-syntax-table javaimp-syntax-table)))
+    (modify-syntax-entry ?< "(>" st)
+    (modify-syntax-entry ?> ")<" st)
+    (modify-syntax-entry ?. "_" st) ; separates parts of fully-qualified type
+    st)
+  "Enables parsing angle brackets as lists")
 
 
 ;;;###autoload
@@ -312,8 +348,8 @@ PREDICATE returns non-nil."
 PREDICATE returns non-nil."
   (javaimp--collect-nodes predicate javaimp-project-forest))
 
-(defun javaimp-map-modules (mapper)
-  (javaimp--map-nodes mapper #'always javaimp-project-forest))
+(defun javaimp-map-modules (function)
+  (javaimp--map-nodes function #'always javaimp-project-forest))
 
 
 ;;; Adding imports
@@ -441,6 +477,7 @@ prefix arg is given, don't do this filtering."
 (defun javaimp--get-file-classes (file)
   (with-temp-buffer
     (insert-file-contents file)
+    (setq javaimp--parse-dirty-pos (point-min))
     (let ((package (javaimp--parse-get-package)))
       (mapcar (lambda (class)
                 (if package
@@ -576,49 +613,61 @@ is `ordinary' or `static'.  Interactively, NEW-IMPORTS is 
nil."
            ;; plain list of methods
            (let ((entries
                   (mapcar #'javaimp-imenu--make-entry
-                          (javaimp--collect-nodes
-                           #'javaimp--parse-is-imenu-included-method forest))))
-             (mapcar (lambda (entry)
-                       ;; disambiguate same method names
-                       (when (assoc (car entry) entries)
-                         (setcar entry
-                                 (format "%s [%s]"
-                                         (car entry)
-                                         (javaimp--parse-concat-parent-names
-                                          (nth 3 entry))))))
-                     entries)))
+                          (seq-sort-by
+                           #'javaimp-scope-start #'<
+                           (javaimp--collect-nodes
+                            #'javaimp--is-imenu-included-method forest))))
+                 name-alist)
+             (mapc (lambda (entry)
+                     (setf (alist-get (car entry) name-alist 0 nil #'equal)
+                           (1+ (alist-get (car entry) name-alist 0 nil 
#'equal))))
+                   entries)
+             (mapc (lambda (entry)
+                     ;; disambiguate same method names
+                     (when (> (alist-get (car entry) name-alist 0 nil #'equal) 
1)
+                       (setcar entry
+                               (format "%s [%s]"
+                                       (car entry)
+                                       (javaimp--concat-scope-parents
+                                        (nth 3 entry))))))
+                   entries)))
           ((eq javaimp-imenu-group-methods 'qualified)
            ;; list of qualified methods
-           (mapcar (lambda (entry)
-                     ;; prepend parents to name
-                     (setcar entry (concat (javaimp--parse-concat-parent-names
-                                            (nth 3 entry))
-                                           "."
-                                           (car entry))))
-                   (mapcar #'javaimp-imenu--make-entry
-                           (javaimp--collect-nodes
-                            #'javaimp--parse-is-imenu-included-method 
forest))))
+           (mapc (lambda (entry)
+                   ;; prepend parents to name
+                   (setcar entry (concat (javaimp--concat-scope-parents
+                                          (nth 3 entry))
+                                         "."
+                                         (car entry))))
+                 (mapcar #'javaimp-imenu--make-entry
+                         (seq-sort-by
+                          #'javaimp-scope-start #'<
+                          (javaimp--collect-nodes
+                           #'javaimp--is-imenu-included-method forest)))))
 
           (t
            ;; group methods inside their enclosing class
            (javaimp--map-nodes
             (lambda (scope)
-              (cond ((javaimp--parse-is-classlike scope)
-                     ;; this will be a sub-alist's car
-                     (javaimp-scope-name scope))
-                    ((javaimp--parse-is-imenu-included-method scope)
-                     (javaimp-imenu--make-entry scope))))
-            #'cdr                ;don't include empty container scopes
+              (cond ((javaimp--is-classlike scope)
+                     ;; sub-alist
+                     (cons t (javaimp-scope-name scope)))
+                    ((javaimp--is-imenu-included-method scope)
+                     ;; entry
+                     (cons nil (javaimp-imenu--make-entry scope)))))
+            (lambda (res)
+              (or (functionp (nth 2 res)) ;entry
+                  (cdr res)))             ;non-empty sub-alist
             forest)))))
 
 (defsubst javaimp-imenu--make-entry (scope)
   (list (javaimp-scope-name scope)
         (javaimp-scope-start scope)
-        #'javaimp-imenu--go
+        #'javaimp-imenu--function
         scope))
 
-(defun javaimp-imenu--go (scope)
-  (goto-char (javaimp-scope-start scope))
+(defun javaimp-imenu--function (index-name index-position scope)
+  (goto-char index-position)
   (back-to-indentation))
 
 
@@ -658,13 +707,12 @@ start (`javaimp-scope-start') instead."
                      (javaimp-scope-open-brace scope)))))))
 
 
-(defun javaimp-help-show-scopes (arg)
-  "Show scopes in a *javaimp-scopes* buffer,
-which is first cleared.  With a prefix arg, first reset parsed
-scopes cache."
-  (interactive "P")
+(defun javaimp-help-reparse-and-show-scopes ()
+  "Reparse scopes and show them in a *javaimp-scopes* buffer."
+  (interactive)
+  (setq javaimp--parse-dirty-pos (point-min))
   (let ((scopes (save-excursion
-                  (javaimp--parse-get-all-scopes arg)))
+                  (javaimp--parse-get-all-scopes)))
         (file buffer-file-name)
         (buf (get-buffer-create "*javaimp-scopes*")))
     (with-current-buffer buf



reply via email to

[Prev in Thread] Current Thread [Next in Thread]