Top > Emacs >

JDEE

Emacs  Ant  JDEE  Java  Maven 

仕事では共同開発が重要なのでEclipseで開発環境を構築する事がほとんどだが、個人的にプログラミングする際はやはりEmacs+Ant(or Maven)が好み。

そうなると、JDEEは欠かせない。
といっても全機能は要らなくて、便利でスマートなところだけ使っているという感じ。

そんなJDEEに関するメモ。

Contents

  1. .emacsの設定
    1. Antでの開発のための設定
    2. Mavenでの開発のための設定
    3. JUnit4のための設定
  2. prj.elの設定
    1. 全プロジェクト共通のprj.el
    2. Antでの開発用prj.elテンプレート
    3. Mavenでの開発用prj.elテンプレート
    4. pom.xmlからprj.elの自動生成
  3. よく使う関数
    1. コーディング
      1. import文(複数)の挿入(jde-import-all)
      2. 無駄なimport文の削除(jde-import-kill-extra-imports)
      3. import文(単体)の挿入(jde-import-find-and-import)
      4. import文のソート(jde-import-sort)
      5. 文字列補完(dabbrev-expand)
      6. クラス/メソッド名補完(jde-complete)
      7. リージョンのインデント(ident-region)
      8. リージョンをコメント/アンコメント(comment-region)
      9. 行末の無駄なスペースを削除(delete-trailing-whitespace)
    2. ソース自動生成
      1. Getter/Setterの生成(jde-gen-get-set, jde-wiz-get-set-method)
      2. インターフェースの実装(jde-wiz-implement-interface)
      3. メソッドのオーバーライド(jde-wiz-override-method)
      4. JavaDocの生成(jde-javadoc-autodoc-at-line)
    3. ファイル操作
      1. 単純クラス名でソースファイルを開く(jde-open-class-source)
      2. TAGSに基づいたクラス/メソッド名でファイルを開く(find-tag)
      3. grep検索(jde-find)
      4. 最近開いたファイル(recentf-open-files)
    4. ビルド
      1. プロジェクトのビルド(jde-build)
      2. 次の/前のエラーに飛ぶ(next-error/previous-error)
    5. テスト
      1. JUnitテストの実行(jde-junit-run)
    6. その他
      1. JavaDocの表示(jde-help-class)
      2. BeanShellプロセスの終了(jde-bsh-exit)
      3. prj.elの再読込(jde-load-project-file)
  4. 便利かもしれない自作関数
    1. メソッドのボディ部の表示/非表示を切り替える
    2. メソッドの一覧表示(検索)
    3. 単純クラス名でカレントウィンドウにソースファイルを開く
    4. メインクラスとテストクラスを切り替える
    5. 現在開いているテストクラスをコンパイルしてテストを実行

.emacsの設定

自分のJDEE関連の設定ファイル。
自分はこれを.emacsからloadしている。

Antでの開発のための設定

プロジェクトに依存しないAntのための設定

(setq jde-ant-read-target t) ; targetを問い合わせる
(setq jde-ant-enable-find t) ; antに-findオプションを指定する

Mavenでの開発のための設定

プロジェクトに依存しないMavenのための設定。
compilationでエラーにリンクを貼るためにcompilation-error-regexp-alistにMavenの出力用の正規表現を追加している。

(add-hook 'jde-mode-hook
          '(lambda ()
             ; for Maven
             (add-to-list 'compilation-error-regexp-alist
                          '("\\(/.+\\):\\[\\([0-9]+\\),\\([0-9]+\\)] .*" 1 2 3))
             ))

JUnit4のための設定

JUnit4でjde-junit-runが利用できるようにjde-junit-testrunner-typeを変更している。

(setq jde-junit-testrunner-type "org.junit.runner.JUnitCore")

prj.elの設定

プロジェクトに依存しないが環境に依存するような設定は.emacsに記述してもよいのだが、自分はプロジェクト共通のprj.elを作成し、以下のように全プロジェクトで読み込まれる位置に置いている。

  • development/
    • prj.el
      各プロジェクト共通の環境依存の設定(Java VMやANT_HOMEなど)
    • project1/
      • prj.el
        project1固有の設定
    • project2/
      • prj.el
        project2固有の設定

こうすると、例えばproject1では、project1直下のprj.elとdevelopment直下のprj.elが両方適用される。

全プロジェクト共通のprj.el

Gentooでの環境設定例。

