번역 문서 저작권 표시 :
저작자표시-비영리-동일조건 변경 허락 (CC BY-NC-SA)
원문 저작권 표시 :
(Developing a customer text editor for the Eclipse IDE, Lars Vogel (c) 2009 - 2020 vogella GmbHVersion 1.1,09.09.2020
, CC BY-NC-SA )
소스 코드의 경우 Eclipse Public License 2.0 를 따른다.

[원문 출처] www.vogella.com/tutorials/EclipseEditors/article.html


해당 문서의 경우 vogella사의 Eclipse IDE Extensions 
Document 문서 일부를 번역 한 것입니다. 
번역본인 해당 문서의 경우 역자에게 있음을 알리며 상업적 이용을 불허합니다. 
번역 작업 시 이클립스 내 고유이름을 가진 값의 경우, 가령 Tab의 명칭 등의 경우 화면에서 표시되는 이름이므로 별도로 번역없이 제공하거나 괄호를 사용하여 동시에 명기한다.
저작자표시-비영리-동일조건 변경 허락 (BY-NC-SA)

www.sogomsoft.co.kr (주) 소곰소프트 

1. 이클립스 에디터

텍스트 에디터는 일반적으로 텍스트 데이타를 변경하는 것을 허용하고, 저장 액션이 발생했을때는 언제나 기본 모델의 변경을 적용한다. 

어떤 파일 확장자 또는 컨텍스트 유형을 위한 기능성 에디터를 제공하기 위해 , 다음과 같이 할 수 있다:

  • 일반적인 에디터를 확장한다. 

  • 자체 에디터를 구현한다. 

일반적인 에디터를 확장하는 것은 구현을 가속화시키고 단순화 할수 있기 때문에 새 파일을 위한 선호하는 선택이다.

일반적인 에디터에서 새 컨텍스트 유형을 지원을 추가하기 위해 다음을 할 필요가 있다. 

  • 컨텍스트 유형을 추가한다. 

  • "org.eclipse.ui.genericeditor.presentationReconcilers" 확장점을 통해 에디터에 프리젠테이션조정자(PresentationReconsiler)를 등록한다.

1.1. JFace 텍스트 프레임워크

JFace 텍스트에서, 텍스트 문서는 IDocument로 모델링되어 있다.  컨트롤러로 ITextViewer를 사용하는 IDocument 문서를 보거나 편집하기 위해, 문서를 프리젠테이션 하기 위한 StyledText 위젯을 사용한다.

IDocument 인터페이스는 텍스트를 저장하고 다음을 위한 지원을 제공한다.:

  • 라인 정보

  • 텍스트 조작

  • 문서 변경 리스너

  • 맞춤형 위치 관리자

  • 검색

  • 맞춤형 파티션 관리

  • 문서 파티션 변경 리스너

문서들은 문서 파티셔너를 통해 다른 파티션으로 분해 될 수 있다. 즉 그 파티션들은 그 파티션들의 유형에 따라 조작할 수 있고 다른 포그라운트(전경) 색상을 가질 수 있다.

1.2. 프리젠테이션 조정자 소개

사용자가 문서를 변경 할 때마다,  파티션 조정자는 시각적 프리젠테이션이 유효하지 않는 영역과 그 영역을 고칠수 있는 방법을 결정한다.

소스코드의 하이라이팅(강조)은 프리젠테이션 조정자를 사용함으로써 보관될 수 있다.  그런 프리젠테이션 조정자는 "org.eclipse.ui.genericeditor.presentationReconcilers" 확장을 통해서 정의 될 수 있다. 프리젠테이션 조정자는 IPresentationReconciler 인터페이스를 구현할 컨텍스트유형과 클래스의 명세서를 필요로 한다. IPresentationReconciler를 사용할때, 어떤 IRules는 지정된 컨텍스트 유형을 지원할 수 있다. IRule은 문서의 파티셔닝 또는 텍스트의 스타일링의 목적을 위해 텍스트를 스캔하는 것을 사용하여 룰을 정의한다. 

파티션은 문서상에 의미론적인 뷰이다.( 역자주, 파티션은 개념상으로는 뷰이다):

  • 각각의 파티션은 컨텍스트 유형을 가진다. 

  • 문서의 각각의 문자는 파티션에 속해 있다. 

  • 문서는 멀티 파티셔닝을 지원한다. 

  • 파티셔닝은 항상 최신 정보이다.

1.3. 에티터로 동작하기 위한 API

현재 활성화 페이지를 통해서 에디터를 열 수 있다. 그렇게 하기 위해 "org.eclipse.ui.editors" 확장점에서 정의된 에디터를 위한 EditorInput 객체와 ID를 필요하다. 

page.openEditor(new YourEditorInput(), ID_OF_THE_EDITOR);

그외 페이지에서 얻기 위해 다음처럼 사용 할 수  있다.:

// 에디터가 뷰 안에 있다면 
getViewSite().getPage(); 

// 에디터가 커멘드 내에 있다면
HandlerUtil.getActiveWorkbenchWindow(event).getActivePage(); 

// 에디터가 다른 어딘가에 있다면 
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();

Java 에디터에서 엘리먼트에 컨트롤 키를 누르고 클릭하면 그곳을 탐색 할 수 있다.

이 기능은 "org.eclipse.ui.workbench.texteditor.hyperlinkDetectors" 확장점(extension point)을 위한 확장을 통해서 제공된다. 지정된 이름은 General > Editors > Text Editors > Hyperlinkingpreferences에서 보인다.  만약 "org.eclipse.ui.genericeditor.GenericEditor" 대상 id를 사용하는 일반적인 에디터를 사용하기 위해 "org.eclipse.ui.DefaultTextEditor"를 사용하는 모든 텍스트 에디터에서 사용하기를 원하면 대상ID(targetId)가 지원하기를 원하는 에디터 유형에 대상 아이디를 지정한다.

IHyperlinkDetector는 IHyperlink 객체의 배열을 반환하는 것을 하기로 되어 있다. IHyperlink 구현체는 하이퍼링크 액션을 수행한다.

2.2. 색상과 폰트 preferences 추가 하기 

이클립스는 General > Appearance > Colors and Fonts에서 사용자에 의해서 색상과 폰트를 사용자 정의 하기 위한 페이지를 제공한다. 

이 페이지를 위한 엔트리를 정의하기 위해, "org.eclipse.ui.themes" 확장점(extension point)을 위한 확장점을 정의할 필요가 있다.

예를 들면, plugin.xml 파일 또는 플러그인에서 다음 엔트리로 카테고리, 폰트와 컬러를 제공할 수 있다.

<extension point="org.eclipse.ui.themes"> 
	
    <themeElementCategory 
    	id="com.vogella.eclipse.preferences.mythemeElementCategory" 
        label="vogella category"> 
    	<description> An example theme category </description> 
    </themeElementCategory> 
    
    <colorDefinition 
    	categoryId="com.vogella.eclipse.preferences.mythemeElementCategory" 
        id="com.vogella.eclipse.preferences.myFirstColorDefinition" 
        label="vogella color" 
        value="COLOR_DARK_BLUE"> 
    	<description> Your description for the color </description> 
    </colorDefinition> 
    
    <fontDefinition 
    	categoryId="com.vogella.eclipse.preferences.mythemeElementCategory" 
        id="com.vogella.eclipse.preferences.myFirstFontDefinition" 
        label="vogella Font" 
        value="Lucida Sans-italic-18"> 
        <description> Your description for the font </description> 
    </fontDefinition> 
    
</extension>

색상을 위한 값은 SWT 클래스에 정의된 COLOR_* 상수가 될수 있다. 또한 255,0,0같은 RGB 값으로 지정할 수 있다. 폰트를 위해 값이 다음 패턴 `fontname-style-height` 값으로 정의되어 있다.

