[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] javaimp_devel 2f8450a 1/2: in progress
From: |
Filipp Gunbin |
Subject: |
[elpa] javaimp_devel 2f8450a 1/2: in progress |
Date: |
Fri, 08 Apr 2016 19:23:01 +0000 |
branch: javaimp_devel
commit 2f8450a2b4cdf0297b11a929aeeb90753506d7d9
Author: Filipp Gunbin <address@hidden>
Commit: Filipp Gunbin <address@hidden>
in progress
---
packages/javaimp/javaimp.el | 325 ++++++++++++++++++++-----------------------
1 files changed, 152 insertions(+), 173 deletions(-)
diff --git a/packages/javaimp/javaimp.el b/packages/javaimp/javaimp.el
index 6a50e76..3bf11e3 100644
--- a/packages/javaimp/javaimp.el
+++ b/packages/javaimp/javaimp.el
@@ -11,26 +11,45 @@
;; Allows to manage Java import statements in Maven projects.
;;
-;; Quick start: customize `javaimp-import-group-alist', `javaimp-jdk-home'
-;; and call `javaimp-maven-visit-project', then in a Java buffer visiting a
-;; file under that module or one of its submodules call
-;; `javaimp-organize-imports' or `javaimp-add-import'. `javaimp-add-import'
-;; will provide you a helpful completion, and the default value (the one
-;; you'll get if you hit `M-n' in the minibuffer) is the symbol under point,
-;; so usually it's enough to hit `M-n', then add some starting letters of a
-;; package and hit `TAB'. The module does not add all needed imports
-;; automatically! It only helps you to quickly add imports when stepping
-;; through compilation errors.
+;; Quick start:
+;;
+;; - customize `javaimp-import-group-alist'
+;;
+;; - call `javaimp-maven-visit-project', giving it the top-level project
+;; directory where pom.xml resides
+;;
+;; Then in a Java buffer visiting a file under that project or one of its
+;; submodules call `javaimp-organize-imports' or `javaimp-add-import'.
+;;
+;;
+;; Details:
+;;
+;; `javaimp-add-import' will provide you a helpful completion with the list
+;; of classes taken from this module's dependencies. Completion default
+;; value (the one you'll get if you hit `M-n' in the minibuffer) is the
+;; symbol under point, so usually it's enough to hit `M-n', then add some
+;; starting letters of a package and hit `TAB'.
+;;
+;; This module does not add all needed imports automatically! It only helps
+;; you to quickly add imports when stepping through compilation errors.
;;
;; If Maven failed, you can see its output in the buffer named by
;; `javaimp-debug-buf-name' (default is "*javaimp-debug*").
;;
;; Contents of jar files and Maven project structures (pom.xml) are cached,
;; so usually only first command should take a considerable amount of time
-;; to complete. When it is detected that a particular jar or pom.xml file's
-;; timestamp changed, it is re-read and cache is updated.
+;; to complete. If a modules's pom.xml or any of its parents pom.xml was
+;; changed (modification timestamp is checked), `mvn
+;; dependency:build-classpath' is re-run on the current module. If a jar
+;; file was changed, its contents is reloaded.
+;;
+;; If you make some changes which change project hierarchy, you should
+;; re-visit the parent again with `javaimp-maven-visit-project'.
+;;
+;; May work unreliable with inner classes, but you can always import
+;; top-level class and use qualified name.
;;
-;; Details on variables.
+;; User options:
;;
;; `javaimp-import-group-alist' defines the order of import statement
;; groups. By default java.* and javax.* imports are assigned an order of
@@ -38,33 +57,40 @@
;; project's imports typically should come after, so the sample config below
;; sets 80 for them.
;;
-;; `javaimp-jdk-home' is a path for JDK. It is used to scan JDK jars.
-;; Usually you will need to set this.
+;; `javaimp-jdk-home' is a path for JDK. It is used to scan JDK jars. By
+;; default, it is set from the JAVA_HOME environment variable.
;;
;; `javaimp-additional-source-dirs' is a list specifying directories where
;; additional (e.g. generated) source files reside. Each directory is a
;; relative path from ${project.build.directory} project property value.
;;
-;; `javaimp-mvn-program' defines path of the `mvn' program. Use if it's
-;; not on `exec-path'.
+;; `javaimp-mvn-program' defines path to the `mvn' program. Customize it
+;; if `mvn' is not on `exec-path'.
;;
-;; `javaimp-cygpath-program' defines path of the `cygpath' program (applies
-;; to Cygwin only, of course). Use if it's not on `exec-path'.
+;; `javaimp-cygpath-program' defines path to the `cygpath' program (Cygwin
+;; only). Customize it if `cygpath' is not on `exec-path'
;;
-;; `javaimp-jar-program' defines path of the `jar' program. Use if it's
-;; not on `exec-path'.
+;; `javaimp-jar-program' defines path to the `jar' program. Customize it
+;; if `jar' is not on `exec-path'.
;;
-;; Details on commands.
+;; Commands:
;;
-;; `javaimp-maven-visit-project' is the first command you should issue to
-;; use this module. It reads the pom structure recursively and records
-;; which files belong to which module. Maven help:effective-pom command is
-;; used to do that.
+;; `javaimp-maven-visit-project' asks for the project directory, calls
+;; `mvn help:effective-pom' on the pom.xml in that directory, reads project
+;; structure from the output and records which files belong to which
+;; (sub)modules and other information about the modules. Then the project
+;; becomes known to javaimp and `javaimp-add-import' may be called inside
+;; project's files.
;;
-;; `javaimp-organize-imports' groups import statement and writes those
-;; group according to the value of `javaimp-import-group-alist'. Imports
+;; `javaimp-add-import' asks for a class to import, adds import statement
+;; for the selected class and calls `javaimp-organize-imports'. It provides
+;; completion alternatives made from all this module dependencies' classes.
+;;
+;; `javaimp-organize-imports' rearranges import statements so that they
+;; are listed in groups according to the value of
+;; `javaimp-import-group-alist', with blank lines between groups. Imports
;; which are not matched by any regexp in that variable are assigned a
-;; default order defined by `javaimp-import-default-order' (50 by default).
+;; default order defined by `javaimp-import-default-order'.
;;
;; Sample .emacs initialization:
;;
@@ -73,8 +99,6 @@
;; (add-to-list 'javaimp-import-group-alist
;; '("\\`\\(my\\.company\\.\\|my\\.company2\\.\\)" . 80))
;;
-;; (setq javaimp-jdk-home (getenv "JAVA_HOME"))
-;; (setq javaimp-include-current-module-classes t)
;; (setq javaimp-additional-source-dirs '("generated-sources/thrift"))
;;
;; (add-hook 'java-mode-hook
@@ -118,7 +142,7 @@
(require 'seq)
-;;; User options
+;; User options
(defgroup javaimp ()
"Add and reorder Java import statements in Maven projects")
@@ -138,11 +162,9 @@ The order of classes which were not matched is defined by
"Defines the order of classes which were not matched by
`javaimp-import-group-alist'")
-(defcustom javaimp-jdk-home nil
- "Path to the JDK. If you have JAVA_HOME environment variable
-set up, this variable can be set like this:
-
-(setq javaimp-jdk-home (getenv \"JAVA_HOME\"))")
+(defcustom javaimp-jdk-home (getenv "JAVA_HOME")
+ "Path to the JDK. By default, it is set from the JAVA_HOME
+environment variable."
(defcustom javaimp-additional-source-dirs nil
"List of directories where additional (e.g. generated)
@@ -172,13 +194,13 @@ supported yet.")
"Path to the `jar' program")
(defcustom javaimp-include-current-module-classes t
- "If non-nil, current project's classes are included into completion
-alternatives.
-
-Only top-level classes are included.")
+ "If non-nil, current module's classes are included into
+completion alternatives. `javaimp-add-import' will find all java
+files in the current project and add their fully-qualified names
+to the completion alternatives list.")
-;;; Variables and constants
+;; Variables and constants
(defvar javaimp-project-forest nil
"Visited projects")
@@ -188,8 +210,31 @@ Only top-level classes are included.")
(defconst javaimp-debug-buf-name "*javaimp-debug*")
+;; Structs
+
+(cl-defstruct javaimp-node
+ parent children contents)
+
+(cl-defstruct javaimp-module
+ id parent-id file file-ts final-name packaging
+ source-dir test-source-dir build-dir
+ modules
+ dep-jars)
+
+(defun javaimp-print-id (id)
+ (format "%s:%s:%s"
+ (javaimp-id-artifact id)
+ (javaimp-id-group id)
+ (javaimp-id-version id)))
+
+(cl-defstruct (javaimp-id
+ (:print-function #'javaimp-print-id))
+ group artifact version)
+
+(cl-defstruct javaimp-jar
+ file file-ts classes)
+
-;;; XML routines
(defun javaimp-xml-child-list (xml-tree child-name)
"Returns list of children of XML-TREE filtered by CHILD-NAME"
@@ -207,8 +252,6 @@ Only top-level classes are included.")
"Returns a first child of EL"
(car (cddr el)))
-
-;; Cygwin
(defun javaimp-cygpath-convert-maybe (path)
(if (eq system-type 'cygwin)
@@ -216,31 +259,7 @@ Only top-level classes are included.")
path))
-;; Structs
-
-(cl-defstruct javaimp-node
- parent children contents)
-
-(cl-defstruct javaimp-module
- id parent-id file file-ts final-name packaging
- source-dir test-source-dir build-dir
- dep-jars)
-
-(defun javaimp-print-id (id)
- (format "%s:%s:%s"
- (javaimp-id-artifact id)
- (javaimp-id-group id)
- (javaimp-id-version id)))
-
-(cl-defstruct (javaimp-id
- (:print-function #'javaimp-print-id))
- group artifact version)
-
-(cl-defstruct javaimp-jar
- file file-ts classes)
-
-
-;;; Maven
+;; Project loading
;; TODO what if it's already there?
@@ -253,39 +272,45 @@ directory containing pom.xml."
(concat (file-name-as-directory path) "pom.xml"))))
(unless (file-readable-p file)
(error "Cannot read file: %s" file))
- (let ((tree (javaimp--maven-load-tree file)))
+ (let ((tree (javaimp--maven-xml-load-tree file)))
(if tree
(push tree javaimp-project-forest)))
(message "Loaded tree for %s" file)))
-(defun javaimp--maven-load-tree (file)
+;; TODO file should start to sink down from there; at each step append
directory
+;; from <module> to it
+
+(defun javaimp--maven-xml-load-tree (file)
"Invokes `mvn help:effective-pom' on FILE and using its output
creates a tree of Maven projects starting from FILE. Children
which link to the parent via the <parent> element are inheriting
children and are also included. Subordinate modules with no
inheritance are not included."
- (let ((xml-tree (javaimp--maven-read-effective-pom file)))
+ (let ((xml-tree (javaimp--maven-xml-read-effective-pom file)))
(cond ((assq 'project xml-tree)
;; no real children
- (let ((project-elt (assq 'project xml-tree)))
- (message "Independent submodules: %s"
- (mapconcat #'javaimp-xml-first-child
- (javaimp--maven-get-module-elts project-elt)
- ", "))
- (let ((module (javaimp--maven-parse-module project-elt)))
- (javaimp--build-tree (javaimp-module-id module) nil (list
module)))))
+ (let ((project-elt (assq 'project xml-tree))
+ (submodules (javaimp-xml-child-list
+ (javaimp-xml-child 'modules project-elt) 'module)))
+ (and submodules
+ (message "Independent submodules: %s"
+ (mapconcat #'javaimp-xml-first-child submodules ",
")))
+ (let ((module (javaimp--maven-xml-parse-module project-elt)))
+ (javaimp--maven-build-tree
+ (javaimp-module-id module) nil (list module) file))))
((assq 'projects xml-tree)
;; we have are inheriting children - they and their children, if
;; any, are listed in a linear list
(let* ((project-elts (javaimp-xml-child-list
(assq 'projects xml-tree) 'project))
- (all-modules (mapcar #'javaimp--maven-parse-module
project-elts)))
- (javaimp--build-tree (javaimp-module-id (car all-modules)) nil
all-modules)))
+ (all-modules (mapcar #'javaimp--maven-xml-parse-module
project-elts)))
+ (javaimp--maven-build-tree
+ (javaimp-module-id (car all-modules)) nil all-modules file)))
(t
;; neither <project> nor <projects> - error
(error "Invalid `help:effective-pom' output")))))
-(defun javaimp--maven-read-effective-pom (pom)
+(defun javaimp--maven-xml-read-effective-pom (pom)
"Calls `mvn help:effective:pom and returns XML parse tree"
(message "Loading root pom %s..." pom)
(javaimp--maven-call
@@ -314,7 +339,7 @@ the temporary buffer and returns its result"
(with-temp-buffer
(let* ((pom-file (javaimp-cygpath-convert-maybe pom-file))
(status
- ;; FIXME check Maven output on Gnu/Linux
+ ;; TODO check
in Maven output on Gnu/Linux
(let ((coding-system-for-read
(if (eq system-type 'cygwin) 'utf-8-dos)))
(process-file javaimp-mvn-program nil t nil "-f" pom-file
target)))
@@ -327,12 +352,10 @@ the temporary buffer and returns its result"
(goto-char (point-min))
(funcall handler))))
-(defun javaimp--maven-get-module-elts (project-elt)
- (javaimp-xml-child-list
- (javaimp-xml-child 'modules project-elt) 'module))
-(defun javaimp--maven-parse-module (elt)
- ;; TODO file - instead of javaimp-fill-pom-file-paths
+(defun javaimp--maven-xml-parse-module (elt)
+ ;; we set `file' slot later because raw <project> element does not contain
+ ;; pom file path, so we need to construct it during tree construction
;;
;; TODO javaimp-maven-process-projects
;;
@@ -366,32 +389,62 @@ the temporary buffer and returns its result"
projects-elts))
(defun javaimp--maven-extract-id (elt)
- (javaimp-make-artifact
+ (make-javaimp-id
(javaimp-xml-first-child (javaimp-xml-child 'groupId elt))
(javaimp-xml-first-child (javaimp-xml-child 'artifactId elt))
(javaimp-xml-first-child (javaimp-xml-child 'version elt))))
-;; Module tree
-
-(defun javaimp--build-tree (id parent-node all-modules)
+(defun javaimp--maven-build-tree (id parent-node all-modules file)
(let ((this (or (javaimp--find-by-id id all-modules)
(error "Cannot find module %s!" id)))
- ;; although each real parent has <modules> section, better way to
- ;; build hirarchy is to analyze <parent> node in each child
+ ;; although each real parent has <modules> section, more reliable
+ ;; way to build hirarchy is to analyze <parent> node in each child
(children (javaimp--find-by-parent-id id all-modules)))
(if (and (null children)
- (string= (javaimp-module-packaging module) "pom")) ;TODO remove
mvn specifics
+ (string= (javaimp-module-packaging module) "pom"))
;; this module is a pure aggregate module - it has neither real
;; children nor source files, so skip it
nil
- ;; otherwise, make node
+ ;; here we can finally set the `file' slot as the path is known at
+ ;; this time
+ (setf (javaimp-module-file this) file)
+ ;; make node
(let ((this-node (make-javaimp-node parent-node nil this)))
(setf (javaimp-node-children this-node)
(mapcar (lambda (child)
- (javaimp--build-tree (javaimp-module-id child)
this-node all-modules))
+ (let ((child-file
+ (javaimp--maven-get-submodule-file
+ child file (javaimp-module-modules this))))
+ (javaimp--maven-build-tree
+ (javaimp-module-id child) this-node all-modules
child-file)))
children))
this-node))))
-
+
+(defun javaimp--maven-get-submodule-file (module parent-file parent-rel-paths)
+ ;; seems that the only reliable way to match a module parsed from
+ ;; <project> element with module relative path taken from <modules> is to
+ ;; visit pom and check that id and parent-id matche
+ (let* ((parent-dir (file-name-directory parent-file))
+ (files (mapcar (lambda (rel-path)
+ (concat parent-dir
+ (file-name-as-directory rel-path)
+ "pom.xml"))
+ parent-rel-paths)))
+ (or (seq-find
+ (lambda (file)
+ (let* ((xml-tree (with-temp-buffer
+ (insert-file-contents file)
+ (xml-parse-region (point-min) (point-max))))
+ (project-elt (assq 'project xml-tree))
+ (id (javaimp--maven-extract-id project-elt))
+ (parent-id (javaimp--maven-extract-id (assq 'parent
project-elt))))
+ ;; TODO we need lax matching because some id
+ ;; components may be missing
+ (and (equal (javaimp-module-id module) id)
+ (equal (javaimp-module-parent-id module) parent-id)))))
+ files)
+ (error "Cannot find file for module: %s" (javaimp-module-id module))))
+
(defun javaimp--find-by-id (id modules)
;; TODO seq-find
)
@@ -401,80 +454,6 @@ the temporary buffer and returns its result"
)
-;; pom file parsing - this will not be needed?
-
-(defun javaimp-fill-pom-file-paths (modules pom)
- "Subroutine of `javaimp-maven-load-module-tree'"
- (let ((artifact-alist (javaimp-traverse-pom-tree (list pom))))
- (dolist (module modules)
- (let* ((artifact (javaimp-mod-artifact module))
- (path
- (cdr (or (seq-find (lambda (el)
- (equal artifact (car el)))
- artifact-alist)
- ;; Compare just id if comparison by all fields failed
- (seq-find (lambda (el)
- (equal (javaimp-artifact-artifact-id
artifact)
- (javaimp-artifact-artifact-id (car
el))))
- artifact-alist)
- (error "Cannot find path for artifact: %s" artifact)))))
- (javaimp-set-mod-pom-file module path)))))
-
-
-(defun javaimp-parse-pom-file (pom-file)
- "Subroutine of `javaimp-traverse-pom-tree'. Parses POM-FILE.
-Car of result is artifact info. Cdr of result is submodules
-relative path list."
- (message "Parsing pom file mapping for %s" pom-file)
- (let* ((xml-tree (with-temp-buffer
- (insert-file-contents pom-file)
- (xml-parse-region (point-min) (point-max))))
- (project-elt (cond ((assq 'top xml-tree)
- (javaimp-xml-child 'project (assq 'top xml-tree)))
- ((assq 'project xml-tree)
- (assq 'project xml-tree))
- (t
- (error "Cannot find <project> element in pom %s!"
pom-file)))))
- (cons (javaimp--maven-extract-id project-elt)
- (mapcar #'javaimp-xml-first-child
- (javaimp--maven-get-module-elts project-elt)))))
-
-(defun javaimp-traverse-pom-tree (pom-file-list)
- "Traverses pom tree and returns alist where each element is of
-the form (\"ARTIFACT\" . \"POM-FILE-PATH\"). Result paths are in
-platform default format."
- (if pom-file-list
- (append
- ;; this item
- (let* ((this-pom (car pom-file-list))
- (pom-file-data (javaimp-parse-pom-file this-pom)))
- (append
- ;; this pom itself
- (list (cons (car pom-file-data) this-pom))
- ;; children of this pom
- (javaimp-traverse-pom-tree
- (javaimp-create-absolute-submodules-paths
- this-pom (cdr pom-file-data)))))
- ;; rest items
- (javaimp-traverse-pom-tree (cdr pom-file-list)))))
-
-(defun javaimp-create-absolute-submodules-paths (base-pom paths)
- "Subroutine of `javaimp-traverse-pom-tree'"
- (mapcar
- (lambda (rel-path)
- (expand-file-name
- (concat
- ;; base path
- (file-name-directory base-pom)
- ;; submodule relative path
- (file-name-as-directory
- (javaimp-cygpath-convert-maybe rel-path))
- ;; well-known pom name
- "pom.xml")))
- paths))
-
-
-
;;; Working with module dependency JARs
(defun javaimp-maven-fetch-module-deps (module)