/******************************************************************************* * 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.common.base.Optional; import com.google.gwt.core.client.Scheduler; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.ContextMenuEvent; import com.google.gwt.event.dom.client.ContextMenuHandler; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.AcceptsOneWidget; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Widget; import com.google.inject.Inject; import com.google.web.bindery.event.shared.EventBus; import com.google.web.bindery.event.shared.HandlerRegistration; import org.eclipse.che.api.promises.client.Function; import org.eclipse.che.api.promises.client.FunctionException; import org.eclipse.che.api.promises.client.Operation; import org.eclipse.che.api.promises.client.OperationException; import org.eclipse.che.api.promises.client.Promise; import org.eclipse.che.api.promises.client.PromiseError; import org.eclipse.che.ide.actions.LinkWithEditorAction; import org.eclipse.che.ide.api.app.AppContext; import org.eclipse.che.ide.api.debug.BreakpointManager; import org.eclipse.che.ide.api.debug.BreakpointRenderer; import org.eclipse.che.ide.api.debug.BreakpointRendererFactory; import org.eclipse.che.ide.api.debug.HasBreakpointRenderer; import org.eclipse.che.ide.api.dialogs.CancelCallback; import org.eclipse.che.ide.api.dialogs.ConfirmCallback; import org.eclipse.che.ide.api.dialogs.DialogFactory; import org.eclipse.che.ide.api.editor.AbstractEditorPresenter; import org.eclipse.che.ide.api.editor.EditorAgent; import org.eclipse.che.ide.api.editor.EditorInput; import org.eclipse.che.ide.api.editor.EditorLocalizationConstants; import org.eclipse.che.ide.api.editor.EditorWithAutoSave; import org.eclipse.che.ide.api.editor.EditorWithErrors; 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.annotation.AnnotationModelHandler; import org.eclipse.che.ide.api.editor.annotation.ClearAnnotationModelEvent; import org.eclipse.che.ide.api.editor.annotation.ClearAnnotationModelHandler; import org.eclipse.che.ide.api.editor.annotation.HasAnnotationRendering; import org.eclipse.che.ide.api.editor.codeassist.CodeAssistProcessor; import org.eclipse.che.ide.api.editor.codeassist.CodeAssistantFactory; import org.eclipse.che.ide.api.editor.codeassist.CompletionsSource; import org.eclipse.che.ide.api.editor.codeassist.HasCompletionInformation; import org.eclipse.che.ide.api.editor.document.Document; import org.eclipse.che.ide.api.editor.document.DocumentHandle; import org.eclipse.che.ide.api.editor.document.DocumentStorage; import org.eclipse.che.ide.api.editor.editorconfig.EditorUpdateAction; import org.eclipse.che.ide.api.editor.editorconfig.TextEditorConfiguration; import org.eclipse.che.ide.api.editor.events.CompletionRequestEvent; import org.eclipse.che.ide.api.editor.events.DocumentReadyEvent; 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.filetype.FileTypeIdentifier; import org.eclipse.che.ide.api.editor.formatter.ContentFormatter; 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.KeyBindingAction; import org.eclipse.che.ide.api.editor.link.HasLinkedMode; import org.eclipse.che.ide.api.editor.link.LinkedMode; import org.eclipse.che.ide.api.editor.link.LinkedModel; import org.eclipse.che.ide.api.editor.link.LinkedModelData; import org.eclipse.che.ide.api.editor.link.LinkedModelGroup; import org.eclipse.che.ide.api.editor.position.PositionConverter; import org.eclipse.che.ide.api.editor.quickfix.QuickAssistAssistant; import org.eclipse.che.ide.api.editor.quickfix.QuickAssistProcessor; import org.eclipse.che.ide.api.editor.quickfix.QuickAssistantFactory; import org.eclipse.che.ide.api.editor.reconciler.Reconciler; import org.eclipse.che.ide.api.editor.reconciler.ReconcilerWithAutoSave; import org.eclipse.che.ide.api.editor.signature.SignatureHelp; import org.eclipse.che.ide.api.editor.signature.SignatureHelpProvider; import org.eclipse.che.ide.api.editor.text.LinearRange; import org.eclipse.che.ide.api.editor.text.TextPosition; import org.eclipse.che.ide.api.editor.text.TextRange; import org.eclipse.che.ide.api.editor.texteditor.CanWrapLines; import org.eclipse.che.ide.api.editor.texteditor.ContentInitializedHandler; import org.eclipse.che.ide.api.editor.texteditor.CursorModelWithHandler; import org.eclipse.che.ide.api.editor.texteditor.EditorWidget; import org.eclipse.che.ide.api.editor.texteditor.EditorWidgetFactory; import org.eclipse.che.ide.api.editor.texteditor.HandlesTextOperations; import org.eclipse.che.ide.api.editor.texteditor.HandlesUndoRedo; import org.eclipse.che.ide.api.editor.texteditor.HasKeyBindings; import org.eclipse.che.ide.api.editor.texteditor.HasReadOnlyProperty; import org.eclipse.che.ide.api.editor.texteditor.TextEditor; import org.eclipse.che.ide.api.editor.texteditor.TextEditorOperations; import org.eclipse.che.ide.api.editor.texteditor.TextEditorPartView; import org.eclipse.che.ide.api.editor.texteditor.UndoableEditor; import org.eclipse.che.ide.api.event.FileContentUpdateEvent; import org.eclipse.che.ide.api.event.ng.ClientServerEventService; import org.eclipse.che.ide.api.event.ng.DeletedFilesController; import org.eclipse.che.ide.api.hotkeys.HasHotKeyItems; import org.eclipse.che.ide.api.hotkeys.HotKeyItem; import org.eclipse.che.ide.api.notification.NotificationManager; import org.eclipse.che.ide.api.parts.EditorPartStack; import org.eclipse.che.ide.api.parts.EditorTab; import org.eclipse.che.ide.api.parts.PartPresenter; import org.eclipse.che.ide.api.parts.WorkspaceAgent; import org.eclipse.che.ide.api.preferences.PreferencesManager; import org.eclipse.che.ide.api.resources.File; import org.eclipse.che.ide.api.resources.Resource; import org.eclipse.che.ide.api.resources.ResourceChangedEvent; import org.eclipse.che.ide.api.resources.ResourceDelta; import org.eclipse.che.ide.api.resources.VirtualFile; import org.eclipse.che.ide.api.selection.Selection; import org.eclipse.che.ide.editor.orion.client.jso.OrionLinkedModelDataOverlay; import org.eclipse.che.ide.editor.orion.client.jso.OrionLinkedModelGroupOverlay; import org.eclipse.che.ide.editor.orion.client.jso.OrionLinkedModelOverlay; import org.eclipse.che.ide.editor.orion.client.menu.EditorContextMenu; import org.eclipse.che.ide.editor.orion.client.signature.SignatureHelpView; import org.eclipse.che.ide.part.editor.multipart.EditorMultiPartStackPresenter; import org.eclipse.che.ide.resource.Path; import org.vectomatic.dom.svg.ui.SVGResource; import javax.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; import java.util.Map; import static java.lang.Boolean.parseBoolean; import static org.eclipse.che.ide.api.notification.StatusNotification.DisplayMode.NOT_EMERGE_MODE; import static org.eclipse.che.ide.api.notification.StatusNotification.Status.FAIL; import static org.eclipse.che.ide.api.resources.ResourceDelta.ADDED; import static org.eclipse.che.ide.api.resources.ResourceDelta.DERIVED; import static org.eclipse.che.ide.api.resources.ResourceDelta.MOVED_FROM; import static org.eclipse.che.ide.api.resources.ResourceDelta.MOVED_TO; import static org.eclipse.che.ide.api.resources.ResourceDelta.REMOVED; import static org.eclipse.che.ide.api.resources.ResourceDelta.UPDATED; /** * {@link TextEditor} using orion. * This class is only defined to allow the Gin binding to be performed. */ public class OrionEditorPresenter extends AbstractEditorPresenter implements TextEditor, UndoableEditor, HasBreakpointRenderer, HasReadOnlyProperty, HandlesTextOperations, EditorWithAutoSave, EditorWithErrors, HasHotKeyItems, TextEditorPartView.Delegate, HasAnnotationRendering, HasLinkedMode, HasCompletionInformation, HasGutter, CanWrapLines { /** File type used when we have no idea of the actual content type. */ public static final String DEFAULT_CONTENT_TYPE = "text/plain"; private static final String TOGGLE_LINE_BREAKPOINT = "Toggle line breakpoint"; private final CodeAssistantFactory codeAssistantFactory; private final DeletedFilesController deletedFilesController; private final BreakpointManager breakpointManager; private final PreferencesManager preferencesManager; private final BreakpointRendererFactory breakpointRendererFactory; private final DialogFactory dialogFactory; private final DocumentStorage documentStorage; private final EditorMultiPartStackPresenter editorMultiPartStackPresenter; private final EditorLocalizationConstants constant; private final EditorWidgetFactory<OrionEditorWidget> editorWidgetFactory; private final EditorInitializePromiseHolder editorModule; private final TextEditorPartView editorView; private final EventBus generalEventBus; private final FileTypeIdentifier fileTypeIdentifier; private final QuickAssistantFactory quickAssistantFactory; private final WorkspaceAgent workspaceAgent; private final NotificationManager notificationManager; private final AppContext appContext; private final SignatureHelpView signatureHelpView; private final EditorContextMenu contextMenu; private final ClientServerEventService clientServerEventService; private final AnnotationRendering rendering = new AnnotationRendering(); private HasKeyBindings keyBindingsManager; private List<EditorUpdateAction> updateActions; private TextEditorConfiguration configuration; private OrionEditorWidget editorWidget; private Document document; private CursorModelWithHandler cursorModel; private QuickAssistAssistant quickAssistant; /** The editor's error state. */ private EditorState errorState; private boolean delayedFocus; private boolean isFocused; private BreakpointRenderer breakpointRenderer; private List<String> fileTypes; private TextPosition cursorPosition; private HandlerRegistration resourceChangeHandler; private OrionEditorInit editorInit; @Inject public OrionEditorPresenter(final CodeAssistantFactory codeAssistantFactory, final DeletedFilesController deletedFilesController, final BreakpointManager breakpointManager, final PreferencesManager preferencesManager, final BreakpointRendererFactory breakpointRendererFactory, final DialogFactory dialogFactory, final DocumentStorage documentStorage, final EditorMultiPartStackPresenter editorMultiPartStackPresenter, final EditorLocalizationConstants constant, final EditorWidgetFactory<OrionEditorWidget> editorWigetFactory, final EditorInitializePromiseHolder editorModule, final TextEditorPartView editorView, final EventBus eventBus, final FileTypeIdentifier fileTypeIdentifier, final QuickAssistantFactory quickAssistantFactory, final WorkspaceAgent workspaceAgent, final NotificationManager notificationManager, final AppContext appContext, final SignatureHelpView signatureHelpView, final EditorContextMenu contextMenu, final ClientServerEventService clientServerEventService) { this.codeAssistantFactory = codeAssistantFactory; this.deletedFilesController = deletedFilesController; this.breakpointManager = breakpointManager; this.preferencesManager = preferencesManager; this.breakpointRendererFactory = breakpointRendererFactory; this.dialogFactory = dialogFactory; this.documentStorage = documentStorage; this.editorMultiPartStackPresenter = editorMultiPartStackPresenter; this.constant = constant; this.editorWidgetFactory = editorWigetFactory; this.editorModule = editorModule; this.editorView = editorView; this.generalEventBus = eventBus; this.fileTypeIdentifier = fileTypeIdentifier; this.quickAssistantFactory = quickAssistantFactory; this.workspaceAgent = workspaceAgent; this.notificationManager = notificationManager; this.appContext = appContext; this.signatureHelpView = signatureHelpView; this.contextMenu = contextMenu; this.clientServerEventService = clientServerEventService; keyBindingsManager = new TemporaryKeyBindingsManager(); this.editorView.setDelegate(this); } @Override protected void initializeEditor(final EditorAgent.OpenEditorCallback callback) { QuickAssistProcessor processor = configuration.getQuickAssistProcessor(); if (quickAssistantFactory != null && processor != null) { quickAssistant = quickAssistantFactory.createQuickAssistant(this); quickAssistant.setQuickAssistProcessor(processor); } editorInit = new OrionEditorInit(configuration, this.codeAssistantFactory, this.quickAssistant, this); Promise<Void> initializerPromise = editorModule.getInitializerPromise(); initializerPromise.catchError(new Operation<PromiseError>() { @Override public void apply(PromiseError arg) throws OperationException { displayErrorPanel(constant.editorInitErrorMessage()); callback.onInitializationFailed(); } }).thenPromise(new Function<Void, Promise<String>>() { @Override public Promise<String> apply(Void arg) throws FunctionException { return documentStorage.getDocument(input.getFile()); } }).then(new Operation<String>() { @Override public void apply(String content) throws OperationException { createEditor(content); } }).catchError(new Operation<PromiseError>() { @Override public void apply(PromiseError arg) throws OperationException { displayErrorPanel(constant.editorFileErrorMessage()); callback.onInitializationFailed(); } }); } private void createEditor(final String content) { this.fileTypes = detectFileType(getEditorInput().getFile()); editorWidgetFactory.createEditorWidget(fileTypes, new EditorWidgetInitializedCallback(content)); } private void setupEventHandlers() { this.editorWidget.addChangeHandler(new ChangeHandler() { @Override public void onChange(final ChangeEvent event) { handleDocumentChanged(); } }); this.editorWidget.addGutterClickHandler(new GutterClickHandler() { @Override public void onGutterClick(final GutterClickEvent event) { if (Gutters.BREAKPOINTS_GUTTER.equals(event.getGutterId()) || Gutters.LINE_NUMBERS_GUTTER.equals(event.getGutterId())) { breakpointManager.changeBreakpointState(event.getLineNumber()); } } }); this.editorWidget.addKeyBinding(new KeyBinding(true, false, false, false, KeyCodes.KEY_F8, new KeyBindingAction() { @Override public boolean action() { int currentLine = editorWidget.getDocument().getCursorPosition().getLine(); breakpointManager.changeBreakpointState(currentLine); return true; } }), TOGGLE_LINE_BREAKPOINT); } private void setupFileContentUpdateHandler() { resourceChangeHandler = generalEventBus.addHandler(ResourceChangedEvent.getType(), new ResourceChangedEvent.ResourceChangedHandler() { @Override public void onResourceChanged(ResourceChangedEvent event) { final ResourceDelta delta = event.getDelta(); switch (delta.getKind()) { case ADDED: onResourceCreated(delta); break; case REMOVED: onResourceRemoved(delta); break; case UPDATED: onResourceUpdated(delta); } } }); } private void onResourceCreated(ResourceDelta delta) { if ((delta.getFlags() & (MOVED_FROM | MOVED_TO)) == 0) { return; } final Resource resource = delta.getResource(); final Path movedFrom = delta.getFromPath(); //file moved directly if (document.getFile().getLocation().equals(movedFrom)) { deletedFilesController.add(movedFrom.toString()); document.setFile((File)resource); input.setFile((File)resource); updateContent(); } else if (movedFrom.isPrefixOf(document.getFile().getLocation())) { //directory where file moved final Path relPath = document.getFile().getLocation().removeFirstSegments(movedFrom.segmentCount()); final Path newPath = delta.getToPath().append(relPath); appContext.getWorkspaceRoot().getFile(newPath).then(optional -> { if (optional.isPresent()) { final Path location = document.getFile().getLocation(); deletedFilesController.add(location.toString()); File file = optional.get(); clientServerEventService.sendFileTrackingStartEvent(file.getLocation().toString()) .then(aVoid -> { document.setFile(file); input.setFile(file); updateTabReference(file, location); updateContent(); }); } }); } } @Override protected void updateDirtyState(boolean dirty) { if (isReadOnly()) { dirtyState = false; return; } super.updateDirtyState(dirty); } private void updateTabReference(File file, Path oldPath) { final PartPresenter activePart = editorMultiPartStackPresenter.getActivePart(); final EditorPartStack activePartStack = editorMultiPartStackPresenter.getPartStackByPart(activePart); if (activePartStack == null) { return; } final EditorTab editorTab = activePartStack.getTabByPath(oldPath); if (editorTab != null) { editorTab.setFile(file); } } private void onResourceRemoved(ResourceDelta delta) { if ((delta.getFlags() & DERIVED) == 0) { return; } final Resource resource = delta.getResource(); if (resource.isFile() && document.getFile().getLocation().equals(resource.getLocation())) { handleClose(); } } private void onResourceUpdated(ResourceDelta delta) { if ((delta.getFlags() & DERIVED) == 0) { return; } if (delta.getResource().isFile() && document.getFile().getLocation().equals(delta.getResource().getLocation())) { updateContent(); } } private void updateContent() { generalEventBus.fireEvent(new FileContentUpdateEvent(document.getFile().getLocation().toString())); } private void displayErrorPanel(final String message) { this.editorView.showPlaceHolder(new Label(message)); } private void handleDocumentChanged() { Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() { @Override public void execute() { updateDirtyState(editorWidget.isDirty()); } }); } protected void showQuickAssist() { if (quickAssistant == null) { return; } PositionConverter positionConverter = getPositionConverter(); if (positionConverter != null) { TextPosition cursor = getCursorPosition(); PositionConverter.PixelCoordinates pixelPos = positionConverter.textToPixel(cursor); quickAssistant.showPossibleQuickAssists(getCursorModel().getCursorPosition().getOffset(), pixelPos.getX(), pixelPos.getY()); } } @Override public void storeState() { cursorPosition = getCursorPosition(); } @Override public void restoreState() { if (cursorPosition != null) { setFocus(); getDocument().setCursorPosition(cursorPosition); } } @Override public void close(boolean save) { this.documentStorage.documentClosed(this.document); editorInit.uninstall(); workspaceAgent.removePart(this); } @Override public boolean isEditable() { return false; } @Override public void doRevertToSaved() { // do nothing } protected Widget getWidget() { return this.editorView.asWidget(); } @Override public void go(AcceptsOneWidget container) { container.setWidget(getWidget()); } @Override public String getTitleToolTip() { return null; } @Override public void onClose(@NotNull final AsyncCallback<Void> callback) { if (isDirty()) { dialogFactory.createConfirmDialog( constant.askWindowCloseTitle(), constant.askWindowSaveChangesMessage(getEditorInput().getName()), new ConfirmCallback() { @Override public void accepted() { doSave(); handleClose(); callback.onSuccess(null); } }, new CancelCallback() { @Override public void cancelled() { handleClose(); callback.onSuccess(null); } }).show(); } else { handleClose(); callback.onSuccess(null); } } @Override protected void handleClose() { if (resourceChangeHandler != null) { resourceChangeHandler.removeHandler(); resourceChangeHandler = null; } super.handleClose(); } @Override public TextEditorPartView getView() { return this.editorView; } @Override public void activate() { if (editorWidget != null) { editorWidget.refresh(); editorWidget.setFocus(); final String isLinkedWithEditor = preferencesManager.getValue(LinkWithEditorAction.LINK_WITH_EDITOR); if (!parseBoolean(isLinkedWithEditor)) { setSelection(new Selection<>(input.getFile())); } } else { this.delayedFocus = true; } } @Override public void initialize(@NotNull TextEditorConfiguration configuration) { this.configuration = configuration; } @Override public TextEditorConfiguration getConfiguration() { return configuration; } @Override public SVGResource getTitleImage() { return input.getSVGResource(); } @Override public String getTitle() { return input.getFile().getDisplayName(); } @Override public void doSave() { doSave(new AsyncCallback<EditorInput>() { @Override public void onSuccess(final EditorInput result) { // do nothing } @Override public void onFailure(final Throwable caught) { // do nothing } }); } @Override public void doSave(final AsyncCallback<EditorInput> callback) { //If the workspace is stopped we shouldn't try to save a file if (isReadOnly() || appContext.getDevMachine() == null) { return; } this.documentStorage.saveDocument(getEditorInput(), this.document, false, new AsyncCallback<EditorInput>() { @Override public void onSuccess(EditorInput editorInput) { updateDirtyState(false); editorWidget.markClean(); afterSave(); if (callback != null) { callback.onSuccess(editorInput); } } @Override public void onFailure(Throwable caught) { notificationManager.notify(constant.failedToUpdateContentOfFiles(), caught.getMessage(), FAIL, NOT_EMERGE_MODE); if (callback != null) { callback.onFailure(caught); } } }); } @Override public void doSaveAs() { } protected void afterSave() { } @Override public HandlesUndoRedo getUndoRedo() { if (this.editorWidget != null) { return this.editorWidget.getUndoRedo(); } else { return null; } } @Override public EditorState getErrorState() { return this.errorState; } @Override public void setErrorState(EditorState errorState) { this.errorState = errorState; firePropertyChange(ERROR_STATE); } @Override public BreakpointRenderer getBreakpointRenderer() { if (this.breakpointRenderer == null && this.editorWidget != null && this instanceof HasGutter) { this.breakpointRenderer = this.breakpointRendererFactory.create(((HasGutter)this).getGutter(), this.editorWidget.getLineStyler(), this.document); } return this.breakpointRenderer; } @Override public Document getDocument() { return this.document; } @Override public String getContentType() { // Before the editor content is ready, the content type is not defined if (this.fileTypes == null || this.fileTypes.isEmpty()) { return null; } else { return this.fileTypes.get(0); } } @Override public TextRange getSelectedTextRange() { return getDocument().getSelectedTextRange(); } @Override public LinearRange getSelectedLinearRange() { return getDocument().getSelectedLinearRange(); } @Override public void showMessage(String message) { this.editorWidget.showMessage(message); } @Override public TextPosition getCursorPosition() { return getDocument().getCursorPosition(); } @Override public int getCursorOffset() { final TextPosition textPosition = getDocument().getCursorPosition(); return getDocument().getIndexFromPosition(textPosition); } @Override public int getTopVisibleLine() { return editorWidget.getTopVisibleLine(); } @Override public void setTopLine(int line) { editorWidget.setTopLine(line); } @Override public void refreshEditor() { if (this.updateActions != null) { for (final EditorUpdateAction action : this.updateActions) { action.doRefresh(); } } } @Override public void addEditorUpdateAction(EditorUpdateAction action) { if (action == null) { return; } if (this.updateActions == null) { this.updateActions = new ArrayList<>(); } this.updateActions.add(action); } @Override public void addKeybinding(KeyBinding keyBinding) { // the actual HasKeyBindings object can change, so use indirection getHasKeybindings().addKeyBinding(keyBinding); } private List<String> detectFileType(final VirtualFile file) { final List<String> result = new ArrayList<>(); if (file != null) { // use the identification patterns final List<String> types = this.fileTypeIdentifier.identifyType(file); if (types != null && !types.isEmpty()) { result.addAll(types); } } // ultimate fallback - can't make more generic for text result.add(DEFAULT_CONTENT_TYPE); return result; } public HasKeyBindings getHasKeybindings() { return this.keyBindingsManager; } @Override public CursorModelWithHandler getCursorModel() { return this.cursorModel; } @Override public PositionConverter getPositionConverter() { return this.editorWidget.getPositionConverter(); } public void showCompletionProposals(CompletionsSource source) { this.editorView.showCompletionProposals(this.editorWidget, source); } public void showCompletionProposals() { this.editorView.showCompletionProposals(this.editorWidget); } private void switchHasKeybinding() { final HasKeyBindings current = getHasKeybindings(); if (!(current instanceof TemporaryKeyBindingsManager)) { return; } // change the key binding instance and add all bindings to the new one this.keyBindingsManager = this.editorWidget; final List<KeyBinding> bindings = ((TemporaryKeyBindingsManager)current).getbindings(); for (final KeyBinding binding : bindings) { this.keyBindingsManager.addKeyBinding(binding); } } @Override public List<HotKeyItem> getHotKeys() { return editorWidget.getHotKeys(); } @Override public void onResize() { if (this.editorWidget != null) { this.editorWidget.onResize(); } } @Override public void editorLostFocus() { this.editorView.updateInfoPanelUnfocused(this.document.getLineCount()); this.isFocused = false; if (isDirty()) { doSave(); } } @Override public void editorGotFocus() { this.isFocused = true; this.editorView.updateInfoPanelPosition(this.document.getCursorPosition()); } @Override public void editorCursorPositionChanged() { this.editorView.updateInfoPanelPosition(this.document.getCursorPosition()); } @Override public boolean canDoOperation(int operation) { if (TextEditorOperations.CODEASSIST_PROPOSALS == operation) { Map<String, CodeAssistProcessor> contentAssistProcessors = getConfiguration().getContentAssistantProcessors(); if (contentAssistProcessors != null && !contentAssistProcessors.isEmpty()) { return true; } } if (TextEditorOperations.FORMAT == operation) { if (getConfiguration().getContentFormatter() != null) { return true; } } if (TextEditorOperations.QUICK_ASSIST == operation) { if (quickAssistant != null) { return true; } } if (TextEditorOperations.SIGNATURE_HELP == operation) { if (getConfiguration().getSignatureHelpProvider() != null) { return true; } } return false; } @Override public void doOperation(int operation) { switch (operation) { case TextEditorOperations.CODEASSIST_PROPOSALS: if (this.document != null) { this.document.getDocumentHandle().getDocEventBus().fireEvent(new CompletionRequestEvent()); } break; case TextEditorOperations.FORMAT: ContentFormatter formatter = getConfiguration().getContentFormatter(); if (this.document != null && formatter != null) { formatter.format(getDocument()); } break; case TextEditorOperations.QUICK_ASSIST: showQuickAssist(); break; case TextEditorOperations.SIGNATURE_HELP: showSignatureHelp(); break; default: throw new UnsupportedOperationException("Operation code: " + operation + " is not supported!"); } } private void showSignatureHelp() { //TODO XXX SignatureHelpProvider signatureHelpProvider = getConfiguration().getSignatureHelpProvider(); if (document != null && signatureHelpProvider != null) { Promise<Optional<SignatureHelp>> promise = signatureHelpProvider.signatureHelp(document, getCursorOffset()); PositionConverter.PixelCoordinates coordinates = getPositionConverter().offsetToPixel(getCursorOffset()); signatureHelpView.showSignature(promise, coordinates.getX(), coordinates.getY() - editorWidget.getTextView().getLineHeight()); } } @Override public boolean isReadOnly() { return this.editorWidget.isReadOnly(); } @Override public void setReadOnly(boolean readOnly) { this.editorWidget.setReadOnly(readOnly); } @Override public EditorWidget getEditorWidget() { return this.editorWidget; } @Override public boolean isFocused() { return this.isFocused; } @Override public boolean isAutoSaveEnabled() { ReconcilerWithAutoSave autoSave = getAutoSave(); return autoSave != null && autoSave.isAutoSaveEnabled(); } private ReconcilerWithAutoSave getAutoSave() { Reconciler reconciler = getConfiguration().getReconciler(); if (reconciler != null && reconciler instanceof ReconcilerWithAutoSave) { return ((ReconcilerWithAutoSave)reconciler); } return null; } @Override public void enableAutoSave() { ReconcilerWithAutoSave autoSave = getAutoSave(); if (autoSave != null) { autoSave.enableAutoSave(); } } @Override public void disableAutoSave() { ReconcilerWithAutoSave autoSave = getAutoSave(); if (autoSave != null) { autoSave.disableAutoSave(); } } @Override public void configure(AnnotationModel model, DocumentHandle document) { document.getDocEventBus().addHandler(AnnotationModelEvent.TYPE, rendering); document.getDocEventBus().addHandler(ClearAnnotationModelEvent.TYPE, rendering); } @Override public LinkedMode getLinkedMode() { EditorWidget editorWidget = getEditorWidget(); if (editorWidget != null) { OrionEditorWidget orion = ((OrionEditorWidget)editorWidget); return orion.getLinkedMode(); } return null; } @Override public LinkedModel createLinkedModel() { return OrionLinkedModelOverlay.create(); } @Override public LinkedModelGroup createLinkedGroup() { return OrionLinkedModelGroupOverlay.create(); } @Override public LinkedModelData createLinkedModelData() { return OrionLinkedModelDataOverlay.create(); } @Override public void showCompletionInformation() { EditorWidget editorWidget = getEditorWidget(); if (editorWidget != null) { OrionEditorWidget orion = ((OrionEditorWidget)editorWidget); orion.showCompletionInformation(); } } @Override public Gutter getGutter() { final EditorWidget editorWidget = getEditorWidget(); if (editorWidget instanceof HasGutter) { return ((HasGutter)editorWidget).getGutter(); } else { throw new IllegalStateException("incorrect editor state"); } } /** {@inheritDoc} */ @Override public void setFocus() { EditorWidget editorWidget = getEditorWidget(); if (editorWidget != null) { OrionEditorWidget orion = ((OrionEditorWidget)editorWidget); orion.setFocus(); } } private class AnnotationRendering implements AnnotationModelHandler, ClearAnnotationModelHandler { @Override public void onAnnotationModel(AnnotationModelEvent event) { EditorWidget editorWidget = getEditorWidget(); if (editorWidget != null) { OrionEditorWidget orion = ((OrionEditorWidget)editorWidget); orion.showErrors(event); } } @Override public void onClearModel(ClearAnnotationModelEvent event) { EditorWidget editorWidget = getEditorWidget(); if (editorWidget != null) { OrionEditorWidget orion = ((OrionEditorWidget)editorWidget); orion.clearErrors(); } } } private class EditorWidgetInitializedCallback implements EditorWidget.WidgetInitializedCallback { private final String content; private boolean isInitialized; private EditorWidgetInitializedCallback(String content) { this.content = content; } @Override public void initialized(EditorWidget widget) { editorWidget = (OrionEditorWidget)widget; // finish editor initialization editorView.setEditorWidget(editorWidget); document = editorWidget.getDocument(); document.setFile(input.getFile()); cursorModel = new OrionCursorModel(document); editorWidget.setTabSize(configuration.getTabWidth()); // initialize info panel editorView.initInfoPanel(editorWidget.getMode(), editorWidget.getKeymap(), document.getLineCount(), configuration.getTabWidth()); //TODO: delayed activation // handle delayed focus (initialization editor widget) // should also check if I am visible, but how ? if (delayedFocus) { editorWidget.refresh(); editorWidget.setFocus(); setSelection(new Selection<>(input.getFile())); delayedFocus = false; } // delayed keybindings creation ? switchHasKeybinding(); editorWidget.setValue(content, new ContentInitializedHandler() { @Override public void onContentInitialized() { if (isInitialized) { return; } editorInit.init(document); generalEventBus.fireEvent(new DocumentReadyEvent(document)); firePropertyChange(PROP_INPUT); setupEventHandlers(); setupFileContentUpdateHandler(); isInitialized = true; } }); editorWidget.addDomHandler(new ContextMenuHandler() { @Override public void onContextMenu(ContextMenuEvent event) { contextMenu.show(event.getNativeEvent().getClientX(), event.getNativeEvent().getClientY()); } }, ContextMenuEvent.getType()); } } @Override public boolean isWrapLines() { return editorWidget.getTextView().getOptions().getWrapMode(); } @Override public void toggleWrapLines() { editorWidget.getTextView().toggleWrapMode(); } }