이제 preferences은 사용자나 CSS 엔진을 통해 변경 될 수있다. 현재 값을 얻기 위해, IThemeManager를 사용할 수 있다.

// Eclipse 4 API 
@Inject 
IThemeManager themeManager; 

// Eclipse 3 API
IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager(); 
ITheme currentTheme = themeManager.getCurrentTheme(); 

ColorRegistry colorRegistry = currentTheme.getColorRegistry(); 
Color color = colorRegistry.get("com.vogella.eclipse.preferences.myFirstColorDefinition"); 

FontRegistry fontRegistry = currentTheme.getFontRegistry(); 
Font font = fontRegistry.get("com.vogella.eclipse.preferences.myFirstFontDefinition");

2.3. 사용자 정의 스펠링 엔진

"org.eclipse.ui.workbench.texteditor" 플러그인은 "org.eclipse.ui.workbench.texteditor.spellingEngine" 확장점을 통해 사용자 정의 스펠링 엔진을 등록하기 위한 옵션을 제공한다. 

3. 연습 : 사용자 정의 파일 유형을 위한 일반적인 에디터 사용하기

이 연습에서, 일반적인 텍스트 에디터와 작업확장자로 파일을 연결한다. 파일 내에서 예제처럼 속송 파일을 편집하는 것을 지원하기를 원한다. 

test:Hello 
Helper:stuff

3.1. 새 플러그인 생성

"com.vogella.ide.editor.tasks"로 불리는 간단한 새 플러그인 프로젝트를 생성한다.

3.2. Manifest 위존관계 추가하기

MANIFEST.MF 파일을 위한 에디터를 연다. Add 버튼을 사용하여 의존관계(Dependencies) 탭을 통해서 다음 의존관례를 추가한다.

  • org.eclipse.text

  • org.eclipse.ui

  • org.eclipse.ui.editors

  • org.eclipse.ui.genericeditor

  • org.eclipse.ui.workbench.texteditor

  • org.eclipse.jface.text

  • org.eclipse.core.runtime

  • org.eclipse.core.resources

3.3. 텍스티로 manifest 파일 리뷰하기

MANIFEST.MF 탭을 선택하면, 일반적인 텍스트(plain text)로 이 파일을 볼 수 있다. 다음 해결방법과 같이야 힌다. (매번 릴리즈로 변경되기 때문에 버전 번호는 제거 되었다.).

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Tasks
Bundle-SymbolicName: com.vogella.ide.editor.tasks
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: VOGELLA
Automatic-Module-Name: com.vogella.ide.editor.tasks
Bundle-RequiredExecutionEnvironment: JavaSE-11
Require-Bundle: org.eclipse.text,
 org.eclipse.ui,
 org.eclipse.ui.editors,
 org.eclipse.ui.genericeditor,
 org.eclipse.ui.workbench.texteditor,
 org.eclipse.jface.text,
 org.eclipse.core.runtime,
 org.eclipse.core.resources

3.4. 컨텍스트 유형 정의하기

MANIFEST.MF 에디터를 사용하여, 확장(Extensions) 탭을 열고 Add…​ 버튼을 누른다..

"org.eclipse.core.contenttype.contentTypes" 확장점(extension point)을 선택한다.

다이얼로그를 닫고 확장을 추가 하기 위해 Finish을 누른다.

새 엔트리에 오른쪽 마우스 클릭하고 New > content-type을 선택한다.

다음 스크린샷과 유사하게 .tasks 확장자를 사용하는 파일을 위한 컨텍스트 유형을 지정한다. 

plugin.xml 파일에 엔트리를 생성한다..

plugin.xml 파일은 다음 목록과 유사하게 보여야 한다. manifest 편집기의 plugin.xml 탭을 클릭함으로써 그 내용을 볼 수 있다.

<?xml version="1.0" encoding="UTF-8"?> 
<?eclipse version="3.4"?> 
<plugin> 
	<extension point="org.eclipse.core.contenttype.contentTypes"> 
    	<content-type file-extensions="tasks" id="com.vogella.ide.contenttype.tasks" name="Tasks" priority="high"> 
        </content-type> 
    </extension> 
</plugin>

3.5. 에디터로 컨텐츠 유형 연결하기

컨텐츠 유형은 특정 에디터로 연결 될 수 있다. 이를 위해 "org.eclipse.ui.editors" 확장점이 사용 될 수 있다. 

확장(Extensions) 탭에 Add 버튼을 통해서 "org.eclipse.ui.editors extension"을 추가한다.

컨텍스트 유형을 정의하기 위해 "org.eclipse.ui.editors"애 오른쪽 마우스 클릭하고, New > editorContentTypeBinding를 선택한다.

그 결과 plugin.xml은 이제 다음과 같이 보여야 한다. 

<?xml version="1.0" encoding="UTF-8"?> 
<?eclipse version="3.4"?> 
<plugin> 

	<extension point="org.eclipse.core.contenttype.contentTypes"> 
		<content-type file-extensions="tasks" id="com.vogella.ide.contenttype.tasks" name="Tasks" priority="high"> 
		</content-type> 
	</extension> 

	<extension point="org.eclipse.ui.editors"> 
		<editorContentTypeBinding contentTypeId="com.vogella.ide.contenttype.tasks" editorId="org.eclipse.ui.genericeditor.GenericEditor"> 
		</editorContentTypeBinding> 
	</extension> 
    
</plugin>

3.6. feature를 통해서 product에 플러그인 추가 하기

IDE feature에 새 플러그인을 추가하라. features를 사용하지 않는 경우, 이 단계를 넘어가라

3.7. 개발 테스트 하기

 features 와 product를 사용한다면 product를 통해 새 이클립스 런타임을 시작하라.

product를 통한 시작은 런처 환경 구성을 업데이트 하게 될 것이다. 변경되지 않은 런터 환경 구성을 통해 직접 런타임 이클립스를 시작한다면, 새 플러그인은 포함되지 않을 것이다. 

features 와 product를 사용하지 않는다면, 새 플러그인이 시작시에 포함 될 수 있도록 직접 런터 환경 구성을 업데이트하라. 

이클립스 런타임에서 Window > Preferences > General > Content Types에서 컨텐츠 유형이 보이는지 확인하라

새 프로젝트를 생성하라 (General 또는 Java 프로젝트).

새 프로젝트에서 .tasks 확장자로 새 파일을 생성하라. 만약 파일을 연다면, 일반적인 텍스트 데이터에서 열려야 한다.

아이콘은 일번적인 에디터의 아이콘이 되여야 한다. 

4. 연습: 구문 하이라이팅 구현하기

이 연습에서 작업 파일 에디터를 위한 구문 하이라이팅을 구현한다. com.vogella.ide.editor.tasks플러그인 상에서 작업을 계속한다.

4.1. 구문 하이라이팅 구현하기 

IRule을 정의 하기 위한 다음 클래스를 구현한다.

package com.vogella.ide.editor.tasks; 

import org.eclipse.jface.text.rules.ICharacterScanner; 
import org.eclipse.jface.text.rules.IRule; 
import org.eclipse.jface.text.rules.IToken; 
import org.eclipse.jface.text.rules.Token; 

public class PropertyNameRule implements IRule { 

	private final Token token; 
    
    public PropertyNameRule(Token token) { 
    	this.token = token; 
    } 
    
    @Override 
    public IToken evaluate(ICharacterScanner scanner) { 
   		int c = scanner.read(); 
    	int count = 1; 
    	while (c != ICharacterScanner.EOF) { 
    		if (c == ':') { 
    		return token; 
    		} 
    		if ('\n' == c || '\r' == c) { 
    			break; 
    		} 
    		count++; 
    		c = scanner.read(); 
   		} 
    
    	// 일치 하지 않으면 원본 포지선에 스캐너 다시 넣는다.  
    	for (int i = 0; i < count; i++) { 
    		scanner.unread(); 
    	} 
    
    	return Token.UNDEFINED; 
    } 
    
}

