Thursday, July 30, 2015

EASE @ EclipseCon Europe 2015



This years EclipseCon will be very exciting for me. Not only was my talk proposal I love scripting accepted as an early bird pick, there are also other parties starting to propose talks concerning EASE.

I love to see this project start to fly and like to give you some insights and an outlook of what to expect at EclipseCon.

How it all began

I remember my first EclipseCon in 2012. I had this intriguing idea in my mind about a scripting framework in Eclipse, but no idea whom to talk to about it. By chance I met Wayne Beaton who offered help and encouraged me to find some interested parties to support my idea.

It took me almost a year to provide a prototype and start advertising this idea, but soon after my first post on this topic on this blog, Polarsys got interested and things started to move very quickly. In summer 2013 we agreed to introduce scripting (called EScript at that time) to the e4 incubator. Thanks to Paul Webster we got a nice place to play and learn about the eclipse way of working on a software project.

When the call for papers openend for EclipseCon Europe 2013 I thought 'What the hell' and sent a proposal for a talk on scripting. I never expected it to get accepted, but few months later I found myself talking about scripting at EclipseCon.

Encouraged by the positive feedback Arthur Daussy and myself worked hard on the implementation. Finally we moved out of the e4 incubator in 2014 and now you may install this great scripting environment in your IDE.

The role of EclipseCon

Meeting users, committers and eclipse staff members in person helped me a  lot  to make this project happen. In the beginning it was the encouragements and good advice I got from experienced members. Now I am looking forward to hear the user side, your success stories, your fresh ideas.

If you read this far you are definitely interested in scripting, so take the chance and meet me in person @ EclipseCon. If there is enough interest, we may have a BOF on scripting topics there. please leave me a note if you would like to see this happen.


I love scripting - what we will cover

This talk will give an overview what scripting can do for you and how you can implement it in your IDE or RCP. We will start to have a short look on the architecture (1%) and then immediately switch to a live demo (99%). There we will start with simple script commands, continue with Java integration and introduce scripting libraries. Next we will learn how to provide custom libraries for your RCP.

Once the basics are clear, we will start to extend the IDE with user scripts, start to write scripted unit tests and finally show rapid prototyping by dynamically instantiating java files from your workspace.

Expect a fast roller coaster ride through the features of EASE. I am sure you want more of it after your first ride!

see you at EclipseCon

Friday, July 24, 2015

A custom search provider

In this tutorial we will implement a custom search for your RCP. Therefore we will first create the search logic and add UI components in a second step.
I found few resources on search functionality, here are some from the Eclipse FAQs:
To have something to search for we will look for files on the local file system. So this is our target for today:
 

Source code for this tutorial is available on github as a single zip archive, as a Team Project Set or you can browse the files online.

Step 1: Search Results


First create a new Plug-in Project and create a class FileSearchResult that implements ISearchResult. Therefore you need a dependency to org.eclipse.search.
package com.codeandme.searchprovider;

import java.io.File;
import java.util.Collection;
import java.util.HashSet;

import org.eclipse.core.runtime.ListenerList;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.search.ui.ISearchQuery;
import org.eclipse.search.ui.ISearchResult;
import org.eclipse.search.ui.ISearchResultListener;
import org.eclipse.search.ui.SearchResultEvent;

public class FileSearchResult implements ISearchResult {

 private final ISearchQuery fQuery;
 private final ListenerList fListeners = new ListenerList();

 private final Collection<File> fResult = new HashSet<File>();

 public FileSearchResult(ISearchQuery query) {
  fQuery = query;
 }

 @Override
 public String getLabel() {
  return fResult.size() + " file(s) found";
 }

 @Override
 public String getTooltip() {
  return "Found files in the filesystem";
 }

 @Override
 public ImageDescriptor getImageDescriptor() {
  return null;
 }

 @Override
 public ISearchQuery getQuery() {
  return fQuery;
 }

 @Override
 public void addListener(ISearchResultListener l) {
  fListeners.add(l);
 }

 @Override
 public void removeListener(ISearchResultListener l) {
  fListeners.remove(l);
 }

 private void notifyListeners(File file) {
  SearchResultEvent event = new FileSearchResultEvent(this, file);

  for (Object listener : fListeners.getListeners())
   ((ISearchResultListener) listener).searchResultChanged(event);
 }