(jde-project-file-version "1.0")
;; JDK
(setq jde-jdk-registry
      '(("1.4" . "/usr/lib/jvm/blackdown-jdk-1.4.2")
        ("1.5" . "/usr/lib/jvm/sun-jdk-1.5")
        ("1.6" . "/usr/lib/jvm/sun-jdk-1.6")))
(setq jde-jdk '("1.6"))
(setq jde-jdk-doc-url "http://java.sun.com/javase/ja/6/docs/ja/api/")
(setq jde-help-docsets '(("JDK API" "http://java.sun.com/javase/ja/6/docs/ja/api/" nil)))

Antでの開発用prj.elテンプレート

Ant用のビルドはJDEEで用意されているのでそれらの設定をするだけ。

(jde-project-file-version "1.0")
(jde-set-variables
  '(jde-build-function '(jde-ant-build))
  '(jde-ant-working-directory "./")
  '(jde-ant-read-target t)
  '(jde-ant-enable-find t)
  '(jde-sourcepath '("./src/main/java" "./src/test/java" ))
  '(jde-global-classpath '("./target/classes" "./target/test-classes")))

※実際には、後述のpom.xmlからの自動生成で依存ライブラリもクラスパスに追加されたものを利用している。

Mavenでの開発用prj.elテンプレート

JDEEにはMaven用のビルドが用意されていないので、jde-makeを利用してmvnをコールする。
ビルドエラー時のcompilationバッファでのリンクのために上述の.emacsでの設定は必須。

(jde-project-file-version "1.0")
(jde-set-variables
  '(jde-build-function '(jde-make))
  '(jde-make-program "mvn")
  '(jde-make-args "")
  '(jde-make-working-directory "./")
  '(jde-sourcepath '("./src/test/java" "./src/main/java" ))
  '(jde-global-classpath '("./target/classes" "./target/test-classes")))

※jde-make-programは、Gentooでは、"mvn-2.2"などバージョンの付加が必要。
※実際には、後述のpom.xmlからの自動生成で依存ライブラリもクラスパスに追加されたものを利用している。

pom.xmlからprj.elの自動生成

prj.elの自動生成には、m2jdeeを利用していた事もあったが、独自にプロジェクト固有の設定などを付け加えたくてもテンプレートを変更する事ができない。
そこで、Maven-Ant-TasksとGroovyを使って自動生成するAntビルドファイルを作ってみた。

Maven-Ant-Tasksのjarだけはあらかじめダウンロードしておく必要があるが、後は以下のAntビルドファイルで自動生成できる。
ビルドファイル内に、テンプレートとなる部分があるので、プロジェクト固有のカスタマイズはそこで変更すればOK。

Ant + Maven-Ant-Tasksでの開発用prj.elとMavenでの開発用prj.elが作成可能。

ダウンロードはこちら。

フォルダ構成は以下を想定。

  • project/
    • build.xml(Ant開発時のみ)
    • pom.xml
    • pom2prj.xml
    • lib/
      • maven-ant-tasks-2.0.10.jar
    • src/
      • main/
        • java/
      • test/
        • java/
    • target/
      • classes/
      • test-classes/

使い方はAntで実行するだけ。

Ant開発用のprj.el生成

$ ant -f pom2prj.xml for-ant

Maven開発用のprj.el生成

$ ant -f pom2prj.xml for-maven

上記、各開発用のテンプレートが気に入らなければ、pom2prj.xml内の<echo>あたりをカスタマイズして利用可能。

よく使う関数

コーディング

import文(複数)の挿入(jde-import-all)

機能
現在のバッファで必要なimport文を挿入する。同名のクラスが複数ある場合は選択バッファで選択する必要がある。
jde-import-kill-extra-importsと組み合わせた関数を作成しておくと便利。
滅多に使わないクラスでも選択肢に出るのがうっとうしい場合は、jde-import-exluded-classesを設定する事で除外できる。
これはプロジェクトによって違うだろうから、prj.elで設定するのがよさそう。
(jde-set-variables
  '(jde-import-excluded-classes (append jde-import-excluded-classes '(("^org\\.datanucleus\\..*") ("^java\\.awt\\..*"))))
  ...)
除外されているクラスをimportしたい場合は、jde-import-find-and-importすれば、除外されているクラスも選択できる。
キー
C-c C-v z

無駄なimport文の削除(jde-import-kill-extra-imports)