에디터를 위한 조정자로 사용되게 될 다음 클래스를 구현한다. 

package com.vogella.ide.editor.tasks; 

import org.eclipse.jface.text.IDocument; 
import org.eclipse.jface.text.TextAttribute; 
import org.eclipse.jface.text.presentation.PresentationReconciler; 
import org.eclipse.jface.text.rules.DefaultDamagerRepairer; 
import org.eclipse.jface.text.rules.IRule; 
import org.eclipse.jface.text.rules.RuleBasedScanner; 
import org.eclipse.jface.text.rules.Token; 
import org.eclipse.swt.SWT; 
import org.eclipse.swt.widgets.Display; 

public class PropertiesReconciler extends PresentationReconciler { 

	private final TextAttribute tagAttribute = new TextAttribute( Display.getCurrent().getSystemColor(SWT.COLOR_DARK_GREEN)); 
	
    public PropertiesReconciler() { 
		RuleBasedScanner scanner = new RuleBasedScanner(); 
		IRule rule = new PropertyNameRule(new Token(tagAttribute)); 
		scanner.setRules(new IRule[] { rule }); 
		
        DefaultDamagerRepairer dr = new DefaultDamagerRepairer(scanner); 
		this.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE); 
		this.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE); 
	} 
    
}

"com.vogella.ide.editor.tasks" 플러그인의 plugin.xml 파일에 "org.eclipse.ui.genericeditor.presentationReconcilers" 확장점에 확장을 추가한다..

<extension point="org.eclipse.ui.genericeditor.presentationReconcilers"> 
	<presentationReconciler class="com.vogella.ide.editor.tasks.PropertiesReconciler" contentType="com.vogella.ide.contenttype.tasks"> 
    </presentationReconciler> 
</extension>

4.2. 구현체 테스트하기

런타임 이클립스를 재시작한다. 

.tasks 파일을 연다. 다음 예처럼 파일에 몇몇 프로퍼티 값을 넣는다. :

ID: 1 
Summary: Eclipse IDE Training 
Description: 
Done: 
Duedate: 
Dependent:

결과는 다음 처럼 보여야 한다.:

5. 연습: 색상을 사용자정의하기 위한 사용자를 허용한다. 

이전 연습에서, 하드코드 컬러를 사용한 .task. 파일 에디터를 위한 구문 하이라이팅을 구현했다. 사용자가 생상을 커스터마이징하는 것을 방지할 수 있기 때무네 이것은 최적은 아니다.

이 연습에서 이 색상을 사용자정의 하는 것을 사용자를 허용하기 위해 "com.vogella.ide.editor.tasks" 플러그인을 확장한다. 

5.1. 색상 정의하기

"org.eclipse.ui.themes" 확장점(extension point)을 위한 확장을 추가 하기 위해 manifest 에디터의 확장(Extensions) 탭을 사용한다. 생성된 엔트리에 오른쪽 마우스 클릭하고, themeElementCategory를 선택하고 사용하다. :

  • id: com.vogella.ide.editor.tasks.settings

  • label: Tasks settings

생성된 엔트리에 오른쪽 마우스 클릭하고 colorDefinition를 선택한다.

사용:

  • id: com.vogella.ide.editor.tasks.key

  • label: Task key color

  • value: 255,0,0

  • categoryId: com.vogella.ide.editor.tasks.settings

plugin.xml 컨텐츠가 다음과 같이 보여야 한다. :

<?xml version="1.0" encoding="UTF-8"?> 
<?eclipse version="3.4"?> 
<plugin> 

	<extension point="org.eclipse.core.contenttype.contentTypes"> 
		<content-type 
        	file-extensions="tasks" 
        	id="com.vogella.ide.contenttype.tasks" 
            name="Tasks" 
            priority="high"> 
		</content-type> 
	</extension> 

	<extension point="org.eclipse.ui.editors"> 
		<editorContentTypeBinding 
        	contentTypeId="com.vogella.ide.contenttype.tasks" 
        	editorId="org.eclipse.ui.genericeditor.GenericEditor"> 
		</editorContentTypeBinding> 
	</extension> 

	<extension point="org.eclipse.ui.genericeditor.presentationReconcilers"> 
		<presentationReconciler 
        	class="com.vogella.ide.editor.tasks.PropertiesReconciler" 
    		contentType="com.vogella.ide.contenttype.tasks"> 
		</presentationReconciler> 
	</extension> 

	<extension point="org.eclipse.ui.themes"> 
		<colorDefinition 
        	categoryId="com.vogella.ide.editor.tasks.settings" 
        	id="com.vogella.ide.editor.tasks.key" 
            label="Task key color" 
            value="255,0,0"> 
		</colorDefinition> 
		<themeElementCategory 
        	id="com.vogella.ide.editor.tasks.settings" 
        	label="Tasks settings"> 
		</themeElementCategory> 
	</extension> 
    
</plugin>

5.2. 검증하기

만약 런타임 이클립스를 실행하면, Window > Preferences > General > Appearance > Colors and Fonts 설정에서 카테고리와 컬러를 보는것이 가능해야 한다.

5.3. 구문 하이라이팅을 위한 색상 사용하기

색상 레지스터리에 접근하고 에디터에서 그것을 사용한다. 다음 코드 짧은 발췌는 도움이 될 것이다.

IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager(); 
ITheme currentTheme = themeManager.getCurrentTheme(); 
ColorRegistry colorRegistry = currentTheme.getColorRegistry(); 
Color color = colorRegistry.get("com.vogella.ide.editor.tasks.key");
package com.vogella.ide.editor.tasks;

import org.eclipse.jface.resource.ColorRegistry;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.TextAttribute;
import org.eclipse.jface.text.presentation.PresentationReconciler;
import org.eclipse.jface.text.rules.DefaultDamagerRepairer;
import org.eclipse.jface.text.rules.IRule;
import org.eclipse.jface.text.rules.RuleBasedScanner;
import org.eclipse.jface.text.rules.Token;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.themes.ITheme;
import org.eclipse.ui.themes.IThemeManager;

public class PropertiesReconciler extends PresentationReconciler {

    public PropertiesReconciler() {

        IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager();
        ITheme currentTheme = themeManager.getCurrentTheme();
        ColorRegistry colorRegistry = currentTheme.getColorRegistry();
        Color color = colorRegistry.get("com.vogella.ide.editor.tasks.key");

        TextAttribute tagAttribute = new TextAttribute(color);

        RuleBasedScanner scanner = new RuleBasedScanner();
        IRule rule = new PropertyNameRule(new Token(tagAttribute));
        scanner.setRules(new IRule[] { rule });
        DefaultDamagerRepairer dr = new DefaultDamagerRepairer(scanner);
        this.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE);
        this.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE);
    }
}

5.4. 변경 테스트하기

Window > Preferences > General > Appearance > Colors and Fonts를 연다.  에디터의 색상을 검색하고 변경한다. 

에디터를 닫고 새로 연다. 새 색상이 사용되었는지를 검증한다. 

5.5. 색상을 업데이트 하기 위해 preferences 리스너 사용하기 

preferences은 사용자 세팅이 지속됩니다. preferences 리스터를 통해 노드에서 변경을 등록 하는 것이 가능하다. 

프로퍼티 조정자(` PropertiesReconciler`) 재정의에서 설치 메서드는 색상을 지속하기 위해 사용되는 "org.eclipse.ui.workbench" 노드에서 변경을 리스팅할 수 있다..

