I’ve been working on some utilities for coding in Java. JDE and CEDET ground my emacs to a halt the last time I tried them, so I wanted something lightweight. So far, I mostly have some functions for looking up documentation - including c++ documentation - that I store locally on my computer and keep in a repository, but I also have a few utilities for auto-importing Java classes.
The utilities to follow need these macros defined. I talked about them previously at:
http://nflath.com/2009/08/emacs-timing-and-upgrades/. They are utilities for generating functions that take arguments defaulting to word at point.
(defun my-fn (fn prompt) "When given a function taking one argument and applying a function to it, will use that function and default to the word at point, with a prompt including that word." (let ((default (current-word))) (let ((needle (read-string (concat prompt " <" default ">: ")))) (if (equal needle "") (funcall fn default) (funcall fn needle))))) (defmacro defun-my (name prompt &rest body) "Will define both a function and a my- version of the function, which defaults to the word at point." `(progn (defun ,name (arg) ,@body) (defun ,(intern (concat "my-" (symbol-name name))) () (interactive) (my-fn (quote ,name) ,prompt))))
These functions will be used in some of the later functions I wrote. These are used for caching large directory structures in a buffer to search for files instead of parsing the output of ‘find’ each time. Specifically, I use these to quickly look up which file I should be referencing to view documentation on Java and C++ classes. create-file-list will just create a list of files in the given buffer, and find-location-for-doc-from-buffer will return the full path of the matching html file you are searching for. Java-find-html-for-class is just a helper function that fills in the arguments for find-location-for-doc-from-buffer for Java buffers.
(defun create-file-list (directory buffer) "Creates the list of files in a directory" (save-window-excursion (let ((default-directory directory)) (shell-command "find . " buffer) (switch-to-buffer buffer) (flush-lines "\.svn") (flush-lines "class-use")))) (defun find-location-for-doc-from-buffer (arg buffer-name buffer-creation-fn begin) "Finds the file for a given documentation name in the buffer that may be created with buffer-creation" (save-excursion (save-window-excursion (let ((doc-buffer (or (get-buffer buffer-name) (funcall buffer-creation-fn)))) (switch-to-buffer doc-buffer) (goto-char (point-min)) (while (not (line-matches (concat "/" arg "\.html"))) (search-forward arg)) (concat begin (buffer-substring (1+ (line-beginning-position)) (line-end-position)))))))
These next functions are used to look up documentation. my-java-describe-class will open up documentation for the input class file, whereas java-describe-variable will take a variable name and look backwards to it’s declaration and find documentation for that class. c-search-docs does something similar; it will prompt you for a keyword and see if anything in my c++ documentation matches it.
(defun-my java-describe-class "Open Javadoc for Class" "Loads javadoc for specified class in your browser." (interactive "MClass Name: ") (browse-url (java-find-html-for-class arg))) (defun-my java-describe-variable "Open Javadoc for Variable" "Opens the javadoc for the variable at point, if possible." (interactive) (save-excursion (re-search-backward (concat "[ \t\n]" "[A-Za-z]+" "<[A-Za-z0-9<>]*>" "[ \t\n]" arg)) (forward-char) (java-describe-class (current-word)))) (defun-my c-search-docs "Documentation For" "Searches C++ Documentation for the requested term" (browse-url (find-location-for-doc-from-buffer arg "*C Documentation*" (lambda () (create-file-list "~/.emacs.d/documentation/c++/" "*C Documentation*")) "~/.emacs.d/documentation/c++/")))
Another task that I frequently have to do is fix imports in Java classes. Doing this manually is a huge pain, so I wrote a few functions to help. my-java-import-class will prompt for a class, look up it’s full package name, and add the import to the top of your file. Java-get-undefined-classes will run compile-command and parse the output to add all unimported classes. This needs java-undefined-symbol-regexp to be defined correctly, as well as compile-command to be set to something like ‘javac filename’.
(defun-my java-import-class "Import Class" "Adds an import statement for the class at point." (save-excursion (let ((my-retn-value nil)) (let ((my-string (java-find-html-for-class arg))) (find-file my-string) (end-of-buffer) (re-search-backward "\"\\([A-Za-z0-9]+\\.\\)+[A-Za-z0-9]+ [ci][ln][at][se][sr]" ) (let ((start (point))) (re-search-forward " " ) (setq my-retn-value (substring (buffer-string) start (- (point) 2))))) (kill-buffer (current-buffer)) (beginning-of-buffer) (re-search-forward "import " (point-max) t) (beginning-of-line) (when (looking-at "import") (end-of-line) (newline)) (insert "import " my-retn-value ";\n" )))) (defvar java-undefined-symbol-regexp "symbol : class \\([A-Za-z0-9]*\\)") (defun java-get-undefined-class-names () (interactive) (save-window-excursion (remove-if #'not (remove-duplicates (mapcar (lambda (x) (if (string-match java-undefined-symbol-regexp x) (match-string 1 x))) (split-string (shell-command-to-string compile-command) "\n *^\n")) :test #'string-equal)))) (defun java-import-undefined-classes () (interactive) (save-window-excursion (mapc #'java-import-class (java-get-undefined-class-names))))