├── .gitignore ├── Makefile ├── Cask ├── .travis.yml ├── test └── java-imports-test.el ├── README.org └── java-imports.el /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled and temporary files 2 | *.elc 3 | *~ 4 | 5 | # Cask 6 | /.cask 7 | dist 8 | 9 | # Ecukes 10 | /features/project/.cask 11 | /features/project/test/*.el 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CASK ?= cask 2 | EMACS ?= emacs 3 | 4 | all: test 5 | 6 | test: unit 7 | 8 | unit: 9 | ${CASK} exec ert-runner 10 | 11 | ecukes: 12 | ${CASK} exec ecukes 13 | 14 | install: 15 | ${CASK} install 16 | -------------------------------------------------------------------------------- /Cask: -------------------------------------------------------------------------------- 1 | (source gnu) 2 | (source melpa) 3 | 4 | (package-file "java-imports.el") 5 | 6 | (depends-on "s") 7 | (depends-on "pcache") 8 | 9 | (development 10 | (depends-on "cask-package-toolset") 11 | (depends-on "f") 12 | (depends-on "ecukes") 13 | (depends-on "ert-runner") 14 | (depends-on "s") 15 | (depends-on "pcache") 16 | (depends-on "el-mock")) 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: generic 2 | sudo: false 3 | before_install: 4 | - curl -fsSkL https://gist.github.com/rejeep/ebcd57c3af83b049833b/raw > x.sh && source ./x.sh 5 | - evm install $EVM_EMACS --use --skip 6 | - cask 7 | env: 8 | - EVM_EMACS=emacs-24.4-travis 9 | - EVM_EMACS=emacs-24.5-travis 10 | script: 11 | - emacs --version 12 | - make test 13 | 14 | notifications: 15 | email: false 16 | -------------------------------------------------------------------------------- /test/java-imports-test.el: -------------------------------------------------------------------------------- 1 | ;;; java-imports-test.el --- tests for java imports 2 | 3 | ;; Copyright (C) 2015 Matthew Lee Hinman 4 | 5 | ;;; Code: 6 | 7 | (require 'ert) 8 | (load-file "java-imports.el") 9 | 10 | 11 | (ert-deftest t-import-for-line () 12 | (with-temp-buffer 13 | (insert "import java.util.List;") 14 | (should (equal (java-imports-import-for-line) 15 | "java.util.List"))) 16 | (with-temp-buffer 17 | (insert " import org.writequit.Thingy; ") 18 | (should (equal (java-imports-import-for-line) 19 | "org.writequit.Thingy")))) 20 | 21 | 22 | (ert-deftest t-go-to-imports-start () 23 | ;; both package and imports present? Goto to the first import line beginning 24 | (with-temp-buffer 25 | (insert "package mypackage;\n") 26 | (insert "\n") 27 | (insert "import java.util.List;\n") 28 | (insert "import java.util.ArrayList;\n") 29 | (insert "\n\n") 30 | (java-imports-go-to-imports-start) 31 | (should (equal (line-number-at-pos) 3))) 32 | 33 | ;; no package and imports present? First import line 34 | (with-temp-buffer 35 | (insert "\n") 36 | (insert "\n") 37 | (insert "\n") 38 | (insert "import java.util.List;\n") 39 | (insert "import java.util.ArrayList;\n") 40 | (insert "\n\n") 41 | (java-imports-go-to-imports-start) 42 | (should (equal (line-number-at-pos) 4))) 43 | 44 | ;; package present, no imports? Add a correct import place, keeping the empty 45 | ;; lines 46 | (with-temp-buffer 47 | (insert "\n") 48 | (insert "package mypackage;\n") 49 | (insert "\n") 50 | (insert "\n") 51 | (insert "class A {}\n") 52 | (java-imports-go-to-imports-start) 53 | (should (equal (line-number-at-pos) 4)) 54 | (should (equal (count-lines (point-min) (point-max)) 7))) 55 | 56 | ;; no package, no imports? Stay in the beginning, add lines required 57 | (with-temp-buffer 58 | (insert "\n") 59 | (insert "\n") 60 | (insert "\n") 61 | (insert "class A {}\n") 62 | (java-imports-go-to-imports-start) 63 | (should (equal (line-number-at-pos) 1)) 64 | (should (equal (count-lines (point-min) (point-max)) 5)))) 65 | 66 | (ert-deftest t-add-imports () 67 | (with-temp-buffer 68 | (setq-local java-imports-find-block-function 69 | #'java-imports-find-place-after-last-import) 70 | (insert "package mypackage;\n\n") 71 | (insert "import java.util.List;\n\n\n") 72 | (java-imports-add-import-with-package "ArrayList" "java.util") 73 | (should 74 | (equal 75 | (buffer-string) 76 | (concat 77 | "package mypackage;\n\n" 78 | "import java.util.List;\n" 79 | "import java.util.ArrayList;\n\n\n")))) 80 | 81 | ;; Test for annotation importing 82 | (with-temp-buffer 83 | (insert "package mypackage;\n\n") 84 | (insert "import java.util.List;\n\n\n") 85 | (java-imports-add-import-with-package "@MyAnnotation" "org.foo") 86 | (should 87 | (equal 88 | (buffer-string) 89 | (concat 90 | "package mypackage;\n\n" 91 | "import java.util.List;\n" 92 | "import org.foo.MyAnnotation;\n\n\n")))) 93 | 94 | (with-temp-buffer 95 | (setq-local java-imports-find-block-function 96 | #'java-imports-find-place-sorted-block) 97 | (insert "package mypackage;\n\n") 98 | (insert "import java.util.List;\n\n\n") 99 | (java-imports-add-import-with-package "ArrayList" "java.util") 100 | (should 101 | (equal 102 | (buffer-string) 103 | (concat 104 | "package mypackage;\n\n" 105 | "import java.util.ArrayList;\n" 106 | "import java.util.List;\n\n\n"))))) 107 | 108 | (ert-deftest t-list-imports () 109 | (with-temp-buffer 110 | (insert "package mypackage;\n") 111 | (insert "\n") 112 | (insert "import org.Thing;\n") 113 | (insert "\n") 114 | (insert "import java.util.List;\n") 115 | (insert "import java.util.ArrayList;\n") 116 | (insert "\n") 117 | (insert "public class Foo {}") 118 | (should 119 | (equal 120 | (java-imports-list-imports) 121 | '("org.Thing" "java.util.List" "java.util.ArrayList"))))) 122 | 123 | (ert-deftest t-pkg-and-class-from-import () 124 | (should 125 | (equal (java-imports-get-package-and-class "java.util.Map") 126 | '("java.util" "Map"))) 127 | (should 128 | (equal (java-imports-get-package-and-class "org.foo.bar.baz.ThingOne") 129 | '("org.foo.bar.baz" "ThingOne")))) 130 | 131 | ;; End: 132 | ;;; java-imports-test.el ends here 133 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Emacs Java Imports 2 | #+AUTHOR: Lee Hinman 3 | #+EMAIL: leehinman@fastmail.com 4 | #+LANGUAGE: en 5 | #+PROPERTY: header-args :results code replace :exports both :noweb yes :tangle no 6 | #+HTML_HEAD: 7 | #+EXPORT_SELECT_TAGS: export 8 | #+EXPORT_EXCLUDE_TAGS: noexport 9 | #+OPTIONS: H:4 num:nil toc:t \n:nil @:t ::t |:t ^:{} -:t f:t *:t 10 | #+OPTIONS: skip:nil d:(HIDE) tags:not-in-toc 11 | #+STARTUP: fold nodlcheck lognotestate showall 12 | 13 | * Introduction 14 | 15 | [[https://travis-ci.org/dakrone/emacs-java-imports][file:https://travis-ci.org/dakrone/emacs-java-imports.svg]] 16 | [[http://melpa.org/#/java-imports][file:http://melpa.org/packages/java-imports-badge.svg]] 17 | 18 | I needed a way to import java classes easily in Emacs. I also didn't want a 19 | package that was tied to a particular tool like maven or gradle (especially a 20 | potentially long startup time tool). 21 | 22 | This adds the =import-java-class= function, which will prompt for the class at 23 | point, then the package, then add the required import statement for the class 24 | and cache a "class-name -> package" relationship for any future importing of the 25 | class. 26 | 27 | It does this because I didn't want to drop into Intellij to add all the import 28 | statements for my Java code, I just want to hit a key and have an import 29 | statement added for the class under the point. 30 | 31 | * Installation 32 | 33 | =java-imports= is available in the MELPA repository. 34 | 35 | Do this, if MELPA isn't already in your sources: 36 | 37 | #+BEGIN_SRC emacs-lisp 38 | (require 'package) 39 | (add-to-list 'package-archives 40 | '("MELPA" . "https://melpa.org/packages/" )) 41 | #+END_SRC 42 | 43 | Then run =M-x package-refresh-contents= to load the contents of the new 44 | repository, and =M-x package-install RET java-imports RET= to install 45 | =java-imports=. 46 | 47 | * Usage 48 | 49 | #+BEGIN_SRC emacs-lisp 50 | (require 'java-imports) 51 | 52 | ;; whatever you want to bind it to 53 | (define-key java-mode-map (kbd "M-I") 'java-imports-add-import-dwim) 54 | 55 | ;; See customization below for where to put java imports 56 | (setq java-imports-find-block-function 'java-imports-find-place-sorted-block) 57 | #+END_SRC 58 | 59 | I also recommend having java-imports automatically add any seen imports to the 60 | import cache by adding: 61 | 62 | #+BEGIN_SRC emacs-lisp 63 | (add-hook 'java-mode-hook 'java-imports-scan-file) 64 | #+END_SRC 65 | 66 | ** Functions 67 | 68 | Functions you may want to bind to a key in Java-mode: 69 | 70 | | Function | Use | 71 | |--------------------------------+--------------------------------------------------------| 72 | | =java-imports-add-import-dwim= | Add import for the symbol at point (or ask if none) | 73 | | =java-imports-add-import= | Add import for symbol at point, confirming class first | 74 | | =java-imports-scan-file= | Scan imports in the file, adding them to the cache | 75 | 76 | Other useful functions for writing your own tools: 77 | 78 | | Function | 79 | |----------------------------------------| 80 | | =java-imports-add-import-with-package= | 81 | | =java-imports-list-imports= | 82 | 83 | ** Customization 84 | 85 | *** Saving buffer automatically after adding an import 86 | 87 | =java-imports= will default to saving the buffer after adding an import, but you 88 | can customize =java-imports-save-buffer-after-import-added= to change this. 89 | 90 | *** Caching 91 | 92 | By default packages are cached the first time they're manually entered, if you 93 | want to overwrite what's in the cache you can invoke =java-imports-add-import= 94 | with the prefix key (=C-u=). 95 | 96 | To disable caching, set =java-imports-use-cache= to =nil=. 97 | 98 | *** Import style 99 | 100 | You can customize =java-imports-find-block-function=, either setting it to a 101 | custom function, or one of the included ones: 102 | 103 | - =java-imports-find-place-after-last-import= (default) 104 | 105 | Simply appends the import to the end of the list of imports 106 | 107 | - =java-imports-find-place-sorted-block= 108 | 109 | Places the import alphabetically sorted into the list of imports, so they will 110 | go into: 111 | 112 | #+BEGIN_SRC fundamental 113 | 114 | 115 | 116 | 117 | 118 | 119 | public class Whatever { 120 | ... 121 | } 122 | #+END_SRC 123 | 124 | For example: 125 | 126 | #+BEGIN_SRC java 127 | package org.writequit; 128 | 129 | import org.writequit.Strings; 130 | 131 | import java.util.ArrayList; 132 | import java.util.List; 133 | 134 | class Foo { 135 | public void main() { 136 | String[] s = Strings.EMPTY_ARRAY; 137 | List = new ArrayList<>(); 138 | } 139 | } 140 | #+END_SRC 141 | 142 | *** Cache name 143 | 144 | By default java-imports will use "=java-imports=" as the name of the cache of 145 | class->package names, however, if you want to have separate caches per project, 146 | you can customize =java-imports-cache-name= to have a separate String name 147 | (perhaps in a =.dir-locals.el= for per-project imports). 148 | 149 | * Things to do: 150 | 151 | - [X] Avoid importing packages that already have import statements 152 | - [X] Handle annotations correctly 153 | - [ ] Handle =*= imports 154 | - [ ] Inner classes? 155 | - [X] Scan java files for classes and add to the cache 156 | - [X] Add tests 157 | - [X] Hook up to travis-ci 158 | -------------------------------------------------------------------------------- /java-imports.el: -------------------------------------------------------------------------------- 1 | ;;; java-imports.el --- Code for dealing with Java imports 2 | 3 | ;; Copyright (C) 2015 Matthew Lee Hinman 4 | 5 | ;; Author: Lee Hinman 6 | ;; URL: http://www.github.com/dakrone/emacs-java-imports 7 | ;; Version: 0.1.1 8 | ;; Keywords: java kotlin 9 | ;; Package-Requires: ((emacs "24.4") (s "1.10.0") (pcache "0.5.1")) 10 | 11 | ;; This file is not part of GNU Emacs. 12 | 13 | ;;; Commentary: 14 | 15 | ;; Provides a way to easily add `import' statements for Java classes 16 | 17 | ;;; Usage: 18 | 19 | ;; (require 'java-imports) 20 | ;; (define-key java-mode-map (kbd "M-I") 'java-imports-add-import) 21 | 22 | ;;; License: 23 | 24 | ;; This program is free software; you can redistribute it and/or 25 | ;; modify it under the terms of the GNU General Public License 26 | ;; as published by the Free Software Foundation; either version 3 27 | ;; of the License, or (at your option) any later version. 28 | ;; 29 | ;; This program is distributed in the hope that it will be useful, 30 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 31 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 32 | ;; GNU General Public License for more details. 33 | ;; 34 | ;; You should have received a copy of the GNU General Public License 35 | ;; along with GNU Emacs; see the file COPYING. If not, write to the 36 | ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 37 | ;; Boston, MA 02110-1301, USA. 38 | 39 | ;;; Code: 40 | 41 | (require 'cl-lib) 42 | (require 'thingatpt) 43 | (require 'subr-x) 44 | (require 's) 45 | (require 'pcache) 46 | 47 | (defgroup java-imports nil 48 | "Customization for java imports package" 49 | :group 'languages) 50 | 51 | (defcustom java-imports-save-buffer-after-import-added t 52 | "`t' to save the current buffer after inserting an import statement." 53 | :group 'java-imports 54 | :type 'boolean) 55 | 56 | (defcustom java-imports-use-cache t 57 | "Whether packages for classes should be cached" 58 | :group 'java-imports 59 | :type 'boolean) 60 | 61 | (defcustom java-imports-find-block-function 'java-imports-find-place-after-last-import 62 | "A function that should find a proper insertion place within 63 | the block of import declarations." 64 | :group 'java-imports 65 | :type 'function) 66 | 67 | (defcustom java-imports-cache-name "java-imports" 68 | "Name of the cache to be used for the ClassName to Package 69 | mapping cache." 70 | :group 'java-imports 71 | :type 'string) 72 | 73 | (defcustom java-imports-default-packages 74 | '(("List" . "java.util") 75 | ("Collection" . "java.util") 76 | ("Set" . "java.util") 77 | ("Queue" . "java.util") 78 | ("Deque" . "java.util") 79 | ("HashSet" . "java.util") 80 | ("TreeSet" . "java.util") 81 | ("ArrayList" . "java.util") 82 | ("LinkedList" . "java.util") 83 | ("ArrayDeque" . "java.util") 84 | ("PriorityQueue" . "java.util") 85 | ("HashMap" . "java.util") 86 | ("TreeMap" . "java.util") 87 | ("Iterator" . "java.util")) 88 | "An alist mapping class names to probable packages of the 89 | classes." 90 | :group 'java-imports 91 | :type '(alist :key-type string :value-type string)) 92 | 93 | (defun java-imports-go-to-imports-start () 94 | "Go to the point where java import statements start or should 95 | start (if there are none)." 96 | (goto-char (point-min)) 97 | ;; package declaration is always in the beginning of a file, so no need to 98 | ;; reset the point after the first search 99 | (let ((package-decl-point (re-search-forward "package .*;" nil t)) 100 | (import-decl-point (re-search-forward "import .*;" nil t))) 101 | ;; 1. If there are imports in the file - go to the first one 102 | ;; 103 | ;; 2. No imports, and the package declaration is available - go to the end 104 | ;; of the declaration 105 | ;; 106 | ;; 3. Neither package nor import declarations are present - just go to the 107 | ;; first line 108 | (cond (import-decl-point (goto-char import-decl-point) 109 | (beginning-of-line)) 110 | (package-decl-point (goto-char package-decl-point) 111 | (forward-line) 112 | (open-line 2) 113 | (forward-line)) 114 | (t (goto-char (point-min)) 115 | (open-line 1))))) 116 | 117 | (defun java-imports-get-import (line) 118 | "Return the fully-qualified package for the given import line." 119 | (when line 120 | (cadr (s-match "import \\\(.*?\\\);?$" 121 | (string-trim line))))) 122 | 123 | (defun java-imports-get-package-and-class (import) 124 | "Explode the import and return (pkg . class) for the given import. 125 | 126 | Example 'java.util.Map' returns '(\"java.util\" \"Map\")." 127 | (when import 128 | (cl-subseq (s-match "\\\(.*\\\)\\\.\\\([A-Z].+?\\\)\\\(?:;\\\|$\\\)" import) 1))) 129 | 130 | (defun java-imports-import-for-line () 131 | "Returns the fully-qualified class name for the import line." 132 | (java-imports-get-import (thing-at-point 'line))) 133 | 134 | (defun java-imports-import-exists-p (full-name) 135 | "Checks if the import already exists" 136 | (save-excursion 137 | (goto-char (point-min)) 138 | (re-search-forward (concat "^[ \t]*import[ \t]+" full-name "[ \t]*;") nil t))) 139 | 140 | (defun java-imports-find-place-sorted-block (full-name class-name package) 141 | "Finds the insertion place within a sorted import block. 142 | 143 | Follows a convention where non-JRE imports are separated from JRE 144 | imports by a single line, and both blocks are always present." 145 | 146 | ;; Skip builtin imports if not a JRE import 147 | (and (not (s-starts-with? "java." (java-imports-import-for-line))) 148 | (when (s-starts-with? "java." full-name) 149 | (re-search-forward "^$" nil t) 150 | (forward-line 1))) 151 | 152 | ;; Search for a proper place within a block 153 | (while (and (java-imports-import-for-line) 154 | (string< (java-imports-import-for-line) full-name)) 155 | (forward-line 1)) 156 | (open-line 1)) 157 | 158 | (defun java-imports-find-place-after-last-import (full-name class-name package) 159 | "Finds the insertion place by moving past the last import declaration in the file." 160 | (while (re-search-forward "import[ \t]+.+[ \t]*;" nil t)) 161 | (beginning-of-line) 162 | (unless (equal (point-at-bol) (point-at-eol)) 163 | (forward-line) 164 | (open-line 1))) 165 | 166 | (defun java-imports-read-package (class-name cached-package) 167 | "Reads a package name for a class, offers default values for 168 | known classes" 169 | (or (car (s-match ".*\\\..*" class-name)) 170 | (and (not current-prefix-arg) 171 | cached-package) 172 | (let* ((default-package (cdr (assoc-string class-name java-imports-default-packages))) 173 | (default-prompt (if default-package 174 | (concat "[" default-package "]") "")) 175 | (prompt (concat "Package " default-prompt ": "))) 176 | (read-string prompt nil nil default-package)))) 177 | 178 | ;;;###autoload 179 | (defun java-imports-scan-file () 180 | "Scans a java-mode or kotlin-mode buffer, adding any import class -> package 181 | mappings to the import cache. If called with a prefix arguments 182 | overwrites any existing cache entries for the file." 183 | (interactive) 184 | (when (member major-mode '(java-mode kotlin-mode)) 185 | (let* ((cache (pcache-repository java-imports-cache-name))) 186 | (dolist (import (java-imports-list-imports)) 187 | (let ((pkg-class-list (java-imports-get-package-and-class import))) 188 | (when pkg-class-list 189 | (let* ((pkg (car pkg-class-list)) 190 | (class (intern (cadr pkg-class-list))) 191 | (exists-p (pcache-get cache class))) 192 | (when (or current-prefix-arg (not exists-p)) 193 | (message "Adding %s -> %s to the java imports cache" class pkg) 194 | (pcache-put cache class pkg)))))) 195 | (pcache-save cache)))) 196 | 197 | ;;;###autoload 198 | (defun java-imports-list-imports () 199 | "Return a list of all fully-qualified packages in the current 200 | Java-mode or Kotlin-mode buffer" 201 | (interactive) 202 | (cl-mapcar 203 | #'java-imports-get-import 204 | (cl-remove-if-not (lambda (str) (s-matches? "import[ \t]+.+?[ \t]*;?" str)) 205 | (s-lines (buffer-string))))) 206 | 207 | ;;;###autoload 208 | (defun java-imports-add-import-with-package (class-name package) 209 | "Add an import for the class for the name and package. Uses no caching." 210 | (interactive (list (read-string "Class name: " (thing-at-point 'symbol)) 211 | (read-string "Package name: " (thing-at-point 'symbol)))) 212 | (save-excursion 213 | (let ((class-name (s-chop-prefix "@" class-name)) 214 | (full-name (or (car (s-match ".*\\\..*" class-name)) 215 | (concat package "." class-name)))) 216 | (when (java-imports-import-exists-p full-name) 217 | (user-error "Import for '%s' already exists" full-name)) 218 | 219 | ;; Goto the start of the imports block 220 | (java-imports-go-to-imports-start) 221 | 222 | ;; Search for a proper insertion place within the block of imports 223 | (funcall java-imports-find-block-function full-name class-name package) 224 | 225 | ;; The insertion itself. Note that the only thing left to do here is to 226 | ;; insert the import. 227 | (insert "import " (concat package "." class-name) ";") 228 | full-name))) 229 | 230 | ;;;###autoload 231 | (defun java-imports-add-import (class-name) 232 | "Import the Java class for the symbol at point. Uses the symbol 233 | at the point for the class name, ask for a confirmation of the 234 | class name before adding it. 235 | 236 | Checks the import cache to see if a package entry exists for the 237 | given class. If found, adds an import statement for the class. If 238 | not found, prompts for the package and saves it to the cache. 239 | 240 | If called with a prefix argument, overwrites the package for an 241 | already-existing class name." 242 | (interactive (list (read-string "Class name: " (thing-at-point 'symbol)))) 243 | (save-excursion 244 | (let* ((class-name (s-chop-prefix "@" class-name)) 245 | (key (intern class-name)) 246 | (cache (pcache-repository java-imports-cache-name)) 247 | ;; Check if we have seen this class's package before 248 | (cached-package (and java-imports-use-cache 249 | (pcache-get cache key))) 250 | ;; If called with a prefix, overwrite the cached value always 251 | (add-to-cache? (or current-prefix-arg 252 | (eq nil cached-package))) 253 | (package (java-imports-read-package class-name cached-package)) 254 | (full-name (java-imports-add-import-with-package 255 | class-name package))) 256 | 257 | ;; Optionally save the buffer and cache the full package name 258 | (when java-imports-save-buffer-after-import-added 259 | (save-buffer)) 260 | (when add-to-cache? 261 | (message "Adding %s -> %s to java imports cache" 262 | class-name package) 263 | (pcache-put cache key package) 264 | (pcache-save cache)) 265 | full-name))) 266 | 267 | ;;;###autoload 268 | (defun java-imports-add-import-dwim () 269 | "Add an import statement for the class at point. If no class is 270 | found, prompt for the class name. If the class's package already 271 | exists in the cache, add it and return, otherwise prompt for the 272 | package and cache it for future statements." 273 | (interactive) 274 | (let ((class (or (thing-at-point 'symbol) 275 | (read-string "Class name: ")))) 276 | (java-imports-add-import (s-chop-prefix "@" class)))) 277 | 278 | ;;;###autoload 279 | (defun java-imports-scan-local-jars (&optional local-repo) 280 | "scan the local repository, find local jars and add them to the cache. 281 | if a class is found in multiple packages, any such package may be registered 282 | for that class in the cache. 283 | This is currently a synchronous and potentially slow operation, but 284 | hopefully faster than adding imports manually or using eclipse" 285 | (interactive) 286 | (cl-labels ((shell-command-to-lines 287 | (cmd) 288 | (s-split "\n" (shell-command-to-string cmd) t))) 289 | (let* ((local-repo (or local-repo (expand-file-name 290 | "~/.m2/repository/"))) 291 | (jars (shell-command-to-lines 292 | (format "find '%s' -name '*.jar'" 293 | local-repo))) 294 | (cache (pcache-repository java-imports-cache-name))) 295 | (dolist (jar jars) 296 | (dolist (line (shell-command-to-lines (concat "jar -tf " jar))) 297 | (when (string-match "\\(.*\\)/\\(.*\\)[.]class" line) 298 | (let ((class (intern (match-string 2 line))) 299 | (package-name 300 | (replace-regexp-in-string "/" "." (match-string 1 line)))) 301 | (if (pcache-get cache class) 302 | (message "skipping %s -> %s" class package-name) 303 | (progn (message "adding %s -> %s..." class package-name) 304 | (pcache-put cache class package-name)))))))))) 305 | 306 | (provide 'java-imports) 307 | 308 | ;;; java-imports.el ends here 309 | --------------------------------------------------------------------------------