This repository has been archived by the owner. It is now read-only.
Permalink
Cannot retrieve contributors at this time
goclipse/plugin_ide.ui/src-lang/melnorme/lang/ide/ui/editor/structure/AbstractLangStructureEditor.java /
Go to file/******************************************************************************* | |
* Copyright (c) 2015, 2015 IBM Corporation and others. | |
* 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: | |
* Bruno Medeiros - initial API and implementation | |
*******************************************************************************/ | |
package melnorme.lang.ide.ui.editor.structure; | |
import static melnorme.utilbox.core.Assert.AssertNamespace.assertNotNull; | |
import static melnorme.utilbox.core.Assert.AssertNamespace.assertTrue; | |
import static melnorme.utilbox.core.CoreUtil.array; | |
import org.eclipse.jface.text.IDocument; | |
import org.eclipse.jface.text.TextSelection; | |
import org.eclipse.jface.viewers.ISelection; | |
import org.eclipse.jface.viewers.ISelectionProvider; | |
import org.eclipse.jface.viewers.SelectionChangedEvent; | |
import org.eclipse.jface.viewers.StructuredSelection; | |
import org.eclipse.swt.widgets.Composite; | |
import org.eclipse.swt.widgets.Display; | |
import org.eclipse.ui.IEditorInput; | |
import org.eclipse.ui.IPageLayout; | |
import org.eclipse.ui.part.IShowInTargetList; | |
import org.eclipse.ui.texteditor.ITextEditor; | |
import org.eclipse.ui.views.contentoutline.IContentOutlinePage; | |
import melnorme.lang.ide.core.LangCore; | |
import melnorme.lang.ide.core.engine.IStructureModelListener; | |
import melnorme.lang.ide.core.engine.SourceModelManager; | |
import melnorme.lang.ide.core.engine.SourceModelManager.StructureInfo; | |
import melnorme.lang.ide.core.engine.SourceModelManager.StructureModelRegistration; | |
import melnorme.lang.ide.ui.editor.AbstractLangEditor; | |
import melnorme.lang.ide.ui.editor.EditorSourceBuffer; | |
import melnorme.lang.ide.ui.editor.EditorUtils; | |
import melnorme.lang.ide.ui.text.AbstractLangSourceViewerConfiguration; | |
import melnorme.lang.ide.ui.text.LangSourceViewerConfiguration; | |
import melnorme.lang.tooling.LocationKey; | |
import melnorme.lang.tooling.ast.SourceRange; | |
import melnorme.lang.tooling.structure.SourceFileStructure; | |
import melnorme.lang.tooling.structure.StructureElement; | |
import melnorme.utilbox.core.CommonException; | |
import melnorme.utilbox.core.fntypes.Result; | |
import melnorme.utilbox.fields.Field; | |
import melnorme.utilbox.misc.Location; | |
/** | |
* Extension to {@link AbstractLangEditor} with functionality to hande {@link StructureElement}s | |
*/ | |
public abstract class AbstractLangStructureEditor extends AbstractLangEditor { | |
protected final SourceModelManager sourceModelMgr = LangCore.getSourceModelManager(); | |
protected final LangOutlinePage outlinePage = addOwned(init_createOutlinePage()); | |
public AbstractLangStructureEditor() { | |
super(); | |
} | |
@Override | |
protected AbstractLangSourceViewerConfiguration createSourceViewerConfiguration() { | |
return new LangSourceViewerConfiguration(getPreferenceStore(), new EditorSourceBuffer(this), this); | |
} | |
/* ----------------- ----------------- */ | |
protected Location editorLocation; | |
protected StructureModelRegistration modelRegistration; | |
{ | |
owned.bind(this::disconnectUpdates); | |
} | |
@Override | |
protected void internalDoSetInput(IEditorInput input) { | |
super.internalDoSetInput(input); | |
// Disconnect updates for previous input | |
disconnectUpdates(); | |
LocationKey editorKey = getStructureModelKeyFromEditorInput(input); | |
editorLocation = editorKey.getLocation(); | |
IDocument doc = getDocumentProvider().getDocument(input); | |
modelRegistration = sourceModelMgr.connectStructureUpdates(editorKey, doc, structureInfoListener); | |
// Send initial update | |
handleEditorStructureUpdated(modelRegistration.structureInfo); | |
} | |
protected void disconnectUpdates() { | |
if(modelRegistration != null) { | |
modelRegistration.dispose(); | |
modelRegistration = null; | |
} | |
} | |
public static LocationKey getStructureModelKeyFromEditorInput(IEditorInput input) { | |
try { | |
// Try to adapt as Location | |
Location location = EditorUtils.getLocationFromEditorInput(input); | |
return new LocationKey(location); | |
} catch(CommonException e) { | |
// Is input thread-safe? We assume so since IEditorInput is supposed to be immutable. | |
return new LocationKey(input, input.toString()); | |
} | |
} | |
protected final Field<Result<SourceFileStructure, CommonException>> structureResultField = new Field<>(); | |
public Field<Result<SourceFileStructure, CommonException>> getStructureField() { | |
return structureResultField; | |
} | |
public Result<SourceFileStructure, CommonException> getSourceStructureResult() { | |
return getStructureField().getFieldValue(); | |
} | |
public SourceFileStructure getSourceStructure() throws CommonException { | |
return getSourceStructureResult().get(); | |
} | |
protected final Field<StructureElement> selectedElementField = new Field<>(); | |
public Field<StructureElement> getSelectedElementField() { | |
return selectedElementField; | |
} | |
public StructureElement getSelectedElement() { | |
return selectedElementField.getFieldValue(); | |
} | |
public StructuredSelection getSelectedElementAsStructureSelection() { | |
StructureElement selectedElement = getSelectedElement(); | |
if(selectedElement == null) { | |
return StructuredSelection.EMPTY; | |
} | |
return new StructuredSelection(selectedElement); | |
} | |
protected final IStructureModelListener structureInfoListener = new IStructureModelListener() { | |
@Override | |
public void dataChanged(StructureInfo lockedStructureInfo) { | |
Display.getDefault().asyncExec(new Runnable() { | |
@Override | |
public void run() { | |
// editor input might have changed, so check this update still applies to editor binding | |
if(modelRegistration == null || lockedStructureInfo != modelRegistration.structureInfo) { | |
return; | |
} | |
handleEditorStructureUpdated(modelRegistration.structureInfo); | |
} | |
}); | |
} | |
}; | |
protected void handleEditorStructureUpdated(StructureInfo structureInfo) { | |
assertTrue(Display.getCurrent() != null); | |
structureResultField.setFieldValue(structureInfo.getStoredData()); | |
setSelectedElementField(); | |
} | |
/* ----------------- Selection ----------------- */ | |
protected class EditorSelectionChangedListener extends AbstractSelectionChangedListener { | |
@Override | |
public void selectionChanged(SelectionChangedEvent event) { | |
setSelectedElementField(); | |
} | |
} | |
protected void setSelectedElementField() { | |
ISelectionProvider selectionProvider = getSelectionProvider(); | |
if(selectionProvider == null) { | |
return; // Can happen during dispose | |
} | |
ISelection selection = selectionProvider.getSelection(); | |
if(selection instanceof TextSelection) { | |
TextSelection textSelection = (TextSelection) selection; | |
int caretOffset = textSelection.getOffset(); | |
SourceFileStructure structure; | |
try { | |
structure = getSourceStructure(); | |
} catch(CommonException e) { | |
return; | |
} | |
if(structure != null) { | |
StructureElement selectedElement = structure.getStructureElementAt(caretOffset); | |
selectedElementField.setFieldValue(selectedElement); | |
} | |
} | |
} | |
protected final EditorSelectionChangedListener editorSelectionListener = new EditorSelectionChangedListener(); | |
@Override | |
public void createPartControl(Composite parent) { | |
super.createPartControl(parent); | |
editorSelectionListener.install(assertNotNull(getSourceViewer_())); | |
owned.bind(() -> editorSelectionListener.uninstall(getSourceViewer_())); | |
} | |
/* ----------------- Outline ----------------- */ | |
protected LangOutlinePage init_createOutlinePage() { | |
return new LangOutlinePage(this); | |
} | |
/* ----------------- ----------------- */ | |
@SuppressWarnings("unchecked") | |
@Override | |
public <T> T getAdapter(Class<T> requestedClass) { | |
if (IContentOutlinePage.class.equals(requestedClass)) { | |
IContentOutlinePage outlinePage = this.outlinePage; | |
return (T) outlinePage; | |
} | |
if(requestedClass == IShowInTargetList.class) { | |
return (T) new IShowInTargetList() { | |
@Override | |
public String[] getShowInTargetIds() { | |
return array(IPageLayout.ID_OUTLINE); | |
} | |
}; | |
} | |
return super.getAdapter(requestedClass); | |
} | |
/* ----------------- ----------------- */ | |
public void setElementSelection(StructureElement element) { | |
if(getSelectedElementField().isNotifyingListeners()) { | |
return; // Ignore | |
} | |
setElementSelection(this, element); | |
markInNavigationHistory(); | |
} | |
public static void setElementSelection(ITextEditor editor, StructureElement element) { | |
SourceRange nameSR = element.getNameSourceRange2(); | |
if(nameSR != null) { | |
editor.selectAndReveal(nameSR.getOffset(), nameSR.getLength()); | |
} | |
} | |
} |