Imediava's Blog

Just another WordPress.com site

Tag Archives: Eclipse

Using Eclipse completion features in your own plug-in

One of the coolest things I found when I first moved to Eclipse was code completion. It was useful and pretty, it allowed me to save typing and it provided useful information in a visually appealing way.

When you’re developing your own plugin for Eclipse you want it to be as cool as possible, so using Eclipse code completion system may come to your mind.

However I could not find many examples online about how to benefit from the system from a plugin developer’s perspective. This post is my try to facilitate the task of familiarizing with the tool. It’s an extension from some of the few links I’ve found with useful and simple information about the subject (http://www.eclipsezone.com/eclipse/forums/t83426.html) .

As the link says, it’s really easy to add content assistance to a text field. Just the following two lines do the trick:

Text t = new Text(parent, SWT.BORDER) // your SWT text field
ContentProposalAdapter adapter = new ContentProposalAdapter( t,
	new TextContentAdapter(),
	new SimpleContentProposalProvider(new String [] {"A", "B", "C"}),
	null,  null)

However there’s a few things that we can modify in the example to give the proposals a better, more Eclipse-like behaviour. The code for the examples is going to be coded with groovyMonkey but translating it to Java it’s fairly simple.

First thing I want to change is the filtering behaviour. With the code from the first example the suggestion is not affected by a change in the text field content. However when developing a plugin what we usually want is that only the proposals that match the typed text are shown. This can be modified enabling the filtering for the proposals provider:

proposalsProvider = new SimpleContentProposalProvider(proposals)
proposalsProvider.setFiltering(true)

The next behaviour we can replace is the way the proposals accepted are handled. The default behaviour is appending the proposal text to the text field content, but that’s not the way we usually want our assistant to behave. What we usually want is to set the selected proposal as the current text for the field. To get that, all we have to do is modify the “acceptance style” to PROPOSAL_REPLACE:

adapter = new ContentProposalAdapter( nameText,  new TextContentAdapter(), proposalsProvider, null,  null)
adapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE)

A bigger example

Finally I’m gonna show how the whole code works in a bigger example. Thanks to groovyMonkey features, playing with Eclipse API is quite easy. I’m gonna use this features to take the names of all the user-defined types in the workspace. This can be done using groovyMonkey with the following snippet:

List types = []
workspace.root.projects.each { project ->

   //selects only the java projects
   if (project.isOpen() && project.isNatureEnabled("org.eclipse.jdt.core.javanature")){
 	   javaProject = JavaCore.create(project)
	   roots = javaProject.getPackageFragments()

	   // Filters the source code packages
	   .findAll{ fragment -> fragment.getKind() == IPackageFragmentRoot.K_SOURCE}

	   .each { fragment ->
	 	    fragment.compilationUnits.each { compilationUnit ->

	 	        for(type in compilationUnit.getAllTypes()){
	 	        	types.add(type.getElementName())
	 	        }
	 	    }

	   }

   }
}

Now I can create a really simple wizard with a text field that uses all the code completion features I have talked about in the post. When the content of the field is modified it suggests user-defined types from the workspace.

/*
 * Menu: CodeCompletion
 * Script-Path: /GroovyMonkeyScripts/propios/CodeCompletion.gm
 * Kudos: imediava
 * License: EPL 1.0
 * Job: UIJob
 */

def wizardDialog1

import org.eclipse.swt.widgets.Text;

import org.eclipse.jface.fieldassist.ContentProposalAdapter
import org.eclipse.jface.fieldassist.SimpleContentProposalProvider
import org.eclipse.jface.fieldassist.TextContentAdapter

import org.eclipse.core.resources.*
import org.eclipse.jdt.core.IPackageFragmentRoot
import org.eclipse.core.runtime.Path
import org.eclipse.jdt.core.JavaCore

List types = []
workspace.root.projects.each { project ->

   //selects only the java projects
   if (project.isOpen() && project.isNatureEnabled("org.eclipse.jdt.core.javanature")){
 	   javaProject = JavaCore.create(project)
	   roots = javaProject.getPackageFragments()

	   // Filters the source code packages
	   .findAll{ fragment -> fragment.getKind() == IPackageFragmentRoot.K_SOURCE}

	   .each { fragment ->
	 	    fragment.compilationUnits.each { compilationUnit ->

	 	        for(type in compilationUnit.getAllTypes()){
	 	        	types.add(type.getElementName())
	 	        }
	 	    }

	   }

   }
}

