/*******************************************************************************
* Copyright (c) 2012-2017 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.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.HasChangeHandlers;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import com.google.web.bindery.event.shared.EventBus;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.ide.api.dialogs.DialogFactory;
import org.eclipse.che.ide.api.editor.annotation.AnnotationModel;
import org.eclipse.che.ide.api.editor.annotation.AnnotationModelEvent;
import org.eclipse.che.ide.api.editor.codeassist.AdditionalInfoCallback;
import org.eclipse.che.ide.api.editor.codeassist.CompletionProposal;
import org.eclipse.che.ide.api.editor.codeassist.CompletionReadyCallback;
import org.eclipse.che.ide.api.editor.codeassist.CompletionsSource;
import org.eclipse.che.ide.api.editor.events.CursorActivityEvent;
import org.eclipse.che.ide.api.editor.events.CursorActivityHandler;
import org.eclipse.che.ide.api.editor.events.GutterClickEvent;
import org.eclipse.che.ide.api.editor.events.GutterClickHandler;
import org.eclipse.che.ide.api.editor.events.HasCursorActivityHandlers;
import org.eclipse.che.ide.api.editor.gutter.Gutter;
import org.eclipse.che.ide.api.editor.gutter.Gutters;
import org.eclipse.che.ide.api.editor.gutter.HasGutter;
import org.eclipse.che.ide.api.editor.keymap.KeyBinding;
import org.eclipse.che.ide.api.editor.keymap.Keymap;
import org.eclipse.che.ide.api.editor.keymap.KeymapChangeEvent;
import org.eclipse.che.ide.api.editor.keymap.KeymapChangeHandler;
import org.eclipse.che.ide.api.editor.link.LinkedMode;
import org.eclipse.che.ide.api.editor.position.PositionConverter;
import org.eclipse.che.ide.api.editor.text.Position;
import org.eclipse.che.ide.api.editor.text.Region;
import org.eclipse.che.ide.api.editor.text.RegionImpl;
import org.eclipse.che.ide.api.editor.text.TextRange;
import org.eclipse.che.ide.api.editor.text.annotation.Annotation;
import org.eclipse.che.ide.api.editor.texteditor.ContentInitializedHandler;
import org.eclipse.che.ide.api.editor.texteditor.EditorWidget;
import org.eclipse.che.ide.api.editor.texteditor.HandlesUndoRedo;
import org.eclipse.che.ide.api.editor.texteditor.LineStyler;
import org.eclipse.che.ide.api.event.EditorSettingsChangedEvent;
import org.eclipse.che.ide.api.event.EditorSettingsChangedEvent.EditorSettingsChangedHandler;
import org.eclipse.che.ide.api.event.SelectionChangedEvent;
import org.eclipse.che.ide.api.event.SelectionChangedHandler;
import org.eclipse.che.ide.api.hotkeys.HotKeyItem;
import org.eclipse.che.ide.api.preferences.PreferencesManager;
import org.eclipse.che.ide.editor.orion.client.events.HasScrollHandlers;
import org.eclipse.che.ide.editor.orion.client.events.ScrollEvent;
import org.eclipse.che.ide.editor.orion.client.events.ScrollHandler;
import org.eclipse.che.ide.editor.orion.client.incremental.find.IncrementalFindReportStatusObserver;
import org.eclipse.che.ide.editor.orion.client.jso.OrionAnnotationModelOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionAnnotationOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionCodeEditWidgetOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionContentAssistOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionEditorOptionsOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionEditorOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionEditorViewOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionEventTargetOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionExtRulerOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionInputChangedEventOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionKeyBindingsRelationOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionKeyModeOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionKeyStrokeOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionProblemOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionRulerClickEventOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionSelectionOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionStyleOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionTextViewOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.StatusMessageReporterOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.UiUtilsOverlay;
import org.eclipse.che.ide.editor.preferences.editorproperties.EditorPropertiesManager;
import org.eclipse.che.ide.editor.preferences.keymaps.KeyMapsPreferencePresenter;
import org.eclipse.che.ide.status.message.StatusMessageReporter;
import org.eclipse.che.ide.util.browser.UserAgent;
import org.eclipse.che.ide.util.loging.Log;
import org.eclipse.che.requirejs.ModuleHolder;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.eclipse.che.ide.editor.orion.client.KeyMode.EMACS;
import static org.eclipse.che.ide.editor.orion.client.KeyMode.VI;
/**
* Orion implementation for {@link EditorWidget}.
*
* @author "Mickaël Leduque"
*/
public class OrionEditorWidget extends Composite implements EditorWidget,
HasChangeHandlers,
HasCursorActivityHandlers,
HasScrollHandlers,
HasGutter {
/** The UI binder instance. */
private static final OrionEditorWidgetUiBinder UIBINDER = GWT.create(OrionEditorWidgetUiBinder.class);
/** The logger. */
private static final Logger LOG = Logger.getLogger(OrionEditorWidget.class.getSimpleName());
private final ModuleHolder moduleHolder;
private final EventBus eventBus;
private final KeyModeInstances keyModeInstances;
private final JavaScriptObject uiUtilsOverlay;
private final ContentAssistWidgetFactory contentAssistWidgetFactory;
private final DialogFactory dialogFactory;
private final PreferencesManager preferencesManager;
@UiField
SimplePanel panel;
/** The instance of the orion editor native element style. */
@UiField
EditorElementStyle editorElementStyle;
private OrionEditorViewOverlay editorViewOverlay;
private OrionEditorOverlay editorOverlay;
private String modeName;
private OrionExtRulerOverlay orionLineNumberRuler;
/** Component that handles undo/redo. */
private HandlesUndoRedo undoRedo;
private OrionDocument embeddedDocument;
private OrionKeyModeOverlay cheContentAssistMode;
private EditorPropertiesManager editorPropertiesManager;
private Keymap keymap;
private ContentAssistWidget assistWidget;
private Gutter gutter;
private boolean changeHandlerAdded = false;
private boolean focusHandlerAdded = false;
private boolean blurHandlerAdded = false;
private boolean scrollHandlerAdded = false;
private boolean cursorHandlerAdded = false;
private boolean gutterClickHandlerAdded = false;
/** Component that handles line styling. */
private LineStyler lineStyler;
@AssistedInject
public OrionEditorWidget(final ModuleHolder moduleHolder,
final KeyModeInstances keyModeInstances,
final EventBus eventBus,
final EditorPropertiesManager editorPropertiesManager,
final Provider<OrionCodeEditWidgetOverlay> orionCodeEditWidgetProvider,
final ContentAssistWidgetFactory contentAssistWidgetFactory,
final DialogFactory dialogFactory,
final PreferencesManager preferencesManager,
@Assisted final List<String> editorModes,
@Assisted final WidgetInitializedCallback widgetInitializedCallback,
final Provider<OrionEditorOptionsOverlay> editorOptionsProvider,
final StatusMessageReporter statusMessageReporter,
final IncrementalFindReportStatusObserver incrementalFindObserver) {
this.contentAssistWidgetFactory = contentAssistWidgetFactory;
this.moduleHolder = moduleHolder;
this.keyModeInstances = keyModeInstances;
this.eventBus = eventBus;
this.dialogFactory = dialogFactory;
this.preferencesManager = preferencesManager;
initWidget(UIBINDER.createAndBindUi(this));
this.editorPropertiesManager = editorPropertiesManager;
this.uiUtilsOverlay = moduleHolder.getModule("UiUtils");
// just first choice for the moment
if (editorModes != null && !editorModes.isEmpty()) {
setMode(editorModes.get(0));
}
panel.getElement().setId("orion-parent-" + Document.get().createUniqueId());
panel.getElement().addClassName(this.editorElementStyle.editorParent());
OrionEditorOptionsOverlay editorOptions = initEditorOptions(editorOptionsProvider.get(), statusMessageReporter);
orionCodeEditWidgetProvider.get().createEditorView(panel.getElement(), editorOptions)
.then(new EditorViewCreatedOperation(widgetInitializedCallback));
incrementalFindObserver.setEditorWidget(this);
statusMessageReporter.registerObserver(incrementalFindObserver);
registerPromptFunction();
eventBus.addHandler(EditorSettingsChangedEvent.TYPE, new EditorSettingsChangedHandler() {
@Override
public void onEditorSettingsChanged(EditorSettingsChangedEvent event) {
final JSONObject properties = editorPropertiesManager.getJsonEditorProperties();
editorViewOverlay.updateSettings(properties.getJavaScriptObject());
}
});
}
private OrionEditorOptionsOverlay initEditorOptions(OrionEditorOptionsOverlay orionEditorOptionsOverlay,
StatusMessageReporter statusMessageReporter) {
StatusMessageReporterOverlay statusMessageReporterOverlay = StatusMessageReporterOverlay.create(statusMessageReporter);
orionEditorOptionsOverlay.setStatusReporter(statusMessageReporterOverlay);
return orionEditorOptionsOverlay;
}
private Gutter initBreakpointRuler(ModuleHolder moduleHolder) {
JavaScriptObject orionEventTargetModule = moduleHolder.getModule("OrionEventTarget");
orionLineNumberRuler = editorOverlay.getTextView().getRulers()[1];
orionLineNumberRuler.overrideOnClickEvent();
OrionEventTargetOverlay.addMixin(orionEventTargetModule, orionLineNumberRuler);
return new OrionBreakpointRuler(orionLineNumberRuler, editorOverlay);
}
@Override
public String getValue() {
return editorOverlay.getText();
}
@Override
public void setValue(String newValue, final ContentInitializedHandler initializationHandler) {
editorOverlay.addEventListener(OrionInputChangedEventOverlay.TYPE,
new OrionEditorOverlay.EventHandler<OrionInputChangedEventOverlay>() {
@Override
public void onEvent(OrionInputChangedEventOverlay event) {
if (initializationHandler != null) {
initializationHandler.onContentInitialized();
}
}
}, true);
this.editorViewOverlay.setContents(newValue, modeName);
this.editorOverlay.getUndoStack().reset();
}
@Override
public String getMode() {
return modeName;
}
public void setMode(final String modeName) {
String mode = modeName;
if (modeName.equals("text/x-java")) {
mode = "text/x-java-source";
}
LOG.fine("Requested mode: " + modeName + " kept " + mode);
this.modeName = mode;
}
@Override
public boolean isReadOnly() {
return this.editorOverlay.getTextView().getOptions().isReadOnly();
}
@Override
public void setReadOnly(final boolean isReadOnly) {
editorViewOverlay.setReadonly(isReadOnly);
final JSONObject properties = editorPropertiesManager.getJsonEditorProperties();
editorViewOverlay.updateSettings(properties.getJavaScriptObject());
}
@Override
public void setAnnotationRulerVisible(boolean show) {
editorOverlay.setAnnotationRulerVisible(show);
}
@Override
public void setFoldingRulerVisible(boolean show) {
editorOverlay.setFoldingRulerVisible(show);
}
@Override
public void setZoomRulerVisible(boolean show) {
editorOverlay.setZoomRulerVisible(show);
}
@Override
public void setOverviewRulerVisible(boolean show) {
editorOverlay.setOverviewRulerVisible(show);
}
@Override
public boolean isDirty() {
return this.editorOverlay.isDirty();
}
@Override
public void markClean() {
this.editorOverlay.setDirty(false);
}
private void selectKeyMode(Keymap keymap) {
resetKeyModes();
Keymap usedKeymap = keymap;
if (usedKeymap == null) {
usedKeymap = KeyMode.DEFAULT;
}
if (KeyMode.DEFAULT.equals(usedKeymap)) {
// nothing to do
} else if (EMACS.equals(usedKeymap)) {
this.editorOverlay.getTextView().addKeyMode(keyModeInstances.getInstance(EMACS));
} else if (VI.equals(usedKeymap)) {
this.editorOverlay.getTextView().addKeyMode(keyModeInstances.getInstance(VI));
} else {
usedKeymap = KeyMode.DEFAULT;
Log.error(OrionEditorWidget.class, "Unknown keymap type: " + keymap + " - changing to default one.");
}
this.keymap = usedKeymap;
}
private void resetKeyModes() {
this.editorOverlay.getTextView().removeKeyMode(keyModeInstances.getInstance(VI));
this.editorOverlay.getTextView().removeKeyMode(keyModeInstances.getInstance(EMACS));
}
@Override
public org.eclipse.che.ide.api.editor.document.Document getDocument() {
if (this.embeddedDocument == null) {
this.embeddedDocument = new OrionDocument(this.editorOverlay.getTextView(), this, editorOverlay);
}
return this.embeddedDocument;
}
@Override
public Region getSelectedRange() {
final OrionSelectionOverlay selection = this.editorOverlay.getSelection();
final int start = selection.getStart();
final int end = selection.getEnd();
if (start < 0 || end > this.editorOverlay.getModel().getCharCount() || start > end) {
throw new RuntimeException("Invalid selection");
}
return new RegionImpl(start, end - start);
}
@Override
public void setSelectedRange(final Region selection, final boolean show) {
this.editorOverlay.setSelection(selection.getOffset(), selection.getLength(), show);
}
@Override
public void setDisplayRange(final Region range) {
// show the line at the head of the range
final int headOffset = range.getOffset() + range.getLength();
if (range.getLength() < 0) {
this.editorOverlay.getTextView().setTopIndex(headOffset);
} else {
this.editorOverlay.getTextView().setBottomIndex(headOffset);
}
}
@Override
public int getTabSize() {
return this.editorOverlay.getTextView().getOptions().getTabSize();
}
@Override
public void setTabSize(int tabSize) {
this.editorOverlay.getTextView().getOptions().setTabSize(tabSize);
}
@Override
public HandlerRegistration addChangeHandler(final ChangeHandler handler) {
if (!changeHandlerAdded) {
changeHandlerAdded = true;
final OrionTextViewOverlay textView = this.editorOverlay.getTextView();
textView.addEventListener(OrionEventConstants.MODEL_CHANGED_EVENT, new OrionTextViewOverlay.EventHandlerNoParameter() {
@Override
public void onEvent() {
fireChangeEvent();
}
});
}
return addHandler(handler, ChangeEvent.getType());
}
private void fireChangeEvent() {
DomEvent.fireNativeEvent(Document.get().createChangeEvent(), this);
}
@Override
public HandlerRegistration addCursorActivityHandler(CursorActivityHandler handler) {
if (!cursorHandlerAdded) {
cursorHandlerAdded = true;
final OrionTextViewOverlay textView = this.editorOverlay.getTextView();
textView.addEventListener(OrionEventConstants.SELECTION_EVENT, new OrionTextViewOverlay.EventHandlerNoParameter() {
@Override
public void onEvent() {
fireCursorActivityEvent();
}
});
}
return addHandler(handler, CursorActivityEvent.TYPE);
}
private void fireCursorActivityEvent() {
fireEvent(new CursorActivityEvent());
}
@Override
public HandlerRegistration addFocusHandler(FocusHandler handler) {
if (!focusHandlerAdded) {
focusHandlerAdded = true;
final OrionTextViewOverlay textView = this.editorOverlay.getTextView();
textView.addEventListener(OrionEventConstants.FOCUS_EVENT, new OrionTextViewOverlay.EventHandlerNoParameter() {
@Override
public void onEvent() {
fireFocusEvent();
}
});
}
return addHandler(handler, FocusEvent.getType());
}
private void fireFocusEvent() {
DomEvent.fireNativeEvent(Document.get().createFocusEvent(), this);
}
@Override
public HandlerRegistration addBlurHandler(BlurHandler handler) {
if (!blurHandlerAdded) {
blurHandlerAdded = true;
final OrionTextViewOverlay textView = this.editorOverlay.getTextView();
textView.addEventListener(OrionEventConstants.BLUR_EVENT, new OrionTextViewOverlay.EventHandlerNoParameter() {
@Override
public void onEvent() {
fireBlurEvent();
}
});
}
return addHandler(handler, BlurEvent.getType());
}
private void fireBlurEvent() {
DomEvent.fireNativeEvent(Document.get().createBlurEvent(), this);
}
@Override
public HandlerRegistration addScrollHandler(final ScrollHandler handler) {
if (!scrollHandlerAdded) {
scrollHandlerAdded = true;
final OrionTextViewOverlay textView = this.editorOverlay.getTextView();
textView.addEventListener(OrionEventConstants.SCROLL_EVENT, new OrionTextViewOverlay.EventHandlerNoParameter() {
@Override
public void onEvent() {
fireScrollEvent();
}
});
}
return addHandler(handler, ScrollEvent.TYPE);
}
private void fireScrollEvent() {
fireEvent(new ScrollEvent());
}
private void setupKeymode() {
final String propertyValue = preferencesManager.getValue(KeyMapsPreferencePresenter.KEYMAP_PREF_KEY);
Keymap keymap;
try {
keymap = Keymap.fromKey(propertyValue);
} catch (final IllegalArgumentException e) {
LOG.log(Level.WARNING, "Unknown value in keymap preference.", e);
return;
}
selectKeyMode(keymap);
}
@Override
public Keymap getKeymap() {
return this.keymap;
}
@Override
public PositionConverter getPositionConverter() {
return embeddedDocument.getPositionConverter();
}
@Override
public void setFocus() {
this.editorOverlay.focus();
}
@Override
public void showMessage(final String message) {
this.editorOverlay.reportStatus(message);
}
@Override
protected void onLoad() {
// fix for native editor height
if (panel.getElement().getChildCount() > 0) {
final Element child = panel.getElement().getFirstChildElement();
child.setId("orion-editor-" + Document.get().createUniqueId());
child.getStyle().clearHeight();
} else {
LOG.severe("Orion insertion failed.");
}
}
@Override
public void onResize() {
// redraw text and rulers
// maybe just redrawing the text would be enough
this.editorOverlay.getTextView().redraw();
}
@Override
public HandlesUndoRedo getUndoRedo() {
return this.undoRedo;
}
@Override
public void addKeyBinding(final KeyBinding keyBinding) {
addKeyBinding(keyBinding, "");
}
@Override
public void addKeyBinding(final KeyBinding keyBinding, String actionDescription) {
OrionKeyStrokeOverlay strokeOverlay;
JavaScriptObject keyBindingModule = moduleHolder.getModule("OrionKeyBinding").cast();
String type = keyBinding.getType();
boolean modifier1 = UserAgent.isMac() ? keyBinding.isCmd() : keyBinding.isControl();
boolean modifier2 = keyBinding.isShift();
boolean modifier3 = keyBinding.isAlt();
boolean modifier4 = UserAgent.isMac() ? keyBinding.isControl() : false;
if (keyBinding.isCharacterBinding()) {
strokeOverlay = OrionKeyStrokeOverlay.create(keyBinding.getCharacter(), modifier1, modifier2, modifier3,
modifier4, type, keyBindingModule);
} else {
strokeOverlay = OrionKeyStrokeOverlay.create(keyBinding.getKeyCodeNumber(), modifier1, modifier2, modifier3,
modifier4, type, keyBindingModule);
}
String actionId = "che-action-" + keyBinding.getAction().toString();
editorOverlay.getTextView().setKeyBinding(strokeOverlay, actionId);
editorOverlay.getTextView().setAction(actionId, new Action() {
@Override
public boolean onAction() {
return keyBinding.getAction().action();
}
}, actionDescription);
}
@Override
public List<HotKeyItem> getHotKeys() {
OrionTextViewOverlay orionTextViewOverlay = editorOverlay.getTextView();
List<HotKeyItem> hotKeyItems = new ArrayList<>();
JsArray<OrionKeyBindingsRelationOverlay> keyBindings = OrionKeyModeOverlay.getKeyBindings_(orionTextViewOverlay);
for (int i = 0; i < keyBindings.length(); i++) {
OrionKeyBindingsRelationOverlay key = keyBindings.get(i);
String actionId = key.getActionId();
String actionDescription = orionTextViewOverlay.getActionDescription(actionId);
String hotKey = UiUtilsOverlay.getUserKeyString(uiUtilsOverlay, key.getKeyBindings());
if (actionDescription != null) {
hotKeyItems.add(new HotKeyItem(actionDescription, hotKey));
}
}
return hotKeyItems;
}
@Override
public MarkerRegistration addMarker(final TextRange range, final String className) {
final OrionAnnotationOverlay annotation = OrionAnnotationOverlay.create();
OrionStyleOverlay styleOverlay = OrionStyleOverlay.create();
styleOverlay.setStyleClass(className);
int start = embeddedDocument.getIndexFromPosition(range.getFrom());
int end = embeddedDocument.getIndexFromPosition(range.getTo());
annotation.setStart(start);
annotation.setEnd(end);
annotation.setRangeStyle(styleOverlay);
annotation.setType("che-marker");
editorOverlay.getAnnotationModel().addAnnotation(annotation);
return new MarkerRegistration() {
@Override
public void clearMark() {
editorOverlay.getAnnotationModel().removeAnnotation(annotation);
}
};
}
@Override
public void showCompletionsProposals(final List<CompletionProposal> proposals) {
if (proposals == null || proposals.isEmpty()) {
/** Hide autocompletion when it's visible and it is nothing to propose */
if (assistWidget.isVisible()) {
assistWidget.hide();
}
return;
}
assistWidget.show(proposals);
}
@Override
public void showCompletionProposals(final CompletionsSource completionsSource) {
completionsSource.computeCompletions(new CompletionReadyCallback() {
@Override
public void onCompletionReady(List<CompletionProposal> proposals) {
showCompletionsProposals(proposals);
}
});
}
@Override
public LineStyler getLineStyler() {
return lineStyler;
}
@Override
public HandlerRegistration addGutterClickHandler(final GutterClickHandler handler) {
if (!gutterClickHandlerAdded) {
gutterClickHandlerAdded = true;
orionLineNumberRuler.addEventListener(OrionEventConstants.RULER_CLICK_EVENT,
new OrionExtRulerOverlay.EventHandler<OrionRulerClickEventOverlay>() {
@Override
public void onEvent(OrionRulerClickEventOverlay parameter) {
final int lineIndex = parameter.getLineIndex();
fireGutterClickEvent(lineIndex);
}
},
false);
}
return addHandler(handler, GutterClickEvent.TYPE);
}
private void fireGutterClickEvent(final int line) {
final GutterClickEvent gutterEvent = new GutterClickEvent(line, Gutters.BREAKPOINTS_GUTTER, null);
fireEvent(gutterEvent);
this.embeddedDocument.getDocEventBus().fireEvent(gutterEvent);
}
@Override
public void showCompletionProposals() {
editorOverlay.getContentAssist().activate();
}
@Override
public void showCompletionProposals(final CompletionsSource completionsSource, final AdditionalInfoCallback additionalInfoCallback) {
showCompletionProposals(completionsSource);
}
@Override
public void refresh() {
this.editorOverlay.getTextView().redraw();
}
@Override
public boolean isCompletionProposalsShowing() {
return assistWidget.isVisible();
}
public void scrollToLine(int line) {
this.editorOverlay.getTextView().setTopIndex(line);
}
public void showErrors(AnnotationModelEvent event) {
List<Annotation> addedAnnotations = event.getAddedAnnotations();
JsArray<OrionProblemOverlay> jsArray = JsArray.createArray().cast();
AnnotationModel annotationModel = event.getAnnotationModel();
OrionAnnotationSeverityProvider severityProvider = null;
if (annotationModel instanceof OrionAnnotationSeverityProvider) {
severityProvider = (OrionAnnotationSeverityProvider)annotationModel;
}
for (Annotation annotation : addedAnnotations) {
Position position = annotationModel.getPosition(annotation);
OrionProblemOverlay problem = JavaScriptObject.createObject().cast();
problem.setDescription(annotation.getText());
problem.setStart(position.getOffset());
problem.setEnd(position.getOffset() + position.getLength());
problem.setId("che-annotation");
problem.setSeverity(getSeverity(annotation.getType(), severityProvider));
jsArray.push(problem);
}
editorOverlay.showProblems(jsArray);
}
private String getSeverity(String type, OrionAnnotationSeverityProvider provider) {
if (provider != null) {
return provider.getSeverity(type);
} else {
return "error";
}
}
public void clearErrors() {
editorOverlay.showProblems(JavaScriptObject.createArray().<JsArray<OrionProblemOverlay>>cast());
}
public OrionTextViewOverlay getTextView() {
return editorOverlay.getTextView();
}
public LinkedMode getLinkedMode() {
return editorOverlay.getLinkedMode(editorOverlay.getAnnotationModel());
}
public void showCompletionInformation() {
if (assistWidget.isVisible()) {
assistWidget.showCompletionInfo();
}
}
public OrionAnnotationModelOverlay getAnnotationModel() {
return editorOverlay.getAnnotationModel();
}
/**
* Returns {@link OrionEditorOverlay}.
*/
public OrionEditorOverlay getEditor() {
return editorOverlay;
}
@Override
public Gutter getGutter() {
return gutter;
}
public int getTopVisibleLine() {
return editorOverlay.getTextView().getTopIndex();
}
public void setTopLine(int line) {
editorOverlay.getTextView().setTopIndex(line);
}
/**
* UI binder interface for this component.
*
* @author "Mickaël Leduque"
*/
interface OrionEditorWidgetUiBinder extends UiBinder<SimplePanel, OrionEditorWidget> {
}
/**
* CSS style for the orion native editor element.
*
* @author "Mickaël Leduque"
*/
public interface EditorElementStyle extends CssResource {
@ClassName("editor-parent")
String editorParent();
}
private class EditorViewCreatedOperation implements Operation<OrionEditorViewOverlay> {
private final WidgetInitializedCallback widgetInitializedCallback;
private EditorViewCreatedOperation(WidgetInitializedCallback widgetInitializedCallback) {
this.widgetInitializedCallback = widgetInitializedCallback;
}
@Override
public void apply(OrionEditorViewOverlay arg) throws OperationException {
editorViewOverlay = arg;
editorOverlay = arg.getEditor();
final OrionContentAssistOverlay contentAssist = editorOverlay.getContentAssist();
eventBus.addHandler(SelectionChangedEvent.TYPE, new SelectionChangedHandler() {
@Override
public void onSelectionChanged(SelectionChangedEvent event) {
if (contentAssist.isActive()) {
contentAssist.deactivate();
}
}
});
lineStyler = new OrionLineStyler(editorOverlay);
final OrionTextViewOverlay textView = editorOverlay.getTextView();
keyModeInstances.add(VI, OrionKeyModeOverlay.getViKeyMode(moduleHolder.getModule("OrionVi"), textView));
keyModeInstances.add(EMACS, OrionKeyModeOverlay.getEmacsKeyMode(moduleHolder.getModule("OrionEmacs"), textView));
setupKeymode();
eventBus.addHandler(KeymapChangeEvent.TYPE, new KeymapChangeHandler() {
@Override
public void onKeymapChanged(final KeymapChangeEvent event) {
setupKeymode();
}
});
undoRedo = new OrionUndoRedo(editorOverlay.getUndoStack());
editorOverlay.setZoomRulerVisible(true);
editorOverlay.getAnnotationStyler().addAnnotationType("che-marker", 100);
cheContentAssistMode = OrionKeyModeOverlay.getCheCodeAssistMode(moduleHolder.getModule("CheContentAssistMode"),
editorOverlay.getTextView());
assistWidget = contentAssistWidgetFactory.create(OrionEditorWidget.this, cheContentAssistMode);
gutter = initBreakpointRuler(moduleHolder);
final JSONObject editorProperties = editorPropertiesManager.getJsonEditorProperties();
editorViewOverlay.updateSettings(editorProperties.getJavaScriptObject());
widgetInitializedCallback.initialized(OrionEditorWidget.this);
}
}
/**
* Registers global prompt function to be accessible directly from JavaScript.
* <p/>
* Function promptIDE(title, text, defaultValue, callback)
* title Dialog title
* text The text to display in the dialog box
* defaultValue The default value
* callback function(value)
* clicking "OK" will return input value
* clicking "Cancel" will return null
*/
private native void registerPromptFunction() /*-{
if (!$wnd["promptIDE"]) {
var instance = this;
$wnd["promptIDE"] = function (title, text, defaultValue, callback) {
instance.@org.eclipse.che.ide.editor.orion.client.OrionEditorWidget::askLineNumber(*)(title, text, defaultValue, callback);
};
}
}-*/;
/**
* Custom callback to pass given value to native javascript function.
*/
private class InputCallback implements org.eclipse.che.ide.api.dialogs.InputCallback {
private JavaScriptObject callback;
public InputCallback(JavaScriptObject callback) {
this.callback = callback;
}
@Override
public native void accepted(String value) /*-{
var callback = this.@org.eclipse.che.ide.editor.orion.client.OrionEditorWidget.InputCallback::callback;
callback(value);
}-*/;
}
private void askLineNumber(String title, String text, String defaultValue, final JavaScriptObject callback) {
if (defaultValue == null) {
defaultValue = "";
} else {
// It's strange situation defaultValue.length() returns 'undefined' but must return a number.
// Reinitialise the variable resolves the problem.
defaultValue = "" + defaultValue;
}
dialogFactory.createInputDialog(title, text, defaultValue, 0, defaultValue.length(), new InputCallback(callback), null).show();
}
}