Customize the Web editor

/Customize the Web editor
Customize the Web editor 2016-06-04T23:10:09+00:00

The generated code contains Java classes and JavaScript sripts; both artefacts can be customized according to the developer’s needs. Below some hints on how to proceed. For more advanced customizations, feel free contact the support.

Java Customization

The Java classes are located under src and src-gen packages.

Web Plugin Packages

Web Plugin Packages

The Java packages contain mainly the web editor, the language-specific text widget, the runtime Guice binding module, and the standalone setup.

All text widgets extend org.dslforge.styledtext.BasicText which is RAP Custom Widget that provides the base API for building DSL editors. The widget manages the interaction between the client and the server using the RemoteObject API and the methods: set, call, and notify.

All DSL editors extend org.dslforge.xtext.common.BasicXtextEditor, which handles syntax validation, EMF-based validation, code generation, etc.

All DSL binding modules extend org.dslforge.xtext.common.shared.SharedModule which comes with some default bindings that you can override the classic way you do with Guice.

For example:

public class SharedModule extends AbstractGenericModule{
@Override
public void configure(Binder binder) {
binder.bind(ILinkingService.class).to(BasicTextLinkingService.class);
binder.bind(IGlobalScopeProvider.class).to(BasicTextGlobalScopeProvider.class);
binder.bind(IContainer.Manager.class).to(BasicTextContainerManager.class);
binder.bind(IXtextResourceFactory.class).to(XtextResourceFactory.class);
binder.bind(IXtextResourceSetProvider.class).to(XtextResourceSetProvider.class);
}
}

A web standalone setup class has been introduced. It overrides the default RCP bindings with the web bindings (default Xtext runtime bindings are kept as-is, only the UI bindings are replaced based on the RAP styled text widget) defined in the web module. The injector like in classic RCP scenarios is configured in a standalone mode. An example:

private Module getRuntimeModule() {
org.eclipse.xtext.common.TerminalsStandaloneSetup.doSetup();
StatemachineRuntimeModule original = new StatemachineRuntimeModule();
WebStatemachineRuntimeModule module = new WebStatemachineRuntimeModule();
Module mergedModule = Modules2.mixin((Module) original, module);
return mergedModule;
}

 

In org.dslforge.texteditor.BasicTextEditor there is an example on how to customize the text widget:

BasicText textWidget = new BasicText(parent, SWT.FILL);

GridData textLayoutData = new GridData();

textLayoutData.horizontalAlignment = SWT.FILL;

textLayoutData.verticalAlignment = SWT.FILL;

textLayoutData.grabExcessHorizontalSpace = true;

textLayoutData.grabExcessVerticalSpace = true;

textWidget.setLayoutData(textLayoutData);

 

// set font

textWidget.setFont(font);

 

// set background

Color color = new Color(parent.getDisplay(), new RGB(180, 200, 250));

textWidget.setBackground(color);

 

// set read/write access

textWidget.setEditable(true);

 

// add annotations

List<Annotation> annotations = new ArrayList<Annotation>();

annotations.add(new Annotation(AnnotationType.error, 1, 3, “This is an error”));

annotations.add(new Annotation(AnnotationType.warning, 3, 1, “This is a warning”));

annotations.add(new Annotation(AnnotationType.info, 5, 1, “This is an info”));

textWidget.setAnnotations(annotations);

 

// highlight text ranges

List<TextRange> ranges = new ArrayList<TextRange>();

ranges.add(new TextRange(1, 1, 0, 1));

ranges.add(new TextRange(2, 3, 0, 5));

ranges.add(new TextRange(4, 4, 0, 4));

textWidget.setMarkers(ranges);

 

JavaScript Customization

In the generated JavaScript packages, you find the custom RAP widget client, which has the same name as the grammar short name. Additionally the global-index, which is a shared worker between the web editors, makes it possible to cross reference editors between each other.

Web Plugin - JavaScript packages

Web Plugin – JavaScript packages

 

The ace package contains the main ACE files . There are three files in this packages which are language-specific:

  • mode-language: ACE uses the so called “modes” to embed language-specific attributes, such as syntax highlighting,
  • worker-language : a JavaScript worker which manages the interaction between the generated parser and lexer, and the language mode,
  • theme : contains the CSS theme of the editor.

In ace/snippets, there is the templates file loaded by ACE Language Tools module, so that the templates appear in the content assist popup. Below an example of a snippets file:

Customizing Templates

Customizing Templates

In the web/parser package, gibt es the generated ANTLR grammar file (.g), the JavaScript parser and lexer, and the base ANTLR library necessary to run the parser/lexer in a standalone mode.

Because ACE uses the so called modules, changing the theme is a matter of changing one file. To change the theme, replace eclipse-theme.js by another script from ACE repository, then change the theme module in index.html:

editor.setTheme(“ace/theme/eclipse”);

Refer to ACE (some links are available below) for a more detailed guideline on how to customize ACE modules.

Multi-Threading

The forged editors are enhanced with JavaScript Workers and Shared Workers in order to minimize the computation overhead on the server. Every client parser is run in a separate JavaScript thread with notifications back to the main script, like the ability to render annotations (errors, warning, and info).

//Language Worker

this.createWorker = function(session) {

var worker = new WorkerClient([“ace”], “ace/mode/statemachine_worker”, “Worker”);

worker.attachToDocument(session.getDocument());

 

worker.on(“error”, function(e) {

var annotations = session.getAnnotations();

 

var filtered = [];

for (var i = 0; i < annotations.length; i++) {

var annotation = annotations[i];

var server = annotation.server;

if (server && server==true) {

filtered.push(annotation)

}

}

 

filtered.push(e.data);

session.setAnnotations(filtered);

});

 

worker.on(“ok”, function(e) {

var annotations = session.getAnnotations();

var filtered = [];

for (var i = 0; i < annotations.length; i++) {

var annotation = annotations[i];

var server = annotation.server;

if (server && server==true) {

filtered.push(annotation)

}

}

session.clearAnnotations();

session.setAnnotations(filtered);

});

 

return worker;

};

The following snippet is executed in a separate thread, to store the cross references contributed by each editor. Because the thread is shared, it notifies all the opened editors at the same time, in parallel. This solution allows pointing between editors using references.

var counter = 0;

var connections = [];

var index = [];

 

onconnect = function(e) {

if(e) {

var port = e.ports[0];

port.onmessage = function(j) {

counter++;

var guid = j.data.guid;

var content = j.data.message;

var contribution = j.data.index;

for (var i=0; i < connections.length; i++) {

connections[i].postMessage({

counter: counter,

message: “index”,

guid: guid,

index: contribution

});

}

}

port.start();

connections.push(port);

}

}

 

How to call the shared worker from the editor:

//The cross reference array

var index = [];

 

//Compute worker’s http URL

var filePath = ‘rwt-resources/src-js/org/dslforge/styledtext/global-index.js’;

var httpURL = computeWorkerPath(filePath);

 

//Create the shared worker

var worker = this.worker = new SharedWorker(httpURL);

 

//…

 

//on change

editor.on(“change”, function(event) {

worker.port.postMessage({

message: editor.getValue(),

guid: guid,

index: index

});

 

worker.port.onmessage = function(e) {

//update the index reference

index = e.data.index;

};