機能
無駄なimport文を削除する。
jde-import-allと組み合わせた関数を作成しておくと便利。
使った事無いが、C-u M-x jde-import-kill-extra-imports とするとコメントアウトするらしい。
定義例
(defun jde-epn-organize-imports ()
  "必要なimport文の追加と不必要なimport文の削除を行います。"
  (interactive)
  (jde-import-all)
  (jde-import-kill-extra-imports))

import文(単体)の挿入(jde-import-find-and-import)

機能
カーソル位置のクラス名のimport文を挿入する。
キー
C-c C-v C-z
C-c C-v TAB

import文のソート(jde-import-sort)

機能
import文をソートする。
jde-import-all,jde-import-find-and-import時に勝手にソートする用に設定している
設定例
(setq jde-import-auto-sort t)

文字列補完(dabbrev-expand)

機能
開いている全てのバッファ内の単語に基づいた文字列補完。
補完が一瞬でできるのでまずはこれを使う。特にJDEEの関数というわけではない。
キー
M-/

クラス/メソッド名補完(jde-complete)

機能
クラスパスのクラス定義に基づいた補完。
候補が多い場合はポップアップメニューが出る。
挙動の違いで、jde-complete-in-lineやjde-complete-minibufもあるが、これらはあまり使わない。
キー
C-c C-v C-.

リージョンのインデント(ident-region)

機能
リージョンの範囲をインデント。
特にJDEEの関数ではない。
キー
C-M-\

リージョンをコメント/アンコメント(comment-region)

機能
リージョンの範囲をコメント化する。
C-uに続けて実行するとアンコメントする。
特にJDEEの関数ではない。
キー
C-c C-c
C-u C-c C-c

行末の無駄なスペースを削除(delete-trailing-whitespace)

機能
行末の無駄なスペースを削除する。
特にJDEEの関数ではない。
保存時のフックに設定する事で、特に意識して実行しなくても良い。
しかし、たまにあえて行末にスペースを残したい場合に困る。
設定例
(add-hook 'before-save-hook 'delete-trailing-whitespace) ; 保存時に無駄なスペースを削除

ソース自動生成

Getter/Setterの生成(jde-gen-get-set, jde-wiz-get-set-method)

