S-JIS[2013-02-10/2013-10-29] 変更履歴

EclipseプラグインDMDLエディター(マーカー)

Eclipseプラグイン開発自作DMDLエディターでエラーマーカーを表示してみる。


概要

Javaエディターでは、(コンパイル)エラーがあるとその部分に赤い波線(下線・アンダーライン)が引かれ、その行の左側や右側にエラーマークが付く。
これは「マーカー」という機能。

マーカーはリソース(ファイル)に対して付ける。

マーカーを付けると、ファイルの該当部分にマークが付く他に、タスク(問題)リストにもマーカーの情報が表示される。


エラーマーカーの例

エラーマーカーを設定するに当たり、どこかでコンパイルしてエラーをチェックする必要がある。
今回は(フォールディングアウトラインと同様に)ファイル保存時にチェックすることにする。

DMDLEditor.java:

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;

import org.eclipse.ui.IFileEditorInput;
public class DMDLEditor extends TextEditor {
〜
	// ファイル保存時に呼ばれる
	private void update() {
		DMDLDocument document = getDocument();
		ModelList models = document.getModelList();

		// フォールディング範囲を最新状態に更新する
		foldingManager.updateFolding(document, models);

		// アウトラインを最新状態に更新する
		if (outlinePage != null) {
			outlinePage.refresh(models);
		}

		// エラーマーカーを更新する
		IFileEditorInput input = (IFileEditorInput) getEditorInput();
		parse(input.getFile(), document);
	}

マーカーはリソース(ファイル)に対して付けるので、エディターで編集している対象ファイルのIFileを取得(input.getFile())している。
IFileとIFileEditorInputの依存プラグイン

	private void parse(IFile file, IDocument document) {
		// Asakusa Framework本家のDMDLパーサーを使って構文解析
		DmdlParser parser = new DmdlParser();
		StringReader reader = new StringReader(document.get());
		URI uri = file.getFullPath().toFile().toURI();
		try {
			parser.parse(reader, uri);
		} catch (DmdlSyntaxException e) {
			// パースエラー
			createErrorMarker(e, file, document);
		}
	}
	private void createErrorMarker(DmdlSyntaxException e, IFile file, IDocument document) {
		Region region = e.getRegion();
		int beginOffset = document.getLineOffset(region.beginLine - 1) + (region.beginColumn - 1);
		int endOffset   = document.getLineOffset(region.endLine   - 1) + (region.endColumn   - 1) + 1;

		IMarker marker = file.createMarker(IMarker.PROBLEM);
		marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
		marker.setAttribute(IMarker.MESSAGE, e.getMessage());
//		marker.setAttribute(IMarker.LINE_NUMBER, region.beginLine - 1);
		marker.setAttribute(IMarker.CHAR_START, beginOffset);
		marker.setAttribute(IMarker.CHAR_END, endOffset);
		marker.setAttribute(IMarker.LOCATION, String.format("%d:%d-%d:%d", region.beginLine, region.beginColumn, region.endLine, region.endColumn));
	}
}

Asakusa Framework本家のDmdlParserでは、構文エラーがあるとDmdlSyntaxExceptionがスローされる。
そこからgetRegion()でエラーのあった場所が取れるので、ドキュメント上のオフセットに変換している。
(DmdlSyntaxExceptionのエラーメッセージにはファイル名が含まれている…今回のようにファイル名が自明なケースでは冗長になってしまうが^^;)

そして、IFile(IResource)のcreateMarker()でマーカーを生成する。
引数がマーカーのタイプで、色々な種類のマーカーがある(自分で作ることも出来る)らしい。
「問題(エラー)」を示すマーカータイプは「eclipse.core.resources.problemmarker」だが、IMarkerに定数が定義されているので、それを使う。

setAttribute()でマーカーの情報を色々設定する。
(MarkerUtilitiesに値をセットしたり取得したりするメソッドが用意されている。[2013-02-17]

属性名 属性値 備考
SEVERITY 重要度  
MESSAGE メッセージ  
LINE_NUMBER 行番号 省略可能
CHAR_START マーカーの開始位置 ドキュメント内のオフセット
CHAR_END マーカーの終了位置 ドキュメント内のオフセット
LOCATION マーカーの位置 人が見て分かるような文言
TRANSIENT true/false trueにすると、Eclipseを閉じると消える。[2013-02-11]

CHAR_START・CHAR_ENDを省略すると、波線(下線)が付かない。

LINE_NUMBERを指定してLOCATIONを省略した場合、タスクリストのLOCATION欄にLINE_NUMBERの内容が表示される。


マーカーの取得

リソース(ファイル)に付けられているマーカーの一覧を取得するにはfindMarkers()を使う。[2013-10-29]

	IResource resource = 〜; //IFile等
	IMarker[] markers = resource.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE);
	for (IMarker marker : markers) {
		〜
	}

マーカーの削除

マーカーをクリアするにはdelete()を使う。[2013-02-11]

	IMarker marker = 〜;
	marker.delete();

