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

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

[elpa] externals/javaimp f4216f2: Improve javaimp-organize-imports and f


From: Filipp Gunbin
Subject: [elpa] externals/javaimp f4216f2: Improve javaimp-organize-imports and friends
Date: Thu, 18 Nov 2021 21:38:31 -0500 (EST)

branch: externals/javaimp
commit f4216f241ed8b8d78a7ba234af00c7430563a294
Author: Filipp Gunbin <fgunbin@fastmail.fm>
Commit: Filipp Gunbin <fgunbin@fastmail.fm>

    Improve javaimp-organize-imports and friends
---
 javaimp-parse.el |  58 +++++++++++++++++--
 javaimp-tests.el |  32 ++++++++++-
 javaimp.el       | 166 +++++++++++++++++++++++--------------------------------
 3 files changed, 149 insertions(+), 107 deletions(-)

diff --git a/javaimp-parse.el b/javaimp-parse.el
index 3ff62c3..13950d2 100644
--- a/javaimp-parse.el
+++ b/javaimp-parse.el
@@ -31,6 +31,19 @@
 (defconst javaimp--parse-stmt-keyword-maxlen
   (seq-max (mapcar #'length javaimp--parse-stmt-keywords)))
 
+(defconst javaimp--parse-package-regexp
+  (javaimp--directive-regexp "package"))
+(defconst javaimp--parse-import-regexp
+  (javaimp--directive-regexp "import\\(?:[[:space:]]+static\\)?"))
+
+(defun javaimp--directive-regexp (directive)
+  "Return regexp suitable for matching package-like DIRECTIVE, a
+regexp.  First group is directive, second group is identifier."
+  (rx bol (* space)
+      (group (regexp directive)) (+ space)
+      (group (+ (any alnum ?_)) (* ?. (+ (any alnum ?_ ?*))))
+      (* space) ?\;))
+
 (defvar-local javaimp--parse-dirty-pos nil
   "Marker which points to a buffer position after which all parsed
 information should be considered as stale.  Usually set by
@@ -45,9 +58,9 @@ everything's up-to-date.")
       str)))
 
 (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
-point is outside of any context initially."
+  "Like `re-search-backward', but count only occurences which start
+outside any syntactic context as given by `syntax-ppss-context'.
+Assumes point is outside of any context initially."
   (or count (setq count 1))
   (let ((step (if (>= count 0) 1 -1))
         (case-fold-search nil)
@@ -396,6 +409,7 @@ anywhere.  Makes it point nowhere when done."
 (defun javaimp--parse-setup-buffer ()
   ;; FIXME This may be done in major/minor mode setup
   (setq syntax-ppss-table javaimp-syntax-table)
+  (setq-local multibyte-syntax-as-symbol t)
   (add-hook 'after-change-functions #'javaimp--parse-update-dirty-pos))
 
 (defun javaimp--parse-class-abstract-methods ()
@@ -451,13 +465,34 @@ anywhere.  Makes it point nowhere when done."
 ;; responsibility.
 
 (defun javaimp--parse-get-package ()
-  "Return the package declared in the current file."
+  "Return the package declared in the current file.  Leaves point
+at the end of directive."
   (javaimp--parse-all-scopes)
   (goto-char (point-max))
-  (when (javaimp--parse-rsb-keyword
-         "^[ \t]*package[ \t]+\\([^ \t;\n]+\\)[ \t]*;" nil t 1)
+  (when (javaimp--parse-rsb-keyword javaimp--parse-package-regexp nil t 1)
+    (goto-char (match-end 0))
     (match-string 1)))
 
+(defun javaimp--parse-get-imports ()
+  "Parse import directives in the current buffer and return (REGION
+. CLASS-ALIST).  REGION, a cons of two positions, spans from bol
+of first import to eol of last import.  CLASS-ALIST contains
+elements (CLASS . TYPE), where CLASS is a string and TYPE is
+either of symbols `normal' or 'static'."
+  (javaimp--parse-all-scopes)
+  (goto-char (point-max))
+  (let (start-pos end-pos class-alist)
+    (while (javaimp--parse-rsb-keyword javaimp--parse-import-regexp nil t)
+      (setq start-pos (line-beginning-position))
+      (unless end-pos
+        (setq end-pos (line-end-position)))
+      (push (cons (match-string 2)
+                  (if (string-search "static" (match-string 1))
+                      'static 'normal))
+            class-alist))
+    (cons (and start-pos end-pos (cons start-pos end-pos))
+          class-alist)))
+
 (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.  Neither of
@@ -488,4 +523,15 @@ them should move point."
     (seq-mapcat #'javaimp--parse-interface-abstract-methods
                 interfaces)))
 
