branch: externals/javaimp commit 048a0761547ccb7cf6a7a78a79f03169bcf65bd5 Author: Filipp Gunbin <fgun...@fastmail.fm> Commit: Filipp Gunbin <fgun...@fastmail.fm>
Simplify initialization, reshuffle code a bit --- javaimp-parse.el | 49 +++++++++----- javaimp-tests.el | 197 +++++++++++++++++++++++-------------------------------- javaimp.el | 107 +++++++++++------------------- 3 files changed, 153 insertions(+), 200 deletions(-) diff --git a/javaimp-parse.el b/javaimp-parse.el index 21bda4b..e00fedb 100644 --- a/javaimp-parse.el +++ b/javaimp-parse.el @@ -31,10 +31,9 @@ (defconst javaimp--parse-stmt-keyword-maxlen (seq-max (mapcar #'length javaimp--parse-stmt-keywords))) -(defvar-local javaimp--parse-dirty-pos nil +(defvar-local javaimp--parse-dirty-pos 'init "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.") +considered as stale. Usually set by modification change hooks.") (defsubst javaimp--parse-substr-before-< (str) (let ((end (string-search "<" str))) @@ -367,10 +366,13 @@ then goes all the way up. Examines and sets property "Entry point to the scope parsing. Parses scopes in this buffer which are after `javaimp--parse-dirty-pos', if it is non-nil. Resets this variable after parsing is done." - ;; FIXME Set parse-sexp-.. vars, as well as syntax-table and - ;; syntax-ppss-table, in major mode function. Remove let-binding of - ;; inhibit-modification-hooks here. (when javaimp--parse-dirty-pos + (when (eq javaimp--parse-dirty-pos 'init) + ;; TODO better way to do this + (setq javaimp--parse-dirty-pos (point-min)) + (setq syntax-ppss-table javaimp-syntax-table) + (add-hook 'after-change-functions #'javaimp--parse-update-dirty-pos)) + ;; FIXME this inhibits costly cc-mode hooks on prop updates (let ((inhibit-modification-hooks t)) (remove-text-properties javaimp--parse-dirty-pos (point-max) '(javaimp-parse-scope nil)) @@ -385,7 +387,7 @@ non-nil. Resets this variable after parsing is done." (javaimp--parse-scopes nil))))) (setq javaimp--parse-dirty-pos nil)))) -(defun javaimp--parse-abstract-class-methods () +(defun javaimp--parse-class-abstract-methods () (goto-char (point-max)) (let (res) (while (javaimp--parse-rsb-keyword "\\_<abstract\\_>" nil t) @@ -405,7 +407,7 @@ non-nil. Resets this variable after parsing is done." (push scope res)))))) res)) -(defun javaimp--parse-abstract-interface-methods (int-scope) +(defun javaimp--parse-interface-abstract-methods (int-scope) (let ((start (1+ (javaimp-scope-open-brace int-scope))) (end (ignore-errors (1- (scan-lists (javaimp-scope-open-brace int-scope) 1 0)))) @@ -425,6 +427,13 @@ non-nil. Resets this variable after parsing is done." (goto-char (nth 1 (syntax-ppss)))))) res)) +(defun javaimp--parse-update-dirty-pos (beg _end _old-len) + "Function to add to `after-change-functions' hook." + (when (or (not javaimp--parse-dirty-pos) + (and (numberp javaimp--parse-dirty-pos) + (< beg javaimp--parse-dirty-pos))) + (setq javaimp--parse-dirty-pos beg))) + ;; Functions intended to be called from other parts of javaimp. @@ -433,6 +442,7 @@ non-nil. Resets this variable after parsing is done." (save-excursion (save-restriction (widen) + (javaimp--parse-all-scopes) (goto-char (point-max)) (when (javaimp--parse-rsb-keyword "^[ \t]*package[ \t]+\\([^ \t;\n]+\\)[ \t]*;" nil t 1) @@ -459,18 +469,23 @@ them should move point." (push scope res))) res)))) -(defun javaimp--parse-abstract-methods (interfaces) +(defun javaimp--parse-get-class-abstract-methods () (save-excursion (save-restriction (widen) - (append (javaimp--parse-abstract-class-methods) - (seq-mapcat #'javaimp--parse-abstract-interface-methods - interfaces))))) + (javaimp--parse-all-scopes) + (javaimp--parse-class-abstract-methods)))) -(defun javaimp--parse-update-dirty-pos (beg _end _old-len) - "Function to add to `after-change-functions' hook." - (when (or (not javaimp--parse-dirty-pos) - (< beg javaimp--parse-dirty-pos)) - (setq javaimp--parse-dirty-pos beg))) +(defun javaimp--parse-get-interface-abstract-methods () + (save-excursion + (save-restriction + (widen) + (let ((interfaces + (javaimp--parse-get-all-scopes + (lambda (scope) + (javaimp-test-scope-type scope + '(interface) javaimp--classlike-scope-types))))) + (seq-mapcat #'javaimp--parse-interface-abstract-methods + interfaces))))) (provide 'javaimp-parse) diff --git a/javaimp-tests.el b/javaimp-tests.el index 0ad3fd8..f90c148 100644 --- a/javaimp-tests.el +++ b/javaimp-tests.el @@ -62,7 +62,7 @@ Exception4<? super Exception5>>") -;; Tests for scope parsers, which should be in +;; Tests for single scope parsers, which should be in ;; `javaimp--parse-scope-hook'. (ert-deftest javaimp-test--parse-scope-class () @@ -165,8 +165,6 @@ throws E1 {" (dolist (item test-items) (with-temp-buffer (insert (nth 0 item)) - (setq syntax-ppss-table javaimp-syntax-table) - (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))) @@ -182,17 +180,14 @@ throws E1 {" /* package commented.block; */") - (setq syntax-ppss-table javaimp-syntax-table) (should (equal (javaimp--parse-get-package) "foo.bar.baz")))) (ert-deftest javaimp-test--parse-get-all-scopes () (with-temp-buffer (insert-file-contents (concat javaimp--basedir "testdata/test1-misc-classes.java")) - (setq syntax-ppss-table javaimp-syntax-table) (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) ;; ;; reparse half of buffer @@ -289,121 +284,12 @@ package commented.block; -;; Tests for imenu function - -(ert-deftest javaimp-test--imenu-group () - (let* ((javaimp-imenu-group-methods t) - (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 syntax-ppss-table javaimp-syntax-table) - (setq javaimp--parse-dirty-pos (point-min)) - (let ((imenu-use-markers nil)) - (javaimp-imenu-create-index))))) - (javaimp-test--imenu-simplify-entries actual) - (should - (equal - '(("Top" - ("CInner1" - ("foo()" . 98) - ("CInner1_CInner1" - ("foo()" . 1099) - ("abstract_method()" . 1148) - ("bar()" . 1192) - ("baz()" . 1281))) - ("IInner1" - ("foo()" . 1603) - ("abstract_method()" . 1715) - ("IInner1_CInner1" - ("foo()" . 1798)) - ("baz()" . 1934) - ("defaultMethod(String)" . 1963) - ("IInner1_IInner1" - ("foo()" . 2122) - ("defaultMethod(String)" . 2157) - ("baz()" . 2258))) - ("EnumInner1" - ("EnumInner1()" . 2353) - ("foo()" . 2399) - ;; "EnumInner1_EInner1" omitted because no methods inside - )) - ("ColocatedTop" - ("foo()" . 2554) - ("bar(String, String)" . 2578))) - actual)))) - -(defun javaimp-test--imenu-simplify-entries (alist) - (dolist (elt alist) - (if (and (= (length elt) 4) - (functionp (nth 2 elt))) - (setcdr elt (nth 1 elt)) - (javaimp-test--imenu-simplify-entries (cdr elt))))) - -(ert-deftest javaimp-test--imenu-simple () - (let ((javaimp-format-method-name #'javaimp-format-method-name-types) - (javaimp-imenu-group-methods nil)) - (javaimp-test--imenu-method-list - '("foo() [Top.CInner1]" - "foo() [Top.CInner1.CInner1_CInner1]" - "abstract_method() [Top.CInner1.CInner1_CInner1]" - "bar()" - "baz() [Top.CInner1.CInner1_CInner1]" - "foo() [Top.IInner1]" - "abstract_method() [Top.IInner1]" - "foo() [Top.IInner1.IInner1_CInner1]" - "baz() [Top.IInner1]" - "defaultMethod(String) [Top.IInner1]" - "foo() [Top.IInner1.IInner1_IInner1]" - "defaultMethod(String) [Top.IInner1.IInner1_IInner1]" - "baz() [Top.IInner1.IInner1_IInner1]" - "EnumInner1()" - "foo() [Top.EnumInner1]" - "foo() [ColocatedTop]" - "bar(String, String)")))) - -(ert-deftest javaimp-test--imenu-qualified () - (let ((javaimp-format-method-name #'javaimp-format-method-name-types) - (javaimp-imenu-group-methods 'qualified)) - (javaimp-test--imenu-method-list - '("Top.CInner1.foo()" - "Top.CInner1.CInner1_CInner1.foo()" - "Top.CInner1.CInner1_CInner1.abstract_method()" - "Top.CInner1.CInner1_CInner1.bar()" - "Top.CInner1.CInner1_CInner1.baz()" - "Top.IInner1.foo()" - "Top.IInner1.abstract_method()" - "Top.IInner1.IInner1_CInner1.foo()" - "Top.IInner1.baz()" - "Top.IInner1.defaultMethod(String)" - "Top.IInner1.IInner1_IInner1.foo()" - "Top.IInner1.IInner1_IInner1.defaultMethod(String)" - "Top.IInner1.IInner1_IInner1.baz()" - "Top.EnumInner1.EnumInner1()" - "Top.EnumInner1.foo()" - "ColocatedTop.foo()" - "ColocatedTop.bar(String, String)")))) - -(defun javaimp-test--imenu-method-list (expected-names) - (let ((actual - (with-temp-buffer - (insert-file-contents - (concat javaimp--basedir "testdata/test1-misc-classes.java")) - (setq syntax-ppss-table javaimp-syntax-table) - (setq javaimp--parse-dirty-pos (point-min)) - (let ((imenu-use-markers nil)) - (javaimp-imenu-create-index))))) - (should (= (length expected-names) (length actual))) - (dotimes (i (length expected-names)) - (should (equal (nth i expected-names) (car (nth i actual))))))) - +;; Tests for javaimp--get-file-classes-1 (ert-deftest javaimp-test--get-file-classes () (with-temp-buffer (insert-file-contents (concat javaimp--basedir "testdata/test1-misc-classes.java")) - (setq syntax-ppss-table javaimp-syntax-table) - (setq javaimp--parse-dirty-pos (point-min)) (should (equal (javaimp--get-file-classes-1) '("org.foo.Top" "org.foo.Top.CInner1" @@ -415,5 +301,84 @@ package commented.block; "org.foo.Top.EnumInner1.EnumInner1_EInner1" "org.foo.ColocatedTop"))))) + +;; Tests for imenu function + +(ert-deftest javaimp-test--imenu () + (let* ((javaimp-format-method-name #'javaimp-format-method-name-types) + (actual + (with-temp-buffer + (insert-file-contents + (concat javaimp--basedir "testdata/test1-misc-classes.java")) + (let ((imenu-use-markers nil)) + (javaimp-imenu-create-index)))) + (expected-names + '("foo() [Top.CInner1]" + "foo() [Top.CInner1.CInner1_CInner1]" + "abstract_method() [Top.CInner1.CInner1_CInner1]" + "bar()" + "baz() [Top.CInner1.CInner1_CInner1]" + "foo() [Top.IInner1]" + "abstract_method() [Top.IInner1]" + "foo() [Top.IInner1.IInner1_CInner1]" + "baz() [Top.IInner1]" + "defaultMethod(String) [Top.IInner1]" + "foo() [Top.IInner1.IInner1_IInner1]" + "defaultMethod(String) [Top.IInner1.IInner1_IInner1]" + "baz() [Top.IInner1.IInner1_IInner1]" + "EnumInner1()" + "foo() [Top.EnumInner1]" + "foo() [ColocatedTop]" + "bar(String, String)"))) + (should (= (length expected-names) (length actual))) + (dotimes (i (length expected-names)) + (should (equal (nth i expected-names) (car (nth i actual))))))) + +(ert-deftest javaimp-test--imenu-group-methods () + (let* ((javaimp-format-method-name #'javaimp-format-method-name-types) + (javaimp-imenu-group-methods t) + (actual (with-temp-buffer + (insert-file-contents + (concat javaimp--basedir "testdata/test1-misc-classes.java")) + (let ((imenu-use-markers nil)) + (javaimp-imenu-create-index)))) + (expected + '(("Top" + ("CInner1" + ("foo()" . 98) + ("CInner1_CInner1" + ("foo()" . 1099) + ("abstract_method()" . 1148) + ("bar()" . 1192) + ("baz()" . 1281))) + ("IInner1" + ("foo()" . 1603) + ("abstract_method()" . 1715) + ("IInner1_CInner1" + ("foo()" . 1798)) + ("baz()" . 1934) + ("defaultMethod(String)" . 1963) + ("IInner1_IInner1" + ("foo()" . 2122) + ("defaultMethod(String)" . 2157) + ("baz()" . 2258))) + ("EnumInner1" + ("EnumInner1()" . 2353) + ("foo()" . 2399) + ;; "EnumInner1_EInner1" omitted because no methods inside + )) + ("ColocatedTop" + ("foo()" . 2554) + ("bar(String, String)" . 2578))))) + (javaimp-test--imenu-simplify-entries actual) + (should (equal expected actual)))) + +(defun javaimp-test--imenu-simplify-entries (alist) + (dolist (elt alist) + (if (and (= (length elt) 4) + (functionp (nth 2 elt))) + (setcdr elt (nth 1 elt)) + (javaimp-test--imenu-simplify-entries (cdr elt))))) + (provide 'javaimp-tests) diff --git a/javaimp.el b/javaimp.el index f4a4693..3397d8c 100644 --- a/javaimp.el +++ b/javaimp.el @@ -149,17 +149,13 @@ files in the current project and add their fully-qualified names to the completion alternatives list." :type 'boolean) -(defcustom javaimp-imenu-group-methods t - "How to lay out methods in Imenu index. -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-format-method-name'." +(defcustom javaimp-imenu-group-methods nil + "Non-nil means group methods in their enclosing scopes. +nil (the default) means just use flat list of simple method +names. See also `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))) + (const :tag "Grouped" t) + (const :tag "Simple list" nil))) (defcustom javaimp-cygpath-program (if (eq system-type 'cygwin) "cygpath") @@ -479,7 +475,6 @@ prefix arg is given, don't do this filtering." (javaimp--get-file-classes-1)) (with-temp-buffer (insert-file-contents file) - (setq javaimp--parse-dirty-pos (point-min)) (javaimp--get-file-classes-1))))) (defun javaimp--get-file-classes-1 () @@ -625,58 +620,38 @@ is `ordinary' or `static'. Interactively, NEW-IMPORTS is nil." ;;;###autoload (defun javaimp-imenu-create-index () - "Function to use as `imenu-create-index-function'. - -Currently it requires some manual setup, something like this in -the `java-mode-hook': - - (setq imenu-create-index-function #'javaimp-imenu-create-index) - (setq syntax-ppss-table javaimp-syntax-table) - (setq javaimp--parse-dirty-pos (point-min)) - (add-hook 'after-change-functions #'javaimp--parse-update-dirty-pos) - -In future, when we implement a minor / major mode, it will be -done in mode functions automatically." + "Function to use as `imenu-create-index-function', can be set +in a major mode hook." (let ((forest (javaimp-imenu--get-forest))) - (cond ((not javaimp-imenu-group-methods) - ;; plain list of methods - (let ((entries (javaimp-imenu--make-entries-simple 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 - (mapc (lambda (entry) - ;; prepend parents to name - (setcar entry (concat (javaimp--concat-scope-parents - (nth 3 entry)) - "." - (car entry)))) - (javaimp-imenu--make-entries-simple forest))) - (t - ;; group methods inside their enclosing class - (javaimp--map-nodes - (lambda (scope) - (if (eq (javaimp-scope-type scope) 'method) - ;; entry - (cons nil (javaimp-imenu--make-entry scope)) - ;; sub-alist - (cons t (javaimp-scope-name scope)))) - (lambda (res) - (or (functionp (nth 2 res)) ;entry - (cdr res))) ;non-empty sub-alist - forest))))) + (if javaimp-imenu-group-methods + ;; group methods inside parents + (javaimp--map-nodes + (lambda (scope) + (if (eq (javaimp-scope-type scope) 'method) + ;; entry + (cons nil (javaimp-imenu--make-entry scope)) + ;; sub-alist + (cons t (javaimp-scope-name scope)))) + (lambda (res) + (or (functionp (nth 2 res)) ;entry + (cdr res))) ;non-empty sub-alist + forest) + ;; plain list of methods + (let ((entries (javaimp-imenu--make-entries-simple 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))))) (defun javaimp-imenu--get-forest () (let* ((scopes (javaimp--parse-get-all-scopes @@ -695,11 +670,9 @@ done in mode functions automatically." (top-classes (seq-filter (lambda (s) (null (javaimp-scope-parent s))) classes)) - (abstract-methods (javaimp--parse-abstract-methods - (seq-filter - (lambda (scope) - (eq (javaimp-scope-type scope) 'interface)) - scopes)))) + (abstract-methods (append + (javaimp--parse-get-class-abstract-methods) + (javaimp--parse-get-interface-abstract-methods)))) (mapcar (lambda (top-class) (message "Building tree for top-level class-like scope: %s"