 public void addFile(File file) {
  fResult.add(file);
  notifyListeners(file);
 }
}
Nothing special here, we will use addFile() later in this tutorial to add more and more files to our result set. Whenever the result changes, we need to inform interested listeners. So lets see how FileSearchResultEvent looks like:
package com.codeandme.searchprovider;

import java.io.File;

import org.eclipse.search.ui.ISearchResult;
import org.eclipse.search.ui.SearchResultEvent;

public class FileSearchResultEvent extends SearchResultEvent {

 private final File fAddedFile;

 protected FileSearchResultEvent(ISearchResult searchResult, File addedFile) {
  super(searchResult);
  fAddedFile = addedFile;
 }

 public File getAddedFile() {
  return fAddedFile;
 }
}
Even simpler! To update results incrementally we hold a reference to the added file.

Step 2: The search query

The main logic for a search is implemented as an ISearchQuery. It has 2 jobs: do the actual search and populate the search result.
package com.codeandme.searchprovider;

import java.io.File;
import java.io.FileFilter;
import java.util.Collection;
import java.util.HashSet;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.search.ui.ISearchQuery;
import org.eclipse.search.ui.ISearchResult;

public class FileSearchQuery implements ISearchQuery {

 private final File fRoot;
 private final String fFilter;
 private final boolean fRecursive;

 private final FileSearchResult fSearchResult;

 public FileSearchQuery(String root, String filter, boolean recursive) {
  fRoot = (root.isEmpty()) ? File.listRoots()[0] : new File(root);
  fFilter = filter;
  fRecursive = recursive;
  fSearchResult = new FileSearchResult(this);
 }

 @Override
 public IStatus run(IProgressMonitor monitor) throws OperationCanceledException {
  Collection<File> entries = new HashSet<File>();
  entries.add(fRoot);

  do {
   File entry = entries.iterator().next();
   entries.remove(entry);

   entry.listFiles(new FileFilter() {

    @Override
    public boolean accept(File pathname) {
     if ((pathname.isFile()) && (pathname.getName().contains(fFilter))) {
      // accept file
      fSearchResult.addFile(pathname);

      return true;
     }

     if ((pathname.isDirectory()) && (fRecursive))
      entries.add(pathname);

     return false;
    }
   });

  } while (!entries.isEmpty());

  return Status.OK_STATUS;
 }

 @Override
 public String getLabel() {
  return "Filesystem search";
 }

 @Override
 public boolean canRerun() {
  return true;
 }

 @Override
 public boolean canRunInBackground() {
  return true;
 }

 @Override
 public ISearchResult getSearchResult() {
  return fSearchResult;
 }
}
Our query needs 3 parameters:
  • a root object
  • a file name filter
  • a recursive flag to scan subfolders
In the constructor we create the FileSearchResult and link it to our query. Then we traverse folders and add file by file to our search result. We do not check our input parameters very thoroughly for the sake of simplicity.

The logic is implemented, now lets add some nice UI elements.

Step 3: Search Dialog

To add a new search page to the search dialog (main menu/Search/Search...) we use the extension point org.eclipse.search.searchPages.
 
Just provide id, label and a class reference to:
package com.codeandme.searchprovider.ui;

import org.eclipse.jface.dialogs.DialogPage;
import org.eclipse.search.ui.ISearchPage;
import org.eclipse.search.ui.ISearchPageContainer;
import org.eclipse.search.ui.NewSearchUI;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;

import com.codeandme.searchprovider.FileSearchQuery;

public class FileSearchPage extends DialogPage implements ISearchPage {

 private ISearchPageContainer fContainer;

 @Override
 public boolean performAction() {
  FileSearchQuery searchQuery = new FileSearchQuery("/home", "txt", true);
  NewSearchUI.runQueryInForeground(fContainer.getRunnableContext(), searchQuery);

  return true;
 }

 @Override
 public void setContainer(ISearchPageContainer container) {
  fContainer = container;
 }

 @Override
 public void createControl(Composite parent) {
  Composite root = new Composite(parent, SWT.NULL);

  [...]

  // need to set the root element
  setControl(root);
 }
}
I have removed the UI code from createControl() as it is not relevant here. If interested you can find it on github. The only important thing here is to set the root control using setControl().

performAction() starts the actual search. Therefore we create a query and run it either as foreground or as background job. By returning true we indicate that the search dialog shall be closed.