mainapp = jface.shell( window.getShell()) {

	wizardDialog1 = wizardDialog() {

				wizardPage1 = wizardPage( title:"Step 1", description:"Step 1", closure: { parent ->

					jface.composite( parent ) {
						gridLayout( numColumns : 2, verticalSpacing: 20)

						label( text: "Name" )
						nameText = text(style: "border")

						proposals = types.toArray(new String[0])
						proposalsProvider = new SimpleContentProposalProvider(proposals)
						proposalsProvider.setFiltering(true)
						adapter = new ContentProposalAdapter( nameText,  new TextContentAdapter(), proposalsProvider, null,  null)
						adapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE)
					}
				})
	}

  	wizardDialog1.open()

	wizardPage1.setPageComplete(false)
}

I’ll leave for following posts other changes that can improve the assistant’s look and behaviour like adding icons next to every proposal.

Create search for type dialog with Eclipse JDT API

If you’ve ever wondered how to get the cool window that allow you to select a class type in Eclipse (see the snapshot above) and reuse it for your plugin, the good news are that doing so is fairly simple.

Selec type window

You just have to decide what types you want Eclipse to allow the user to chose. If you want Eclipse to allow any type within a project, get the reference to the project and pass it to JavaUI.createTypeDialog(). Here’s how to do it with groovyMonkey:

/*
 * Menu: Remove Markers
 * Script-Path: /GroovyMonkeyScripts/propios/seleccion_classes.gm
 * Kudos: ERVIN
 * License: EPL 1.0
 * Job: UIJob
 * DOM: http://groovy-monkey.sourceforge.net/update/plugins/net.sf.groovyMonkey.dom
 */

import org.eclipse.jdt.core.JavaCore
import org.eclipse.jdt.core.search.SearchEngine
import org.eclipse.jdt.ui.JavaUI
import org.eclipse.jdt.ui.IJavaElementSearchConstants
import org.eclipse.jdt.core.IJavaElement
import org.eclipse.jface.dialogs.ProgressMonitorDialog

project = workspace.root.projects.find { project -> project.isOpen() && project.isNatureEnabled("org.eclipse.jdt.core.javanature")}
javaProject = JavaCore.create(project)
typeDialog = JavaUI.createTypeDialog(window.getShell(), new ProgressMonitorDialog(window.getShell()),
                                     project,
                                     IJavaElementSearchConstants.CONSIDER_TYPES,
                                     false)
typeDialog.open()

// Type selected by the user
result = typeDialog.getResult()

First you get the project from the workspace and then you need to use JavaCore.create() to get a reference to it as an IJavaProject.

If you want to be picky and decide what specific types you want to be int the dialog, you can do so using SearchEngine.createJavaSearchScope(). Here’s an example on how to add any type in any open Java project in the workspace to the dialog:

/*
 * Menu: Remove Markers
 * Script-Path: /GroovyMonkeyScripts/propios/seleccion_classes.gm
 * Kudos: ERVIN
 * License: EPL 1.0
 * Job: UIJob
 * DOM: http://groovy-monkey.sourceforge.net/update/plugins/net.sf.groovyMonkey.dom
 */

import org.eclipse.jdt.core.JavaCore
import org.eclipse.jdt.core.search.SearchEngine
import org.eclipse.jdt.ui.JavaUI
import org.eclipse.jdt.ui.IJavaElementSearchConstants
import org.eclipse.jdt.core.IJavaElement
import org.eclipse.jface.dialogs.ProgressMonitorDialog

import org.eclipse.core.resources.*
import org.eclipse.jdt.core.IPackageFragmentRoot
import org.eclipse.core.runtime.Path

List types = []
workspace.root.projects.each { project ->

   //selects only the java projects
   if (project.isOpen() && project.isNatureEnabled("org.eclipse.jdt.core.javanature")){
 	   javaProject = JavaCore.create(project)
	   roots = javaProject.getPackageFragments()

	   // Filters the source code packages
	   .findAll{ fragment -> fragment.getKind() == IPackageFragmentRoot.K_SOURCE}

	   .each { fragment ->
	 	    fragment.compilationUnits.each { compilationUnit ->
	 	        types.addAll(compilationUnit.getAllTypes())
	 	    }

	   }

   }
}

searchScope = SearchEngine.createJavaSearchScope(types.toArray(new IJavaElement[0]))
typeDialog = JavaUI.createTypeDialog(window.getShell(),
                                  new ProgressMonitorDialog(window.getShell()),
                                  searchScope,
                                  IJavaElementSearchConstants.CONSIDER_TYPES,
                                  false)
typeDialog.open()