public void install(ITextViewer viewer) { 
	super.install(viewer); 
	IEclipsePreferences node = InstanceScope.INSTANCE.getNode("org.eclipse.ui.workbench"); 
	node.addPreferenceChangeListener(event -> { 
		// TODO 새 색상으로 변경된 룰로 룰을 업데이트한다. 
    	viewer.invalidateTextPresentation(); 
    }); 
}

TODO 를 해결하고 색상이 업데이트 되었는지 체크하라 

preference를 위한 preferences 노드를 찾기 위해 선호도 스파이(preference spy)를 사용 할수 있다. 뷰를 열고 preferences를 위한 trace를 껐다 켰다한다. 색상 변경 이후 그 데이타를 본다.

 

package com.vogella.ide.editor.tasks;

import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jface.resource.ColorRegistry;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.TextAttribute;
import org.eclipse.jface.text.presentation.PresentationReconciler;
import org.eclipse.jface.text.rules.DefaultDamagerRepairer;
import org.eclipse.jface.text.rules.IRule;
import org.eclipse.jface.text.rules.RuleBasedScanner;
import org.eclipse.jface.text.rules.Token;
import org.eclipse.swt.graphics.Color;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.themes.ITheme;
import org.eclipse.ui.themes.IThemeManager;

public class PropertiesReconciler extends PresentationReconciler {

    private ColorRegistry colorRegistry;
    private RuleBasedScanner scanner;
    private IRule rule;

    @Override
    public void install(ITextViewer viewer) {
        super.install(viewer);

        IEclipsePreferences node = InstanceScope.INSTANCE.getNode("org.eclipse.ui.workbench");

        node.addPreferenceChangeListener(event -> {
            updateRule();
            viewer.invalidateTextPresentation();
        });
    }

    private void updateRule() {
        Color color = colorRegistry.get("com.vogella.ide.editor.tasks.key");
        TextAttribute tagAttribute = new TextAttribute(color);
        rule = new PropertyNameRule(new Token(tagAttribute));
        scanner.setRules(new IRule[] { rule });

    }

    public PropertiesReconciler() {

        IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager();
        ITheme currentTheme = themeManager.getCurrentTheme();
        colorRegistry = currentTheme.getColorRegistry();

        scanner = new RuleBasedScanner();
        updateRule();

        DefaultDamagerRepairer dr = new DefaultDamagerRepairer(scanner);
        this.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE);
        this.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE);
    }
}

6. 연습: TODO 프로퍼티를 위한 컨텐츠 지원을 구현

이 연습에서 .tasks 파일을 위한 컨텐츠 지원(코드 완성)을 구현한다. 앞서 논의 했듯이, 그 파일들은 컬럼으로 구분된 키와 값의 쌍을 포함 하여야 한다. 속성(Properties)은 커서가 줄의 시작에 위치 하는 경우에 제안되게 될 것이다. 

6.1. 컨텐츠 지원 확장 추가하기

"com.vogella.ide.editor.tasks" 플러그인 manifest 파일에 "org.eclipse.ui.genericeditor.contentAssistProcessors" 확장점(extension point)을 위한 확장을 추가한다. 

<extension point="org.eclipse.ui.genericeditor.contentAssistProcessors"> 
	<contentAssistProcessor class="com.vogella.ide.editor.tasks.TodoPropertiesContentAssistProcessor" contentType="com.vogella.ide.contenttype.tasks"> 
	</contentAssistProcessor> 
</extension>

TodoPropertiesContentAssistProcessor 구현체는 다음 코드와 유사하게 보여야 한다. 

package com.vogella.ide.editor.tasks; import java.util.Arrays; 

import java.util.List; 
import org.eclipse.jface.text.BadLocationException; 
import org.eclipse.jface.text.IDocument; 
import org.eclipse.jface.text.ITextViewer; 
import org.eclipse.jface.text.contentassist.CompletionProposal; 
import org.eclipse.jface.text.contentassist.ICompletionProposal; 
import org.eclipse.jface.text.contentassist.IContentAssistProcessor; 
import org.eclipse.jface.text.contentassist.IContextInformation; 
import org.eclipse.jface.text.contentassist.IContextInformationValidator; 

public class TodoPropertiesContentAssistProcessor implements IContentAssistProcessor { 

	// public as used later by other code 
	public static final List<String> PROPOSALS = Arrays.asList( "ID:", "Summary:", "Description:", "Done:", "Duedate:", "Dependent:"); 

	@Override 
	public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) { 
		
    	IDocument document = viewer.getDocument(); 
		try { 
			int lineOfOffset = document.getLineOfOffset(offset); 
			int lineOffset = document.getLineOffset(lineOfOffset); 

			// 줄의 시작이 아닌 위치의 경우에 어떠한 컨텐츠 지원도 보여주지 않는다. 
			if (offset != lineOffset) { 
				return new ICompletionProposal[0]; 
			} 
		} catch (BadLocationException e) { 
			// 여기서는 무시하고 계속...
		} 

		return PROPOSALS.stream().filter(proposal -> !viewer.getDocument().get().contains(proposal)) 
								.map(proposal -> new CompletionProposal(proposal, offset, 0, proposal.length())) 
								.toArray(ICompletionProposal[]::new); 
	} 

	@Override 
	public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { 
		return null; 
	} 

	@Override 
	public char[] getCompletionProposalAutoActivationCharacters() { 
		return null; 
	} 

	@Override 
	public char[] getContextInformationAutoActivationCharacters() { 
		return null; 
	} 

	@Override 
    public String getErrorMessage() { 
		return null; 
	} 

	@Override 
	public IContextInformationValidator getContextInformationValidator() { 
		return null; 
	} 
}

6.2. 구현체 테스트하기 

IDE를 시작하고 .tasks 파일을 연다. 그리고 컨테츠 지원 활성화 시키기 위해 CTRL+Space 를 누른다. 그 결과는 다음과 같다:

6.3. 추가적인 연습 - 지연된 컨텐츠 지원 프로세서 구현하기

일반적인 에디터는 기본적으로 비동기적 코드 완성을 사용한다. 즉, 심지어 제안 컴퓨터 중에 하나가 느리더라도 사용자 이터페이스를 막지 않는다. 

코드 완성 프로세스에 지연을 추가 함으로써 이것을 테스트 한다. 심지어 컨텐츠 지원이 촉발되더라도 에디터가 사용 가능하게 남아 있는 것을 확인한다.

이후에 다시 지연을 제거하라 

7. 향상된 코드 완성

7.1. 추가 연습 - 접두사 완성 추가하기

IDocument 클래스는 컨텐츠들을 파싱하고 변경하기 위한 많은 유틸리티들을 제공한다.  TodoPropertiesContentAssistProcessor#computeCompletionProposals 메서드를 확당한다. 그래서 접두사에 일치 시킬수 있다. 

예를 들면, "D"를 입력하면,  D와 일치 하는 모든 제안을 보여야 한다.

 

public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {

    IDocument document = viewer.getDocument();

    try {
        int lineOfOffset = document.getLineOfOffset(offset);
        int lineOffset = document.getLineOffset(lineOfOffset);

        int lineTextLenght = offset - lineOffset;
        String lineStartToOffsetValue = document.get(lineOffset, lineTextLenght).toLowerCase();

        return PROPOSALS.stream()
                .filter(proposal -> !viewer.getDocument().get().contains(proposal)
                        && proposal.toLowerCase().startsWith(lineStartToOffsetValue))
                .map(proposal -> new CompletionProposal(proposal, lineOffset, lineTextLenght, proposal.length()))
                .toArray(ICompletionProposal[]::new);
    } catch (BadLocationException e) {
        e.printStackTrace();
    }
    return new ICompletionProposal[0];
}

7.2. 추가 연습 - 컨텐츠 지원의 자동 활성화 가능하게 하기

