[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
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [elpa] scratch/javaimp-wip 5d00e71: wip,
Filipp Gunbin <=