1. Eclipse Editors

An editor allows to modify data. It typically requires that the user explicitly performs a save action to apply the changes to the data. By default, Eclipse opens editors in the same shared area.

1.1. Defining an editor

If you are planning to provide editor functionality for a certain file extension or content type, you basically have two choices:

  • Re-use the generic editor and extend it

  • Define your own editor

Add support a new content type in the generic editor you need to:

  • add a content type

  • register a PresentationReconsiler to it via the org.eclipse.ui.genericeditor.presentationReconcilers extension point

Reusing the generic editor is the preferred choice as of Eclipse 4.8 and will be demonstrated in the following exercises.

1.2. Introduction to presentation reconciler

The highlighting of source code can be archived by using an presentation reconciler. Such a presentation reconciler can be defined via an org.eclipse.ui.genericeditor.presentationReconcilers extension. It requires the specification of a contentType and a class, which implements the IPresentationReconciler interface. When using an IPresentationReconciler certain IRules can be applied for a specified content type. An IRule defines a rule used in the scanning of text for the purpose of document partitioning or text styling.

2. Implementing your custom editor

While it is recommended to reuse the generic editor instead defining a new one, this is still supported. This chapter gives an explanation how this can be done.

2.1. IEditorPart and EditorPart

To define a new editor for the Eclipse IDE, you typically: * Create an IEditorInput class * Define an extension for the org.eclipse.ui.editors extension point * Implement a class extending IEditorPart

IEditorInput serves as the model for the editor. Eclipse will buffer IEditorInput objects therefore this object should be relatively small.

It is supposed to be a light-weight representation of the model. For example the Eclipse IDE uses IEditorInput objects to identify files without handling with the complete file.

The equals() of IEditorInput define the identity of the editor, e.g., it will be used to determine if an editor is already open or not.

The editor receives the IEditorSite and the IEditorInput in the init() method. It must set the input via the setInput() method and the side via the setSite() method.

init() is called before createPartControl() (which creates the user interface). Therefore you can use the input during your UI creation.

If you define your own perspective, you can enable the editor area via the following code in your perspective implementation.

import org.eclipse.ui.IPageLayout;

public class Perspective implements IPerspectiveFactory {

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

}

2.2. Setting the editor title and tooltip

By default, the editor will use the tooltip and title from the IEditorInput. Therefore you may want to change the title and tooltip in your Editor. Use setPartName() to set the title of the Editor and getTitleToolTip() for setting the tooltip. See Bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=107772 for details on the tooltip.

2.3. Saving the editor content

The method isDirty() determines if the editor contains modified data. For inform the workbench about changes in the dirty state you fired an event.

firePropertyChange(IEditorPart.PROP.DIRTY);

2.4. API for working with editors

You can open an Editor via the current active page. For this you need the EditorInput object and the ID for the editor which is defined in the "org.eclipse.ui.editors" extension point.

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

To get the page you can use:

// If you are in a view
getViewSite().getPage();
// If you are in an command
HandlerUtil.getActiveWorkbenchWindow(event).getActivePage();
// Somewhere else
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();

If you hold down the Ctrl and click on an element in the Java editor you can navigate to it.

This functionality is provided via extensions for the org.eclipse.ui.workbench.texteditor.hyperlinkDetectors extension point. The specified name is visible in the preferences under General  Editors  Text Editors  Hyperlinking. The targetId points to the type of editor you want to support. If you want to use in all text editors use org.eclipse.ui.DefaultTextEditor. To target the generic editor use the org.eclipse.ui.genericeditor.GenericEditor target id.

An IHyperlinkDetector is supposed to return an array of IHyperlink objects. The IHyperlink implemention performs the hyperlink action.

3.2. Adding colors and fonts preferences

Eclipse provides a page for the customizations of colors and fonts by the user under General  Appearance  Colors and Fonts.

To define an entry for this page, you need to define an extension for the org.eclipse.ui.themes extension point.

For example, you can provide a category, font and color with the following entry in the plugin.xml file or your plug-in.

 <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>

The value for the color can be a COLOR_* constants defined in the SWT class. You can also specify RGB values like 255,0,0. The value for the font is defined via the following pattern:`fontname-style-height`

The preference can now be changed via the user or via the CSS engine. To get the current value you can use the 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");

3.3. Custom spelling engine

The org.eclipse.ui.workbench.texteditor plug-in provides the option to register a custom spelling engine via the org.eclipse.ui.workbench.texteditor.spellingEngine extension point.

4. Exercise : Extend the generic editor to support a custom file type

In this exercise you associate files with the tasks extension with the generic text editor in Eclipse. Within this file, you want to support editing property files as for example.

test:Hello
Helper:stuff

4.1. Create a new plug-in

Create a new plug-in project called com.vogella.ide.editor.tasks.

4.2. Add Manifest dependencies