機能
Getter/Setterを生成する。
ミニバッファで型や変数名を入力すれば、フィールドとメソッドを生成してくれる。
設定例
(setq jde-wiz-get-set-variable-prefix "") ; Getter/Setterの引数プレフィックス
(setq jde-wiz-get-javadoc-template '("/**" "* %nを返します。" "* @return %nの値" "*/"))
(setq jde-wiz-set-javadoc-template '("/**" "* %nを設定します。" "* @param %p 設定する値" "*/"))
(setq jde-wiz-show-report nil) ; jde-wiz-get-setのレポートを表示しない
(setq jde-wiz-get-set-static-members nil) ; staticメンバーのGetter/Setterは作成しない
これらの設定は今のところjde-wiz-get-set-methodにしか効かない。

インターフェースの実装(jde-wiz-implement-interface)

機能
対話的にインターフェース名を指定する事で、実装すべきメソッドのひな形を生成してくれる。
implements宣言やimportも挿入してくれる。
インターフェース名の指定時に補完が効かないのが微妙。

メソッドのオーバーライド(jde-wiz-override-method)

機能
対話的にオーバーライドするメソッドを指定する事で、そのメソッドのひな形を生成してくれる。
メソッド名の指定は補完が効くので便利。
キー
C-c C-v C-o

JavaDocの生成(jde-javadoc-autodoc-at-line)

機能
カレント行のJavaDocのひな形を生成してくれる。
キー
C-c C-v j

ファイル操作

単純クラス名でソースファイルを開く(jde-open-class-source)

機能
指定した単純クラス名のソースをother-windowして開く。
jde-sourcepath内のクラスのみ対象。
補完が効かないのがつらい。
カーソル位置のクラス名が自動的に入力されるので、あらかじめカーソルを開きたいクラス名の上に置いておくと便利。

TAGSに基づいたクラス/メソッド名でファイルを開く(find-tag)

機能
あらかじめ生成しておいたTAGSに基づいてカーソル位置のクラス/メソッド名が定義されているファイルを開く。
補完が効くのがうれしい。
特にJDEEの機能というわけではない。
あらかじめTAGSを生成しておかなければならないのと、更新が面倒なのでアクティブなプロジェクトではあまり利用しない。
オープンソースのソースをチェックアウトしてきてそのソースを見て回る場合など、prj.elを設定するのが面倒な場合はTAGSの方が便利な事がある。

grep検索(jde-find)

機能
プロジェクト内のファイルをgrep検索する。
Emacs標準のgrep-findは、カレントディレクトリ以下が検索対象なのに対し、どのファイルを開いていてもプロジェクト全体が検索できるので便利。
検索対処の拡張子はカスタマイズ(jde-find-file-regexp)したり、実行時に詳細設定が可能なjde-find-dlgも便利。
キー
C-c C-v C-f
設定例
; jde-findで対象にするファイルの正規表現
(setq jde-find-file-regexp '("*.java" "*.jsp" "*.tag" "*.xml" "*.properties"))

最近開いたファイル(recentf-open-files)

機能
最近開いたファイルの一覧を表示し、そこからファイルを開く。
特にJDEEの関数ではない。
設定例
;;; recent files
(recentf-mode t)
(setq recentf-max-saved-items 50)

ビルド

プロジェクトのビルド(jde-build)

機能
プロジェクトのビルドを行う。
jde-build-functionなどで、あらかじめ実際のビルドツールを指定しておく。
キー
C-c C-v C-b
設定例
;; Ant
(setq jde-build-function '(jde-ant-build)) ; ビルドにantを利用する
(setq jde-ant-read-target t) ; targetを問い合わせる
(setq jde-ant-enable-find t) ; antに-findオプションを指定する(要らないかも)

次の/前のエラーに飛ぶ(next-error/previous-error)

機能
ビルド時に出たコンパイルエラーのコードに飛ぶ。
Ant + JUnit3までならばテストのエラーにも飛べたと思う。
Ant + JUnit4から飛べなくなり、これがかなり不便で何とかしたい。
とりあえず今は、compilation-mode-mapでM-.にjde-open-class-sourceを割り当てる事で対応している。
キー
次のエラー C-x `, M-g n, M-g M-n
前のエラー M-g p, M-g M-p

テスト

JUnitテストの実行(jde-junit-run)

機能
設定されたクラスパスでカレントバッファのクラスを引数にしてJUnitを実行する。
全体のテストはjde-buildで行うが、カレントバッファのクラスだけテストしたい場合などにさくっとテストできるので便利。
JUnit4を利用する場合はjde-junit-testrunner-typeを設定する必要がある。
これも、jde-run-mode-mapでM-.にjde-open-class-sourceを割り当てておくと便利。
設定例
;; JUnit4
(setq jde-junit-testrunner-type "org.junit.runner.JUnitCore")

その他

JavaDocの表示(jde-help-class)

機能
指定したクラスのJavaDocをブラウザなどで表示する。
JavaDocの場所はあらかじめjde-help-docsetsで指定しておく。
設定例
(setq jde-jdk-doc-url "http://java.sun.com/javase/ja/6/docs/ja/api/")
(setq jde-help-docsets '(("JDK API" "http://java.sun.com/javase/ja/6/docs/ja/api/" nil)))

BeanShellプロセスの終了(jde-bsh-exit)

機能
クラスをロードしているBeanShellプロセスを終了する。
コーディングが進んで新しいクラスやメソッドを追加した場合に、既にBeanShellが起動しているとそれらはjde-complete等の対象にならない、そこで一度BeanShellを終了しクラスをロードし直させる際に利用する。

prj.elの再読込(jde-load-project-file)

機能
prj.elを再読込する。
prj.elを更新した場合などにjde-bsh-exitなどと共に利用する。
個人的には、jde-bsh-exitとjde-load-project-fileを実行する関数を作成し、ファンクションキーを割り当てている。

便利かもしれない自作関数

メソッドのボディ部の表示/非表示を切り替える

機能
メソッドのボディ部をset-selective-displayにて表示/非表示を切り替える。
関数定義
(defun jde-epn-toggle-method-body ()
  "メソッドのボディ部の表示/非表示を切り替える。"
  (interactive)
  (if selective-display
      (set-selective-display nil)
    (set-selective-display 5)))

メソッドの一覧表示(検索)

機能
occurを利用してメソッドの一覧を表示(検索)する。
関数定義
(defun jde-epn-list-class-methods ()
  "メソッドの一覧を表示(検索)"
  (interactive)
  (let ((list-matching-lines-face nil))
  (occur "\\s-\\{4\\}\\(\\(public\\)\\|\\(protected\\)\\|\\(private\\)\\)[^=;]+(")))

正規表現は改善の余地あり。
今のところインターフェースや抽象クラスで使う場合には問題あり。
アクセス修飾子が無い(package private)メソッドも駄目。

単純クラス名でカレントウィンドウにソースファイルを開く

機能
指定した単純クラス名のソースをカレントウィンドウに開く。
jde-sourcepath内のクラスのみ対象。
jde-open-class-sourceはother-windowするのに対し、この関数はカレントウィンドウにファイルを開く。
関数定義
(defun jde-epn-open-class-source (&optional simple-class-name)
  "指定した単純クラス名に対応したソースファイルをカレントウィンドウに開きます。"
  (interactive)
  (let* ((unqualified-name
          (or simple-class-name
              (read-from-minibuffer "Class: " (thing-at-point (quote symbol)))))
         (qualified-name
          (jde-parse-select-qualified-class-name unqualified-name)))
    (condition-case err
        (let ((target-file
               (jde-find-class-source-file qualified-name)))
          (find-file target-file))
      (error (message "Could not find source file for \"%s\" in this project's source path." qualified-name)))))
キー
個人的には、find-tagを置き換えるレベルで使えるので、M-.に割り当てている。
(define-key jde-mode-map [?\M-.] 'jde-epn-open-class-source)
(define-key jde-run-mode-map [?\M-.] 'jde-epn-open-class-source)
(define-key compilation-mode-map [?\M-.] 'jde-epn-open-class-source)

補完もできるようになったら完璧なんだが…。

メインクラスとテストクラスを切り替える

機能
現在のクラスに対応するメインクラスとテストクラスのソースを切り替える。
テストクラスの名前は、メインクラス名+"Test"であると想定。
jde-sourcepathが適切に設定されている必要がある。
関数定義
(defun jde-epn-get-simple-class-name ()
  "カレントバッファの単純クラス名を返します。"
  (file-name-sans-extension (file-name-nondirectory buffer-file-name)))
(defun jde-epn-get-class-name ()
  "カレントバッファの完全修飾クラス名を返します。"
  (concat (jde-parse-get-package-name) "." (jde-epn-get-simple-class-name)))
(defun jde-epn-toggle-main-test ()
  "メインクラスとテストクラスのソースを切り替えます。"
  (interactive)
  (let* ((cname
          (jde-epn-get-class-name))
         (tname
          (if (string= (substring cname -4) "Test")
              (substring cname 0 -4)
            (concat cname "Test"))))
    (condition-case err
        (let ((tfile
               (jde-find-class-source-file tname)))
          (find-file tfile))
      (error (message "Could not find source file for \"%s\" in this project's source path." tname)))))

jde-epn-toggle-main-testが、メインクラスとテストクラスを行き来する関数。

以前はファイルを開く所でjde-open-class-sourceを利用していたが、必ずother-windowするのが気にくわなかったので、一手間加えて、カレントウィンドウで切り替えができるようにした。

これをファンクションキーなどに設定しておけば切り替えが非常に楽。

あとは、テストクラスがまだ存在しない場合に、自動的に新規バッファを作成してくれれば完璧なんだけど、「テストクラスを作成するソースパス」という概念(設定)がJDEEに無いので独自変数を定義しなくちゃいけなくなる…。
そこが踏み切れない。

現在開いているテストクラスをコンパイルしてテストを実行

依然として未完成。試行錯誤中!!

テストコードを編集して、コンパイルして、テスト実行というよくある作業を一度にすませたい。
Antでのビルドは時間がかかるので、できれば

  • jde-compile
  • jde-junit-run

の組み合わせでなんとかしたい。
しかし、

  • jde-compileは、メインコードとテストコードを区別せずにjde-compile-option-directoryで指定された同じ場所にclassファイルを出力する

という問題がある。
そこで、とりあえず考えたのが以下の関数

(defun jde-epn-junit-run ()
  (interactive)
  (let ((jde-compile-option-directory "./target/test-classes"))
    (jde-compile)
    (jde-junit-run)
    ))

これで、開いているファイルが確かにテストコードであれば、コンパイルして、classファイルをtarget/test-classesに出力し、テストを実行してくれる。

しかし、

  • メインコードでこの関数を実行すると、メインコードがtest-classフォルダに出力されてしまう
  • コンパイル失敗しても関係なくjde-junit-runを実行してしまう
  • 関数内に、出力先フォルダが直書きされている

など、問題山積み。
時間ができたら改善策を練ろう。