Dirigible's Logodirigible

How the Orion editor is integrated in Dirigible


Why Orion? How the code-completion is achieved? How the Orion editor is integrated with RAP?

Why Orion?

Our choice for using the Orion editor as the primary editor in Dirigible is bases on that it is has the best support and tooling for JavaScript. Also JavaScript is the language of choice for writing services with Dirigible. Beyond these arguments, Dirigible and Orion are part of the Eclipse Cloud Development iniciative, that strives to set up the standarts and the best practices for “developing in the cloud for the cloud”. Taking advantage of the open source eco system is key mindset, layed in the foundations of the project.




Why Tern.js?

Tern.js is a code-analysis and code-completion library for JavaScript. It can run both on client-side and on server-side. In order to achive real time proposals and to remove the overhead from server communication, in Dirigible we use Tern.js as a client-side library. In addition to the JavaScript code-completion, Tern.js allows to introduce custom suggestions - the way to integrate and allow code-completion for Dirigible API.




How is the Injected API integrated in Orion?

We use the standard Tern.js approach leveraged by Orion, by declaring objects and functions for code-completion as a JavaScript plugin. You can find the plugin here. After the build of Orion itself, there is a generated dirigible.json file out of the declarations.

To use and package the embedded Orion editor in Dirigible we need to go over the following steps:

Build the json definition with:

1
2
3
npm install tern
git clone orion.client
git reset --hard origin/stable_20150817

Add your declaration file in ternWorkerCore.js

1
2
3
orion.client/bundles/org.eclipse.orion.client.javascript/web/node_modules/tern/bin/condense --name dirigible --no-spans --plugin doc_comment --def ecma5 --def browser  dirigible.js > orion.client/bundles/org.eclipse.orion.client.javascript/web/tern/defs/dirigible.json
mvn clean install
copy orion.client/build-js/codeEdit > resources

How it is integrated with RAP?

By using RAP scripting capabilities for callbacks, we have client and backend sides communicating via the standard RAP chanel. Thanks to functions like getText(), setText(), setDirty(), setDebugRow(), etc., we are on the half of the way. To glue to whole thing to works as one, we have in the backend EditorWidget.java and its coresponding client-side controller editor.html.


What about Debugging?

Last but not least, here comes the integrated debuggier in Dirigible. This was not so easy and trivial part, but finally the Dirigible’s debugger uses the Orion editor.

Client-side integration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
	...
	function getBreakpointsEnabled() {
	    return breakpointsEnabled;
	}

	function setBreakpointsEnabled(status) {
		breakpointsEnabled = status;
	}

	function loadBreakpoint(breakpoint) {
		handleAddRemoveBreakpoint(breakpoint);
	}


	function setDebugRow(row) {
		editor.setCaretOffset(editor.getLineStart(row));
	}

	function handleAddRemoveBreakpoint(lineIndex) {
		if(typeof(Storage) === "undefined") {
	    	alert("Session storage is not available!")
	    } else if (getBreakpointsEnabled()) {
			var breakpointsArray;
	    	if (sessionStorage.breakpoints) {
	    		breakpointsArray = JSON.parse(sessionStorage.breakpoints);
	    		var index = breakpointsArray.indexOf(lineIndex);
	    		if (index > -1) {
	    			breakpointsArray.splice(index, 1);
	    		    clearBreakpoint(lineIndex);
	    		} else {
	    			breakpointsArray.push(lineIndex);
	    			setBreakpoint(lineIndex);
	    		}
	    	} else {
	    		breakpointsArray = [];
	    		breakpointsArray.push(lineIndex);
	    	    setBreakpoint(lineIndex);
	    	}
		    sessionStorage.breakpoints = JSON.stringify(breakpointsArray);
	    }
	}
	...

The whole file can be found here.

Server-side

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
	...
	new BrowserFunction(browser, "setBreakpoint") {
		@Override
		public Object function(final Object[] arguments) {
			if ((listener != null) && (arguments[0] != null) && (arguments[0] instanceof Number)) {
				listener.setBreakpoint(((Number) arguments[0]).intValue());
			}
			return null;
		}
	};

	new BrowserFunction(browser, "clearBreakpoint") {
		@Override
		public Object function(final Object[] arguments) {
			if ((listener != null) && (arguments[0] != null) && (arguments[0] instanceof Number)) {
				listener.clearBreakpoint(((Number) arguments[0]).intValue());
			}
			return null;
		}
	};

	...
	public void setDebugRow(final int row) {
		execute("setDebugRow", row);
	}

	public void loadBreakpoints(final int[] breakpoints) {
		for (final int breakpoint : breakpoints) {
			execute("loadBreakpoint", breakpoint);
		}
	}

	private void execute(final String function, final Object... arguments) {
		browser.execute(buildFunctionCall(function, arguments));
	}
	...

The whole file can be found here.

Special thanks to Libing Wang and the orion-dev team for helping us with the integration between the debugger and the Orion editor. You can find the whole conversation here.

Tweet