// Type selected by the user
result = typeDialog.getResult()

Restore files from local history with Eclipse API

If you work with Eclipse you probably know that it keeps a copy of every modification made to a file. This is thanks to a backup system Eclipse guys call the Local history. This system is a really useful feature that can be a real lifesaver in some cases. It’s true that the copies are only kept for a relatively short period of time, but that can be configured by changing the Eclipse preferences.

However my interest is not to talk about its features, but to explain how the Local history system can be used when developing Eclipse plugins to implement undo capabilities for your actions.

Suppose your plugin has an action that modifies more than one file of an user’s project and you want to provide the user with the chance to undo that action. In that case, just by reverting every modified file to the state it had before the action’s date using the local history you would have an undo system ready. As simple as that. There is no need to develop the opposite action or to store manually the system state to go back to it.

An example of restoring a file to the it’s inmediately previous state in the local history is the following one coded with GroovyMonkey:

/*
 * Menu: Remove Markers
 * Script-Path: /GroovyMonkeyScripts/monkey/historial_ficheros.gm
 * Kudos: ERVIN
 * License: EPL 1.0
 * DOM: http://groovy-monkey.sourceforge.net/update/plugins/net.sf.groovyMonkey.dom
 */

import org.eclipse.core.resources.*
import org.eclipse.jdt.core.JavaCore
import org.eclipse.jdt.core.IPackageFragmentRoot
import org.eclipse.core.runtime.Path

workspace.root.projects.each { project ->

   //selects only the java projects
   if (project.isOpen() && project.isNatureEnabled("org.eclipse.jdt.core.javanature")){
 	   javaProject = JavaCore.create(project)
	   roots = javaProject.getPackageFragments()
	   
	   // Filters the source code packages
	   .findAll{ fragment -> fragment.getKind() == IPackageFragmentRoot.K_SOURCE}
	   
	   .each { fragment ->
	 	    fragment.compilationUnits.each {
			    file = it.resource
			    file.setContents(file.getHistory(null)[0].
                                         contents,IFile.KEEP_HISTORY, null)
			    file.refreshLocal(IResource.DEPTH_INFINITE, null)
	 	    }
		
	   }
	 
   }
}

Basically what this code does is: it takes all the open Java Projects in Eclipse, and restore the content of every source code file to the last state in their history.

The most interesting part of the snippet are the two following lines:


file.setContents(file.getHistory(null)[0].
                       contents,IFile.KEEP_HISTORY, null)

file.refreshLocal(IResource.DEPTH_INFINITE, null)


The first line takes the first element from the file’s history. Since the getHistory() method returns an ordered array, the first element corresponds to the last state of the file in it’s history. Then it takes the last state’s content and assigns it to the file, thus restoring the file’s content to it’s previous state.

The second line is just in charge of making Eclipse aware of the file’s change.

Hide a project from the workspace in Eclipse

When you’re developing an Eclipse plugin sometimes you need to use the functionality JDT provides for tasks you don’t want the users of your plugin to be aware of. I ran into an example of this while developing a plugin. I needed to create a project in the workspace that I didn’t want users to see.

The solution came from the JavaElementFilters extension. To set up this extension you have to go to your plugin.xml file and add something like the following:

<extension
       point="org.eclipse.jdt.ui.javaElementFilters">
    <filter
          class="org.test.FilterClass"
          description="Description of the filter you wanna create."
          enabled="true"
          id="filter-id"
          name="FilterName">
    </filter>
 </extension>

The description of each of this fields can be found at:

http://help.eclipse.org/help33/index.jsp?topic=/org.eclipse.jdt.doc.isv/reference/extension-points/org_eclipse_jdt_ui_javaElementFilters.html

However the most important field is the class field. The class field points out to the actual class which implements the project filter. This class must extend org.eclipse.jface.viewers.ViewerFilter. To extend this abstract class we just have to override the select method. As an example of overriding this method I’m gonna use the code I used to solve my initial problem:

@Override
	public boolean select(Viewer viewer, Object parentElement, Object element) {
		if(element instanceof IJavaProject){
			return !((IJavaProject) element).getElementName().equals("ProjectNameToFilter");
		}
		return true;
	}

What this example does is just:

  • First it makes sure that the element received is an instance of IJavaProject (it’s a Java project in the workspace).
  • Then it filters the project if it has a concrete name.

PS: I have to thank Prakash from http://www.eclipse-tips.com for redirecting me to the JavaElementFilters extension as the solution of my problem.

Prototyping SWT with GroovyMonkey