Open the editor for the MANIFEST.MF file either via double-click or via right-mouse click → Open with - Plug-in Manifest Editor. Add the following dependencies via the Dependencies tab using the Add button.

  • 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

The resulting MANIFEST.MF should look similar to the following. Bundle-versions have been removed as they change to frequently.

After each change to the MANIFEST.MF file you need to save the changes by pressing Ctrl + S.

contentTypes extension08

If you select the MANIFEST.MF tab, you can see this file as plain text. It should look like the following (again version numbers have been removed as they change frequently).

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-1.8
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

4.3. Define content type

Still using the MANIFEST.MF editor, open the Extensions tab.

contentTypes extension09

Add an extension for the org.eclipse.core.contenttype.contentTypes extension point. Use the Add button for this.

contentTypes extension10

Right-click on your new entry and select New  content-type.

contentTypes extension12

Specify a content type for files with the .tasks extension similar to the following screenshot.

contentTypes extension14

You can see the text representation of this data by clicking the the plugin.xml.

Your plugin.xml file should look similar to the following listing.

<?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>

4.4. Associate content type with editor

This content type can be associated with a certain editor. The org.eclipse.ui.editors extension point can be used for this.

Add the org.eclipse.ui.editors extension via the Add button on the Extensions tab.

editor association10

Right-click on your org.eclipse.ui.editors, and select New  editorContentTypeBinding to define this.

editor association20
editor association30

The resulting plugin.xml should now looks similar to the following.

<?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>

4.5. Add your plug-in to your product via the feature

If you are using a product, add your new plug-in to your feature and start a runtime Eclipse via the product. Otherwise, update the launch configuration directly to ensure you new plug-in is included in the start.

4.6. Test your development

Start a runtime Eclipse which contains your new plug-in.

Ensure that you content type is visible in Window  Preferences  General  Content Types.

content type task result

Create a new project and a new file with the .tasks extension. If you open the file, it should open in the generic text editor.

tasks in genericeditor10

Use the Plug-in Spy Shift+Alt+F1 to see the editor class, it should be the ` ExtensionBasedTextEditor` class. To use the Plug-in Spy in your runtime IDE, add the org.eclipse.pde feature to your product. Ensure you start via the product.

5. Optional exercise: Create a editor association for .gradle files

5.1. Associate Gradle with the generic editor

Associate the .gradle file extension with the generic editor. If you have Gradle installed into your IDE, ensure that you do not include it into your runtime IDE.

5.2. Validate

Ensure that .gradle files open in the generic editor.

6. Exercise: Implementing syntax highlighting

In this exercise .tasks files you implement syntax highlighting for your editor.

6.1. Implement syntax highlighting

Continue to work in the com.vogella.ide.editor.tasks plug-in.

Implement the following class to define a 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();
        }

        // put the scanner back to the original position if no match
        for (int i = 0; i < count; i++) {
            scanner.unread();
        }

        return Token.UNDEFINED;
    }
}

Implement the following PresentationReconciler as your reconciler.

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);
    }
}

Add the an extension to the org.eclipse.ui.genericeditor.presentationReconcilers extension point to the plugin.xml file of the com.vogella.ide.editor.tasks plug-in.

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

6.2. Test your implementation

Restart your runtime Eclipse and open your file. Enter a few property values in the file.

test:Hello
Helper:stuff

The result should look similar to this:

editor syntax highlight

7. Optional exercise: Add Gradle syntax highlighting to the generic editor

This exercise requires that you associated the .gradle file extension with the generic editor.

Use two SingleLineRule to highlight quotes, string between '' and "", e.g., 'doit'. Use a NumberRule to highlight numbers.

Implement your customer rule GradleKeywordRule extending WordRule to identify at highlight the words allprojects and apply. This requires your custom IWordDetector implementation.

package com.vogella.ide.editor.gradle;

import org.eclipse.jface.text.rules.IWordDetector;

class Detector implements IWordDetector {
    public boolean isWordStart(char c) {
        return Character.isAlphabetic(c);
    }

    public boolean isWordPart(char c) {
        return Character.isAlphabetic(c);
    }
}

Use WordRule#addWord in your GradleKeywordRule constructor for adding words.

 public GradleKeywordRule() {
        super(new Detector());
        // TODO add your works with Tolkens
    }

7.1. Validate

Ensure that .gradle files open in the generic editor. Create the following file and check that your syntax highlighting works.

buildscript {
    repositories {
        jcenter()
        mavenLocal()
        maven{ url 'https://dl.bintray.com/vogellacompany/Gradle-AsciiDoctor-Plugin/' }
    }
    dependencies {
        classpath 'com.vogella.gradle:com.vogella.gradle.plugin.asciidoc:0.15.0'
    }
}

allprojects {
    apply plugin: 'com.vogella.gradle.plugin'
}

The result should be similar to the following.

generic editor gradle result