Step 4: Display search results

To add a new page to the Search View we need another extension point: org.eclipse.search.searchResultViewPages.

Add a new viewPage with an id and label. To link this page with search results of a certain kind we provide the class of search results this page can display. Set it to our FileSearchResult type. Finally we provide a class implementation for the page:
package com.codeandme.searchprovider.ui;

import org.eclipse.search.ui.ISearchResult;
import org.eclipse.search.ui.ISearchResultListener;
import org.eclipse.search.ui.ISearchResultPage;
import org.eclipse.search.ui.ISearchResultViewPart;
import org.eclipse.search.ui.SearchResultEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.IPageSite;

import com.codeandme.searchprovider.FileSearchResultEvent;

public class SearchResultPage implements ISearchResultPage, ISearchResultListener {

 private String fId;
 private Composite fRootControl;
 private IPageSite fSite;
 private Text ftext;

 @Override
 public Object getUIState() {
  return null;
 }

 @Override
 public void setInput(ISearchResult search, Object uiState) {
  search.addListener(this);
 }

 @Override
 public void setViewPart(ISearchResultViewPart part) {
 }

 @Override
 public void setID(String id) {
  fId = id;
 }

 @Override
 public String getID() {
  return fId;
 }

 @Override
 public String getLabel() {
  return "Filesystem Search Results";
 }

 @Override
 public IPageSite getSite() {
  return fSite;
 }

 @Override
 public void init(IPageSite site) throws PartInitException {
  fSite = site;
 }

 @Override
 public void createControl(Composite parent) {
  fRootControl = new Composite(parent, SWT.NULL);
  fRootControl.setLayout(new FillLayout(SWT.HORIZONTAL));

  ftext = new Text(fRootControl, SWT.BORDER | SWT.READ_ONLY | SWT.H_SCROLL | SWT.V_SCROLL | SWT.CANCEL | SWT.MULTI);
 }

 @Override
 public void dispose() {
  // nothing to do
 }

 @Override
 public Control getControl() {
  return fRootControl;
 }

 @Override
 public void setActionBars(IActionBars actionBars) {
 }

 @Override
 public void setFocus() {
  fRootControl.setFocus();
 }

 @Override
 public void restoreState(IMemento memento) {
  // nothing to do
 }

 @Override
 public void saveState(IMemento memento) {
  // nothing to do
 }

 @Override
 public void searchResultChanged(SearchResultEvent event) {
  if (event instanceof FileSearchResultEvent) {
   Display.getDefault().asyncExec(new Runnable() {
    @Override
    public void run() {
     String newText = ftext.getText() + "\n" + ((FileSearchResultEvent) event).getAddedFile().getAbsolutePath();
     ftext.setText(newText);
    }
   });
  }
 }
}
Lots of getters and setters, but nothing complicated. For this example we use a simple text control to add the names of found files. setInput() will be called by the framework to link this view with a search result. Now we attach a listeners to get notified on changes on the result.

I found one caveat while implementing: when your query code (from there we trigger searchResultChanged()) raises an exception, you do not get the output on the console as you might expect. Basically this means that your result page remains empty without any notification that something went wrong. Use the debugger to single step through your code in that case.

Wednesday, June 3, 2015

Generic drag and drop in eclipse

The default drag and drop from SWT relies on the fact that source and target both agree on certain transfer types. The source adds the drop data, the target has to know how to deal with that data.

A drop target in eclipse therefore cannot deal with unknown objects by default. Eg you cannot drop your plugin local objects on a Navigator view. This article describes how you can drop your own objects into existing eclipse views and how to enrich your local drop target with generic drop support.

Source code for this tutorial is available on github as a single zip archive, as a Team Project Set or you can browse the files online.

Step 1: Simple drag support

Lets start with default SWT drag support. Create a new Plug-in Project named com.codeandme.draganddrop. Add a new view with a Text element to it. Name the text element txtInput. I expect you are familiar with that procedure.