현재 컨텐츠 지원은 단지 사용자가 CTRL+Space를 누를때 제안들을 보여 준다.  사용자를 위해서는 분명하지 않을지 모른다. 

IContentAssistProcessor는 getCompletionProposalAutoActivationCharacters 메서드를 사용하여 이 행위(CTRL+Space를 누르는 행위)를 변경하는 것을 허용한다. .

사용자를 위해 CTRL+Space 를 누른느 것은 분명하지 않을지 모른다. 모든 문자 컨텐츠 지원을 활성화하는 것을 구현한다.

@Override
public char[] getCompletionProposalAutoActivationCharacters() {
    String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    return str.toCharArray();
}

8. 연습 : 다른 정보를 사용하여 컨텐츠 지원을 구현하기 

같은 컨텐츠 유형을 위해 복수로 컨텐츠 지원 프로세스를 등록하는 것이 가능하다. 

이 연습에서 에디터에 다른 컨텐츠 지원을 추가한다. 이 프로세서는 _ property를 위한 값을 설정하는 것을 허용하게 될 것이다. 이것은 다른 작업에 종속적인 작업을 모델링 하는 것을 허용한다. 

8.1. 새 컨텐츠 지원 프로세서 구현과 등록

IContentAssistProcessor 인터페이스를 구현하는 DependentTodoContentAssistProcessor 클래스를 생성한다. 

"org.eclipse.ui.genericeditor.contentAssistProcessors" 확장점(extension point)에 확장으로 plugin.xml 파일을 통해 이 클래스를 등록한다.

<extension
      ​point="org.eclipse.ui.genericeditor.contentAssistProcessors">
   ​<contentAssistProcessor
         ​class="com.vogella.ide.editor.tasks.TodoPropertiesContentAssistProcessor"
         ​contentType="com.vogella.ide.contenttype.tasks">
   ​</contentAssistProcessor>
​</extension>
​<extension
      ​point="org.eclipse.ui.genericeditor.contentAssistProcessors">
   ​<contentAssistProcessor
         ​class="com.vogella.ide.editor.tasks.DependentTodoContentProcessor"
         ​contentType="com.vogella.ide.contenttype.tasks">
   ​</contentAssistProcessor>
​</extension>

활성화 데이터에 접근하기 위해, 다음 유틸 클래스를 생성한다.

package com.vogella.ide.editor.tasks; 

import org.eclipse.ui.IEditorPart; 
import org.eclipse.ui.IWorkbench; 
import org.eclipse.ui.IWorkbenchPage; 
import org.eclipse.ui.IWorkbenchWindow; 
import org.eclipse.ui.PlatformUI; 

public class Util { 

	private Util() { 
		// 단지 핼퍼 클래스
	} 

	public static IEditorPart getActiveEditor() { 
		IWorkbench workbench = PlatformUI.getWorkbench(); 
		IWorkbenchWindow activeWorkbenchWindow = workbench.getActiveWorkbenchWindow(); 
		if (null == activeWorkbenchWindow) { 
			activeWorkbenchWindow = workbench.getWorkbenchWindows()[0]; 
		} 

		IWorkbenchPage activePage = activeWorkbenchWindow.getActivePage(); 
		if (activePage == null) { 
			return null; 
		} 
        
		return activePage.getActiveEditor(); 
	} 
    
}

DependentTodoContentAssistProcessor 구현체는 다음과 같이 보여야 한다. :

package com.vogella.ide.editor.tasks; 

import static com.vogella.ide.editor.tasks.Util.getActiveEditor; 
import java.util.Arrays; import org.eclipse.core.resources.IContainer; 
import org.eclipse.core.resources.IResource; 
import org.eclipse.core.runtime.CoreException; 
import org.eclipse.jface.text.BadLocationException; 
import org.eclipse.jface.text.IDocument; 
import org.eclipse.jface.text.ITextViewer; 
import org.eclipse.jface.text.contentassist.CompletionProposal; 
import org.eclipse.jface.text.contentassist.ICompletionProposal; 
import org.eclipse.jface.text.contentassist.IContentAssistProcessor; 
import org.eclipse.jface.text.contentassist.IContextInformation; 
import org.eclipse.jface.text.contentassist.IContextInformationValidator; 
import org.eclipse.ui.IEditorInput; 
import org.eclipse.ui.IEditorPart; 

public class DependentTodoContentAssistProcessor implements IContentAssistProcessor { 

	@Override 
	public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) { 

		IDocument document = viewer.getDocument(); 
		IEditorPart activeEditor = getActiveEditor(); 
        
		if (activeEditor != null) { 
			IEditorInput editorInput = activeEditor.getEditorInput(); 
			IResource adapter = editorInput.getAdapter(IResource.class); 
			IContainer parent = adapter.getParent(); 
            
			try { 
				int lineOfOffset = document.getLineOfOffset(offset); 
				int lineOffset = document.getLineOffset(lineOfOffset); 
				String lineProperty = document.get(lineOffset, offset - lineOffset); 

				// 컨텐츠 지원은 단지 라인에 종속적으로 사용되어야 한다. 
				if (lineProperty.startWith("Dependent:")) { 
					IResource[] members = parent.members(); 
					// Only take resources, which have the "tasks" file extension and skip the current resource itself 
					return Arrays.asList(members).stream().filter( res -> !adapter.getName().equals(res.getName()) && "tasks".equals(res.getFileExtension())) 
                    									.map(res -> new CompletionProposal(res.getName(), offset, 0, res.getName().length())) 
                                                        .toArray(ICompletionProposal[]::new); 
				} 
			} catch (CoreException | BadLocationException e) { 
				// 여기 무시 하고 계속... 
			} 
		} 
        
		return new ICompletionProposal[0]; 
        
	} 

	@Override 
	public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { 
	return null; 
	} 

	@Override 
	public char[] getCompletionProposalAutoActivationCharacters() { 
		return null; 
	} 

	@Override 
	public char[] getContextInformationAutoActivationCharacters() { 
		return null; 
	} 

	@Override 
	public String getErrorMessage() { 
		return null; 
	} 

	@Override 
	public IContextInformationValidator getContextInformationValidator() { 
		return null; 
	} 
}

8.2. 검증하기

IDE를 시작하고 적어도 2 .tasks 파일들이 가능한지를 확인한다 그 파일 중 하나를 오픈한다. 그리고 컨텐츠 지원을 활성화 하기 위한 프로퍼티에 종속된 이후 바로 CTRL+Space 키를 누른다.  결과는 다음과 같아야 한다:

 

9. 연습: 문서 변경에 반응하기

IDocumentSetupParticipant는 기능을 촉발하는 문서의 설정과 변경하는 동안 알림을 받는 것을 허용한다. 컨텐츠 유형을 위한 "org.eclipse.core.filebuffers.documentSetup" 확장점(extension point)을 통한 구현체를 등록 할 수 있다. 

예제에서, .tasks 파일에 빠진 키를 위해 문제점(Problems) 뷰에 마커를 추가 하기 위해 사용한다.

9.1. IDocumentSetupParticipant 구현과 등록

TodoMarkerDocumentSetup은 현재 변경된 IResource를 위한 마커를 적용하기 위한 IDocumentListener을 등록한다.

package com.vogella.ide.editor.tasks; 

import static com.vogella.ide.editor.tasks.Util.getActiveEditor; 
import java.util.Arrays; import java.util.List; 
import java.util.Optional; 
import org.eclipse.core.filebuffers.IDocumentSetupParticipant; 
import org.eclipse.core.resources.IMarker; 
import org.eclipse.core.resources.IResource; 
import org.eclipse.core.runtime.CoreException; 
import org.eclipse.core.runtime.ICoreRunnable; 
import org.eclipse.core.runtime.jobs.Job; 
import org.eclipse.jface.text.DocumentEvent; 
import org.eclipse.jface.text.IDocument; 
import org.eclipse.jface.text.IDocumentListener; 
import org.eclipse.ui.IEditorInput; 
import org.eclipse.ui.IEditorPart; 
import org.eclipse.ui.PlatformUI; 

