I am writing Java code with Emacs from time to time and normally miss a lot of the „sugar“ that „the other IDE I am using“ provides me with.
From time to time I also stretch my fingers and try to improve my Emacs experience with some tooling. This resulted recently in a short coding session the outcome of which was a Maven Plugin (For those used to maven language: It’s only a single mojo), that generated a .dirlocals.el file for emacs that contains mavens understanding of the compile time classpath. I am actually a bit suspicious about the result of this since it looks much too easy.
The emacs maven plugin
„The“ emacs maven plugin is hosted on github here It collects jar files from mavens compile time classpath and writes them into a .dir-locals.el file.
How to use it
What do do with this information is yet undetermined. Those who are used to Java programming know that the classpath is one of the more important parameters when running/compiling Java programs. It’s also an essential piece of information when writing Java code: What is not in the classpath should not be in the source code 🙂
I have taken the classpath and extracted the names of the classes contained within the jar files and then build a function that inserts for me the class name:
(defun java-read-classes-from-classpath () "Iterate over classpath and gather classes from jar files. Evaluates into one large list containing all classes." (let* ((jarfiles nil) (jarfile nil) (result '())) (progn (dolist (file (directory-files (concat jdk-location "jre/lib/") t "\.\*.jar\$")) (setq jarfiles (cons file jarfiles))) (dolist (file (reverse java-classpath)) (setq jarfiles (cons file jarfiles)))) (with-temp-buffer (while jarfiles (progn (setq jarfile (car jarfiles) jarfiles (cdr jarfiles)) (call-process "/usr/bin/unzip" nil t nil "-l" (expand-file-name jarfile)) (goto-char (point-min)) (let ((end 0) (classname "")) (while (search-forward ".class" nil t nil) (end-of-line) (setq end (point)) (beginning-of-line) (goto-char (+ (point) 30)) (setq classname (substring (replace-regexp-in-string "/" "." (buffer-substring-no-properties (point) end)) 0 -6)) (setq result (cons classname result)) (forward-line 1) (beginning-of-line)) (erase-buffer))))) result))
And the source for auto-complete:
(defun java-insert-classname-completing-read (prefix) "Query the user for a class name. With prefix argument insert classname with package name. Otherwise omit package name." (interactive "P") (let* ((default (thing-at-point 'symbol)) (classname (completing-read "Class: " java-classes-cache))) (if prefix (insert classname) (insert (replace-regexp-in-string ".*\\." "" classname))))) (defun java-mode-process-dir-locals () (when (derived-mode-p 'java-mode (progn (when (stringp java-project-root) ;; sell the stock from emacs-maven-plugin: (progn (setq-local java-classes-cache (java-read-classes-from-classpath))) (local-set-key (kbd "C-x c") 'java-insert-classname-completing-read))))))
Now I can insert classnames (with or without package) enhanced with completing read (I do with ivy)).