Now we add TextTransfer support:
  int operations = DND.DROP_MOVE | DND.DROP_COPY;
  DragSource source = new DragSource(txtInput, operations);

  Transfer[] types = new Transfer[] { TextTransfer.getInstance() };
  source.setTransfer(types);

  source.addDragListener(new DragSourceListener() {
   @Override
   public void dragStart(DragSourceEvent event) {
    if (txtInput.getText().length() == 0)
     event.doit = false;
   }

   @Override
   public void dragSetData(DragSourceEvent event) {
    // for text drag to editors
    if (TextTransfer.getInstance().isSupportedType(event.dataType))
     event.data = txtInput.getSelectionText();
   }

   @Override
   public void dragFinished(DragSourceEvent event) {
   }
  });
This is default SWT dnd code, nothing special so far.

Step 1: Add drag support for generic objects

By default the action to be performed on a drop is implemented by the DropListener of the target. Fortunately eclipse provides a special transfer type that delegates the drop action to a dedicated class which we may provide in our plugin.

Switch to your plugin.xml and add a new Extension of type org.eclipse.ui.dropActions. Add an action element to it, set the id to com.codeandme.draganddrop.dropText and implement a class TextDropActionDelegate:
package com.codeandme.draganddrop;

import java.io.ByteArrayInputStream;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.part.IDropActionDelegate;

public class TextDropActionDelegate implements IDropActionDelegate {

 public static final String ID = "com.codeandme.draganddrop.dropText";

 @Override
 public boolean run(Object source, Object target) {
  if (source instanceof byte[]) {
   if (target instanceof IContainer) {
    IContainer parent = (IContainer) target;
    IFile file = parent.getFile(new Path("dropped text.txt"));
    if (!file.exists()) {
     try {
      file.create(new ByteArrayInputStream((byte[]) source), true, new NullProgressMonitor());
     } catch (CoreException e) {
      e.printStackTrace();
     }
    }

    return true;

   } else if (target instanceof Text)
    ((Text) target).setText(new String((byte[]) source));
  }

  return false;
 }
}
The run() method gets called when the drop action is to be performed. The target is automatically set to the object under the mouse when we perform the drop, eg a folder in a Navigator view. The provided code is a bit simplified, eg we do not try to adapt the target in case it is not an IContainer.

Now we need to add our drop delegate to our source object. Switch back to your view and modify the code:
  Transfer[] types = new Transfer[] { TextTransfer.getInstance(), PluginTransfer.getInstance() };
add a new transfer type: PluginTransfer.
   public void dragSetData(DragSourceEvent event) {
    // for text drag to editors
    if (TextTransfer.getInstance().isSupportedType(event.dataType))
     event.data = txtInput.getSelectionText();

    // for plugin transfer drags to navigator views
    if (PluginTransfer.getInstance().isSupportedType(event.dataType))
     event.data = new PluginTransferData(TextDropActionDelegate.ID, txtInput.getSelectionText().getBytes());
   }
When a plugin transfer is accepted by the target we need to set a PluginTransferData object that links to our action delegate and provides the source data.

Everything is in place, so give it a try: type and select some text in your view and drop it on a folder in the Project Explorer view.

Step 3: Add generic drop support to your own views

Generic drop support does not come entirely for free. If you want to add it to a JFace tree or table it is dead simple as that:

  int operations = DND.DROP_MOVE | DND.DROP_COPY;
  DropTarget target = new DropTarget(viewer.getControl(), operations);

  Transfer[] types = new Transfer[] { PluginTransfer.getInstance() };
  target.setTransfer(types);

  target.addDropListener(new PluginDropAdapter(viewer));
We may also reuse the PluginDropAdapter on simple Controls like text boxes. Therefore we need to overwrite some methods:
  target.addDropListener(new PluginDropAdapter(null) {
   @Override
   protected int determineLocation(DropTargetEvent event) {
    return LOCATION_ON;
   }

   @Override
   protected Object getCurrentTarget() {
    return txtTarget;
   }
  });
If you provide drop support in general it is advised to add the plugin transfer type to allow other plugins to use your drop support.

Friday, May 22, 2015

Unit Testing with scripts in EASE


In the company I work for we do lots of regression testing, mostly for interface testing of hardware devices. Therefore we needed a unit testbench similar to JUnit without the complexity of writing java code for our tests. So we implemented a simple Unit test framework for EASE.

Source code for this tutorial is available in the EASE scripts repository.

Step 1: Create a simple test script

Create a new Project in your workspace named JavaScript Unit Testing Tutorial. Create a new file Tests/Simple/01 Valid tests.js
// starts a simple test
startTest("empty", "an empty test case");
// ends a test
endTest();