public class TodoMarkerDocumentSetup implements IDocumentSetupParticipant { 

	private static final String TODO_PROPERTY = "todoProperty"; 

	@Override 
	public void setup(IDocument document) { 
    
		document.addDocumentListener(new IDocumentListener() { 
			private Job markerJob; 
        
			@Override 
			public void documentChanged(DocumentEvent event) { 
				IEditorPart activeEditor = getActiveEditor(); 
				if (activeEditor != null) { 
					IEditorInput editorInput = activeEditor.getEditorInput(); 
					IResource adapter = editorInput.getAdapter(IResource.class); 
					if (markerJob != null) { 
						markerJob.cancel(); 
					} 
					markerJob = Job.create("Adding Marker", (ICoreRunnable) monitor -> createMarker(event, adapter));  // --> 1
					markerJob.setUser(false); markerJob.setPriority(Job.DECORATE); 

					// 계속 타이핑을 핸들링하기 위해 사용자 액션에 반응하기 전에 지연 설정 
					markerJob.schedule(500);  // --> 2
				} 
			} 

			@Override 
			public void documentAboutToBeChanged(DocumentEvent event) { 
				// 필요하지 않음
			} 
		}); 
	}

	private void createMarker(DocumentEvent event, IResource adapter) throws CoreException { 
		String docText = event.getDocument().get(); 
		for (String todoProperty : TodoPropertiesContentAssistProcessor.PROPOSALS) { 
			List<IMarker> markers = Arrays .asList(adapter.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE)); 
			Optional<IMarker> findAny = markers.stream().filter(m -> todoProperty.equals(m.getAttribute(TODO_PROPERTY, ""))).findAny(); 
			if (docText.contains(todoProperty) && findAny.isPresent()) { 
				findAny.get().delete(); 
			} else if (!docText.contains(todoProperty) && !findAny.isPresent()) { 
				IMarker marker = adapter.createMarker(IMarker.PROBLEM); 
				marker.setAttribute(IMarker.MESSAGE, todoProperty + " property is not set"); 
				marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO); 
				marker.setAttribute(IMarker.LOCATION, "Missing line"); 
				marker.setAttribute(TODO_PROPERTY, todoProperty); 
			} 
		} 
	} 
}
1 이 구현체는 Job 구현체를 사용한다. 그래서 UI 쓰레드는 처리 하는 동안 블럭되지 않는다. 
2 job이 문서 변경이 시작 되었기 때문에, 지연으로 시작된다. 그래서 다른 문서의 변경으로 취소 될 수 있고 불필요한 마커 생성이 생략 될 수 있다. 

plugin.xml에 다음 확장을 추가한다..

<extension point="org.eclipse.core.filebuffers.documentSetup"> 
	<participant class="com.vogella.ide.editor.tasks.TodoMarkerDocumentSetup" contentTypeId="com.vogella.ide.contenttype.tasks"> 
    </participant> 
</extension>

9.2. IDocumentSetupParticipant 등록과 구현

그 결과는 다음과 같이 보여야 한다.:

현재 이미지 링크가 깨져 있슴

10. 연습: 작업을 위한 하이라이팅 구현

이클립스는 다른 파일들에 참조 사이에 탐색을 지원한다. 소스 에디터는 전형적으로 열려있는 파일에 참조에 CTRL + left 마우스 클릭을 지원한다.

*.tasks 파일들은 종속(Dependent)을 통해서 가리킬수 있다: 같은 프로젝트 내에 다른 *.task 파일에 프로퍼티 이 연습에서 에디터는 사용자가 종속 작업 파일 사이 탐색하기 위한것을 허용해야한다.

 

10.1. manifest 위존관계 추가하기

"com.vogella.ide.editor.tasks"에 플러그인 의존관계로 "org.eclipse.ui.ide" 추가한다.

10.2. 종속 task 파일을 위한 하이퍼링크 구현하기

다음 IHyperlink 구현체 생성하기.

import org.eclipse.core.resources.IFile; 
import org.eclipse.jface.text.IRegion; 
import org.eclipse.jface.text.hyperlink.IHyperlink; 
import org.eclipse.ui.IWorkbenchPage; 
import org.eclipse.ui.PartInitException; 
import org.eclipse.ui.PlatformUI; 
import org.eclipse.ui.ide.IDE; 

public class ResourceHyperlink implements IHyperlink { 

	private IRegion region; 
	private String hyperlinkText; 
	private IFile resource; 

	public ResourceHyperlink(IRegion region, String hyperlinkText, IFile resource) { 
		this.region = region; 
		this.hyperlinkText = hyperlinkText; 
		this.resource = resource;
	}

	@Override 
	public IRegion getHyperlinkRegion() { 
		return region; 
	} 

	@Override 
	public String getTypeLabel() { 
		return null; 
	} 

	@Override 
	public String getHyperlinkText() { 
		return hyperlinkText; 
	} 

	@Override 
	public void open() { 
	IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); 
		try { 
			IDE.openEditor(activePage, resource); 
		} catch (PartInitException e) { 
			e.printStackTrace(); 
		} 
	}
    
}

다음 IHyperlinkDetector 구현체를 생성한다.

import java.util.Arrays; 
import org.eclipse.core.resources.IContainer; 
import org.eclipse.core.resources.IFile; 
import org.eclipse.core.resources.IResource; 
import org.eclipse.core.runtime.CoreException; 
import org.eclipse.jface.text.BadLocationException; 
import org.eclipse.jface.text.IDocument; 
import org.eclipse.jface.text.IRegion; 
import org.eclipse.jface.text.ITextViewer; 
import org.eclipse.jface.text.Region; 
import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector; 
import org.eclipse.jface.text.hyperlink.IHyperlink; 
import org.eclipse.ui.IEditorInput; 
import org.eclipse.ui.IEditorPart; 
import org.eclipse.ui.IWorkbench; 
import org.eclipse.ui.IWorkbenchPage; 
import org.eclipse.ui.IWorkbenchWindow; 
import org.eclipse.ui.PlatformUI; 

public class DependentTodoHyperlinkDetector extends AbstractHyperlinkDetector { 

	private static final String DEPENDENT_PROPERTY = "Dependent:"; 

	@Override 
	public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) { 
		IDocument document = textViewer.getDocument(); 
		IWorkbench workbench = PlatformUI.getWorkbench(); 
		IWorkbenchWindow activeWorkbenchWindow = workbench.getActiveWorkbenchWindow(); 
		if (null == activeWorkbenchWindow) { 
			activeWorkbenchWindow = workbench.getWorkbenchWindows()[0]; 
		} 

		IWorkbenchPage activePage = activeWorkbenchWindow.getActivePage(); 
		IEditorPart activeEditor = activePage.getActiveEditor();

		if (activeEditor != null) { 
			IEditorInput editorInput = activeEditor.getEditorInput(); 
			IResource adapter = editorInput.getAdapter(IResource.class); 
			IContainer parent = adapter.getParent(); 

			try { 
				int offset = region.getOffset(); 
				IRegion lineInformationOfOffset = document.getLineInformationOfOffset(offset); 
				String lineContent = document.get(lineInformationOfOffset.getOffset(), lineInformationOfOffset.getLength()); 

				// 컨텐츠 지원은 단지 종속 라인에서 사용되어야 한다.  
				if (lineContent.startsWith(DEPENDENT_PROPERTY)) { 
					String dependentResourceName = lineContent.substring(DEPENDENT_PROPERTY.length()).trim(); 
					Region targetRegion = new Region(lineInformationOfOffset.getOffset() + DEPENDENT_PROPERTY.length(), lineInformationOfOffset.getLength() - DEPENDENT_PROPERTY.length()); 
					IResource[] members = parent.members(); 

					// 단지 "todo" 파일 확장자를 가지는 리소스만 취한다 
					// 그리고 현재 자신의 리소스를 넘어간다. 
					return Arrays.asList(members).stream() 
								.filter(res -> res instanceof IFile && dependentResourceName.equals(res.getName())) 
								.map(res -> new ResourceHyperlink(targetRegion, res.getName(), (IFile) res)) 
								.toArray(IHyperlink[]::new); 
				} 
			} catch (CoreException | BadLocationException e) { 
				e.printStackTrace(); 
			} 
		} 

		// 새 IHyperlink[0]를 반환하지 않는다 
    	// 왜냐하면 배열 단지 null일 수 있고 또는 비어 있지 않을 수 있기 때문이다
		return null; 
	} 
}

