branch: externals/javaimp commit 62d9fcaae4c667ecb6644ad8befbd94081b086a0 Author: Filipp Gunbin <fgun...@fastmail.fm> Commit: Filipp Gunbin <fgun...@fastmail.fm>
Add javaimp-parse.el --- javaimp-parse.el | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ javaimp-tests.el | 8 +-- javaimp-util.el | 52 -------------------- javaimp.el | 5 +- 4 files changed, 152 insertions(+), 58 deletions(-) diff --git a/javaimp-parse.el b/javaimp-parse.el new file mode 100644 index 0000000..7c29794 --- /dev/null +++ b/javaimp-parse.el @@ -0,0 +1,145 @@ +;;; javaimp-parse.el --- javaimp parsing -*- lexical-binding: t; -*- + +;; Copyright (C) 2021 Free Software Foundation, Inc. + +;; Author: Filipp Gunbin <fgun...@fastmail.fm> +;; Maintainer: Filipp Gunbin <fgun...@fastmail.fm> + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <http://www.gnu.org/licenses/>. + +(require 'cl-lib) +(require 'seq) + +(cl-defstruct javaimp-scope + type ; one of anonymous-class, class, interface, enum, local-class, unknown + name + start + open-brace) + +(defsubst javaimp--parse-is-class (scope) + (memq (javaimp-scope-type scope) '(class interface enum))) + +(defconst javaimp--parse-class-re + (concat + (regexp-opt '("class" "interface" "enum") 'words) + (rx (and (+ (syntax whitespace)) + (group (+ (any alnum ?_))))))) + + +(defun javaimp--parse-get-package () + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (catch 'found + (while (re-search-forward "^\\s *package\\s +\\([^;]+\\)\\s *;" nil t) + (let ((state (syntax-ppss))) + (unless (syntax-ppss-context state) + (throw 'found (match-string 1))))))))) + + +(defvar javaimp--parse-scope-hook + '(javaimp--parse-scope-class + javaimp--parse-scope-anonymous-class + javaimp--parse-scope-unknown ;fallback + )) + +(defun javaimp--parse-scope-class (state) + (save-excursion + (if (and (re-search-backward javaimp--parse-class-re nil t) + ;; if there's no paren in between - assume we're looking at + ;; class declaration + (not (save-match-data + (search-forward "(" (nth 1 state) t)))) + (make-javaimp-scope :type (intern (match-string 1)) + :name (match-string 2) + :start (match-beginning 1) + :open-brace (nth 1 state))))) + +(defun javaimp--parse-scope-anonymous-class (state) + (save-excursion + (let (end) + (if (and (re-search-backward "\\<new\\s +" nil t) + ;; skip arg list and ws + (setq end (save-excursion + (ignore-errors + (goto-char + (scan-lists (nth 1 state) -1 0)) + (skip-syntax-backward "-") + (point)))) + (not (save-match-data + (search-forward "(" end t)))) + (make-javaimp-scope :type 'anonymous-class + :name (concat + "Anon_" + (buffer-substring-no-properties + (match-end 0) end)) + :start (match-beginning 0) + :open-brace (nth 1 state)))))) + +(defun javaimp--parse-scope-unknown (state) + (make-javaimp-scope :type 'unknown + :name "unknown" + :start nil + :open-brace (nth 1 state))) + +(defun javaimp--parse-scopes (count) + (let ((state (syntax-ppss)) + res) + (unless (syntax-ppss-context state) + (save-excursion + (while (and (nth 1 state) + (or (not count) + (>= (setq count (1- count)) 0))) + ;; find innermost enclosing open-bracket + (goto-char (nth 1 state)) + (when (= (char-after) ?{) + (let ((scope (run-hook-with-args-until-success + 'javaimp--parse-scope-hook state))) + (push scope res) + (if (javaimp-scope-start scope) + (goto-char (javaimp-scope-start scope))))) + (setq state (syntax-ppss))))) + ;; if a class is enclosed in anything other than a class, then it + ;; should be local + (let ((tmp res) + in-local) + (while tmp + (if (javaimp--parse-is-class (car tmp)) + (if in-local + (setf (javaimp-scope-type (car tmp)) 'local-class)) + (setq in-local t)) + (setq tmp (cdr tmp)))) + res)) + +(defun javaimp--parse-get-file-classes (file) + (with-temp-buffer + (insert-file-contents file) + (let ((parse-sexp-ignore-comments t) + (package (javaimp--parse-get-package)) + res) + (while (re-search-forward javaimp--parse-class-re nil t) + (when (and (ignore-errors + (goto-char (scan-lists (point) 1 -1))) + (= (char-before) ?{)) + (let ((scopes (javaimp--parse-scopes nil)) + curr) + (when (seq-every-p #'javaimp--parse-is-class scopes) + (setq curr (mapconcat #'javaimp-scope-name scopes ".")) + (if package + (setq curr (concat package "." curr))) + (push curr res))))) + (nreverse res)))) + +(provide 'javaimp-parse) diff --git a/javaimp-tests.el b/javaimp-tests.el index 36b4686..9951a81 100644 --- a/javaimp-tests.el +++ b/javaimp-tests.el @@ -23,15 +23,15 @@ (should (eql (length projects) 2))))) -(ert-deftest javaimp-test--get-package () +(ert-deftest javaimp-test--parse-get-package () (with-temp-buffer (insert "//package org.commented1; /*package org.commented2;*/ package org.foo;") - (should (equal (javaimp--get-package) "org.foo")))) + (should (equal (javaimp--parse-get-package) "org.foo")))) -(ert-deftest javaimp-test--get-file-classes () - (should (equal (javaimp--get-file-classes +(ert-deftest javaimp-test--parse-get-file-classes () + (should (equal (javaimp--parse-get-file-classes (concat javaimp--basedir "testdata/test-get-file-classes-1.java")) '("org.foo.Top" "org.foo.Top.CInner1" diff --git a/javaimp-util.el b/javaimp-util.el index f2f4c00..9304fce 100644 --- a/javaimp-util.el +++ b/javaimp-util.el @@ -157,56 +157,4 @@ buffer and returns its result" (setf (javaimp-node-children this-node) child-nodes) this-node))) - -;; Java source parsing - -(defun javaimp--get-package () - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (catch 'found - (while (re-search-forward "^\\s *package\\s +\\([^;]+\\)\\s *;" nil t) - (let ((state (syntax-ppss))) - (unless (syntax-ppss-context state) - (throw 'found (match-string 1))))))))) - -(defun javaimp--get-file-classes (file) - (with-temp-buffer - (insert-file-contents file) - (let ((parse-sexp-ignore-comments t) - (class-re (concat - (regexp-opt '("class" "interface" "enum") 'words) - (rx (and (+ (syntax whitespace)) - (group (+ (any alnum ?_))))))) - res) - (while (re-search-forward class-re nil t) - (let ((state (syntax-ppss)) - curr) - (unless (syntax-ppss-context state) - (setq curr (list (match-string 2))) - ;; collect enclosing classes, if any - (save-excursion - (catch 'stop - (while (nth 1 state) - ;; find innermost enclosing open-bracket - (goto-char (nth 1 state)) - (if (and (= (char-after) ?{) - (re-search-backward class-re nil t) - ;; if there's no paren in between - assume - ;; it's a valid class (not a method - this - ;; way we exclude local classes) - (not (save-match-data - (search-forward "(" (nth 1 state) t)))) - (progn - (push (match-string 2) curr) - (setq state (syntax-ppss))) - (setq curr nil) - (throw 'stop nil))))) - (when curr - (let ((package (javaimp--get-package))) - (if package (push package curr))) - (push (mapconcat #'identity curr ".") res))))) - (nreverse res)))) - (provide 'javaimp-util) diff --git a/javaimp.el b/javaimp.el index 8b368ea..a0dd555 100644 --- a/javaimp.el +++ b/javaimp.el @@ -77,6 +77,7 @@ (require 'javaimp-maven) (require 'javaimp-gradle) +(require 'javaimp-parse) @@ -444,7 +445,7 @@ prefix arg is given, don't do this filtering." javaimp-additional-source-dirs))) (defun javaimp--dir-above-current-package () - (let ((package (javaimp--get-package))) + (let ((package (javaimp--parse-get-package))) (when package (string-remove-suffix (mapconcat #'file-name-as-directory @@ -453,7 +454,7 @@ prefix arg is given, don't do this filtering." (defun javaimp--get-directory-classes (dir) (if (file-accessible-directory-p dir) - (seq-mapcat #'javaimp--get-file-classes + (seq-mapcat #'javaimp--parse-get-file-classes (seq-filter (lambda (file) (not (file-symlink-p file))) (directory-files-recursively dir "\\.java\\'")))))