/*******************************************************************************
* Copyright (c) 2014-2015 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.editor.orion.client;
import com.google.gwt.core.client.Callback;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptException;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.LinkElement;
import com.google.gwt.dom.client.Node;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.eclipse.che.ide.api.extension.Extension;
import org.eclipse.che.ide.api.notification.NotificationManager;
import org.eclipse.che.ide.api.notification.StatusNotification;
import org.eclipse.che.ide.editor.orion.client.jso.OrionKeyBindingModule;
import org.eclipse.che.ide.editor.orion.client.jso.OrionTextThemeOverlay;
import org.eclipse.che.ide.jseditor.client.defaulteditor.EditorBuilder;
import org.eclipse.che.ide.jseditor.client.editorconfig.AutoSaveTextEditorConfiguration;
import org.eclipse.che.ide.jseditor.client.editortype.EditorType;
import org.eclipse.che.ide.jseditor.client.editortype.EditorTypeRegistry;
import org.eclipse.che.ide.jseditor.client.requirejs.RequireJsLoader;
import org.eclipse.che.ide.jseditor.client.requirejs.RequirejsErrorHandler.RequireError;
import org.eclipse.che.ide.jseditor.client.texteditor.AbstractEditorModule.EditorInitializer;
import org.eclipse.che.ide.jseditor.client.texteditor.AbstractEditorModule.InitializerCallback;
import org.eclipse.che.ide.jseditor.client.texteditor.ConfigurableTextEditor;
import org.eclipse.che.ide.jseditor.client.texteditor.EmbeddedTextEditorPresenter;
import org.eclipse.che.ide.util.loging.Log;
import javax.inject.Inject;
import java.util.logging.Level;
import java.util.logging.Logger;
@Extension(title = "Orion Editor", version = "1.1.0")
@Singleton
public class OrionEditorExtension implements Provider<OrionKeyBindingModule>{
/** The logger. */
private static final Logger LOG = Logger.getLogger(OrionEditorExtension.class.getSimpleName());
/** The editor type key. */
public static final String ORION_EDITOR_KEY = "orion";
private final NotificationManager notificationManager;
private final EditorTypeRegistry editorTypeRegistry;
private final RequireJsLoader requireJsLoader;
private final OrionTextEditorFactory orionTextEditorFactory;
private final OrionResource orionResource;
private boolean initFailedWarnedOnce = false;
private OrionKeyBindingModule keyBindingModule;
@Inject
public OrionEditorExtension(final EditorTypeRegistry editorTypeRegistry,
final NotificationManager notificationManager,
final RequireJsLoader requireJsLoader,
final OrionEditorModule editorModule,
final OrionTextEditorFactory orionTextEditorFactory,
final OrionResource orionResource) {
this.notificationManager = notificationManager;
this.editorTypeRegistry = editorTypeRegistry;
this.requireJsLoader = requireJsLoader;
this.orionTextEditorFactory = orionTextEditorFactory;
this.orionResource = orionResource;
editorModule.setEditorInitializer(new EditorInitializer() {
@Override
public void initialize(final InitializerCallback callback) {
// add code-splitting of the whole orion editor
GWT.runAsync(new RunAsyncCallback() {
@Override
public void onSuccess() {
injectOrion(callback);
}
@Override
public void onFailure(final Throwable reason) {
callback.onFailure(reason);
}
});
}
});
// must not be delayed
registerEditor();
KeyMode.init();
}
private void injectOrion(final InitializerCallback callback) {
final String[] scripts = new String[]{
"built-codeEdit-10.0/code_edit/built-codeEdit-amd",
"orion/CheContentAssistMode",
"orion/uiUtils"
};
requireJsLoader.require(new Callback<JavaScriptObject[], Throwable>() {
@Override
public void onSuccess(final JavaScriptObject[] result) {
requireOrion(callback);
}
@Override
public void onFailure(final Throwable e) {
if (e instanceof JavaScriptException) {
final JavaScriptException jsException = (JavaScriptException)e;
final Object nativeException = jsException.getThrown();
if (nativeException instanceof RequireError) {
final RequireError requireError = (RequireError)nativeException;
final String errorType = requireError.getRequireType();
String message = "Orion injection failed: " + errorType;
final JsArrayString modules = requireError.getRequireModules();
if (modules != null) {
message += modules.join(",");
}
Log.debug(OrionEditorExtension.class, message);
}
}
initializationFailed(callback, "Failed to inject Orion editor", e);
}
}, scripts, new String[0]);
injectCssLink(GWT.getModuleBaseForStaticFiles() + "built-codeEdit-10.0/code_edit/built-codeEdit.css");
}
private static void injectCssLink(final String url) {
final LinkElement link = Document.get().createLinkElement();
link.setRel("stylesheet");
link.setHref(url);
Document.get().getHead().appendChild(link);
}
private void requireOrion(final InitializerCallback callback) {
requireJsLoader.require(new Callback<JavaScriptObject[], Throwable>() {
@Override
public void onFailure(final Throwable reason) {
LOG.log(Level.SEVERE, "Unable to initialize Orion ", reason);
initializationFailed(callback, "Failed to initialize Orion editor", reason);
}
@Override
public void onSuccess(final JavaScriptObject[] result) {
//use 4th element as keybinding module
keyBindingModule = result[3].cast();
endConfiguration(callback);
}
},
new String[]{"orion/codeEdit",
"orion/editor/emacs",
"orion/editor/vi",
"orion/keyBinding",
"che/editor/contentAssist",
"orion/editor/eventTarget",
"uiUtils"},
new String[]{"CodeEditWidget",
"OrionEmacs",
"OrionVi",
"OrionKeyBinding",
"CheContentAssistMode",
"OrionEventTarget",
"UiUtils"});
}
private void endConfiguration(final InitializerCallback callback) {
defineDefaultTheme();
callback.onSuccess();
}
private void registerEditor() {
LOG.fine("Registering Orion editor type.");
editorTypeRegistry.registerEditorType(EditorType.fromKey(ORION_EDITOR_KEY), "Orion", new EditorBuilder() {
@Override
public ConfigurableTextEditor buildEditor() {
final EmbeddedTextEditorPresenter<OrionEditorWidget> editor = orionTextEditorFactory.createTextEditor();
editor.initialize(new AutoSaveTextEditorConfiguration(), notificationManager);
return editor;
}
});
}
private void defineDefaultTheme() {
// The codenvy theme uses both an orion css file and a CssResource
orionResource.editorStyle().ensureInjected();
OrionTextThemeOverlay.setDefaultTheme("orionCodenvy", "orion-codenvy-theme.css");
}
private void initializationFailed(final InitializerCallback callback, final String errorMessage, Throwable e) {
if (initFailedWarnedOnce) {
return;
}
initFailedWarnedOnce = true;
notificationManager.notify(errorMessage, StatusNotification.Status.FAIL, true);
LOG.log(Level.SEVERE, errorMessage + " - ", e);
callback.onFailure(e);
}
@Override
public OrionKeyBindingModule get() {
return keyBindingModule;
}
}