DependentTodoHyperlinkDetector 클래스는 다음처럼 plugin.xml에 등록될 수 있다.:

<extension point="org.eclipse.ui.workbench.texteditor.hyperlinkDetectors"> 
	<hyperlinkDetector activate="true" 
    		class="com.vogella.ide.editor.tasks.DependentTodoHyperlinkDetector" 
        	id="com.vogella.ide.editor.tasks.hyperlinkDetector" 
       		name="Hyperlink to other tasks files" 
        	targetId="org.eclipse.ui.genericeditor.GenericEditor"> 
    </hyperlinkDetector> 
</extension>

10.3. 검증하기

검증하기 위해 적어도 2개 *.tasks 파일들을 생성하고 그 중에 하나를 가르키기 위한 종속적 속성을 사용한다. 연결된 파일을 탐색하기 위해 CTRL + 왼쪽 마우스 클릭을 사용할수 있는것을 검증한다. 

Dependent:Training2.tasks

 

이 연습에서 일반적 에디터에서 vogella 키워드를 위한 하이퍼링크 탐지기를 생성한다.

11.1. 프로젝트를 생성하고 의존관계를 추가한다. 

"com.vogella.ide.editor.companylink" 이름으로 된 새 플러그인 프로젝트를 생성한다..

의존관계로 다음 플러그인들을 추가한다.:

  • org.eclipse.ui

  • org.eclipse.jface

  • org.eclipse.ui.workbench.texteditor

유니크 ID와 기술한 이름으로 "org.eclipse.ui.workbench.texteditor.hyperlinkDetectors" 확장 추가하기.

하이퍼링크를 결정하고 그 하이퍼링크와 상효작용하기 위한 다음 두 클래스를 생성한다. 

package com.vogella.ide.editor.companylink; 

import org.eclipse.jface.text.IRegion; 
import org.eclipse.jface.text.hyperlink.IHyperlink; 
import org.eclipse.swt.program.Program; 

public class VogellaHyperlink implements IHyperlink { 

	private final IRegion fUrlRegion;
    
	public VogellaHyperlink(IRegion urlRegion) { 
		fUrlRegion = urlRegion; 
	} 

	@Override 
	public IRegion getHyperlinkRegion() { 
		return fUrlRegion; 
	} 

	@Override 
	public String getTypeLabel() { 
		return null; 
	} 

	@Override 
	public String getHyperlinkText() { 
		return "Open vogella website"; 
	} 

	@Override 
	public void open() { 
		Program.launch("https://www.vogella.com/"); 
	} 
}
package com.vogella.ide.editor.companylink; 

import org.eclipse.jface.text.BadLocationException; 
import org.eclipse.jface.text.IDocument; 
import org.eclipse.jface.text.IRegion; 
import org.eclipse.jface.text.ITextViewer; 
import org.eclipse.jface.text.Region; 
import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector; 
import org.eclipse.jface.text.hyperlink.IHyperlink; 

public class VogellaHyperlinkDetector extends AbstractHyperlinkDetector { 

	public VogellaHyperlinkDetector() { 
	} 

	@Override 
	public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) { 

		IDocument document = textViewer.getDocument(); 
		int offset = region.getOffset(); 

		// 관련 문자 추출한다
		IRegion lineRegion; 
		String candidate; 
		try { 
			lineRegion = document.getLineInformationOfOffset(offset); 
			candidate = document.get(lineRegion.getOffset(), lineRegion.getLength()); 
		} catch (BadLocationException ex) { 
			return null; 
		} 

		// 키워드를 찾는다.
		int index = candidate.indexOf("vogella"); 
		if (index != -1) { 

			// 키워드를 포함하는 영역을 탐색한다. 
			IRegion targetRegion = new Region(lineRegion.getOffset() + index, "vogella".length()); 
			if ((targetRegion.getOffset() <= offset) && ((targetRegion.getOffset() + targetRegion.getLength()) > offset)) {
				// 링크를 생성한다. 
				return new IHyperlink[] { new VogellaHyperlink(targetRegion) };
            }
		} 
        
		return null; 
        
	} 
    
}

11.3. 검증하기

플러그인을 시작하고 텍스트 에디터에 예를 들면, Java 에디터에서 "vogella"를 추가 한다. Ctrl을 누르고 "vogella"를 클릭한다.외부 브라우저에서 https://www.vogella.com/ 웹사이트가 열려야 한다.

12. 연습: task 파일에 코드 마이닝(추출) 정보 추가 하기.

이클립스는 텍스트 에디터에서 그 정보를 향샹 시키기 위해 추가적인 정보를 보여 주는 것을 지원한다. 그 정보는 저장되지 않는다. 그러나 사용자가 더 나은 내용을 이해 하는데 도움을 준다. 또한 코드 마이닝은 행위를 등록하고 텍스트 에디터를 사용한 디렉토리의 행위를 수행할 수 있다. 

이 연습에서 추가적인 정보를 보여주고 사용자가 코드 마이닝을 통해 어떤 행위를 수행하는 것을 허용하기 위해 task 에디터를 위한 코드 마이닝을 향상 시키게 될 것이다. 

12.1. 코드 마이닝 구현과 등록 

라인 헤더 어노테이션을 생성한 다음 클래스를 구현한다. 

package com.vogella.ide.editor.tasks; 

import java.util.concurrent.CompletableFuture; 
import org.eclipse.core.runtime.IProgressMonitor; 
import org.eclipse.jface.text.BadLocationException; 
import org.eclipse.jface.text.IDocument; 
import org.eclipse.jface.text.ITextViewer; 
import org.eclipse.jface.text.codemining.ICodeMiningProvider; 
import org.eclipse.jface.text.codemining.LineHeaderCodeMining; 

public class TaskCodeMining extends LineHeaderCodeMining { 

	public TaskCodeMining(int beforeLineNumber, IDocument document, ICodeMiningProvider provider) throws BadLocationException { 
		super(beforeLineNumber, document, provider); 
	} 

	@Override 
	protected CompletableFuture<Void> doResolve(ITextViewer viewer, IProgressMonitor monitor) { 
		return CompletableFuture.runAsync(() -> { super.setLabel("This is additional information about the tasks"); }); 
	} 
    
}

다음 ICodeMiningProvider의 구현체를 생성한다..

package com.vogella.ide.editor.tasks; 

import java.util.ArrayList; 
import java.util.List; 
import java.util.concurrent.CompletableFuture; 
import org.eclipse.core.runtime.IProgressMonitor; 
import org.eclipse.jface.text.BadLocationException; 
import org.eclipse.jface.text.IDocument; 
import org.eclipse.jface.text.ITextViewer; 
import org.eclipse.jface.text.codemining.ICodeMining; 
import org.eclipse.jface.text.codemining.ICodeMiningProvider; 