// start another test
startTest("no test code", "a test containing no assertions");
print("Hi from valid test");
endTest();

// third test
startTest("valid assertions", "a test containing valid assertions");
// check
assertTrue(true);
endTest();

// code outside of a testcase
print('"' + getTestFile() + '" completed');
You might ask where these test functions come from as we do not load any modules. The unit test framework comes with a module called Unittest. It is hidden by default but will be loaded automatically when we execute EASE UnitTests. To unhide go to the Preferences/Scripting/Modules (Note: this is not necessary to use the module, it will only display it in UI components).

If you are familiar with unit testing in general you also know assertions and according methods like assertTrue(). In general these methods perform a check for validity. In case an expected result does not match with a current result, an error is generated for the containing test case.

So test cases are encapsulated between a startTest() and an endTest(). It is recommended to not use assertions outside test cases, yet not mandatory.

Step 2: Create and execute a test suite

To execute test files, we need to create a Test Suite, that sets up a test environment for us. Create a new Scripting/Script Testsuite named Testsuite.suite.  The editor will allow you to select test files you want to execute. Therefore we look for script files in the same project where the suite is located.

Select our previously created test file and hit the Run Test Suite button in the top right corner.



 A new view is opened that displays unit test results.



The top tree view reflects the file structure of our test files and displays decorators indicating the test execution result. If you select a test file, detailed results are displayed in the bottom section.

Step 3: Test errors

Create a new file Tests/Simple/02 Test errors.js.
// test containing assertions that fail
startTest("invalid assertions", "a test containing invalid assertions");
assertTrue(true);
assertTrue(false);
assertFalse(true);
assertFalse(false);
endTest();

// manually create a failure
startTest("failure", "test throwing a failure");
failure("test broken, stop execution here");

// an exception would also create a failure
throw new java.lang.Exception("code exception");
endTest();

startTest("never to be reached");
// not being reached as the failure above terminates test file execution
endTest();
Save the test, add it to your suite and run the suite right away. When an assertion fails, error markers are generated on your script files on the according code location. You will also see the most critical assertion reflected in the Script Unit Test view. Double clicking on errors will open the source editor on the corresponding location.



Similar to JUnit there exist two different types of things that can go wrong: errors and failures. Errors are assertions that do not meet their criteria. They are recoverable, meaning script execution of the test file continues.

Failures on the other hand are not recoverable, so script execution of that test file is terminated immediately.

Step 4: Test Suite configuration

Typically tests need to be adapted to a certain environment. In general you do not want test engineers to modify your script code. So the test suite supports setting of Variables that are automatically available in all test files. The Setup section allows to execute arbitrary code on the Test Suite start, Test File start, startTest() call and also for the corresponding teardown sections. As you may provide multiple suite files within a project, these secions are a good place to adapt tests according to your environment.

Optional: Test automation

Often tests need to be executed automatically, eg for a nightly test run. The Unittest module allows to run a testsuite from a script. Together with the headless application from EASE you can run a suite from the commandline and create a report (we currently support the JUnit xml format that can be picked up from Hudson/Jenkins).

Optional: Additional assertions

If you want to provide your own assertion methods, simply create your own module and let your wrapped method return an IAssertion. If you call your method from within a test script it will automatically validate your assertion method.

So why do I want to write scripted tests?

In the company I work for we use these kind of tests for interface verification of hardware devices. Therefore we provide our own modules that allow us to send stimuli and validate expected responses. We also use it in combination with analog measurement devices to verify that certain hardware parameters are within valid ranges.

Currently Script Unit Testing is supported for the Rhino script engine only.

Wednesday, April 8, 2015

Live charting with EASE

Recently we added charting support to EASE using the Nebula XY chart widget. Say you have a script acquiring some data you now may plot them as your measurement advances.

Step 1: Installation

For this tutorial you need to register the nebula update site:
http://download.eclipse.org/technology/nebula/snapshot
in Preferences/Install/Update/Available Software Sites.

Afterwards install EASE and add EASE Charting Support to install all required modules. This component depends on Nebula Visualization Widgets, therefore we added the nebula update site before. Currently you have to stick to the nighty update site of EASE as charting is not part of the current release.

Step 2: Some example plots

 
Try out this little script to get these sample plots:
loadModule('/Charting');