+(defmacro javaimp--parse-without-hook (&rest body)
+  "Execute BODY, temporarily removing
+`javaimp--parse-update-dirty-pos' from `after-change-functions'
+hook."
+  (declare (debug t) (indent 0))
+  `(unwind-protect
+       (progn
+         (remove-hook 'after-change-functions 
#'javaimp--parse-update-dirty-pos)
+         ,@body)
+     (add-hook 'after-change-functions #'javaimp--parse-update-dirty-pos)))
+
 (provide 'javaimp-parse)
diff --git a/javaimp-tests.el b/javaimp-tests.el
index 16de66a..33b46a6 100644
--- a/javaimp-tests.el
+++ b/javaimp-tests.el
@@ -172,17 +172,43 @@ throws E1 {"
         (should (equal (javaimp-scope-name (car scopes)) (nth 2 item)))))))
 
 
-;; Tests for parsing
+;; Tests for parse "api"
 
 (ert-deftest javaimp-test--parse-get-package ()
   (with-temp-buffer
-    (insert "  package  foo.bar.baz  ;
+    (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-imports ()
+  (with-temp-buffer
+    (insert "
+  import  some.class1  ;
+import  static  some.class.fun1;
+//import commented.line;
+/*
+import static commented.block;
+*/
+import someclass2;
+import my.package.* ;
+
+import static some_class.fun_2; // comment
+// comment outside
+")
+    (should (equal
+             (javaimp--parse-get-imports)
+             '((2 . 206)
+               ("some.class1" . normal)
+               ("some.class.fun1" . static)
+               ("someclass2" . normal)
+               ("my.package.*" . normal)
+               ("some_class.fun_2" . static))))))
+
 (ert-deftest javaimp-test--parse-get-all-scopes ()
   (with-temp-buffer
     (insert-file-contents
diff --git a/javaimp.el b/javaimp.el
index cbd1283..328a5ae 100644
--- a/javaimp.el
+++ b/javaimp.el
@@ -422,7 +422,7 @@ current module or source tree, see
                                eol))))))
      (list (completing-read "Import: " classes nil t nil nil
                             (symbol-name (symbol-at-point))))))
-  (javaimp-organize-imports (cons classname 'ordinary)))
+  (javaimp-organize-imports (list (cons classname 'normal))))
 
 (defun javaimp--get-jdk-classes (java-home)
   "If 'jmods' subdirectory exists in JAVA-HOME (Java 9+), read all
@@ -542,117 +542,87 @@ current buffer."
 ;; Organizing imports
 
 ;;;###autoload
-(defun javaimp-organize-imports (&rest new-imports)
-  "Groups import statements according to the value of
+(defun javaimp-organize-imports (&optional add-alist)
+  "Group import statements according to the value of
 `javaimp-import-group-alist' (which see) and prints resulting
-groups leaving one blank line between groups.
+groups putting one blank line between groups.
 
-If the file already contains some import statements, this command
-rewrites them, starting with the same place.  Else, if the the
-file contains package directive, this command inserts one blank
-line below and then imports.  Otherwise, imports are inserted at
-the beginning of buffer.
+If buffer already contains some import statements, put imports at
+that place.  Else, if there's a package directive, put imports
+below it, separated by one line.  Else, just put them at bob.
 
-Classes within a single group are ordered in a lexicographic
-order.  Imports not matched by any regexp in `javaimp-import-group-alist'
+Classes within a single group are sorted lexicographically.
+Imports not matched by any regexp in `javaimp-import-group-alist'
 are assigned a default order defined by
-`javaimp-import-default-order'.  Duplicate imports are squashed.
+`javaimp-import-default-order'.  Duplicate imports are elided.
 
-NEW-IMPORTS is a list of additional imports; each element should
-be of the form (CLASS . TYPE), where CLASS is a string and TYPE
-is `ordinary' or `static'.  Interactively, NEW-IMPORTS is nil."
+Additionally, merge imports from ADD-ALIST, an alist of the same
+form as CLASS-ALIST in return value of
+`javaimp--parse-get-imports'."
   (interactive)
   (barf-if-buffer-read-only)
   (save-excursion
-    (goto-char (point-min))
-    (let* ((old-data (javaimp--parse-imports))
-          (first (car old-data))
-          (last (cadr old-data))
-          (all-imports (append new-imports (cddr old-data))))
-      (if all-imports
-         (progn
-           ;; delete old imports, if any
-           (if first
-               (progn
-                 (goto-char last)
-                 (forward-line)
-                 (delete-region first (point))))
-           (javaimp--prepare-for-insertion first)
-           (setq all-imports
-                 (cl-delete-duplicates
-                   all-imports
-                   :test (lambda (first second)
-                           (equal (car first) (car second)))))
-           ;; assign order
-           (let ((with-order
+    (save-restriction
+      (widen)
+      (let ((parsed (javaimp--parse-get-imports)))
+        (when (or (cdr parsed) add-alist)
+          (javaimp--parse-without-hook
+            (javaimp--position-for-insert-imports (car parsed))
+            (let ((with-order
                   (mapcar
                    (lambda (import)
-                     (let ((order (or (assoc-default (car import)
-                                                     javaimp-import-group-alist
-                                                     'string-match)
-                                      javaimp-import-default-order)))
-                       (cons import order)))
-                   all-imports)))
+                     (let ((order
+                             (or (assoc-default (car import)
+                                                javaimp-import-group-alist
+                                               'string-match)
+                                javaimp-import-default-order)))
+                       (cons import order)))
+                    (delete-dups (append (cdr parsed) add-alist))))
+                  by-type)
              (setq with-order
                    (sort with-order
                          (lambda (first second)
-                           ;; sort by order, name
-                           (if (= (cdr first) (cdr second))
-                               (string< (caar first) (caar second))
-                             (< (cdr first) (cdr second))))))
-             (javaimp--insert-imports with-order)))
-        (message "Nothing to organize!")))))
-
-(defun javaimp--parse-imports ()
-  "Returns (FIRST LAST . IMPORTS)"
-  (let (first last imports)
-    (while (re-search-forward "^\\s *import\\s +\\(static\\s 
+\\)?\\([._[:word:]]+\\)" nil t)
-      (let ((type (if (match-string 1) 'static 'ordinary))
-           (class (match-string 2)))
-       (push (cons class type) imports))
-      (setq last (line-beginning-position))
-      (or first (setq first last)))
-    (cons first (cons last imports))))
-
-(defun javaimp--prepare-for-insertion (start)
-  (cond (start
-        ;; if there were any imports, we start inserting at the same place
-        (goto-char start))
-       ((re-search-forward "^\\s *package\\s " nil t)
-        ;; if there's a package directive, insert one blank line
-        ;; below it
-        (end-of-line)
-        (if (eobp)
-            (insert ?\n)
-          (forward-line))
-        (insert ?\n))
-       (t
-        ;; otherwise, just go to bob
-        (goto-char (point-min)))))
-
-(defun javaimp--insert-imports (imports)
-  (let ((static (seq-filter (lambda (elt)
-                             (eq (cdar elt) 'static))
-                           imports))
-       (ordinary (seq-filter (lambda (elt)
-                               (eq (cdar elt) 'ordinary))
-                             imports)))
-    (javaimp--insert-import-group "import static %s;" static)
-    (and static ordinary (insert ?\n))
-    (javaimp--insert-import-group "import %s;" ordinary)))
-
-(defun javaimp--insert-import-group (pattern imports)
-  (let (last-order)
+                           ;; sort by order then name
+                           (if (/= (cdr first) (cdr second))
+                                (< (cdr first) (cdr second))
+                             (string< (caar first) (caar second))))))
+              (setq by-type (seq-group-by #'cdar with-order))
+              (javaimp--insert-import-group
+               (cdr (assq 'normal by-type)) "import %s;\n")
+              (javaimp--insert-import-group
+               (cdr (assq 'static by-type)) "import static %s;\n"))
+            ;; Make sure there's only one blank line after
+            (forward-line -2)
+            (delete-blank-lines)
+            (end-of-line)
+            (insert ?\n)))))))
+
+(defun javaimp--position-for-insert-imports (old-region)
+  (if old-region
+      (progn
+        (delete-region (car old-region) (cdr old-region))
+        (goto-char (car old-region)))
+    (if (javaimp--parse-get-package)
+        (insert "\n\n")
+      ;; As a last resort, go to bob and skip comments
+      (goto-char (point-min))
+      (forward-comment (buffer-size))
+      (skip-chars-backward " \t\n")
+      (unless (bobp)
+        (insert "\n\n")))))
+
+(defun javaimp--insert-import-group (imports fmt)
+  (let (prev-order)
     (dolist (import imports)
-      ;; if adjacent imports have different order value, insert a newline
-      ;; between them
-      (let ((order (cdr import)))
-       (and last-order
-            (/= order last-order)
-            (insert ?\n))
-       (insert (format pattern (caar import)) ?\n)
-       (setq last-order order)))))
-
+      ;; If adjacent imports have different order value, insert a
+      ;; newline between them
+      (and prev-order
+          (/= (cdr import) prev-order)
+          (insert ?\n))
+      (insert (format fmt (caar import)))
+      (setq prev-order (cdr import)))
+    (when imports
+      (insert ?\n))))
 
 
 ;; Imenu support



reply via email to

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