Tweaking Flymake for Java

There is one thing that most of us like about those fancy java IDE, the way the mark syntax errors on the fly. Also if you tried something like Intellij IDEA there are a lot more options regarding coding style, java doc checks, etc … that can be very useful.

If you have been coding C or Java in Emacs you probably already know about flymake which basically sends your buffer through a compiler and highlight the warning and errors.

The JDE documentation describes how to use the eclipse compiler (ECJ) to do just that with java files, but here is a few tricks to make it even more useful.

Tweaking ECJ options

There are some tweak you can do the ECJ configuration which can help you with two issues:

First, the default configuration would compile your buffer in a temporary directory and then forget about it. This is fine when working on just one file, but if your working on a project then you would be interested on how the changes you are making might effect other files. To do that you would have to give ECJ an output directory for the compiled classes which would be on top of you classpath.

Second, you can customize the warnings from ECJ with some flags.

To find what options are available, run ECJ from the command line:

java -cp /path/to/ecj.jar org.eclipse.jdt.internal.compiler.batch.Main -?
java -cp /path/to/ecj.jar org.eclipse.jdt.internal.compiler.batch.Main -?:warn

In Emacs, the options passed to ECJ can be customized using the jde-ecj-command-line-args variable.

For example i use something like the following in my project prj.el file:

'(jde-global-classpath (quote ("/my/project/flymake-build" "rest of the class path ...")))
'(jde-ecj-command-line-args '("-d" "/my/project/flymake-build" "-1.5" "-referenceInfo" "-enableJavadoc" "-warn:+over-ann,uselessTypeCheck,javadoc"))

Integrating flymake with other tools

Now you would say that IDEs don’t just perform syntax check (although here ECJ have some style checks as well), so what if you wanted to use an additonal tool for code style analysis?

Flymake itself is very simple, all it does is call a program and parse its output, so it doesn’t matter if that program is a compiler, a style checker, a spell checker, or a script that would launch all of the above …

For example, there is a small bash script that call both ECJ and CheckStyle (named java-check.sh)

#!/bin/bash
# Call ECJ compiler and CheckStyle
if [[ ! "$6" ]]; then
    echo "Missing some arguments."
    exit 1
fi
CLASSPATH="$1" #project classpath used for compilation, taken from jde classpath setting
ECJ_JAR="$2"
ECJ_OPTS="$3" #the customization we talked about earlier
CHECKSTYLE_JAR="$4"
CHECKSTYLE_CONFIG="$5" #this would at least contains -c /path/to/checks.xml with configures the checks to perform
TARGET="$6"
java -cp "${ECJ_JAR}:${CLASSPATH}" org.eclipse.jdt.internal.compiler.batch.Main -Xemacs ${ECJ_OPTS} ${TARGET} 2>&1 | awk '/:[0-9]+:/ {$3 = "ECJ: "$3;print}'
java -cp "${CLASSPATH}" -jar ${CHECKSTYLE_JAR} ${CHECKSTYLE_CONFIG} ${TARGET} | awk '/:[0-9]+:/ {$2 = "warning: CheckStyle: "$2 ; print}' | sed -e 's/:\([0-9]\+\):[0-9]\+:/:\1:/g'

The arguments will be passed by flymake later on, the awk scripts are mainly to make CheckStyle messages warnings instead of errors, and to add ECJ or CheckStyle tags in the messages so that you can know from which tool the warnings come from.

Now all we need to do is to call it from Emacs with the right arguments:

; for jde integration with ecj and flymake
(require 'jde-eclipse-compiler-server)
(require 'flymake)

;; flymake for java checkstyle
(defun flymake-java-ecj-checkstyle-init ()
  "Use ECJ and then CheckStyle to check the current java file."
  (if (not (object-of-class-p (jde-compile-get-the-compiler) 'jde-compile-ejc-server))
      (error "The ecj option for flymake can only be set when the jde-compiler is also set to ecj")
    (let* ((temp-file   (flymake-init-create-temp-buffer-copy
                         'jde-ecj-create-temp-file))
           (local-file  (file-relative-name
                         temp-file
                         (file-name-directory buffer-file-name))))
    (list "java-check.sh" (append
                                 ; jde classpath
                                 (cdr (jde-compile-classpath-arg (jde-compile-get-the-compiler)))
                                 ; ecj jar file
                                 (list
                                  (oref (jde-compile-get-ejc) path)
                                 ; all ecj options in one string
                                  (reduce #'(lambda (x y) (concatenate 'string x " " y)) jde-ecj-command-line-args)
                                 ; checkstyle jar
                                  "/path/to/checkstyle.jar"
                                 ; checkstyle options
                                  "-c /path/to/checks.xml"
                                 ; target
                                  local-file))))))

(add-to-list 'flymake-allowed-file-name-masks '("\\.java\\'" flymake-java-ecj-checkstyle-init))

The last line activates the custom checker for all .java files.

Note: here is a bug in CheckStyle regarding exceptions checks where it would throw a RuntimeError and stop process the file. Be sure to disable the RedundantThrows check in the checks.xml you are using.

Using flymake with BSH scripts

If you are working with BeanShell scripts you may have found that even the popular IDEs do not provide much in that area. It is actually very difficult to spot runtime errors in advance since you would use variables assigned in a servlet context.

There is however a basic syntax check that can be done:

;; flymake for BSH scripts
(require 'flymake)

(defun flymake-bsh-init ()
  "Use BSH to check the syntax of the current file."
  (let* ((temp (flymake-init-create-temp-buffer-copy 'flymake-create-temp-inplace))
     (local (file-relative-name temp (file-name-directory buffer-file-name))))
    (list "bsh-check.sh"
            (list local ))))

(add-to-list 'flymake-err-line-patterns
  '("^Exception in thread \"main\" In file: <\\(.*\\)> \\(.*\\) at line \\([0-9]+\\), column \\([0-9]+\\)" nil 3 2))

(add-to-list 'flymake-allowed-file-name-masks '("\\.bsh\\'" flymake-bsh-init))

And the bash script (named bsh-check.sh):

#!/bin/sh
java -cp /home/jeremy/programmation/opentaps-1.0-autocomplete-branch/framework/base/lib/scripting/bsh-2.0b4.jar bsh.Parser "$1" 2>&1 | head -n 1

Note that it is indeed basic and does not spot wrong class names or wrong method names but only syntax issues such as missing closing quotes or brackets …

Advertisements

One Response to Tweaking Flymake for Java

  1. tron says:

    I have a question that I cannot seem to find an answer for.

    What I want to do is be able to pass a parameter (a flash file name) to a blog page. This blog page contains a swf player that will then play that flash file. Is this an easy thing to do? If so, I am lost would appreciate any help.

    Thank you in advance.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: