/*******************************************************************************
* Copyright © 2000, 2013 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:
* IBM Corporation - initial API and implementation
*
*******************************************************************************/
package org.eclipse.edt.ide.ui.internal.editor;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Stack;
import java_cup.runtime.Symbol;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.edt.compiler.core.ast.ImportDeclaration;
import org.eclipse.edt.compiler.core.ast.Lexer;
import org.eclipse.edt.compiler.core.ast.Node;
import org.eclipse.edt.compiler.core.ast.NodeTypes;
import org.eclipse.edt.compiler.core.ast.PackageDeclaration;
import org.eclipse.edt.ide.core.internal.model.document.EGLDocument;
import org.eclipse.edt.ide.core.model.EGLCore;
import org.eclipse.edt.ide.core.model.IEGLElement;
import org.eclipse.edt.ide.core.model.document.IEGLDocument;
import org.eclipse.edt.ide.ui.EDTUIPlugin;
import org.eclipse.edt.ide.ui.EDTUIPreferenceConstants;
import org.eclipse.edt.ide.ui.bidi.IBIDIProvider;
import org.eclipse.edt.ide.ui.editor.IEGLEditor;
import org.eclipse.edt.ide.ui.editor.IFoldingStructureProvider;
import org.eclipse.edt.ide.ui.internal.EGLLogger;
import org.eclipse.edt.ide.ui.internal.EGLUI;
import org.eclipse.edt.ide.ui.internal.IUIHelpConstants;
import org.eclipse.edt.ide.ui.internal.outline.ImportGroup;
import org.eclipse.edt.ide.ui.internal.outline.OutlineAdapterFactory;
import org.eclipse.edt.ide.ui.internal.outline.OutlineLabelProvider;
import org.eclipse.edt.ide.ui.internal.outline.OutlinePage;
import org.eclipse.edt.ide.ui.internal.preferences.ContentAssistPreference;
import org.eclipse.edt.ide.ui.internal.results.views.AbstractResultsViewPart;
import org.eclipse.help.IContextProvider;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.ui.PreferenceConstants;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.BadPositionCategoryException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IPositionUpdater;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewerExtension;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.jface.text.contentassist.ContentAssistant;
import org.eclipse.jface.text.contentassist.IContentAssistant;
import org.eclipse.jface.text.link.ILinkedModeListener;
import org.eclipse.jface.text.link.LinkedModeModel;
import org.eclipse.jface.text.link.LinkedModeUI;
import org.eclipse.jface.text.link.LinkedModeUI.ExitFlags;
import org.eclipse.jface.text.link.LinkedModeUI.IExitPolicy;
import org.eclipse.jface.text.link.LinkedPosition;
import org.eclipse.jface.text.link.LinkedPositionGroup;
import org.eclipse.jface.text.reconciler.IReconciler;
import org.eclipse.jface.text.source.IOverviewRuler;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.jface.text.source.projection.ProjectionSupport;
import org.eclipse.jface.text.source.projection.ProjectionViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.custom.VerifyKeyListener;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IPageLayout;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.editors.text.TextEditor;
import org.eclipse.ui.part.IShowInSource;
import org.eclipse.ui.part.IShowInTargetList;
import org.eclipse.ui.part.MultiPageEditorSite;
import org.eclipse.ui.part.ShowInContext;
import org.eclipse.ui.texteditor.ChainedPreferenceStore;
import org.eclipse.ui.texteditor.DefaultRangeIndicator;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
import org.eclipse.ui.texteditor.link.EditorLinkedModeUI;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
public class EGLEditor extends TextEditor implements IEGLEditor {
/**
* ID of the action to toggle the style of the presentation.
*/
public static final String TOGGLE_PRESENTATION= "togglePresentation"; //$NON-NLS-1$
public static final String FOLDING_TOGGLE_ID = "FoldingToggle"; //$NON-NLS-1$
public static final String FOLDING_EXPANDALL_ID = "FoldingExpandAll"; //$NON-NLS-1$
public static final String FOLDING_EXPAND_ID = "FoldingExpand"; //$NON-NLS-1$
public static final String FOLDING_COLLAPSE_ID = "FoldingCollapse"; //$NON-NLS-1$
public static final String RENAME_ID = "org.eclipse.jdt.ui.actions.Rename"; //$NON-NLS-1$
public static final String MOVE_ID = "org.eclipse.jdt.ui.actions.Move"; //$NON-NLS-1$
/** Preference key for compiler task tags */
private static final String COMPILER_TASK_TAGS = JavaCore.COMPILER_TASK_TAGS;
private OutlinePage fOutlinePage;
protected String fOutlinerContextMenuID;
private boolean disableRulerArea;
// HashMaps to contain nodes that have errors or warnings. This is initialized
// when the editor is opened and then is updated each time the markers change (which
// happens on save). These hashMaps are used by the outline adapters to put the
// appropriate red X's on icons in the outline view.
protected HashMap nodesWithSavedErrors;
protected HashMap nodesWithSavedWarnings;
AbstractResultsViewPart sqlErrorView;
protected TextTools tools;
/** The preference property change listener */
private IPropertyChangeListener fPropertyChangeListener;
protected ISelectionChangedListener fSelectionChangedListener = new SelectionChangedListener();
private int fIgnoreOutlinePageSelection = 0;
private OutlineAdapterFactory factory;
private ProjectionSupport fProjectionSupport;
private IFoldingStructureProvider fFoldingStructureProvider;
private IBIDIProvider fBIDIProvider;
private ToggleFoldingRunner fFoldingRunner;
/** The bracket inserter. */
private BracketInserter fBracketInserter= new BracketInserter();
class AdaptedSourceViewer extends EGLSourceViewer {
public AdaptedSourceViewer(Composite parent, IVerticalRuler verticalRuler, IOverviewRuler overviewRuler, boolean showAnnotationsOverview, int styles) {
super(parent, verticalRuler, overviewRuler, showAnnotationsOverview, styles);
}
public IContentAssistant getContentAssistant() {
return fContentAssistant;
}
}
class SelectionChangedListener implements ISelectionChangedListener {
public void selectionChanged(SelectionChangedEvent event) {
if(primGetOutlinePage() != null) {
Control control = primGetOutlinePage().getControl();
if(control != null && !control.isDisposed() && control.isFocusControl()) {
doSelectionChanged(event);
}
}
}
};
/**
* Runner that will toggle folding either instantly (if the editor is
* visible) or the next time it becomes visible. If a runner is started when
* there is already one registered, the registered one is canceled as
* toggling folding twice is a no-op.
* <p>
* The access to the fFoldingRunner field is not thread-safe, it is assumed
* that <code>runWhenNextVisible</code> is only called from the UI thread.
* </p>
*
* @since 3.1
*/
private final class ToggleFoldingRunner implements IPartListener2 {
/**
* The workbench page we registered the part listener with, or
* <code>null</code>.
*/
private IWorkbenchPage fPage;
/**
* Does the actual toggling of projection.
*/
private void toggleFolding() {
ISourceViewer sourceViewer= getSourceViewer();
if (sourceViewer instanceof ProjectionViewer) {
ProjectionViewer pv= (ProjectionViewer) sourceViewer;
if (pv.isProjectionMode() != isFoldingEnabled()) {
if (pv.canDoOperation(ProjectionViewer.TOGGLE))
pv.doOperation(ProjectionViewer.TOGGLE);
}
}
}
/**
* Makes sure that the editor's folding state is correct the next time
* it becomes visible. If it already is visible, it toggles the folding
* state. If not, it either registers a part listener to toggle folding
* when the editor becomes visible, or cancels an already registered
* runner.
*/
public void runWhenNextVisible() {
// if there is one already: toggling twice is the identity
if (fFoldingRunner != null) {
fFoldingRunner.cancel();
return;
}
IWorkbenchPartSite site= getSite();
// The visual editor (EvEditor) is a multi page editor
// Always do the toggle for the visual editor
if (site != null && site instanceof MultiPageEditorSite == false ) {
IWorkbenchPage page= site.getPage();
// This visibility check returns false for the visual
// editor when the Source page (EGLEditor) is visible
// because the Source page does not have its own pane
if (!page.isPartVisible(EGLEditor.this)) {
// if we're not visible - defer until visible
fPage= page;
fFoldingRunner= this;
page.addPartListener(this);
return;
}
}
// we're visible - run now
toggleFolding();
}
/**
* Remove the listener and clear the field.
*/
private void cancel() {
if (fPage != null) {
fPage.removePartListener(this);
fPage= null;
}
if (fFoldingRunner == this)
fFoldingRunner= null;
}
/*
* @see org.eclipse.ui.IPartListener2#partVisible(org.eclipse.ui.IWorkbenchPartReference)
*/
public void partVisible(IWorkbenchPartReference partRef) {
if (EGLEditor.this.equals(partRef.getPart(false))) {
cancel();
toggleFolding();
}
}
/*
* @see org.eclipse.ui.IPartListener2#partClosed(org.eclipse.ui.IWorkbenchPartReference)
*/
public void partClosed(IWorkbenchPartReference partRef) {
if (EGLEditor.this.equals(partRef.getPart(false))) {
cancel();
}
}
public void partActivated(IWorkbenchPartReference partRef) {}
public void partBroughtToTop(IWorkbenchPartReference partRef) {}
public void partDeactivated(IWorkbenchPartReference partRef) {}
public void partOpened(IWorkbenchPartReference partRef) {}
public void partHidden(IWorkbenchPartReference partRef) {}
public void partInputChanged(IWorkbenchPartReference partRef) {}
}
/** Listener to annotation model changes that updates the error tick in the tab image */
private EditorErrorTickUpdater fEGLEditorErrorTickUpdater;
/**
* Handles a property change event describing a change
* of the java core's preferences and updates the preference
* related editor properties.
*
* @param event the property change event
*/
protected void handlePreferencePropertyChanged(PropertyChangeEvent event) {
ISourceViewer sourceViewer = getSourceViewer();
if (COMPILER_TASK_TAGS.equals(event.getProperty())) {
if (sourceViewer != null
&& affectsTextPresentation(
new PropertyChangeEvent(event.getSource(), event.getProperty(), event.getOldValue(), event.getNewValue())))
sourceViewer.invalidateTextPresentation();
}
if (PreferenceConstants.EDITOR_SHOW_SEGMENTS.equals(event.getProperty())) {
if ((sourceViewer != null)
&& affectsTextPresentation(
new PropertyChangeEvent(event.getSource(), event.getProperty(), event.getOldValue(), event.getNewValue()))) {
sourceViewer.invalidateTextPresentation();
}
}
}
public EGLEditor() {
this(false);
}
//The ruler is disabled when this editor is used within the Quick Edit view of Seoul to
//prevent various problems which may need to get fix in a later release
public EGLEditor(boolean disableRulerArea) {
super();
this.disableRulerArea = disableRulerArea;
if (tools == null)
tools = new TextTools(EDTUIPlugin.getDefault().getPreferenceStore());
initializeKeyBindingScopes();
fEGLEditorErrorTickUpdater= new EditorErrorTickUpdater(this);
}
/*
* @see org.eclipse.ui.texteditor.AbstractTextEditor#initializeEditor()
*/
protected void initializeEditor() {
DocumentProvider documentProvider = (DocumentProvider) EGLUI.getDocumentProvider();
setDocumentProvider(documentProvider);
setRangeIndicator(new DefaultRangeIndicator());
setEditorContextMenuId("#EGLEditorContext"); //$NON-NLS-1$
setRulerContextMenuId("#EGLRulerContext"); //$NON-NLS-1$
setOutlinerContextMenuId("#EGLOutlinerContext"); //$NON-NLS-1$
// ensure that the TextTools adds its listener before this editor adds its listener
if (tools == null)
tools = new TextTools(EDTUIPlugin.getDefault().getPreferenceStore());
// adds PropertyChangeListener
setPreferenceStore(createCombinedPreferenceStore());
setSourceViewerConfiguration(new EGLSourceViewerConfiguration(tools, this));
// Instantiate this factory here and cache the value since it is
// used by the outline adapters for the outline view.
factory = new OutlineAdapterFactory( null, this);
}
protected boolean isEditorInputIncludedInContextMenu() {
return true;
}
protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) {
fAnnotationAccess= getAnnotationAccess();
fOverviewRuler= createOverviewRuler(getSharedColors());
//@bd1a Start
if(Locale.getDefault().toString().toLowerCase().indexOf("ar") != -1) {
styles |= SWT.LEFT_TO_RIGHT;
}
//@bd1a End
AdaptedSourceViewer sourceViewer = new AdaptedSourceViewer(parent, ruler, getOverviewRuler(), isOverviewRulerVisible(), styles);
PlatformUI.getWorkbench().getHelpSystem().setHelp(sourceViewer.getTextWidget(), IUIHelpConstants.EGL_EDITOR);
/*
* This is a performance optimization to reduce the computation of
* the text presentation triggered by {@link #setVisibleDocument(IDocument)}
*/
if (sourceViewer != null && isFoldingEnabled())
sourceViewer.prepareDelayedProjection();
ProjectionViewer projectionViewer= (ProjectionViewer) sourceViewer;
createFoldingSupport(projectionViewer);
// if (isFoldingEnabled()) {
// projectionViewer.doOperation(ProjectionViewer.TOGGLE);
// }
fFoldingStructureProvider = EDTUIPlugin.getDefault().getFoldingStructureProviderRegistry().getCurrentFoldingProvider();
//fFoldingStructureProvider = new FoldingStructureProvider();
if (fFoldingStructureProvider != null) {
fFoldingStructureProvider.install(this, projectionViewer);
}
// ensure decoration support has been created and configured.
getSourceViewerDecorationSupport(sourceViewer);
return sourceViewer;
}
public void doSelectionChanged(SelectionChangedEvent event) {
int offset;
int nodeLength;
StructuredSelection selection = (StructuredSelection) event.getSelection();
if (selection == null || selection.isEmpty())
return;
IEGLDocument document = (IEGLDocument) getDocumentProvider().getDocument(getEditorInput());
if (document == null)
return;
// Use the 1st for selection (know it's not empty because of
// test above)
Object selectedElement = selection.getFirstElement();
if (selectedElement == null)
return;
if (selectedElement instanceof ImportGroup) {
offset = ((ImportGroup) selectedElement).getOffset();
nodeLength = ((ImportGroup) selectedElement).getLength();
} else if (selectedElement instanceof ImportDeclaration || selectedElement instanceof PackageDeclaration) {
offset = ((Node) selectedElement).getOffset();
nodeLength = ((Node) selectedElement).getLength();
} else {
offset = ((Node) selectedElement).getOffset();
nodeLength = ((Node) selectedElement).getLength();
}
try {
editingScriptStarted();
setSelection(offset, nodeLength);
TreeViewer tv = (TreeViewer) event.getSource();
ILabelProvider provider = (ILabelProvider) tv.getLabelProvider();
IRegion region = ((OutlineLabelProvider) provider).getHighlightRange(selectedElement);
if (region.getLength() != 0) {
getSourceViewer().revealRange(region.getOffset(), region.getLength());
getSourceViewer().setSelectedRange(region.getOffset(), region.getLength());
}
} finally {
editingScriptEnded();
}
}
/*
* @see AbstractTextEditor#doSetInput
*/
protected void doSetInput(IEditorInput input) throws CoreException {
ISourceViewer sourceViewer = getSourceViewer();
EGLSourceViewer eglSourceViewer = null;
if(sourceViewer instanceof EGLSourceViewer)
eglSourceViewer = (EGLSourceViewer)sourceViewer;
IPreferenceStore store = getPreferenceStore();
if(eglSourceViewer != null && isFoldingEnabled() && (store == null || !store.getBoolean(EDTUIPreferenceConstants.EDITOR_SHOW_SEGMENTS)))
eglSourceViewer.prepareDelayedProjection();
super.doSetInput(input);
if (eglSourceViewer != null && eglSourceViewer.getReconciler() == null) {
IReconciler reconciler = getSourceViewerConfiguration().getReconciler(eglSourceViewer);
if (reconciler != null) {
reconciler.install(sourceViewer);
}
}
if (fEncodingSupport != null)
fEncodingSupport.reset();
// Initialize the error and warning hashmaps so the outline icons
// are correct. Must be done after the input is initialized.
EditorUtility.populateNodeErrorWarningHashMaps(this);
setOutlinePageInput(fOutlinePage, input);
}
/*
* @see JavaEditor#setOutlinePageInput(JavaOutlinePage, IEditorInput)
*/
protected void setOutlinePageInput(OutlinePage page, IEditorInput input) {
if (page != null) {
DocumentProvider provider = (DocumentProvider) getDocumentProvider();
page.setInput((IEGLDocument)provider.getDocument(input));
}
}
/**
* Creates the outline page used with this editor.
*/
protected OutlinePage createOutlinePage() {
IDocument doc = getDocumentProvider().getDocument(getEditorInput());
if(doc instanceof IEGLDocument){
OutlinePage page =
new OutlinePage((IEGLDocument)doc, fOutlinerContextMenuID, this);
page.addSelectionChangedListener(fSelectionChangedListener);
setOutlinePageInput(page, getEditorInput());
fOutlinePage = page;
return page;
}
return null;
}
/**
* Informs the editor that its outliner has been closed.
*/
public void outlinePageClosed() {
if (fOutlinePage != null) {
fOutlinePage.removeSelectionChangedListener(fSelectionChangedListener);
fOutlinePage = null;
resetHighlightRange();
}
}
public int getLineAtOffset(int offset) {
try {
EGLDocument document = (EGLDocument) getViewer().getDocument();
return document.getLineOfOffset(offset);
} catch (BadLocationException e) {
e.printStackTrace();
EGLLogger.log(this, e);
}
return 0;
}
public synchronized void editingScriptStarted() {
++fIgnoreOutlinePageSelection;
}
public synchronized void editingScriptEnded() {
--fIgnoreOutlinePageSelection;
}
private boolean isFoldingEnabled() {
IPreferenceStore store= getPreferenceStore();
return store.getBoolean(EDTUIPreferenceConstants.EDITOR_FOLDING_ENABLED);
}
public synchronized boolean isEditingScriptRunning() {
return (fIgnoreOutlinePageSelection > 0);
}
public void setSelection(int offset, int nodeLength) {
StyledText textWidget = null;
ISourceViewer sourceViewer = getSourceViewer();
if (sourceViewer != null)
textWidget = sourceViewer.getTextWidget();
if (textWidget == null)
return;
try {
if (offset < 0 || nodeLength < 0)
return;
textWidget.setRedraw(false);
//TODO Do we need to pass in moveCursor value?
setHighlightRange(offset, nodeLength, true);
// sourceViewer.revealRange(offset, nodeLength);
// sourceViewer.setSelectedRange(offset, nodeLength);
} catch (IllegalArgumentException x) {
} finally {
if (textWidget != null)
textWidget.setRedraw(true);
}
}
public void dispose() {
ISourceViewer sourceViewer= getSourceViewer();
if (sourceViewer instanceof ITextViewerExtension)
((ITextViewerExtension) sourceViewer).removeVerifyKeyListener(fBracketInserter);
if (sqlErrorView != null) {
sqlErrorView.closeMyViewerIfNecessary(this);
}
if (tools != null) {
tools.dispose();
tools = null;
}
if(fFoldingStructureProvider != null)
{
fFoldingStructureProvider.uninstall();
fFoldingStructureProvider = null;
}
if(fBIDIProvider != null)
{
fBIDIProvider.uninstall();
fBIDIProvider = null;
}
//((ProjectionViewer)getViewer()).removeProjectionListener(this);
if (fProjectionSupport != null) {
fProjectionSupport.dispose();
fProjectionSupport= null;
}
if (fPropertyChangeListener != null) {
IPreferenceStore pref = getPreferenceStore();
if (pref != null)
pref.removePropertyChangeListener(fPropertyChangeListener);
fPropertyChangeListener = null;
}
if (fEGLEditorErrorTickUpdater != null) {
fEGLEditorErrorTickUpdater.dispose();
fEGLEditorErrorTickUpdater= null;
}
super.dispose();
}
/**
* Returns the outline page
*/
public OutlinePage getOutlinePage() {
if (fOutlinePage == null) {
return createOutlinePage();
}
return fOutlinePage;
}
private IPreferenceStore createCombinedPreferenceStore() {
List stores= new ArrayList(3);
stores.add(EDTUIPlugin.getDefault().getPreferenceStore());
stores.add(EditorsUI.getPreferenceStore());
stores.add(PlatformUI.getPreferenceStore());
return new ChainedPreferenceStore((IPreferenceStore[]) stores.toArray(new IPreferenceStore[stores.size()]));
}
protected void initializeKeyBindingScopes() {
setKeyBindingScopes(new String[] { "org.eclipse.edt.ide.ui.eglEditorScope"}); //$NON-NLS-1$
}
/**
* Sets the outliner's context menu ID.
*/
protected void setOutlinerContextMenuId(String menuId) {
fOutlinerContextMenuID = menuId;
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
public Object getAdapter(Class required) {
if (required == IEGLEditor.class || required == EGLEditor.class) {
return this;
}
if (IContentOutlinePage.class.equals(required)) {
return getOutlinePage();
}
if(required == IShowInTargetList.class){
return new IShowInTargetList(){
public String[] getShowInTargetIds() {
return new String[]{IPageLayout.ID_PROJECT_EXPLORER, IPageLayout.ID_OUTLINE, IPageLayout.ID_RES_NAV };
}
};
}
if(required == IShowInSource.class){
IEditorInput editorInput = getEditorInput();
if(editorInput instanceof IFileEditorInput){
IResource resource = ((IFileEditorInput)getEditorInput()).getFile();
IEGLElement element = EGLCore.create(resource);
if(element != null){
final StructuredSelection selection = new StructuredSelection(element);
return new IShowInSource(){
public ShowInContext getShowInContext() {
return new ShowInContext(getEditorInput(), selection);
}
};
}
}
}
if (fProjectionSupport != null) {
Object adapter= fProjectionSupport.getAdapter(getSourceViewer(), required);
if (adapter != null) {
return adapter;
}
}
if (required == IContextProvider.class)
return EGLUIHelp.getHelpContextProvider(this, IUIHelpConstants.EGL_EDITOR);
return super.getAdapter(required);
}
public StyledText getSourceViewerStyledText() {
return getSourceViewer().getTextWidget();
}
public AbstractResultsViewPart getSqlErrorView() {
return sqlErrorView;
}
public ISourceViewer getViewer() {
return getSourceViewer();
}
public void setSqlErrorView(AbstractResultsViewPart sqlErrorView) {
this.sqlErrorView = sqlErrorView;
}
/*
* @see org.eclipse.ui.texteditor.AbstractTextEditor#doSetSelection(ISelection)
*/
protected void doSetSelection(ISelection selection) {
super.doSetSelection(selection);
synchronizeOutlinePageSelection();
}
/**
* Synchronizes the outliner selection with the actual cursor
* position in the editor.
*/
public void synchronizeOutlinePageSelection() {
if (isEditingScriptRunning())
return;
ISourceViewer sourceViewer = getSourceViewer();
if (sourceViewer == null || fOutlinePage == null)
return;
StyledText styledText = sourceViewer.getTextWidget();
if (styledText == null)
return;
// get the node at the current cursor location
int offset = getSourceViewer().getTextWidget().getSelection().x;
IEGLDocument document = (IEGLDocument) getDocumentProvider().getDocument(getEditorInput());
if (offset > 0) {
offset--;
}
Node node = document.getNewModelNodeAtOffset(offset);
factory = new OutlineAdapterFactory( null, this);
while(node != null && !factory.isDisplayableElement(node)) {
node = node.getParent();
}
if( node == null ) return;
int nodeEndOffset = node.getOffset() + node.getLength();
if( offset+1 > nodeEndOffset ) {
do {
node = node.getParent();
}
while( node != null && !factory.isDisplayableElement(node) );
}
fOutlinePage.removeSelectionChangedListener(fSelectionChangedListener);
fOutlinePage.select( node );
fOutlinePage.addSelectionChangedListener(fSelectionChangedListener);
}
/*
* @see AbstractTextEditor#handlePreferenceStoreChanged(PropertyChangeEvent)
*/
protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {
try {
ISourceViewer sourceViewer = getSourceViewer();
if (sourceViewer == null)
return;
String property = event.getProperty();
if (EDTUIPreferenceConstants.EDITOR_TAB_WIDTH.equals(property)) {
Object value = event.getNewValue();
if (value instanceof Integer) {
sourceViewer.getTextWidget().setTabs(((Integer) value).intValue());
} else if (value instanceof String) {
sourceViewer.getTextWidget().setTabs(Integer.parseInt((String) value));
}
return;
}
if(EDTUIPreferenceConstants.EDITOR_FOLDING_PROVIDER.equals(property))
{
if(sourceViewer instanceof ProjectionViewer)
{
ProjectionViewer projectionViewer= (ProjectionViewer) sourceViewer;
if(fFoldingStructureProvider != null)
fFoldingStructureProvider.uninstall();
//either freshly enabled or provider changed
fFoldingStructureProvider = EDTUIPlugin.getDefault().getFoldingStructureProviderRegistry().getCurrentFoldingProvider();
//fFoldingStructureProvider = new FoldingStructureProvider();
if(fFoldingStructureProvider != null)
fFoldingStructureProvider.install(this, projectionViewer);
}
}
if (EDTUIPreferenceConstants.EDITOR_FOLDING_ENABLED.equals(property)) {
if (sourceViewer instanceof ProjectionViewer) {
new ToggleFoldingRunner().runWhenNextVisible();
}
return;
}
if (EDTUIPreferenceConstants.CODEASSIST_AUTOACTIVATION.equals(property)) {
IContentAssistant c = ((AdaptedSourceViewer)sourceViewer).getContentAssistant();
if (c instanceof ContentAssistant) {
ContentAssistPreference.changeConfiguration((ContentAssistant) c, getPreferenceStore(), event);
}
return;
}
if (EDTUIPreferenceConstants.CODEASSIST_AUTOACTIVATION_DELAY.equals(property)) {
IContentAssistant c = ((AdaptedSourceViewer)sourceViewer).getContentAssistant();
if (c instanceof ContentAssistant) {
ContentAssistPreference.changeConfiguration((ContentAssistant) c, getPreferenceStore(), event);
}
return;
}
} finally {
super.handlePreferenceStoreChanged(event);
}
}
/*
* @see AbstractTextEditor#affectsTextPresentation(PropertyChangeEvent)
*/
protected boolean affectsTextPresentation(PropertyChangeEvent event) {
return tools.affectsBehavior(event);
}
/*
* Update the icon used on the editor's tab
*/
public void updatedTitleImage(Image image) {
setTitleImage(image);
}
public void setNodesWithSavedErrors(HashMap hashMap) {
nodesWithSavedErrors = hashMap;
}
public HashMap getNodesWithSavedErrors() {
return nodesWithSavedErrors;
}
public void setNodesWithSavedWarnings(HashMap hashMap) {
nodesWithSavedWarnings = hashMap;
}
public HashMap getNodesWithSavedWarnings() {
return nodesWithSavedWarnings;
}
public OutlineAdapterFactory getOutlineAdapterFactory(){
return factory;
}
public OutlinePage primGetOutlinePage() {
return fOutlinePage;
}
/* (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
*
* the ruler is disabled when this editor is used within the Quick Edit view of Seoul
*/
public void createPartControl(Composite parent) {
super.createPartControl(parent);
getVerticalRuler().getControl().setEnabled(!disableRulerArea);
fBracketInserter.setCloseBracketsEnabled(true); //always true for now, it could be read from egl editor preference
fBracketInserter.setCloseStringsEnabled(true); //if we decide to add this as preference in the future
ISourceViewer sourceViewer= getSourceViewer();
if (sourceViewer instanceof ITextViewerExtension)
((ITextViewerExtension) sourceViewer).prependVerifyKeyListener(fBracketInserter);
}
private void createFoldingSupport(ProjectionViewer projectionViewer) {
fProjectionSupport= new ProjectionSupport(projectionViewer, getAnnotationAccess(), getSharedColors());
fProjectionSupport.addSummarizableAnnotationType(EGLMarkerAnnotation.ERROR_ANNOTATION_TYPE); //$NON-NLS-1$
fProjectionSupport.addSummarizableAnnotationType(EGLMarkerAnnotation.WARNING_ANNOTATION_TYPE); //$NON-NLS-1$
fProjectionSupport.setHoverControlCreator(new IInformationControlCreator() {
public IInformationControl createInformationControl(Shell shell) {
return new SourceViewerInformationControl(shell);
}
});
fProjectionSupport.install();
}
protected void performRevert() {
ProjectionViewer projectionViewer= (ProjectionViewer) getSourceViewer();
projectionViewer.setRedraw(false);
try {
boolean projectionMode= projectionViewer.isProjectionMode();
if (projectionMode) {
projectionViewer.disableProjection();
if(fFoldingStructureProvider != null)
fFoldingStructureProvider.uninstall();
}
super.performRevert();
if(projectionMode){
if(fFoldingStructureProvider != null)
fFoldingStructureProvider.install(this, projectionViewer);
projectionViewer.enableProjection();
}
} finally {
projectionViewer.setRedraw(true);
}
}
/**
* Position updater that takes any changes at the borders of a position to not belong to the position.
*
* @since 3.0
*/
private static class ExclusivePositionUpdater implements IPositionUpdater {
/** The position category. */
private final String fCategory;
/**
* Creates a new updater for the given <code>category</code>.
*
* @param category the new category.
*/
public ExclusivePositionUpdater(String category) {
fCategory= category;
}
/*
* @see org.eclipse.jface.text.IPositionUpdater#update(org.eclipse.jface.text.DocumentEvent)
*/
public void update(DocumentEvent event) {
int eventOffset= event.getOffset();
int eventOldLength= event.getLength();
int eventNewLength= event.getText() == null ? 0 : event.getText().length();
int deltaLength= eventNewLength - eventOldLength;
try {
Position[] positions= event.getDocument().getPositions(fCategory);
for (int i= 0; i != positions.length; i++) {
Position position= positions[i];
if (position.isDeleted())
continue;
int offset= position.getOffset();
int length= position.getLength();
int end= offset + length;
if (offset >= eventOffset + eventOldLength)
// position comes
// after change - shift
position.setOffset(offset + deltaLength);
else if (end <= eventOffset) {
// position comes way before change -
// leave alone
} else if (offset <= eventOffset && end >= eventOffset + eventOldLength) {
// event completely internal to the position - adjust length
position.setLength(length + deltaLength);
} else if (offset < eventOffset) {
// event extends over end of position - adjust length
int newEnd= eventOffset;
position.setLength(newEnd - offset);
} else if (end > eventOffset + eventOldLength) {
// event extends from before position into it - adjust offset
// and length
// offset becomes end of event, length adjusted accordingly
int newOffset= eventOffset + eventNewLength;
position.setOffset(newOffset);
position.setLength(end - newOffset);
} else {
// event consumes the position - delete it
position.delete();
}
}
} catch (BadPositionCategoryException e) {
// ignore and return
}
}
/**
* Returns the position category.
*
* @return the position category
*/
public String getCategory() {
return fCategory;
}
}
private static class BracketLevel {
int fOffset;
int fLength;
LinkedModeUI fUI;
Position fFirstPosition;
Position fSecondPosition;
}
private class ExitPolicy implements IExitPolicy {
final char fExitCharacter;
final char fEscapeCharacter;
final Stack fStack;
final int fSize;
public ExitPolicy(char exitCharacter, char escapeCharacter, Stack stack) {
fExitCharacter= exitCharacter;
fEscapeCharacter= escapeCharacter;
fStack= stack;
fSize= fStack.size();
}
/*
* @see org.eclipse.jdt.internal.ui.text.link.LinkedPositionUI.ExitPolicy#doExit(org.eclipse.jdt.internal.ui.text.link.LinkedPositionManager, org.eclipse.swt.events.VerifyEvent, int, int)
*/
public ExitFlags doExit(LinkedModeModel model, VerifyEvent event, int offset, int length) {
if (fSize == fStack.size() && !isMasked(offset)) {
if (event.character == fExitCharacter) {
BracketLevel level= (BracketLevel) fStack.peek();
if (level.fFirstPosition.offset > offset || level.fSecondPosition.offset < offset)
return null;
if (level.fSecondPosition.offset == offset && length == 0)
// don't enter the character if if its the closing peer
return new ExitFlags(ILinkedModeListener.UPDATE_CARET, false);
}
// when entering an anonymous class between the parenthesis', we don't want
// to jump after the closing parenthesis when return is pressed
if (event.character == SWT.CR && offset > 0) {
IDocument document= getSourceViewer().getDocument();
try {
if (document.getChar(offset - 1) == '{')
return new ExitFlags(ILinkedModeListener.EXIT_ALL, true);
} catch (BadLocationException e) {
}
}
}
return null;
}
private boolean isMasked(int offset) {
IDocument document= getSourceViewer().getDocument();
try {
return fEscapeCharacter == document.getChar(offset - 1);
} catch (BadLocationException e) {
}
return false;
}
}
private class BracketInserter implements VerifyKeyListener, ILinkedModeListener {
private boolean fCloseBrackets= true;
private boolean fCloseStrings= true;
private final String CATEGORY= toString();
private IPositionUpdater fUpdater= new ExclusivePositionUpdater(CATEGORY);
private Stack fBracketLevelStack= new Stack();
public void setCloseBracketsEnabled(boolean enabled) {
fCloseBrackets= enabled;
}
public void setCloseStringsEnabled(boolean enabled) {
fCloseStrings= enabled;
}
/*
* @see org.eclipse.swt.custom.VerifyKeyListener#verifyKey(org.eclipse.swt.events.VerifyEvent)
*/
public void verifyKey(VerifyEvent event) {
// early pruning to slow down normal typing as little as possible
if (!event.doit || getInsertMode() != SMART_INSERT)
return;
switch (event.character) {
case '(':
case '{':
case '[':
case '\'':
case '\"':
break;
default:
return;
}
final ISourceViewer sourceViewer= getSourceViewer();
IDocument document= sourceViewer.getDocument();
final Point selection= sourceViewer.getSelectedRange();
final int offset= selection.x;
final int length= selection.y;
try {
IRegion startLine= document.getLineInformationOfOffset(offset);
IRegion endLine= document.getLineInformationOfOffset(offset + length);
//only scan the selection part
int scanOffset = startLine.getOffset();
String leftOfSelection = document.get(scanOffset, offset-scanOffset);
String rightOfSelection = document.get(offset, endLine.getOffset()+endLine.getLength()-offset-length);
Symbol prevToken = new Symbol(NodeTypes.EOF);
Symbol nextToken = new Symbol(NodeTypes.EOF);
java_cup.runtime.Scanner leftScanner = null, rightScanner = null;
leftScanner = new Lexer(new StringReader(leftOfSelection));
//find the last token in the leftScanner, which is the previous token of the cursor offset
Symbol tmp = leftScanner.next_token();
while(tmp.sym != NodeTypes.EOF)
{
prevToken = tmp;
tmp = leftScanner.next_token();
}
String previous= prevToken.sym == NodeTypes.EOF ? null : document.get(prevToken.left+scanOffset, offset-scanOffset-prevToken.left).trim();
rightScanner = new Lexer(new StringReader(rightOfSelection));
//find the 1st token in the rightScanner, which is the next token of the cursor offset
nextToken = rightScanner.next_token();
//String next= nextToken.sym == NodeTypes.EOF ? null : document.get(offset+length+nextToken.left, nextToken.right-nextToken.left).trim();
String next= (nextToken.sym == NodeTypes.EOF || nextToken.sym == NodeTypes.END || nextToken.sym == NodeTypes.LINE_COMMENT)? null : document.get(offset+length+nextToken.left, nextToken.right-nextToken.left).trim();
switch (event.character) {
case '(':
if (!fCloseBrackets
|| nextToken.sym == NodeTypes.LPAREN
|| nextToken.sym == NodeTypes.ID
|| next != null && next.length() > 1)
return;
break;
case '{':
if(!fCloseBrackets
|| nextToken.sym == NodeTypes.LCURLY
|| nextToken.sym == NodeTypes.ID
|| next != null && next.length() > 1)
return;
break;
case '[':
if (!fCloseBrackets
|| nextToken.sym == NodeTypes.ID
|| next != null && next.length() > 1)
return;
break;
case '\'':
case '"':
if (!fCloseStrings
|| nextToken.sym == NodeTypes.ID
|| prevToken.sym == NodeTypes.ID
|| next != null && next.length() > 1
|| previous != null && previous.length() > 1
|| previous != null && previous.length()==1 && previous.charAt(0)==event.character)
return;
break;
default:
return;
}
ITypedRegion partition= TextUtilities.getPartition(document, IPartitions.EGL_PARTITIONING, offset, true);
if (!IDocument.DEFAULT_CONTENT_TYPE.equals(partition.getType()))
return;
if (!validateEditorInputState())
return;
final char character= event.character;
final char closingCharacter= getPeerCharacter(character);
final StringBuffer buffer= new StringBuffer();
buffer.append(character);
buffer.append(closingCharacter);
document.replace(offset, length, buffer.toString());
BracketLevel level= new BracketLevel();
fBracketLevelStack.push(level);
LinkedPositionGroup group= new LinkedPositionGroup();
group.addPosition(new LinkedPosition(document, offset + 1, 0, LinkedPositionGroup.NO_STOP));
LinkedModeModel model= new LinkedModeModel();
model.addLinkingListener(this);
model.addGroup(group);
model.forceInstall();
level.fOffset= offset;
level.fLength= 2;
// set up position tracking for our magic peers
if (fBracketLevelStack.size() == 1) {
document.addPositionCategory(CATEGORY);
document.addPositionUpdater(fUpdater);
}
level.fFirstPosition= new Position(offset, 1);
level.fSecondPosition= new Position(offset + 1, 1);
document.addPosition(CATEGORY, level.fFirstPosition);
document.addPosition(CATEGORY, level.fSecondPosition);
level.fUI= new EditorLinkedModeUI(model, sourceViewer);
level.fUI.setSimpleMode(true);
level.fUI.setExitPolicy(new ExitPolicy(closingCharacter, getEscapeCharacter(closingCharacter), fBracketLevelStack));
level.fUI.setExitPosition(sourceViewer, offset + 2, 0, Integer.MAX_VALUE);
level.fUI.setCyclingMode(LinkedModeUI.CYCLE_NEVER);
level.fUI.enter();
IRegion newSelection= level.fUI.getSelectedRegion();
sourceViewer.setSelectedRange(newSelection.getOffset(), newSelection.getLength());
event.doit= false;
} catch (BadLocationException e) {
e.printStackTrace();
EGLLogger.log(this, e);
} catch (BadPositionCategoryException e) {
e.printStackTrace();
EGLLogger.log(this, e);
} catch (IOException e) {
e.printStackTrace();
EGLLogger.log(this, e);
} catch (Exception e) {
e.printStackTrace();
EGLLogger.log(this, e);
}
}
/*
* @see org.eclipse.jface.text.link.ILinkedModeListener#left(org.eclipse.jface.text.link.LinkedModeModel, int)
*/
public void left(LinkedModeModel environment, int flags) {
final BracketLevel level= (BracketLevel) fBracketLevelStack.pop();
if (flags != ILinkedModeListener.EXTERNAL_MODIFICATION)
return;
// remove brackets
final ISourceViewer sourceViewer= getSourceViewer();
final IDocument document= sourceViewer.getDocument();
if (document instanceof IDocumentExtension) {
IDocumentExtension extension= (IDocumentExtension) document;
extension.registerPostNotificationReplace(null, new IDocumentExtension.IReplace() {
public void perform(IDocument d, IDocumentListener owner) {
if ((level.fFirstPosition.isDeleted || level.fFirstPosition.length == 0)
&& !level.fSecondPosition.isDeleted
&& level.fSecondPosition.offset == level.fFirstPosition.offset)
{
try {
document.replace(level.fSecondPosition.offset,
level.fSecondPosition.length,
null);
} catch (BadLocationException e) {
e.printStackTrace();
EGLLogger.log(this, e);
}
}
if (fBracketLevelStack.size() == 0) {
document.removePositionUpdater(fUpdater);
try {
document.removePositionCategory(CATEGORY);
} catch (BadPositionCategoryException e) {
e.printStackTrace();
EGLLogger.log(this, e);
}
}
}
});
}
}
/*
* @see org.eclipse.jface.text.link.ILinkedModeListener#suspend(org.eclipse.jface.text.link.LinkedModeModel)
*/
public void suspend(LinkedModeModel environment) {
}
/*
* @see org.eclipse.jface.text.link.ILinkedModeListener#resume(org.eclipse.jface.text.link.LinkedModeModel, int)
*/
public void resume(LinkedModeModel environment, int flags) {
}
}
private static char getEscapeCharacter(char character) {
switch (character) {
case '"':
case '\'':
return '\\';
default:
return 0;
}
}
private static char getPeerCharacter(char character) {
switch (character) {
case '(':
return ')';
case ')':
return '(';
case '{':
return '}';
case '}':
return '{';
case '[':
return ']';
case ']':
return '[';
case '"':
return character;
case '\'':
return character;
default:
throw new IllegalArgumentException();
}
}
public boolean isPrefQuickDiffAlwaysOn(){
if(disableRulerArea){
return false;
}else{
return super.isPrefQuickDiffAlwaysOn();
}
}
public SourceViewerConfiguration getSourceViewerConfig() {
return getSourceViewerConfiguration();
}
public IVerticalRuler getEditorVerticalRuler() {
return super.getVerticalRuler();
}
protected void editorContextMenuAboutToShow(IMenuManager menu) {
super.editorContextMenuAboutToShow(menu);
addAction(menu, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.SELECT_ALL);
addAction(menu, ITextEditorActionConstants.GROUP_FIND, ITextEditorActionConstants.FIND);
addAction(menu, ITextEditorActionConstants.GROUP_UNDO, ITextEditorActionConstants.REDO);
menu.addMenuListener( new IMenuListener() {
@Override
public void menuAboutToShow(IMenuManager menu) {
try {
IContributionItem item = menu.find( "org.eclipse.edt.ide.ui.file.sourceMenu" );
if ( item != null ) {
menu.remove( item );
}
} finally {
menu.removeMenuListener( this );
}
}
});
}
@Override
protected String[] collectContextMenuPreferencePages() {
return new String[] {
"org.eclipse.edt.ide.ui.editorPreferences", //$NON-NLS-1$
"org.eclipse.edt.ide.ui.contentAssistPreferences", //$NON-NLS-1$
"org.eclipse.edt.ide.ui.foldingPreferences", //$NON-NLS-1$
"org.eclipse.edt.ide.ui.formatterPreferences", //$NON-NLS-1$
"org.eclipse.ui.editors.preferencePages.Spelling", //$NON-NLS-1$
"org.eclipse.edt.ide.ui.organizeImportsPreferencePage", //$NON-NLS-1$
"org.eclipse.edt.ide.ui.sourceStylesPreferences", //$NON-NLS-1$
"org.eclipse.edt.ide.ui.templatePreferences", //$NON-NLS-1$
"org.eclipse.edt.ide.ui.ContentAssistAdvancedPreferences", //$NON-NLS-1$
};
}
}