public class TaskCodeMiningProvider implements ICodeMiningProvider { 

	public TaskCodeMiningProvider() { 
	} 

	@Override 
	public CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor) { 

		return CompletableFuture.supplyAsync(() -> { 
			List<ICodeMining> minings = new ArrayList<>(); 
			IDocument document = viewer.getDocument(); 
			try { 
				minings.add(new TaskCodeMining(0, document, this)); 
			} catch (BadLocationException e) { 
				e.printStackTrace(); 
			} 
            return minings; 
		}); 
	} 

	@Override 
	public void dispose() { 
	} 

}

"org.eclipse.ui.genericeditor.reconcilers" 확장을 통해 컨텐츠 유형을 위한 기본 CodeMiningReconciler를 등록한다..

<extension point="org.eclipse.ui.genericeditor.reconcilers"> 
	<reconciler class="org.eclipse.jface.text.codemining.CodeMiningReconciler" 
			contentType="com.vogella.ide.contenttype.tasks"> 
	</reconciler> 
</extension>

이제 plugin.xml에 다음 확장을 추가한다..

<extension point="org.eclipse.ui.workbench.texteditor.codeMiningProviders"> 
	<codeMiningProvider class="com.vogella.ide.editor.tasks.TaskCodeMiningProvider" 
			id="com.vogella.ide.editor.tasks.codeMiningProvider" 
			label="Show additional task info"> 
		<enabledWhen> 
			<with variable="editorInput"> 
				<adapt type="org.eclipse.core.resources.IFile"> 
					<test property="org.eclipse.core.resources.contentTypeId" 
							value="com.vogella.ide.contenttype.tasks" /> 
				</adapt> 
			</with>
		</enabledWhen> 
	</codeMiningProvider> 
</extension>

12.2. Validate

에디터가 헤더에 추가 정보를 보여주는지 확인하라.

13. 추가 연습 - 코드 마이닝에 액션 추가하기

사용자가 몇몇 액션을 촉발하는 것을 허용하기 위해  TaskCodeMining에 getAction을 재정의 한다. 

package com.vogella.ide.editor.tasks; 

import java.util.concurrent.CompletableFuture; 
import java.util.function.Consumer; 
import org.eclipse.core.runtime.IProgressMonitor; 
import org.eclipse.jface.dialogs.MessageDialog; 
import org.eclipse.jface.text.BadLocationException; 
import org.eclipse.jface.text.IDocument; 
import org.eclipse.jface.text.ITextViewer; 
import org.eclipse.jface.text.codemining.ICodeMiningProvider; 
import org.eclipse.jface.text.codemining.LineHeaderCodeMining; 
import org.eclipse.swt.events.MouseEvent; 

public class TaskCodeMining extends LineHeaderCodeMining { 

	private ITextViewer viewer; 
    
	public TaskCodeMining(int beforeLineNumber, IDocument document, ICodeMiningProvider provider, boolean isValid) throws BadLocationException { 
		super(beforeLineNumber, document, provider); 
	} 

	@Override 
	protected CompletableFuture<Void> doResolve(ITextViewer viewer, IProgressMonitor monitor) { 
		this.viewer = viewer; 
		return CompletableFuture.runAsync(() -> { super.setLabel("This is additional information about the tasks"); }); 
	} 

	@Override 
	public Consumer<MouseEvent> getAction() { 
    	return r->showMessageDialog(); 
    } 

	private void showMessageDialog() { 
		MessageDialog.openInformation(viewer.getTextWidget().getShell(), "Clicked", "You clicked on the code mining annotation"); 
	} 
}

14. Optional exercise - Extend your code mining

Clone the Git repository located at https://github.com/angelozerr/EclipseConFrance2018. Import the included project and test them.

Use the information to extend your code mining, e.g., add code minings to each line in your editor.

15. 추가 연습 - JDT 코드 마이닝 리뷰하기

https://github.com/angelozerr/jdt-codemining 위치한 Git 저장소를 복제한다.

코드를 복제하고 프로젝트를 import 한다. 런타임 이클립스에 해당소스를 포함시킨다. 코드를 리뷰한다.

16. 연습: 사용자 정의 에디터 구현하기

새로운것을 저으이 하는 데신에 일반 에디터를재 사용하는 것을 권장하기 때문에, 이것은 여전히 지원된다. 이 장은 어떻게 할 수 있는지 설명을 제공한다. 

16.1. IEditorPart 와 EditorPart

이클립스 IDE를 위한 새 에디터를 정의 하기 위해, 전형적으로, * IEditorInput 클래스를 생성한다. * "org.eclipse.ui.editors" 확장점(extension point)을 위한 확장을 정의한다. * IEditorPart를 확장한 클래스를 구현한다.

IEditorInput는 에디터를 위한 모델로 서비스한다. 이클립스는 IEditorInput 객체 버퍼를 제공하게 될 것이고 그러므로   그 객체는 상대적으로 작아야 한다.

그것은 모델의 경령화 표현을 제공되게 되도록 되어 있다. 예를들면 이클립스 IDE 완전한 파일로 핸들링 하지 않고 파일을 식별하는 IEditorInput 객체를 사용한다. 

IEditorInput의 equals()은 에디터의 본질을 정의한다. 즉, 만약 에디터가 이미 열려 있는지 아닌지 결정하는데 사용된다.

에디터는  init() 메서드에서 IEditorSite 와IEditorInput 를 받는다. setInput() 메서드를 통한 input 과 setSite() 메서드를 통한 side를 설정해야 한다. .

createPartControl() (사용자 인터페이스를 생성하는 ) 이전에 init()이 호출된다. 그러므로 UI 생성하는 동안 input을 사용할 수 있다. 

자체 퍼스펙티브를 정의 한다면, 퍼스펙티브 구현체에 다음 코드를 통해서 에디터 영역을 가능하게 할 수 있다. 

import org.eclipse.ui.IPageLayout; 

public class Perspective implements IPerspectiveFactory { 

	public void createInitialLayout(IPageLayout layout) { 
		//layout.setEditorAreaVisible(false); 
        layout.setFixed(true); 
	} 
    
}

16.2. 에디터 제목과 툴팁을 설정하기 

기본적으로, 에디터는 IEditorInput으로 부터 툴팁과 제목을 사용하게될 것이다.  그러므로 에디터에 타이틀과 툴팁을 변경하는 것을 원할지 모른다. 에디터의 제목을 설정하기 위해 setPartName()와 툴닙을 설정하기 위해 getTitleToolTip()을 사용한다. 툴팁에 대한 자세한 것은 버그 https://bugs.eclipse.org/bugs/show_bug.cgi?id=107772 를 보라.

16.3. 에디터 내용 저장하기 

isDirty()메서드는 에디터가 변경된 데이타를 포함하는지를 결정하다. 이 더티 상태에 벼경에 대한 워크벤치 정보를 위해, 이벤트를 시작한다.

firePropertyChange(IEditorPart.PROP.DIRTY);

Adding Colors and Font preferences blog post

https://www.eclipse.org/eclipse/platform-text/eclipseCon/talk.pdf

https://flylib.com/books/en/1.70.1/creating_a_text_editor_with_jface_text.html

https://www.eclipse.org/articles/Article-Folding-in-Eclipse-Text-Editors/folding.html

https://wiki.eclipse.org/FAQ_How_do_I_use_the_text_document_model%3F

18. vogella 트레이닝과 컨설팅 지원

온라인 트레이닝

fitness_center

사이트 트레이닝

group

컨설팅

Copyright © 2012-2019 vogella GmbH. Free use of the software examples is granted under the terms of the Eclipse Public License 2.0. This tutorial is published under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Germany license.

Licence

Source code

Sponsor our Open Source development activities and our free content to help us make development easier for everyone

+ Recent posts