figure("Simple Charts");
clear();

series("linear", "m+");
for (x = 0; x <= 20; x += 1)
 plotPoint(x, x / 10 - 1);

series("1/x", "ko:");
for (x = 1; x <= 20; x += 0.2)
 plotPoint(x, 4/x);

series("sine", "bx");
for (x = 0; x < 20; x += 0.05)
 plotPoint(x, Math.sin(x) * Math.sin(3 * x));

figure("Advanced");
clear();

series("drop", "gd");
for (t = 0; t < 2; t += 0.01)
 plotPoint(t, Math.pow(Math.E, t * -3) * Math.cos(t * 30));

series("upper limit", "rf--");
plot([0, 2], [0.4, 0.4]);

series("lower limit", "rf--");
plot([0, 2], [-0.4, -0.4]);
Run the script either in the Script Shell or execute it using launch targets.

Step 3: Charting Module API

The API of the charting module tries to keep close to MatLab style, so the format string for a series is almost identical. You may even omit to create figures or series and start plotting right away and we create everything necessary on the fly.

For a better user experience we extended the XYChart a bit by allowing to zoom directly using the scroll wheel either over the plot or its axis. Use a double click for an auto-zoom.

Sunday, February 22, 2015

Reformatting multiple source files

Recently I had to apply a new code formatter template to a project. Changing 50+ files by hand seemed to be too much monkey work. Writing a custom plug-in seemed to be too much work. Fortunately there is this great scripting framework to accomplish this task quite easily.

Step 1: Detecting affected source files

I have already blogged about EASE and how to install it. So I went directly to the script shell to fetch all java files form a certan project:
loadModule('/System/Resources');

 org.eclipse.ease.modules.platform.ResourcesModule@5f8466b4
files = findFiles("*.java", getProject("org.eclipse.some.dedicated.project"), true)
 [Ljava.lang.Object;@1b61effc
Now we have an array of IFile instances. Easy, isn't it?

Step 2: The formatter script

Ideas how to format source files can be found in the forum. Taking the script and porting it to JS is simple:
function formatUnitSourceCode(file) {
 unit = org.eclipse.jdt.core.JavaCore.create(file);

 unit.becomeWorkingCopy(null);

 formatter = org.eclipse.jdt.core.ToolFactory.createCodeFormatter(null);
 range = unit.getSourceRange();
 formatEdit = formatter
   .format(
     org.eclipse.jdt.core.formatter.CodeFormatter.K_COMPILATION_UNIT
       | org.eclipse.jdt.core.formatter.CodeFormatter.F_INCLUDE_COMMENTS,
     unit.getSource(), 0, unit.getSource().length(), 0, null);
 if (formatEdit.hasChildren()) {
  unit.applyTextEdit(formatEdit, null);
  unit.reconcile(org.eclipse.jdt.core.dom.AST.JLS4, false, null, null);
 }

 unit.commitWorkingCopy(true, null);
}

Step 3: glueing it all together

Load the function into your script shell, fetch the list of files as in step 1 and then process all files in a loop:
for each (file in files) formatUnitSourceCode(file);
That's it. This short script uses your current source formatter settings and applies it to all selected files.

This little helper is available online in the EASE script repository.

Wednesday, January 14, 2015

EASE 0.1.0 - the very first release

I am proud to announce the very first release of the Eclipse Advanced Scripting Environment (EASE).

In case you have no idea what this is about, check out the project page. Afterwards you definitely need to install it right away in your IDE and start scripting.

Some facts: 
  • executes script code in your IDE, providing access to the running JRE and thus to the whole Eclipse API
  • supports JavaScript, Jython, Groovy and JRuby (see details)
  • allows to dynamically integrate scripts into toolbars and menus
  • extensible via script libraries (write your own)

This project started as an in house company solution some years ago. When I released it as open source back in 2013 I was quite astonished about the interest from the community. Soon this project moved into the e4 incubator which gave us a great chance to evolve and build up an infrastructure.
Last summer EASE was accepted as an Eclipse incubation project. With our first release we feel to be part of the eclipse community.

Let me take this opportunity to thank all the people who helped to make this happen. When I started out almost 2 years ago I did not expect to meet such an open minded community. So if you think of starting your own project I can just encourage you to take the first step. There are helping hands everywhere, trying to push you forward. Did I already say that I love this community?