リソース(ファイル)に付けられているマーカーを全て消すにはdeleteMarkers()を使う。

	IResource resource = 〜; //IFile等
	resource.deleteMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE);

アイコンのホバーメッセージ表示

マーカーを設定するとエディター上のその行の左端にアイコンが表示されるが、そこにマウスカーソルを当てても何も表示されない。[2013-02-11]
ホバー(ポップアップ)メッセージを表示するにはIAnnotationHoverを設定する必要がある。

import org.eclipse.jface.text.source.DefaultAnnotationHover;
import org.eclipse.jface.text.source.IAnnotationHover;
public class DMDLConfiguration extends SourceViewerConfiguration {
〜
	@Override
	public IAnnotationHover getAnnotationHover(ISourceViewer sourceViewer) {
		return new DefaultAnnotationHover();
	}
}

これで、マーカーに設定されているメッセージが(アイコンの位置の)ポップアップで表示されるようになる。

参考: stackoverflowのHover text for problem marker in Eclipse plugin


テキストのホバーメッセージ表示

マーカーを設定するとエディター上のその位置に赤い下線(波線)が引かれるが、そこにマウスカーソルを当てても何も表示されない。[2013-02-12]
ホバー(ポップアップ)メッセージを表示するには ITextHoverを設定する必要がある。

import org.eclipse.jface.text.DefaultTextHover;
import org.eclipse.jface.text.ITextHover;
public class DMDLConfiguration extends SourceViewerConfiguration {
〜
	@Override
	public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) {
		return new DefaultTextHover(sourceViewer);
	}
}

これで、マーカーに設定されているメッセージが(テキストの位置の)ポップアップで表示されるようになる。

参考: Eclipse Community ForumsのRe: How to implement Quick Fix / Quick Assist for custom editor?


DefaultTextHoverだと自分が設定したマーカー以外も出てくるようなので、ちょっと条件を付けた方が良さそう。[2013-02-16]

import org.eclipse.jface.text.source.Annotation;
import org.eclipse.ui.texteditor.MarkerAnnotation;
	@Override
	public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) {
		return new DefaultTextHover(sourceViewer) {
			@Override
			protected boolean isIncluded(Annotation annotation) {
				if (annotation instanceof MarkerAnnotation) {
					return true;
				}
				return false;
			}
		};
	}

マーカーを使ったジャンプ

マーカーで指定された場所にジャンプする(エディター上のその単語を選択する)ことが出来る。[2013-02-17]

DMDLEditor.java:

import org.eclipse.ui.ide.IGotoMarker;
	IMarker marker = 〜;
	IGotoMarker target = (IGotoMarker) getAdapter(IGotoMarker.class);
	target.gotoMarker(marker);

ただし、(マーカーはリソース(IFile等)に対して作るが、)自動的にマーカーのリソース(ファイル)にジャンプしてくれるわけではない。
(IGotoMarkerの実装によるが、TextEditorは自分のエディター内のその場所を選択するだけ)

IFileからエディターオブジェクトを取得して、そこからgetAdapter()でIGotoMarkerを取得すればそのファイルへジャンプできる。
IFileからエディターを取得する(開く)方法

	IMarker marker = 〜;
	IEditorPart editor = 〜;
	IGotoMarker target = (IGotoMarker) editor.getAdapter(IGotoMarker.class);
	target.gotoMarker(marker);

nullチェック等のことを考えると、IDEクラスのgotoMarkerメソッドを使う方がいいかも。[2013-06-11]

import org.eclipse.ui.ide.IDE;
	IMarker marker = 〜;
	IEditorPart editor = 〜;
	IDE.gotoMarker(editor, marker);

ルーラーについて

エラー(PROBLEM)マーカーを付けると、エディターの左側にエラーマーク、右側にエラーの印が表示される。[2013-06-05]
この左側のマークが付くエリア(列)を「vertical ruler(垂直方向ルーラー)」といい、右側の印が付くエリア(列)を「overview ruler(概説ルーラー)」という。

TextEditorのデフォルトではどちらのルーラーも表示される。
これはAbstractDecoratedTextEditor#createSourceViewer()で実装されている。

	protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) {

		fAnnotationAccess= getAnnotationAccess();
		fOverviewRuler= createOverviewRuler(getSharedColors());

		ISourceViewer viewer= new SourceViewer(parent, ruler, getOverviewRuler(), isOverviewRulerVisible(), styles);
		// ensure decoration support has been created and configured.
		getSourceViewerDecorationSupport(viewer);

		return viewer;
	}
	protected boolean isOverviewRulerVisible() {
		IPreferenceStore store= getPreferenceStore();
		return store != null ? store.getBoolean(OVERVIEW_RULER) : false;
	}

getOverviewRuler()でnullを返したりisOverviewRulerVisible()でfalseを返したりするようにすると、overview rulerは表示されない。
(その列自体の表示領域が確保されない)

isOverviewRulerVisible()はPreferenceStoreの値を取得するようになっているが、デフォルトではtrueになっているようだ。


Eclipseプラグインへ戻る / Eclipseへ戻る / 技術メモへ戻る
メールの送信先:ひしだま