As a part of an Eclipse plugin I’m developing for my Master’s degree project lately I’ve had to deal with SWT.  Coming from some background at developing GUIs with Swing and Windows Forms I have to admit that SWT it’s not easy to learn. Apart from a bit counterintuitive I find it too verbose.

So far, whenever I wanted to develop a new Wizard or View I had to code it and play with layouts and widgets till I got the look I wanted.

On the other hand, just quite recently I’ve discovered that there’s an scripting environment for Eclipse called GroovyMonkey that can help the task of protoyping with SWT.

To play with it, I’m gonna use an example to compare the code you need to write to create a really simple interface with both approaches to show GroovyMonkey benefits over Java SWT. The example I’m gonna use is extracted from the installation of GroovyMonkey in Eclipse. We’re gonna create the following shell:

Example Shell

The code for this shell with SWT and Java is quite long for what it does, not too elegant and prone to error.

@Override
public void createPartControl(final Composite parent) {

     parent.setLayout(new GridLayout());
     group = new Group(parent, SWT.NONE);
     group.setText("Groovy SWT");
     group.setBackground(parent.getShell().getDisplay().getSystemColor(SWT.COLOR_WHITE));
     group.setLayout(new GridLayout());

     Label label1 = new Label(group, SWT.NONE);
     label1.setText("groove fun !" );
     label1.setBackground(parent.getShell().getDisplay().getSystemColor(SWT.COLOR_WHITE));

     Label label2 = new Label(group, SWT.NONE);
     label2.setText("Email: ckl@dacelo.nl");
     label2.setBackground(parent.getShell().getDisplay().getSystemColor(SWT.COLOR_WHITE));

}

The code we would need with GroovyMonkey is shorter and simpler to understand. Advantages whose impact grows with more complex user interfaces.

 def subapp = jface.shell( window.getShell())
 {
 	gridLayout()
    group( text:"Groovy SWT", background:[255, 255, 255] )
    {
    	gridLayout()
        label( text:"groove fun !" ,background:[255, 255, 255] )
        label( text:"Email: ckl@dacelo.nl", background:[255, 255, 255] )
    }
}

So, those features have made me decide to give GrooveMonkey a try as my tool for quick GUI prototyping with SWT. In the following section, I’m gonna share my experience through a small list of tips I myself find useful.

Since I’m a completely beginner with GrooveMonkey and with Groovy in general, some of the tips may seem extremely obvious for somewhat experienced users. Nonetheless It haven’t been easy for me to find information about how to start with GroovyMonkey. Thus, this article is written with the hope that it can somehow be useful to those like me who have never used GroovyMonkey before and need help in their very first steps.

Tips

Check GroovySWT documentation

This applies to you specially if you’re using GroovyMonkey for prototyping SWT GUIs like I’m doing.
GroovySWT webpage provides a short explanation of the way the library works. If you wanna dig deeper the examples it provides should become your best source.

How to assign values to fields

To assign a value to a field you can take to approaches: either you use the classical “set” method provided by the Java API or you pass the value as a named parameter to the object constructor.

The first way is more flexible since it allows to change the value of the object at any point of the execution. The code to use this approach for setting the text of a label example shell is similar to the Java code:

   miLabel.setText("groove fun !")

On the other hand, the second way is more concise and it’s the preferable way to assign values when creating objects.

   text(text : "groove fun !")

It’s important to point out that this way of assigning values to properties can be used for any property which follows the naming convention marked for JavaBeans. As an illustration we are gonna use the same approach to assign layout data to a control. This can be done since the method setLayoutData exists for any control. The following snippet shows how to do it.

import org.eclipse.swt.layout.GridData

text(style: "Border", layoutData: gridData(grabExcessHorizontalSpace : true, horizontalAlignment : GridData.FILL, verticalAlignment : GridData.FILL))

The last example also shows that this approach can be used recursively, this way increasing it’s benefits.

Styles for controls

The way styles are asigned to controls is also changed with GroovyMonkey.

GroovyMonkey takes the SWT.None as a default value that doesn’t need to be expressed to be assigned to a control.

When other style values need to be assigned the way to do it is passing them through the named parameter style. The value for this parameter is a string which contains the list of styles we wanna set, separated with commas. To represent every style we need to use it’s name deprived from the SWT prefix. Uppercases are ignored so any representation with the same letters as the style name is accepted by GroovyMonkey.

As an example this is the way to create a mulitilined text field with border:


text ( style: 'Border, Multi')

So that’s all for now. Maybe I’ll come back with more tips for GroovyMonkey users when I have more experience with it.

%d bloggers like this: