/******************************************************************************* * Copyright (c) 2009, 2016 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 * Zend Technologies *******************************************************************************/ package org.eclipse.php.internal.ui.editor; import java.io.IOException; import java.text.CharacterIterator; import java.util.*; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.jobs.IJobManager; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; import org.eclipse.core.runtime.preferences.IPreferencesService; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.dltk.core.*; import org.eclipse.dltk.internal.ui.actions.CompositeActionGroup; import org.eclipse.dltk.internal.ui.actions.FoldingMessages; import org.eclipse.dltk.internal.ui.editor.*; import org.eclipse.dltk.internal.ui.text.ScriptWordFinder; import org.eclipse.dltk.ui.DLTKUIPlugin; import org.eclipse.dltk.ui.actions.IScriptEditorActionDefinitionIds; import org.eclipse.dltk.ui.text.folding.IFoldingStructureProvider; import org.eclipse.dltk.ui.text.folding.IFoldingStructureProviderExtension; import org.eclipse.emf.common.command.BasicCommandStack; import org.eclipse.emf.common.command.CommandStack; import org.eclipse.jface.action.*; import org.eclipse.jface.internal.text.html.HTMLTextPresenter; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.text.*; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.information.IInformationProvider; import org.eclipse.jface.text.information.IInformationProviderExtension; import org.eclipse.jface.text.information.IInformationProviderExtension2; import org.eclipse.jface.text.information.InformationPresenter; import org.eclipse.jface.text.link.*; import org.eclipse.jface.text.link.LinkedModeUI.ExitFlags; import org.eclipse.jface.text.link.LinkedModeUI.IExitPolicy; import org.eclipse.jface.text.reconciler.IReconciler; import org.eclipse.jface.text.source.*; import org.eclipse.jface.text.source.ImageUtilities; import org.eclipse.jface.text.source.projection.ProjectionViewer; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.*; import org.eclipse.php.core.PHPToolkitUtil; import org.eclipse.php.core.ast.nodes.*; import org.eclipse.php.internal.core.PHPCoreConstants; import org.eclipse.php.internal.core.PHPCorePlugin; import org.eclipse.php.internal.core.ast.locator.PHPElementConciliator; import org.eclipse.php.internal.core.corext.dom.NodeFinder; import org.eclipse.php.internal.core.documentModel.dom.IImplForPHP; import org.eclipse.php.internal.core.documentModel.parser.PHPSourceParser; import org.eclipse.php.internal.core.documentModel.partitioner.PHPPartitionTypes; import org.eclipse.php.internal.core.format.FormatterUtils; import org.eclipse.php.internal.core.format.PHPHeuristicScanner; import org.eclipse.php.internal.core.format.Symbols; import org.eclipse.php.internal.core.preferences.IPreferencesPropagatorListener; import org.eclipse.php.internal.core.preferences.PreferencePropagatorFactory; import org.eclipse.php.internal.core.preferences.PreferencesPropagator; import org.eclipse.php.internal.core.preferences.PreferencesPropagatorEvent; import org.eclipse.php.internal.core.project.PHPVersionChangedHandler; import org.eclipse.php.internal.core.search.IOccurrencesFinder; import org.eclipse.php.internal.core.search.IOccurrencesFinder.OccurrenceLocation; import org.eclipse.php.internal.core.search.OccurrencesFinderFactory; import org.eclipse.php.internal.ui.IPHPHelpContextIds; import org.eclipse.php.internal.ui.Logger; import org.eclipse.php.internal.ui.PHPUIMessages; import org.eclipse.php.internal.ui.PHPUiPlugin; import org.eclipse.php.internal.ui.actions.*; import org.eclipse.php.internal.ui.actions.GotoMatchingBracketAction; import org.eclipse.php.internal.ui.autoEdit.TabAutoEditStrategy; import org.eclipse.php.internal.ui.autoEdit.TypingPreferences; import org.eclipse.php.internal.ui.editor.configuration.PHPStructuredTextViewerConfiguration; import org.eclipse.php.internal.ui.editor.hover.PHPSourceViewerInformationControl; import org.eclipse.php.internal.ui.editor.selectionactions.*; import org.eclipse.php.internal.ui.folding.IStructuredTextFoldingProvider; import org.eclipse.php.internal.ui.folding.PHPFoldingStructureProviderProxy; import org.eclipse.php.internal.ui.preferences.PreferenceConstants; import org.eclipse.php.internal.ui.text.DocumentCharacterIterator; import org.eclipse.php.internal.ui.text.PHPWordIterator; import org.eclipse.php.internal.ui.util.DocumentModelUtils; import org.eclipse.php.internal.ui.util.PHPPluginImages; import org.eclipse.php.internal.ui.viewsupport.ISelectionListenerWithAST; import org.eclipse.php.internal.ui.viewsupport.SelectionListenerWithASTManager; import org.eclipse.php.ui.editor.SharedASTProvider; import org.eclipse.php.ui.editor.hover.IHoverMessageDecorator; import org.eclipse.php.ui.editor.hover.IPHPTextHover; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ST; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.custom.VerifyKeyListener; import org.eclipse.swt.dnd.*; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.*; import org.eclipse.ui.actions.ActionContext; import org.eclipse.ui.actions.ActionGroup; import org.eclipse.ui.dnd.IDragAndDropService; import org.eclipse.ui.editors.text.EditorsUI; import org.eclipse.ui.editors.text.IFoldingCommandIds; import org.eclipse.ui.texteditor.*; import org.eclipse.ui.texteditor.link.EditorLinkedModeUI; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; import org.eclipse.wst.sse.ui.StructuredTextEditor; import org.eclipse.wst.sse.ui.internal.IStructuredTextEditorActionConstants; import org.eclipse.wst.sse.ui.internal.SSEUIPlugin; import org.eclipse.wst.sse.ui.internal.StorageModelProvider; import org.eclipse.wst.sse.ui.internal.StructuredTextViewer; import org.eclipse.wst.sse.ui.internal.actions.ActionDefinitionIds; import org.eclipse.wst.sse.ui.internal.contentassist.StructuredContentAssistant; import org.eclipse.wst.sse.ui.internal.contentoutline.ConfigurableContentOutlinePage; import org.eclipse.wst.sse.ui.internal.projection.AbstractStructuredFoldingStrategy; import org.eclipse.wst.sse.ui.internal.reconcile.DocumentRegionProcessor; import org.eclipse.wst.sse.ui.internal.reconcile.ReconcileAnnotationKey; import org.eclipse.wst.sse.ui.internal.reconcile.TemporaryAnnotation; import org.eclipse.wst.sse.ui.reconcile.ISourceReconcilingListener; import org.eclipse.wst.sse.ui.views.contentoutline.ContentOutlineConfiguration; import com.ibm.icu.text.BreakIterator; public class PHPStructuredEditor extends StructuredTextEditor implements IPHPScriptReconcilingListener { private static final String ORG_ECLIPSE_PHP_UI_ACTIONS_OPEN_FUNCTIONS_MANUAL_ACTION = "org.eclipse.php.ui.actions.OpenFunctionsManualAction"; //$NON-NLS-1$ private static final String FORMATTER_PLUGIN_ID = "org.eclipse.php.formatter.core"; //$NON-NLS-1$ private IContentOutlinePage fPHPOutlinePage; protected PHPPairMatcher fBracketMatcher = new PHPPairMatcher(BRACKETS); private CompositeActionGroup fContextMenuGroup; private CompositeActionGroup fActionGroups; private long fLastActionsUpdate = 0L; /** Indicates whether the structure editor is displaying an external file */ protected boolean isExternal = false; /** The editor's save policy */ protected ISavePolicy fSavePolicy = null; /** * The internal shell activation listener for updating occurrences. * * @since 3.4 */ private ActivationListener fActivationListener = new ActivationListener(); private ISelectionListenerWithAST fPostSelectionListenerWithAST; private OccurrencesFinderJob fOccurrencesFinderJob; /** The occurrences finder job canceler */ private OccurrencesFinderJobCanceler fOccurrencesFinderJobCanceler; /** * The cached selected range. * * @see ITextViewer#getSelectedRange() * @since 3.3 */ private Point fCachedSelectedRange; /** * The selection used when forcing occurrence marking through code. * * @since 3.4 */ private ISelection fForcedMarkOccurrencesSelection; /** * The document modification stamp at the time when the last occurrence * marking took place. * * @since 3.4 */ private long fMarkOccurrenceModificationStamp = IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; /** * The region of the word under the caret used to when computing the current * occurrence markings. * * @since 3.4 */ private IRegion fMarkOccurrenceTargetRegion; /** * Holds the current occurrence annotations. * * @since 3.4 */ private Annotation[] fOccurrenceAnnotations = null; /** * Tells whether all occurrences of the element at the current caret * location are automatically marked in this editor. * * @since 3.4 */ private boolean fMarkOccurrenceAnnotations; /** * Tells whether the occurrence annotations are sticky i.e. whether they * stay even if there's no valid Java element at the current caret position. * Only valid if {@link #fMarkOccurrenceAnnotations} is <code>true</code>. * * @since 3.4 */ private boolean fStickyOccurrenceAnnotations; /** * Tells whether to mark type occurrences in this editor. Only valid if * {@link #fMarkOccurrenceAnnotations} is <code>true</code>. * * @since 3.4 */ private boolean fMarkTypeOccurrences; /** * Tells whether to mark method and declaration occurrences in this editor. * Only valid if {@link #fMarkOccurrenceAnnotations} is <code>true</code>. * * @since 3.4 */ private boolean fMarkMethodOccurrences; /** * Tells whether to mark function occurrences in this editor. Only valid if * {@link #fMarkOccurrenceAnnotations} is <code>true</code>. * * @since 3.4 */ private boolean fMarkFunctionOccurrences; /** * Tells whether to mark constant occurrences in this editor. Only valid if * {@link #fMarkOccurrenceAnnotations} is <code>true</code>. * * @since 3.4 */ private boolean fMarkConstantOccurrences; /** * Tells whether to mark field global variable in this editor. Only valid if * {@link #fMarkOccurrenceAnnotations} is <code>true</code>. * * @since 3.4 */ private boolean fMarkGlobalVariableOccurrences; /** * Tells whether to mark local variable occurrences in this editor. Only * valid if {@link #fMarkOccurrenceAnnotations} is <code>true</code>. * * @since 3.4 */ private boolean fMarkLocalVariableOccurrences; /** * Tells whether to mark exception occurrences in this editor. Only valid if * {@link #fMarkOccurrenceAnnotations} is <code>true</code>. * * @since 3.4 */ private boolean fMarkExceptions; /** * Tells whether to mark method exits in this editor. Only valid if * {@link #fMarkOccurrenceAnnotations} is <code>true</code>. * * @since 3.4 */ private boolean fMarkMethodExitPoints; /** * Tells whether to mark targets of <code>break</code> and * <code>continue</code> statements in this editor. Only valid if * {@link #fMarkOccurrenceAnnotations} is <code>true</code>. * * @since 3.4 */ private boolean fMarkBreakContinueTargets; /** * Tells whether to mark implementors in this editor. Only valid if * {@link #fMarkOccurrenceAnnotations} is <code>true</code>. * * @since 3.4 */ private boolean fMarkImplementors; /** * Fix for outline synchronization while reconcile */ protected volatile boolean fReconcileSelection = false; /** * The override and implements indicator manager for this editor. * * @since 3.0 */ protected OverrideIndicatorManager fOverrideIndicatorManager; /** * Tells whether text drag and drop has been installed on the control. * * @since 3.3 */ private boolean fIsTextDragAndDropInstalled = false; /** * Helper token to decide whether drag and drop happens inside the same * editor. * * @since 3.3 */ private Object fTextDragAndDropToken; /** * we use this for updating the code folding listeners and other things */ private PHPFoldingStructureProviderProxy fProjectionModelUpdater; /** * mark if we have installed the projectionSupport. */ private boolean projectionSupportInstalled = false;; /** The selection history of the editor */ private SelectionHistory fSelectionHistory; private PHPEditorErrorTickUpdater fPHPEditorErrorTickUpdater; /** The bracket inserter. */ private final BracketInserter fBracketInserter = new BracketInserter(); /** * Internal implementation class for a change listener. * * @since 3.0 */ protected abstract class AbstractSelectionChangedListener implements ISelectionChangedListener { /** * Installs this selection changed listener with the given selection * provider. If the selection provider is a post selection provider, * post selection changed events are the preferred choice, otherwise * normal selection changed events are requested. * * @param selectionProvider */ public void install(ISelectionProvider selectionProvider) { if (selectionProvider == null) return; if (selectionProvider instanceof IPostSelectionProvider) { IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider; provider.addPostSelectionChangedListener(this); } else { selectionProvider.addSelectionChangedListener(this); } } /** * Removes this selection changed listener from the given selection * provider. * * @param selectionProvider * the selection provider */ public void uninstall(ISelectionProvider selectionProvider) { if (selectionProvider == null) return; if (selectionProvider instanceof IPostSelectionProvider) { IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider; provider.removePostSelectionChangedListener(this); } else { selectionProvider.removeSelectionChangedListener(this); } } } /** * Updates the Java outline page selection and this editor's range * indicator. * * @since 3.0 */ private class EditorSelectionChangedListener extends AbstractSelectionChangedListener { /* * @see * org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged * (org.eclipse.jface.viewers.SelectionChangedEvent) */ @Override public void selectionChanged(SelectionChangedEvent event) { // XXX: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=56161 PHPStructuredEditor.this.selectionChanged(); } } /** * Updates the selection in the editor's widget with the selection of the * outline page. */ class OutlineSelectionChangedListener extends AbstractSelectionChangedListener implements IDoubleClickListener { private ContentOutlineConfiguration configuration; public OutlineSelectionChangedListener(ContentOutlineConfiguration configuration) { this.configuration = configuration; } @Override public void selectionChanged(SelectionChangedEvent event) { if (!configuration.isLinkedWithEditor(null)) { return; } doSelectionChanged(event); } @Override public void doubleClick(DoubleClickEvent event) { doSelectionChanged(event); } } private class ExitPolicy implements IExitPolicy { final char fExitCharacter; final char fEscapeCharacter; final Stack<BracketLevel> fStack; final int fSize; public ExitPolicy(char exitCharacter, char escapeCharacter, Stack<BracketLevel> stack) { fExitCharacter = exitCharacter; fEscapeCharacter = escapeCharacter; fStack = stack; fSize = fStack.size(); } @Override public ExitFlags doExit(LinkedModeModel model, VerifyEvent event, int offset, int length) { if (fSize == fStack.size() && !isMasked(offset)) { if (event.character == fExitCharacter) { BracketLevel level = 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) { ISourceViewer sourceViewer = getSourceViewer(); IDocument document = sourceViewer.getDocument(); try { if (document.getChar(offset - 1) == '{') return new ExitFlags(ILinkedModeListener.EXIT_ALL, true); // see bug 308217: while overriding a method and using // '(' followed by parameter type to filter the content // assist proposals, if ')' is added // automatically on typing '(', pressing return key // should not result in jumping after ')' instead of // applying the selected proposal. // if (document.getChar(offset) == ')' && sourceViewer // instanceof AdaptedSourceViewer && // ((AdaptedSourceViewer) // sourceViewer).getContentAssistant() instanceof // ContentAssistant) { // ContentAssistant contentAssistant= (ContentAssistant) // ((AdaptedSourceViewer) // sourceViewer).getContentAssistant(); // IContentAssistProcessor processor= // contentAssistant.getContentAssistProcessor(IDocument.DEFAULT_CONTENT_TYPE); // if (processor instanceof ContentAssistProcessor) { // ICompletionProposal proposal= // ((ContentAssistProcessor) // processor).getSelectedProposal(); // if (proposal instanceof OverrideCompletionProposal) { // 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 static class BracketLevel { LinkedModeUI fUI; Position fFirstPosition; Position fSecondPosition; } /** * 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; } @Override 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 } } } private class BracketInserter implements VerifyKeyListener, ILinkedModeListener { private boolean fCloseBrackets = true; private boolean fCloseStrings = true; private final String CATEGORY = toString(); private final IPositionUpdater fUpdater = new ExclusivePositionUpdater(CATEGORY); private final Stack<BracketLevel> fBracketLevelStack = new Stack<>(); public void setCloseBracketsEnabled(boolean enabled) { fCloseBrackets = enabled; } public void setCloseStringsEnabled(boolean enabled) { fCloseStrings = enabled; } private boolean isMultilineSelection() { ISelection selection = getSelectionProvider().getSelection(); if (selection instanceof ITextSelection) { ITextSelection ts = (ITextSelection) selection; return ts.getStartLine() != ts.getEndLine(); } return false; } @Override public void verifyKey(VerifyEvent event) { // early pruning to slow down normal typing as little as possible if (!event.doit || getInsertMode() != SMART_INSERT || isBlockSelectionModeEnabled() && isMultilineSelection()) 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); PHPHeuristicScanner scanner = PHPHeuristicScanner.createHeuristicScanner(document, offset, true); int nextToken = scanner.nextToken(offset + length, endLine.getOffset() + endLine.getLength()); String next = nextToken == Symbols.TokenEOF ? null : document.get(offset, scanner.getPosition() - offset).trim(); int prevToken = scanner.previousToken(offset - 1, startLine.getOffset() - 1); int prevTokenOffset = scanner.getPosition() + 1; String previous = prevToken == Symbols.TokenEOF ? null : document.get(prevTokenOffset, offset - prevTokenOffset).trim(); switch (event.character) { case '(': if (!fCloseBrackets || nextToken == Symbols.TokenLPAREN || nextToken == Symbols.TokenIDENT || next != null && next.length() > 1) return; break; case '[': if (!fCloseBrackets || nextToken == Symbols.TokenIDENT || next != null && next.length() > 1) return; break; case '\'': case '"': case '`': if (!fCloseStrings || nextToken == Symbols.TokenIDENT || prevToken == Symbols.TokenIDENT || next != null && next.length() > 1 || previous != null && previous.length() > 1) return; break; default: return; } String partitionType = FormatterUtils.getPartitionType((IStructuredDocument) document, offset); if (!PHPPartitionTypes.PHP_DEFAULT.equals(partitionType)) 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(); // 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) { PHPUiPlugin.log(e); } catch (BadPositionCategoryException e) { PHPUiPlugin.log(e); } } @Override public void left(LinkedModeModel environment, int flags) { final BracketLevel level = 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() { @Override 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, ""); //$NON-NLS-1$ } catch (BadLocationException e) { PHPUiPlugin.log(e); } } if (fBracketLevelStack.size() == 0) { document.removePositionUpdater(fUpdater); try { document.removePositionCategory(CATEGORY); } catch (BadPositionCategoryException e) { PHPUiPlugin.log(e); } } } }); } } @Override public void suspend(LinkedModeModel environment) { } @Override public void resume(LinkedModeModel environment, int flags) { } } /** * The editor selection changed listener. */ private EditorSelectionChangedListener fEditorSelectionChangedListener; private IPreferencesPropagatorListener fPhpVersionListener; private IPreferencesPropagatorListener fFormatterProfileListener; private IPreferenceChangeListener fPreferencesListener; private void doSelectionChanged(ISelection selection) { ISourceReference reference = null; Iterator<?> iter = ((IStructuredSelection) selection).iterator(); while (iter.hasNext()) { Object o = iter.next(); if (o instanceof ISourceReference) { reference = (ISourceReference) o; break; } } if (!isActivePart() && PHPUiPlugin.getActivePage() != null) PHPUiPlugin.getActivePage().bringToTop(this); setSelection(reference, !isActivePart()); } protected void doSelectionChanged(SelectionChangedEvent event) { ISelection selection = event.getSelection(); doSelectionChanged(selection); } protected void doSelectionChanged(DoubleClickEvent event) { ISelection selection = event.getSelection(); doSelectionChanged(selection); } /** * This action behaves in two different ways: If there is no current text * hover, the javadoc is displayed using information presenter. If there is * a current text hover, it is converted into a information presenter in * order to make it sticky. */ class InformationDispatchAction extends TextEditorAction { /** The wrapped text operation action. */ private final TextOperationAction fTextOperationAction; /** * Creates a dispatch action. * * @param resourceBundle * the resource bundle * @param prefix * the prefix * @param textOperationAction * the text operation action */ public InformationDispatchAction(final ResourceBundle resourceBundle, final String prefix, final TextOperationAction textOperationAction) { super(resourceBundle, prefix, PHPStructuredEditor.this); if (textOperationAction == null) throw new IllegalArgumentException(); fTextOperationAction = textOperationAction; } // modified version from TextViewer private int computeOffsetAtLocation(final ITextViewer textViewer, final int x, final int y) { final StyledText styledText = textViewer.getTextWidget(); final IDocument document = textViewer.getDocument(); if (styledText == null || document == null) return -1; try { int widgetOffset = styledText.getOffsetAtLocation(new Point(x, y)); final Point p = styledText.getLocationAtOffset(widgetOffset); if (p.x > x) widgetOffset--; if (textViewer instanceof ITextViewerExtension5) { final ITextViewerExtension5 extension = (ITextViewerExtension5) textViewer; return extension.widgetOffset2ModelOffset(widgetOffset); } final IRegion visibleRegion = textViewer.getVisibleRegion(); return widgetOffset + visibleRegion.getOffset(); } catch (final IllegalArgumentException e) { return -1; } } /** * Tries to make an annotation hover focusable (or "sticky"). * * @param sourceViewer * the source viewer to display the hover over * @param annotationHover * the hover to make focusable * @return <code>true</code> if successful, <code>false</code> otherwise * @since 3.2 */ private boolean makeAnnotationHoverFocusable(final ISourceViewer sourceViewer, final IAnnotationHover annotationHover) { final IVerticalRulerInfo info = getVerticalRuler(); final int line = info.getLineOfLastMouseButtonActivity(); if (line == -1) return false; try { // compute the hover information Object hoverInfo; if (annotationHover instanceof IAnnotationHoverExtension) { final IAnnotationHoverExtension extension = (IAnnotationHoverExtension) annotationHover; final ILineRange hoverLineRange = extension.getHoverLineRange(sourceViewer, line); if (hoverLineRange == null) return false; final int maxVisibleLines = Integer.MAX_VALUE; // allow any // number of // lines // being // displayed, // as we // support scrolling hoverInfo = extension.getHoverInfo(sourceViewer, hoverLineRange, maxVisibleLines); } else hoverInfo = annotationHover.getHoverInfo(sourceViewer, line); // hover region: the beginning of the concerned line to place // the control right over the line final IDocument document = sourceViewer.getDocument(); final int offset = document.getLineOffset(line); final String contentType = TextUtilities.getContentType(document, PHPPartitionTypes.PHP_DOC, offset, true); IInformationControlCreator controlCreator = null; /* * XXX: This is a hack to avoid API changes at the end of 3.2, */ if ("org.eclipse.jface.text.source.projection.ProjectionAnnotationHover" //$NON-NLS-1$ .equals(annotationHover.getClass().getName())) controlCreator = new IInformationControlCreator() { @Override public IInformationControl createInformationControl(final Shell shell) { final int shellStyle = SWT.RESIZE | SWT.TOOL | getOrientation(); final int style = SWT.V_SCROLL | SWT.H_SCROLL; return new PHPSourceViewerInformationControl(shell, shellStyle, style); } }; else if (annotationHover instanceof IInformationProviderExtension2) controlCreator = ((IInformationProviderExtension2) annotationHover) .getInformationPresenterControlCreator(); else if (annotationHover instanceof IAnnotationHoverExtension) controlCreator = ((IAnnotationHoverExtension) annotationHover).getHoverControlCreator(); final IInformationProvider informationProvider = new InformationProvider(new Region(offset, 0), hoverInfo, controlCreator); fInformationPresenter.setOffset(offset); fInformationPresenter.setAnchor(AbstractInformationControlManager.ANCHOR_RIGHT); fInformationPresenter.setMargins(4, 0); // AnnotationBarHoverManager // sets (5,0), minus // SourceViewer.GAP_SIZE_1 fInformationPresenter.setInformationProvider(informationProvider, contentType); fInformationPresenter.showInformation(); return true; } catch (final BadLocationException e) { return false; } } /** * Tries to make a text hover focusable (or "sticky"). * * @param sourceViewer * the source viewer to display the hover over * @param textHover * the hover to make focusable * @return <code>true</code> if successful, <code>false</code> otherwise * @since 3.2 */ private boolean makeTextHoverFocusable(final ISourceViewer sourceViewer, final ITextHover textHover) { final Point hoverEventLocation = ((ITextViewerExtension2) sourceViewer).getHoverEventLocation(); final int offset = computeOffsetAtLocation(sourceViewer, hoverEventLocation.x, hoverEventLocation.y); if (offset == -1) return false; try { final IRegion hoverRegion = textHover.getHoverRegion(sourceViewer, offset); if (hoverRegion == null) return false; String hoverInfo = textHover.getHoverInfo(sourceViewer, hoverRegion); if (textHover instanceof IPHPTextHover) { final IHoverMessageDecorator decorator = ((IPHPTextHover) textHover).getMessageDecorator(); if (decorator != null) { final String decoratedMessage = decorator.getDecoratedMessage(hoverInfo); if (decoratedMessage != null && decoratedMessage.length() > 0) hoverInfo = decoratedMessage; } } IInformationControlCreator controlCreator = null; if (textHover instanceof IInformationProviderExtension2) controlCreator = ((IInformationProviderExtension2) textHover) .getInformationPresenterControlCreator(); final IInformationProvider informationProvider = new InformationProvider(hoverRegion, hoverInfo, controlCreator); fInformationPresenter.setOffset(offset); fInformationPresenter.setAnchor(AbstractInformationControlManager.ANCHOR_BOTTOM); fInformationPresenter.setMargins(6, 6); // default values from // AbstractInformationControlManager final String contentType = TextUtilities.getContentType(sourceViewer.getDocument(), PHPPartitionTypes.PHP_DOC, offset, true); fInformationPresenter.setInformationProvider(informationProvider, contentType); fInformationPresenter.showInformation(); return true; } catch (final BadLocationException e) { return false; } } /* * @see org.eclipse.jface.action.IAction#run() */ @Override public void run() { final ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer == null) { fTextOperationAction.run(); return; } if (sourceViewer instanceof ITextViewerExtension4) { final ITextViewerExtension4 extension4 = (ITextViewerExtension4) sourceViewer; if (extension4.moveFocusToWidgetToken()) return; } if (sourceViewer instanceof ITextViewerExtension2) { // does a text hover exist? final ITextHover textHover = ((ITextViewerExtension2) sourceViewer).getCurrentTextHover(); if (textHover != null && makeTextHoverFocusable(sourceViewer, textHover)) return; } if (sourceViewer instanceof ISourceViewerExtension3) { // does an annotation hover exist? final IAnnotationHover annotationHover = ((ISourceViewerExtension3) sourceViewer) .getCurrentAnnotationHover(); if (annotationHover != null && makeAnnotationHoverFocusable(sourceViewer, annotationHover)) return; } // otherwise, just run the action fTextOperationAction.run(); } } /** * Internal activation listener. * * @since 3.0 */ private class ActivationListener implements IWindowListener { /* * @seeorg.eclipse.ui.IWindowListener#windowActivated(org.eclipse.ui. * IWorkbenchWindow) * * @since 3.1 */ @Override public void windowActivated(IWorkbenchWindow window) { if (window == getEditorSite().getWorkbenchWindow() && fMarkOccurrenceAnnotations && isActivePart()) { fForcedMarkOccurrencesSelection = getSelectionProvider().getSelection(); IModelElement sourceModule = getModelElement(); if (sourceModule != null && sourceModule.getElementType() == IModelElement.SOURCE_MODULE) { updateOccurrenceAnnotations((ITextSelection) fForcedMarkOccurrencesSelection, sourceModule); } } } /* * @seeorg.eclipse.ui.IWindowListener#windowDeactivated(org.eclipse.ui. * IWorkbenchWindow) * * @since 3.1 */ @Override public void windowDeactivated(IWorkbenchWindow window) { if (window == getEditorSite().getWorkbenchWindow() && fMarkOccurrenceAnnotations && isActivePart()) { removeOccurrenceAnnotations(); } } /* * @seeorg.eclipse.ui.IWindowListener#windowClosed(org.eclipse.ui. * IWorkbenchWindow) * * @since 3.1 */ @Override public void windowClosed(IWorkbenchWindow window) { } /* * @seeorg.eclipse.ui.IWindowListener#windowOpened(org.eclipse.ui. * IWorkbenchWindow) * * @since 3.1 */ @Override public void windowOpened(IWorkbenchWindow window) { } } /** * Cancels the occurrences finder job upon document changes. * * @since 3.0 */ class OccurrencesFinderJobCanceler implements IDocumentListener, ITextInputListener { public void install() { ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer == null) return; StyledText text = sourceViewer.getTextWidget(); if (text == null || text.isDisposed()) return; sourceViewer.addTextInputListener(this); IDocument document = sourceViewer.getDocument(); if (document != null) document.addDocumentListener(this); } public void uninstall() { ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer != null) sourceViewer.removeTextInputListener(this); IDocumentProvider documentProvider = getDocumentProvider(); if (documentProvider != null) { IDocument document = documentProvider.getDocument(getEditorInput()); if (document != null) document.removeDocumentListener(this); } } /* * @see * org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged * (org.eclipse.jface.text.DocumentEvent) */ @Override public void documentAboutToBeChanged(DocumentEvent event) { if (fOccurrencesFinderJob != null) fOccurrencesFinderJob.doCancel(); } /* * @see * org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse * .jface.text.DocumentEvent) */ @Override public void documentChanged(DocumentEvent event) { } /* * @see org.eclipse.jface.text.ITextInputListener# * inputDocumentAboutToBeChanged (org.eclipse.jface.text.IDocument, * org.eclipse.jface.text.IDocument) */ @Override public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) { if (oldInput == null) return; oldInput.removeDocumentListener(this); } /* * @see * org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org * .eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument) */ @Override public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { if (newInput == null) return; newInput.addDocumentListener(this); } } /** * Finds and marks occurrence annotations. * * @since 3.0 */ class OccurrencesFinderJob extends Job { private final IDocument fDocument; private final ISelection fSelection; private final ISelectionValidator fPostSelectionValidator; private boolean fCanceled = false; private final OccurrenceLocation[] fLocations; public OccurrencesFinderJob(IDocument document, OccurrenceLocation[] locations, ISelection selection) { super("mark occurrences job name"); // TODO should //$NON-NLS-1$ // externals fDocument = document; fSelection = selection; fLocations = locations; if (getSelectionProvider() instanceof ISelectionValidator) fPostSelectionValidator = (ISelectionValidator) getSelectionProvider(); else fPostSelectionValidator = null; } // cannot use cancel() because it is declared final void doCancel() { fCanceled = true; cancel(); } private boolean isCanceled(IProgressMonitor progressMonitor) { return fCanceled || progressMonitor.isCanceled() || fPostSelectionValidator != null && !(fPostSelectionValidator.isValid(fSelection) || fForcedMarkOccurrencesSelection == fSelection) || LinkedModeModel.hasInstalledModel(fDocument); } /* * @see Job#run(org.eclipse.core.runtime.IProgressMonitor) */ @Override public IStatus run(IProgressMonitor progressMonitor) { if (isCanceled(progressMonitor)) return Status.CANCEL_STATUS; ITextViewer textViewer = getTextViewer(); if (textViewer == null) return Status.CANCEL_STATUS; IDocument document = textViewer.getDocument(); if (document == null) return Status.CANCEL_STATUS; IDocumentProvider documentProvider = getDocumentProvider(); if (documentProvider == null) return Status.CANCEL_STATUS; IAnnotationModel annotationModel = documentProvider.getAnnotationModel(getEditorInput()); if (annotationModel == null) return Status.CANCEL_STATUS; // Add occurrence annotations int length = fLocations.length; Map<Annotation, Position> annotationMap = new HashMap<>(length); for (int i = 0; i < length; i++) { if (isCanceled(progressMonitor)) return Status.CANCEL_STATUS; OccurrenceLocation location = fLocations[i]; Position position = new Position(location.getOffset(), location.getLength()); String description = location.getDescription(); String annotationType = (location.getFlags() == IOccurrencesFinder.F_WRITE_OCCURRENCE) ? "org.eclipse.php.ui.occurrences.write" //$NON-NLS-1$ : "org.eclipse.php.ui.occurrences"; //$NON-NLS-1$ // create an annotation to mark the occurrence ReconcileAnnotationKey reconcileAnnotationKey = new ReconcileAnnotationKey(null, PHPPartitionTypes.PHP_DEFAULT, ReconcileAnnotationKey.TOTAL); TemporaryAnnotation annotation = new TemporaryAnnotation(position, annotationType, description, reconcileAnnotationKey) { // Supply an occurrence image to display in the vertical // ruler @Override public void paint(GC gc, Canvas canvas, Rectangle r) { ImageUtilities.drawImage( PHPUiPlugin.getImageDescriptorRegistry().get(PHPPluginImages.DESC_OBJS_OCCURRENCES), gc, canvas, r, SWT.CENTER, SWT.TOP); } }; annotationMap.put(annotation, position); } if (isCanceled(progressMonitor)) return Status.CANCEL_STATUS; synchronized (getLockObject(annotationModel)) { if (annotationModel instanceof IAnnotationModelExtension) { ((IAnnotationModelExtension) annotationModel).replaceAnnotations(fOccurrenceAnnotations, annotationMap); } else { removeOccurrenceAnnotations(); for (Map.Entry<Annotation, Position> entry : annotationMap.entrySet()) { annotationModel.addAnnotation(entry.getKey(), entry.getValue()); } } fOccurrenceAnnotations = annotationMap.keySet().toArray(new Annotation[annotationMap.keySet().size()]); } return Status.OK_STATUS; } } /** * Information provider used to present focusable information shells. * * @since 3.2 */ private static final class InformationProvider implements IInformationProvider, IInformationProviderExtension, IInformationProviderExtension2 { private final IInformationControlCreator fControlCreator; private final Object fHoverInfo; private final IRegion fHoverRegion; InformationProvider(final IRegion hoverRegion, final Object hoverInfo, final IInformationControlCreator controlCreator) { fHoverRegion = hoverRegion; fHoverInfo = hoverInfo; fControlCreator = controlCreator; } /* * @see org.eclipse.jface.text.information.IInformationProvider# * getInformation (org.eclipse.jface.text.ITextViewer, * org.eclipse.jface.text.IRegion) */ @Override public String getInformation(final ITextViewer textViewer, final IRegion subject) { return fHoverInfo == null ? null : fHoverInfo.toString(); } /* * @see * org.eclipse.jface.text.information.IInformationProviderExtension# * getInformation2(org.eclipse.jface.text.ITextViewer, * org.eclipse.jface.text.IRegion) * * @since 3.2 */ @Override public Object getInformation2(final ITextViewer textViewer, final IRegion subject) { return fHoverInfo; } /* * @see * org.eclipse.jface.text.information.IInformationProviderExtension2 * #getInformationPresenterControlCreator() */ @Override public IInformationControlCreator getInformationPresenterControlCreator() { return fControlCreator; } /* * @see * org.eclipse.jface.text.information.IInformationProvider#getSubject * (org.eclipse.jface.text.ITextViewer, int) */ @Override public IRegion getSubject(final ITextViewer textViewer, final int invocationOffset) { return fHoverRegion; } } private void initPHPVersionsListener() { if (fPhpVersionListener != null) { return; } fPhpVersionListener = new IPreferencesPropagatorListener() { @Override public void preferencesEventOccured(PreferencesPropagatorEvent event) { // get the structured document and go over its regions // in case of PhpScriptRegion reparse the region text if (getTextViewer() instanceof PHPStructuredTextViewer) { DocumentModelUtils.reparseAndReconcileDocument((PHPStructuredTextViewer) getTextViewer()); } else { IDocumentProvider documentProvider = getDocumentProvider(); IDocument doc = documentProvider != null ? documentProvider.getDocument(getEditorInput()) : null; DocumentModelUtils.reparseDocument(doc); } } @Override public IProject getProject() { IScriptProject scriptProject = PHPStructuredEditor.this.getProject(); if (scriptProject != null) { return scriptProject.getProject(); } return null; } }; PHPVersionChangedHandler.getInstance().addPHPVersionChangedListener(fPhpVersionListener); fPreferencesListener = new IPreferenceChangeListener() { @Override public void preferenceChange(PreferenceChangeEvent event) { String property = event.getKey(); if (PHPCoreConstants.CODEASSIST_AUTOACTIVATION.equals(property) || PHPCoreConstants.CODEASSIST_AUTOACTIVATION_DELAY.equals(property) || PHPCoreConstants.CODEASSIST_AUTOINSERT.equals(property)) { ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer != null) { PHPStructuredTextViewerConfiguration configuration = (PHPStructuredTextViewerConfiguration) getSourceViewerConfiguration(); if (configuration != null) { StructuredContentAssistant contentAssistant = (StructuredContentAssistant) configuration .getPHPContentAssistant(sourceViewer); IPreferencesService preferencesService = Platform.getPreferencesService(); contentAssistant.enableAutoActivation(preferencesService.getBoolean(PHPCorePlugin.ID, PHPCoreConstants.CODEASSIST_AUTOACTIVATION, false, null)); contentAssistant.setAutoActivationDelay(preferencesService.getInt(PHPCorePlugin.ID, PHPCoreConstants.CODEASSIST_AUTOACTIVATION_DELAY, 0, null)); contentAssistant.enableAutoInsert(preferencesService.getBoolean(PHPCorePlugin.ID, PHPCoreConstants.CODEASSIST_AUTOINSERT, false, null)); } } } } }; InstanceScope.INSTANCE.getNode(PHPCorePlugin.ID).addPreferenceChangeListener(fPreferencesListener); } /** Cursor dependent actions. */ private final List<String> fCursorActions = new ArrayList<>(5); /** The information presenter. */ protected InformationPresenter fInformationPresenter; private ISourceReconcilingListener fEditorReconcilingListener = new EditorReconcilingListener(); public PHPStructuredEditor() { /** * Bug fix: #158170 Set WST's folding support enablement according to * PHP editor folding support status. Must be removed, when WTP releases * folding support */ boolean foldingEnabled = PHPUiPlugin.getDefault().getPreferenceStore() .getBoolean(PreferenceConstants.EDITOR_FOLDING_ENABLED); SSEUIPlugin.getDefault().getPreferenceStore().setValue(AbstractStructuredFoldingStrategy.FOLDING_ENABLED, foldingEnabled); setDocumentProvider(DLTKUIPlugin.getDocumentProvider()); fPHPEditorErrorTickUpdater = new PHPEditorErrorTickUpdater(this); } // added by zhaozw,or there will be a exception for files in the phar @Override protected void setDocumentProvider(IEditorInput input) { setDocumentProvider(DLTKUIPlugin.getDocumentProvider()); } @Override public void init(IEditorSite site, IEditorInput input) throws PartInitException { if (input instanceof IFileEditorInput) { // This is the existing workspace file final IFileEditorInput fileInput = (IFileEditorInput) input; input = new RefactorableFileEditorInput(fileInput.getFile()); } super.init(site, input); } @Override protected void initializeEditor() { super.initializeEditor(); IPreferenceStore store = createCombinedPreferenceStore(); setPreferenceStore(store); fMarkOccurrenceAnnotations = store.getBoolean(PreferenceConstants.EDITOR_MARK_OCCURRENCES); fStickyOccurrenceAnnotations = store.getBoolean(PreferenceConstants.EDITOR_STICKY_OCCURRENCES); fMarkTypeOccurrences = store.getBoolean(PreferenceConstants.EDITOR_MARK_TYPE_OCCURRENCES); fMarkMethodOccurrences = store.getBoolean(PreferenceConstants.EDITOR_MARK_METHOD_OCCURRENCES); fMarkFunctionOccurrences = store.getBoolean(PreferenceConstants.EDITOR_MARK_FUNCTION_OCCURRENCES); fMarkConstantOccurrences = store.getBoolean(PreferenceConstants.EDITOR_MARK_CONSTANT_OCCURRENCES); fMarkGlobalVariableOccurrences = store.getBoolean(PreferenceConstants.EDITOR_MARK_GLOBAL_VARIABLE_OCCURRENCES); fMarkLocalVariableOccurrences = store.getBoolean(PreferenceConstants.EDITOR_MARK_LOCAL_VARIABLE_OCCURRENCES); fMarkImplementors = store.getBoolean(PreferenceConstants.EDITOR_MARK_IMPLEMENTORS); fMarkMethodExitPoints = store.getBoolean(PreferenceConstants.EDITOR_MARK_METHOD_EXIT_POINTS); fMarkBreakContinueTargets = store.getBoolean(PreferenceConstants.EDITOR_MARK_BREAK_CONTINUE_TARGETS); } /** * Create a preference store that combines the source editor preferences * with the base editor's preferences. * * @return IPreferenceStore */ private IPreferenceStore createCombinedPreferenceStore() { IPreferenceStore sseEditorPrefs = SSEUIPlugin.getDefault().getPreferenceStore(); IPreferenceStore baseEditorPrefs = EditorsUI.getPreferenceStore(); IPreferenceStore phpEditorPrefs = PHPUiPlugin.getDefault().getPreferenceStore(); return new ChainedPreferenceStore(new IPreferenceStore[] { sseEditorPrefs, baseEditorPrefs, phpEditorPrefs }); } @Override public void dispose() { ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer instanceof ITextViewerExtension) { ((ITextViewerExtension) sourceViewer).removeVerifyKeyListener(fBracketInserter); } if (fContextMenuGroup != null) { fContextMenuGroup.dispose(); fContextMenuGroup = null; } if (fActionGroups != null) { fActionGroups.dispose(); fActionGroups = null; } if (fInformationPresenter != null) { fInformationPresenter.dispose(); fInformationPresenter = null; } if (fPhpVersionListener != null) { PHPVersionChangedHandler.getInstance().removePHPVersionChangedListener(fPhpVersionListener); fPhpVersionListener = null; } if (fFormatterProfileListener != null) { // workaround for bug 409116 PreferencesPropagator propagator = PreferencePropagatorFactory.getInstance() .getPreferencePropagator(FORMATTER_PLUGIN_ID); propagator.removePropagatorListener(fFormatterProfileListener, "org.eclipse.php.formatter.core.formatter.tabulation.size"); //$NON-NLS-1$ propagator.removePropagatorListener(fFormatterProfileListener, "formatterProfile"); //$NON-NLS-1$ fFormatterProfileListener = null; } if (fPreferencesListener != null) { InstanceScope.INSTANCE.getNode(PHPCorePlugin.ID).removePreferenceChangeListener(fPreferencesListener); } if (fActivationListener != null) { PlatformUI.getWorkbench().removeWindowListener(fActivationListener); fActivationListener = null; } // some things in the configuration need to clean // up after themselves if (fPHPOutlinePage != null) { if (fPHPOutlinePage instanceof ConfigurableContentOutlinePage && fPHPOutlinePageListener != null) { ((ConfigurableContentOutlinePage) fPHPOutlinePage).removeDoubleClickListener(fPHPOutlinePageListener); } if (fPHPOutlinePageListener != null) { fPHPOutlinePageListener.uninstall(fPHPOutlinePage); } } uninstallOccurrencesFinder(); uninstallOverrideIndicator(); // remove the listener we added in createAction method if (getSelectionProvider() instanceof IPostSelectionProvider) { IPostSelectionProvider psp = (IPostSelectionProvider) getSelectionProvider(); try { IAction action = getAction(IPHPEditorActionDefinitionIds.OPEN_TYPE_HIERARCHY); if (action instanceof ISelectionChangedListener) { psp.removePostSelectionChangedListener((ISelectionChangedListener) action); } action = getAction(IPHPEditorActionDefinitionIds.OPEN_CALL_HIERARCHY); if (action instanceof ISelectionChangedListener) { psp.removePostSelectionChangedListener((ISelectionChangedListener) action); } } catch (NullPointerException ex) { // NPE thrown by getAction in case when class has already been // disposed but dispose is called again. } } if (fProjectionModelUpdater != null) { fProjectionModelUpdater.uninstall(); fProjectionModelUpdater = null; } if (fPHPEditorErrorTickUpdater != null) { fPHPEditorErrorTickUpdater.dispose(); fPHPEditorErrorTickUpdater = null; } super.dispose(); } /* * @see AbstractTextEditor#editorContextMenuAboutToShow(IMenuManager) */ @Override public void editorContextMenuAboutToShow(IMenuManager menu) { super.editorContextMenuAboutToShow(menu); if (fContextMenuGroup != null) { ActionContext context = new ActionContext(getSelectionProvider().getSelection()); fContextMenuGroup.setContext(context); fContextMenuGroup.fillContextMenu(menu); fContextMenuGroup.setContext(null); } } @Override protected void addSourceMenuActions(IMenuManager menu) { super.addSourceMenuActions(menu); IContributionItem find = menu.find(IStructuredTextEditorActionConstants.SOURCE_CONTEXT_MENU_ID); if (find instanceof MenuManager) { ((MenuManager) find).setActionDefinitionId(PHPActionConstants.SOURCE_QUICK_MENU); } } @Override protected void addRefactorMenuActions(IMenuManager menu) { super.addRefactorMenuActions(menu); IContributionItem find = menu.find(IStructuredTextEditorActionConstants.REFACTOR_CONTEXT_MENU_ID); if (find instanceof MenuManager) { ((MenuManager) find).setActionDefinitionId(PHPActionConstants.REFACTOR_QUICK_MENU); } } @Override protected void addContextMenuActions(final IMenuManager menu) { if (getSourceViewer().isEditable()) { final String openGroup = "group.open"; //$NON-NLS-1$ menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, new Separator(openGroup)); IAction action = getAction(PHPStructuredEditor.ORG_ECLIPSE_PHP_UI_ACTIONS_OPEN_FUNCTIONS_MANUAL_ACTION); if (action != null) menu.appendToGroup(openGroup, action); action = getAction(IPHPEditorActionDefinitionIds.OPEN_DECLARATION); if (action != null) menu.appendToGroup(openGroup, action); action = getAction(IScriptEditorActionDefinitionIds.SHOW_OUTLINE); if (action != null) menu.appendToGroup(openGroup, action); action = getAction(IPHPEditorActionDefinitionIds.OPEN_TYPE_HIERARCHY); if (action != null) menu.appendToGroup(openGroup, action); action = getAction(IScriptEditorActionDefinitionIds.OPEN_HIERARCHY); if (action != null) menu.appendToGroup(openGroup, action); action = getAction(IPHPEditorActionDefinitionIds.OPEN_CALL_HIERARCHY); if (action != null) menu.appendToGroup(openGroup, action); } } @Override protected void createNavigationActions() { super.createNavigationActions(); final StyledText textWidget = getSourceViewer().getTextWidget(); IAction action = new SmartLineStartAction(textWidget, false); action.setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_START); setAction(ITextEditorActionDefinitionIds.LINE_START, action); action = new SmartLineStartAction(textWidget, true); action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_LINE_START); setAction(ITextEditorActionDefinitionIds.SELECT_LINE_START, action); action = new SmartLineEndAction(textWidget, false); action.setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_END); setAction(ITextEditorActionDefinitionIds.LINE_END, action); action = new SmartLineEndAction(textWidget, true); action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_LINE_END); setAction(ITextEditorActionDefinitionIds.SELECT_LINE_END, action); action = new NavigatePreviousSubWordAction(); action.setActionDefinitionId(ITextEditorActionDefinitionIds.WORD_PREVIOUS); setAction(ITextEditorActionDefinitionIds.WORD_PREVIOUS, action); textWidget.setKeyBinding(SWT.CTRL | SWT.ARROW_LEFT, SWT.NULL); action = new NavigateNextSubWordAction(); action.setActionDefinitionId(ITextEditorActionDefinitionIds.WORD_NEXT); setAction(ITextEditorActionDefinitionIds.WORD_NEXT, action); textWidget.setKeyBinding(SWT.CTRL | SWT.ARROW_RIGHT, SWT.NULL); action = new SelectPreviousSubWordAction(); action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS); setAction(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS, action); textWidget.setKeyBinding(SWT.CTRL | SWT.SHIFT | SWT.ARROW_LEFT, SWT.NULL); action = new SelectNextSubWordAction(); action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT); setAction(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT, action); textWidget.setKeyBinding(SWT.CTRL | SWT.SHIFT | SWT.ARROW_RIGHT, SWT.NULL); } /** * This action implements smart home. (Taken from JDT implementation) * Instead of going to the start of a line it does the following: - if smart * home/end is enabled and the caret is after the line's first * non-whitespace then the caret is moved directly before it, taking PHPDoc * and multi-line comments into account. - if the caret is before the line's * first non-whitespace the caret is moved to the beginning of the line - if * the caret is at the beginning of the line see first case. */ protected class SmartLineStartAction extends LineStartAction { private final boolean fDoSelect; /** * Creates a new smart line start action * * @param textWidget * the styled text widget * @param doSelect * a boolean flag which tells if the text up to the beginning * of the line should be selected */ public SmartLineStartAction(final StyledText textWidget, final boolean doSelect) { super(textWidget, doSelect); fDoSelect = doSelect; } /* * @seeorg.eclipse.ui.texteditor.AbstractTextEditor.LineStartAction# * getLineStartPosition(java.lang.String, int, java.lang.String) */ @Override protected int getLineStartPosition(final IDocument document, final String line, final int length, final int offset) { String type = IDocument.DEFAULT_CONTENT_TYPE; try { type = TextUtilities.getContentType(document, PHPPartitionTypes.PHP_DEFAULT, offset, true); } catch (BadLocationException exception) { // Should not happen } int index = super.getLineStartPosition(document, line, length, offset); if (type.equals(PHPPartitionTypes.PHP_DOC) || type.equals(PHPPartitionTypes.PHP_MULTI_LINE_COMMENT)) { if (index < length - 1 && line.charAt(index) == '*' && line.charAt(index + 1) != '/') { do { ++index; } while (index < length && Character.isWhitespace(line.charAt(index))); } } else { if (index < length - 1 && line.charAt(index) == '/' && line.charAt(index + 1) == '/') { index++; do { ++index; } while (index < length && Character.isWhitespace(line.charAt(index))); } } return index; } private IPreferenceStore getPreferenceStore() { return PHPUiPlugin.getDefault().getPreferenceStore(); } /* * @see org.eclipse.jface.action.IAction#run() */ @Override public void run() { boolean isSmartHomeEndEnabled = true; IPreferenceStore store = getPreferenceStore(); if (store != null) { isSmartHomeEndEnabled = store.getBoolean(PreferenceConstants.USE_SMART_HOME_END); } ISourceViewer fSourceViewer = getSourceViewer(); if (fSourceViewer == null) return; StyledText st = fSourceViewer.getTextWidget(); if (st == null || st.isDisposed()) return; int caretOffset = st.getCaretOffset(); int lineNumber = st.getLineAtOffset(caretOffset); int lineOffset = st.getOffsetAtLine(lineNumber); int lineLength; int caretOffsetInDocument; final IDocument document = fSourceViewer.getDocument(); try { caretOffsetInDocument = widgetOffset2ModelOffset(fSourceViewer, caretOffset); lineLength = document.getLineInformationOfOffset(caretOffsetInDocument).getLength(); } catch (BadLocationException ex) { return; } String line = ""; //$NON-NLS-1$ if (lineLength > 0) { int end = lineOffset + lineLength - 1; end = Math.min(end, st.getCharCount() - 1); line = st.getText(lineOffset, end); } // Compute the line start offset int index = getLineStartPosition(document, line, lineLength, caretOffsetInDocument); // Remember current selection Point oldSelection = st.getSelection(); // Compute new caret position int newCaretOffset = -1; if (isSmartHomeEndEnabled) { if (caretOffset - lineOffset == index) // to beginning of line newCaretOffset = lineOffset; else // to beginning of text newCaretOffset = lineOffset + index; } else { if (caretOffset > lineOffset) // to beginning of line newCaretOffset = lineOffset; } if (newCaretOffset == -1) newCaretOffset = caretOffset; else st.setCaretOffset(newCaretOffset); if (fDoSelect) { if (caretOffset < oldSelection.y) st.setSelection(oldSelection.y, newCaretOffset); else st.setSelection(oldSelection.x, newCaretOffset); } else st.setSelection(newCaretOffset); fireSelectionChanged(oldSelection); } } /** * This action implements smart end. (Taken from * org.eclipse.ui.texteditor.AbstractTextEditor.LineEndAction) Instead of * going to the end of a line it does the following: - if smart home/end is * enabled and the caret is before the line's last non-whitespace and then * the caret is moved directly after it - if the caret is after last * non-whitespace the caret is moved at the end of the line - if the caret * is at the end of the line the caret is moved directly after the line's * last non-whitespace character */ protected class SmartLineEndAction extends TextNavigationAction { /** * boolean flag which tells if the text up to the line end should be * selected. */ private final boolean fDoSelect; /** * Create a new line end action. * * @param textWidget * the styled text widget * @param doSelect * a boolean flag which tells if the text up to the line end * should be selected */ public SmartLineEndAction(StyledText textWidget, boolean doSelect) { super(textWidget, ST.LINE_END); fDoSelect = doSelect; } private IPreferenceStore getPreferenceStore() { return PHPUiPlugin.getDefault().getPreferenceStore(); } /* * @see org.eclipse.jface.action.IAction#run() */ @Override public void run() { boolean isSmartHomeEndEnabled = true; IPreferenceStore store = getPreferenceStore(); if (store != null) { isSmartHomeEndEnabled = store.getBoolean(PreferenceConstants.USE_SMART_HOME_END); } ISourceViewer fSourceViewer = getSourceViewer(); if (fSourceViewer == null) return; StyledText st = fSourceViewer.getTextWidget(); if (st == null || st.isDisposed()) return; int caretOffset = st.getCaretOffset(); int lineNumber = st.getLineAtOffset(caretOffset); int lineOffset = st.getOffsetAtLine(lineNumber); int lineLength; try { int caretOffsetInDocument = widgetOffset2ModelOffset(fSourceViewer, caretOffset); lineLength = fSourceViewer.getDocument().getLineInformationOfOffset(caretOffsetInDocument).getLength(); } catch (BadLocationException ex) { return; } int lineEndOffset = lineOffset + lineLength; int delta = lineEndOffset - st.getCharCount(); if (delta > 0) { lineEndOffset -= delta; lineLength -= delta; } String line = ""; //$NON-NLS-1$ if (lineLength > 0) line = st.getText(lineOffset, lineEndOffset - 1); int i = lineLength - 1; while (i > -1 && Character.isWhitespace(line.charAt(i))) { i--; } i++; // Remember current selection Point oldSelection = st.getSelection(); // Compute new caret position int newCaretOffset = -1; if (isSmartHomeEndEnabled) { if (caretOffset - lineOffset == i) // to end of line newCaretOffset = lineEndOffset; else // to end of text newCaretOffset = lineOffset + i; } else { if (caretOffset < lineEndOffset) // to end of line newCaretOffset = lineEndOffset; } if (newCaretOffset == -1) newCaretOffset = caretOffset; int offsetInLine = newCaretOffset - lineOffset; int lineFullLength = st.getLine(lineNumber).length(); while (offsetInLine > lineFullLength) { --newCaretOffset; --offsetInLine; } st.setCaretOffset(newCaretOffset); if (fDoSelect) { if (caretOffset < oldSelection.y) st.setSelection(oldSelection.y, newCaretOffset); else st.setSelection(oldSelection.x, newCaretOffset); } else st.setSelection(newCaretOffset); fireSelectionChanged(oldSelection); } } /** * Text navigation action to navigate to the next sub-word. */ protected abstract class NextSubWordAction extends TextNavigationAction { protected PHPWordIterator fIterator = new PHPWordIterator(); /** * Creates a new next sub-word action. * * @param code * Action code for the default operation. Must be an action * code from * @see org.eclipse.swt.custom.ST. */ protected NextSubWordAction(int code) { super(getSourceViewer().getTextWidget(), code); } private IPreferenceStore getPreferenceStore() { return PHPUiPlugin.getDefault().getPreferenceStore(); } /* * @see org.eclipse.jface.action.IAction#run() */ @Override public void run() { // Check whether the feature is enabled in Preferences final IPreferenceStore store = getPreferenceStore(); if (!store.getBoolean(PreferenceConstants.USE_SUB_WORD_NAVIGATION)) { super.run(); return; } final ISourceViewer viewer = getSourceViewer(); final IDocument document = viewer.getDocument(); fIterator.setText((CharacterIterator) new DocumentCharacterIterator(document)); int position = widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset()); if (position == -1) { return; } int next = findNextPosition(position); if (next != BreakIterator.DONE) { setCaretPosition(next); getTextWidget().showSelection(); fireSelectionChanged(); } } /** * Finds the next position after the given position. * * @param position * the current position * @return the next position */ protected int findNextPosition(int position) { ISourceViewer viewer = getSourceViewer(); int widget = -1; while (position != BreakIterator.DONE && widget == -1) { // TODO: // optimize position = fIterator.following(position); if (position != BreakIterator.DONE) { widget = modelOffset2WidgetOffset(viewer, position); } } return position; } /** * Sets the caret position to the sub-word boundary given with * <code>position</code>. * * @param position * Position where the action should move the caret */ protected abstract void setCaretPosition(int position); } /** * Text operation action to select the next sub-word. */ protected class SelectNextSubWordAction extends NextSubWordAction { /** * Creates a new select next sub-word action. */ public SelectNextSubWordAction() { super(ST.SELECT_WORD_NEXT); } /* * @see NextSubWordAction#setCaretPosition(int) */ @Override protected void setCaretPosition(final int position) { final ISourceViewer viewer = getSourceViewer(); if (viewer == null) return; final StyledText text = viewer.getTextWidget(); if (text != null && !text.isDisposed()) { final Point selection = text.getSelection(); final int caret = text.getCaretOffset(); final int offset = modelOffset2WidgetOffset(viewer, position); if (caret == selection.x) { text.setSelectionRange(selection.y, offset - selection.y); } else { text.setSelectionRange(selection.x, offset - selection.x); } } } } /** * Text navigation action to navigate to the previous sub-word. */ protected abstract class PreviousSubWordAction extends TextNavigationAction { protected PHPWordIterator fIterator = new PHPWordIterator(); /** * Creates a new previous sub-word action. * * @param code * Action code for the default operation. Must be an action * code from * @see org.eclipse.swt.custom.ST. */ protected PreviousSubWordAction(final int code) { super(getSourceViewer().getTextWidget(), code); } private IPreferenceStore getPreferenceStore() { return PHPUiPlugin.getDefault().getPreferenceStore(); } /* * @see org.eclipse.jface.action.IAction#run() */ @Override public void run() { // Check whether we are in a java code partition and the preference // is enabled final IPreferenceStore store = getPreferenceStore(); if (!store.getBoolean(PreferenceConstants.USE_SUB_WORD_NAVIGATION)) { super.run(); return; } final ISourceViewer viewer = getSourceViewer(); final IDocument document = viewer.getDocument(); fIterator.setText((CharacterIterator) new DocumentCharacterIterator(document)); int position = widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset()); if (position == -1) { return; } int previous = findPreviousPosition(position); if (previous != BreakIterator.DONE) { setCaretPosition(previous); getTextWidget().showSelection(); fireSelectionChanged(); } } /** * Finds the previous position before the given position. * * @param position * the current position * @return the previous position */ protected int findPreviousPosition(int position) { ISourceViewer viewer = getSourceViewer(); int widget = -1; while (position != BreakIterator.DONE && widget == -1) { // TODO: // optimize position = fIterator.preceding(position); if (position != BreakIterator.DONE) { widget = modelOffset2WidgetOffset(viewer, position); } } return position; } /** * Sets the caret position to the sub-word boundary given with * <code>position</code>. * * @param position * Position where the action should move the caret */ protected abstract void setCaretPosition(int position); } /** * Text navigation action to navigate to the next sub-word. * * @since 3.0 */ protected class NavigateNextSubWordAction extends NextSubWordAction { /** * Creates a new navigate next sub-word action. */ public NavigateNextSubWordAction() { super(ST.WORD_NEXT); } /* * @see NextSubWordAction#setCaretPosition(int) */ @Override protected void setCaretPosition(final int position) { getTextWidget().setCaretOffset(modelOffset2WidgetOffset(getSourceViewer(), position)); } } /** * Text operation action to select the previous sub-word. * * @since 3.0 */ protected class SelectPreviousSubWordAction extends PreviousSubWordAction { /** * Creates a new select previous sub-word action. */ public SelectPreviousSubWordAction() { super(ST.SELECT_WORD_PREVIOUS); } /* * @see PreviousSubWordAction#setCaretPosition(int) */ @Override protected void setCaretPosition(final int position) { final ISourceViewer viewer = getSourceViewer(); if (viewer == null) return; final StyledText text = viewer.getTextWidget(); if (text != null && !text.isDisposed()) { final Point selection = text.getSelection(); final int caret = text.getCaretOffset(); final int offset = modelOffset2WidgetOffset(viewer, position); if (caret == selection.x) { text.setSelectionRange(selection.y, offset - selection.y); } else { text.setSelectionRange(selection.x, offset - selection.x); } } } } /** * Text navigation action to navigate to the previous sub-word. * * @since 3.0 */ protected class NavigatePreviousSubWordAction extends PreviousSubWordAction { /** * Creates a new navigate previous sub-word action. */ public NavigatePreviousSubWordAction() { super(ST.WORD_PREVIOUS); } /* * @see PreviousSubWordAction#setCaretPosition(int) */ @Override protected void setCaretPosition(final int position) { getTextWidget().setCaretOffset(modelOffset2WidgetOffset(getSourceViewer(), position)); } } @Override protected void createActions() { super.createActions(); fSelectionHistory = new SelectionHistory(this); Action action = new StructureSelectEnclosingAction(this, fSelectionHistory); action.setActionDefinitionId(IPHPEditorActionDefinitionIds.SELECT_ENCLOSING); setAction(StructureSelectionAction.ENCLOSING, action); action = new StructureSelectNextAction(this, fSelectionHistory); action.setActionDefinitionId(IPHPEditorActionDefinitionIds.SELECT_NEXT); setAction(StructureSelectionAction.NEXT, action); action = new StructureSelectPreviousAction(this, fSelectionHistory); action.setActionDefinitionId(IPHPEditorActionDefinitionIds.SELECT_PREVIOUS); setAction(StructureSelectionAction.PREVIOUS, action); StructureSelectHistoryAction historyAction = new StructureSelectHistoryAction(this, fSelectionHistory); historyAction.setActionDefinitionId(IPHPEditorActionDefinitionIds.SELECT_LAST); setAction(StructureSelectionAction.HISTORY, historyAction); fSelectionHistory.setHistoryAction(historyAction); final ResourceBundle resourceBundle = PHPUIMessages.getResourceBundle(); action = new GotoMatchingBracketAction(this); action.setActionDefinitionId(IPHPEditorActionDefinitionIds.GOTO_MATCHING_BRACKET); setAction(GotoMatchingBracketAction.GOTO_MATCHING_BRACKET, action); action = new OpenFunctionsManualAction(resourceBundle, this); action.setActionDefinitionId(IPHPEditorActionDefinitionIds.OPEN_PHP_MANUAL); // $NON-NLS-1$ setAction(ORG_ECLIPSE_PHP_UI_ACTIONS_OPEN_FUNCTIONS_MANUAL_ACTION, action); markAsCursorDependentAction(ORG_ECLIPSE_PHP_UI_ACTIONS_OPEN_FUNCTIONS_MANUAL_ACTION, true); action = new OpenDeclarationAction(resourceBundle, this); action.setActionDefinitionId(IPHPEditorActionDefinitionIds.OPEN_DECLARATION); // $NON-NLS-1$ setAction(IPHPEditorActionDefinitionIds.OPEN_DECLARATION, action); markAsCursorDependentAction(IPHPEditorActionDefinitionIds.OPEN_DECLARATION, true); action = new OpenTypeHierarchyAction(this); action.setActionDefinitionId(IPHPEditorActionDefinitionIds.OPEN_TYPE_HIERARCHY); // $NON-NLS-1$ setAction(IPHPEditorActionDefinitionIds.OPEN_TYPE_HIERARCHY, action); markAsCursorDependentAction(IPHPEditorActionDefinitionIds.OPEN_TYPE_HIERARCHY, true); // add selection changed listener for updating enabled status if (getSelectionProvider() instanceof IPostSelectionProvider) { IPostSelectionProvider psp = (IPostSelectionProvider) getSelectionProvider(); psp.addPostSelectionChangedListener((OpenTypeHierarchyAction) action); } action = new OpenCallHierarchyAction(this); action.setActionDefinitionId(IPHPEditorActionDefinitionIds.OPEN_CALL_HIERARCHY); // $NON-NLS-1$ setAction(IPHPEditorActionDefinitionIds.OPEN_CALL_HIERARCHY, action); markAsCursorDependentAction(IPHPEditorActionDefinitionIds.OPEN_CALL_HIERARCHY, true); // add selection changed listener for updating enabled status if (getSelectionProvider() instanceof IPostSelectionProvider) { IPostSelectionProvider psp = (IPostSelectionProvider) getSelectionProvider(); psp.addPostSelectionChangedListener((OpenCallHierarchyAction) action); } setAction(PHPActionConstants.SOURCE_QUICK_MENU, new PHPSourceQuickMenuAction(this)); setAction(PHPActionConstants.REFACTOR_QUICK_MENU, new PHPRefactorQuickMenuAction(this)); action = new TextOperationAction(DLTKEditorMessages.getBundleForConstructedKeys(), "OpenHierarchy.", this, //$NON-NLS-1$ PHPStructuredTextViewer.SHOW_HIERARCHY, true); action.setActionDefinitionId(IScriptEditorActionDefinitionIds.OPEN_HIERARCHY); setAction(IScriptEditorActionDefinitionIds.OPEN_HIERARCHY, action); markAsCursorDependentAction(IScriptEditorActionDefinitionIds.OPEN_HIERARCHY, true); ResourceAction resAction = new TextOperationAction(DLTKEditorMessages.getBundleForConstructedKeys(), "ShowPHPDoc.", //$NON-NLS-1$ this, ISourceViewer.INFORMATION, true); resAction = new InformationDispatchAction(DLTKEditorMessages.getBundleForConstructedKeys(), "ShowPHPDoc.", //$NON-NLS-1$ (TextOperationAction) resAction); resAction.setActionDefinitionId(IPHPEditorActionDefinitionIds.SHOW_PHPDOC); setAction("ShowPHPDoc", resAction); //$NON-NLS-1$ resAction = new TextOperationAction(DLTKEditorMessages.getBundleForConstructedKeys(), "ShowOutline.", this, //$NON-NLS-1$ PHPStructuredTextViewer.SHOW_OUTLINE, true); resAction.setActionDefinitionId(IScriptEditorActionDefinitionIds.SHOW_OUTLINE); // $NON-NLS-1$ setAction(IScriptEditorActionDefinitionIds.SHOW_OUTLINE, resAction); // workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=305786 // collapse by shortcut does not work resAction = new TextOperationAction(FoldingMessages.getResourceBundle(), "Projection.Expand.", this, //$NON-NLS-1$ ProjectionViewer.EXPAND, true); resAction.setActionDefinitionId(IFoldingCommandIds.FOLDING_EXPAND); setAction("FoldingExpand", resAction); //$NON-NLS-1$ resAction.setEnabled(true); resAction = new TextOperationAction(FoldingMessages.getResourceBundle(), "Projection.Collapse.", this, //$NON-NLS-1$ ProjectionViewer.COLLAPSE, true); resAction.setActionDefinitionId(IFoldingCommandIds.FOLDING_COLLAPSE); setAction("FoldingCollapse", resAction); //$NON-NLS-1$ resAction.setEnabled(true); // workaround end if (isExternal) { // Override the way breakpoints are set on external files. action = new ToggleExternalBreakpointAction(this, getVerticalRuler()); setAction(ActionDefinitionIds.TOGGLE_BREAKPOINTS, action); // StructuredTextEditor Action - manage breakpoints action = new ManageExternalBreakpointAction(this, getVerticalRuler()); setAction(ActionDefinitionIds.MANAGE_BREAKPOINTS, action); // StructuredTextEditor Action - edit breakpoints action = new EditExternalBreakpointAction(this, getVerticalRuler()); setAction(ActionDefinitionIds.EDIT_BREAKPOINTS, action); // Set the ruler double-click behavior. setAction(ITextEditorActionConstants.RULER_DOUBLE_CLICK, new ToggleExternalBreakpointAction(this, getVerticalRuler(), null)); } // ActionGroup rg = new RefactorActionGroup(this, // ITextEditorActionConstants.GROUP_EDIT); ActionGroup jsg = new PHPSearchActionGroup(this); // We have to keep the context menu group separate to have better // control over positioning fActionGroups = new CompositeActionGroup(new ActionGroup[] { jsg }); fContextMenuGroup = new CompositeActionGroup(new ActionGroup[] { jsg }); } /** * Returns the standard action group of this editor. * * @return returns this editor's standard action group */ public ActionGroup getActionGroup() { return fActionGroups; } /** * Jumps to the matching bracket. */ public void gotoMatchingBracket() { ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer == null) return; IDocument document = sourceViewer.getDocument(); if (document == null) return; IRegion selection = getSignedSelection(sourceViewer); int selectionLength = Math.abs(selection.getLength()); if (selectionLength > 1) { setStatusLineErrorMessage(PHPUIMessages.GotoMatchingBracket_error_invalidSelection); sourceViewer.getTextWidget().getDisplay().beep(); return; } // #26314 int sourceCaretOffset = selection.getOffset() + selection.getLength(); if (isSurroundedByBrackets(document, sourceCaretOffset)) sourceCaretOffset -= selection.getLength(); IRegion region = fBracketMatcher.match(document, sourceCaretOffset); if (region == null) { setStatusLineErrorMessage(PHPUIMessages.GotoMatchingBracket_error_noMatchingBracket); sourceViewer.getTextWidget().getDisplay().beep(); return; } int offset = region.getOffset(); int length = region.getLength(); if (length < 1) return; int anchor = fBracketMatcher.getAnchor(); // http://dev.eclipse.org/bugs/show_bug.cgi?id=34195 int targetOffset = ICharacterPairMatcher.RIGHT == anchor ? offset + 1 : offset + length; boolean visible = false; if (sourceViewer instanceof ITextViewerExtension5) { ITextViewerExtension5 extension = (ITextViewerExtension5) sourceViewer; visible = extension.modelOffset2WidgetOffset(targetOffset) > -1; } else { IRegion visibleRegion = sourceViewer.getVisibleRegion(); // http://dev.eclipse.org/bugs/show_bug.cgi?id=34195 visible = targetOffset >= visibleRegion.getOffset() && targetOffset <= visibleRegion.getOffset() + visibleRegion.getLength(); } if (!visible) { setStatusLineErrorMessage(PHPUIMessages.GotoMatchingBracket_error_bracketOutsideSelectedElement); sourceViewer.getTextWidget().getDisplay().beep(); return; } if (selection.getLength() < 0) targetOffset -= selection.getLength(); sourceViewer.setSelectedRange(targetOffset, selection.getLength()); sourceViewer.revealRange(targetOffset, selection.getLength()); } private static boolean isSurroundedByBrackets(IDocument document, int offset) { if (offset == 0 || offset == document.getLength()) return false; try { return isBracket(document.getChar(offset - 1)) && isBracket(document.getChar(offset)); } catch (BadLocationException e) { return false; } } private static boolean isBracket(char character) { for (int i = 0; i != BRACKETS.length; ++i) if (character == BRACKETS[i]) return true; return false; } /** * Returns the signed current selection. The length will be negative if the * resulting selection is right-to-left (RtoL). * <p> * The selection offset is model based. * </p> * * @param sourceViewer * the source viewer * @return a region denoting the current signed selection, for a resulting * RtoL selections length is < 0 */ protected IRegion getSignedSelection(ISourceViewer sourceViewer) { StyledText text = sourceViewer.getTextWidget(); Point selection = text.getSelectionRange(); if (text.getCaretOffset() == selection.x) { selection.x = selection.x + selection.y; selection.y = -selection.y; } selection.x = widgetOffset2ModelOffset(sourceViewer, selection.x); return new Region(selection.x, selection.y); } /** * Return whether document folding should be enabled according to the * preference store settings. * * @return <code>true</code> if document folding should be enabled */ private boolean isFoldingEnabled() { IPreferenceStore store = getPreferenceStore(); // check both preference store and vm argument return (store.getBoolean(AbstractStructuredFoldingStrategy.FOLDING_ENABLED)); } /** * install the old code folding instead of wtp's */ private void installProjectionSupport() { projectionSupportInstalled = true; ProjectionViewer projectionViewer = (ProjectionViewer) getSourceViewer(); if (projectionViewer == null) return; fProjectionModelUpdater = new PHPFoldingStructureProviderProxy(); if (fProjectionModelUpdater != null) fProjectionModelUpdater.install(projectionViewer); if (isFoldingEnabled()) { fProjectionModelUpdater.projectionEnabled(); } } @Override public void createPartControl(final Composite parent) { super.createPartControl(parent); fBracketInserter.setCloseBracketsEnabled(TypingPreferences.closeBrackets); fBracketInserter.setCloseStringsEnabled(TypingPreferences.closeQuotes); ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer instanceof ITextViewerExtension) { ((ITextViewerExtension) sourceViewer).prependVerifyKeyListener(fBracketInserter); } // workaround for code folding if (isFoldingEnabled()) { installProjectionSupport(); } // end if (isMarkingOccurrences()) installOccurrencesFinder(true); final IInformationControlCreator informationControlCreator = new IInformationControlCreator() { @Override public IInformationControl createInformationControl(Shell shell) { boolean cutDown = false; int style = cutDown ? SWT.NONE : SWT.V_SCROLL | SWT.H_SCROLL; return new DefaultInformationControl(shell, SWT.RESIZE | SWT.TOOL, style, new HTMLTextPresenter(cutDown)); } }; fInformationPresenter = new InformationPresenter(informationControlCreator); fInformationPresenter.setSizeConstraints(60, 10, true, true); fInformationPresenter.install(getSourceViewer()); addEditorReconcilingListener(getSourceViewerConfiguration(), getTextViewer()); fEditorSelectionChangedListener = new EditorSelectionChangedListener(); fEditorSelectionChangedListener.install(getSelectionProvider()); PlatformUI.getWorkbench().addWindowListener(fActivationListener); setHelpContextId(IPHPHelpContextIds.EDITOR_PREFERENCES);// $NON-NLS-1$ } private void addEditorReconcilingListener(SourceViewerConfiguration config, StructuredTextViewer textViewer) { IReconciler reconciler = config.getReconciler(textViewer); if (reconciler instanceof DocumentRegionProcessor) { ((DocumentRegionProcessor) reconciler).addReconcilingListener(fEditorReconcilingListener); } } private class EditorReconcilingListener implements ISourceReconcilingListener { @Override public void aboutToBeReconciled() { PHPStructuredEditor.this.aboutToBeReconciled(); } @Override public void reconciled(IDocument document, IAnnotationModel model, boolean forced, IProgressMonitor progressMonitor) { } } @Override protected void setSourceViewerConfiguration(SourceViewerConfiguration config) { SourceViewerConfiguration old = config; super.setSourceViewerConfiguration(config); StructuredTextViewer stv = getTextViewer(); if (stv != null) { removeEditorReconcilingListener(old, stv); addEditorReconcilingListener(config, stv); } } private void removeEditorReconcilingListener(SourceViewerConfiguration config, StructuredTextViewer textViewer) { IReconciler reconciler = config.getReconciler(textViewer); if (reconciler instanceof DocumentRegionProcessor) { ((DocumentRegionProcessor) reconciler).removeReconcilingListener(fEditorReconcilingListener); } } @Override protected void doSetInput(IEditorInput input) throws CoreException { IFile resource = null; isExternal = false; if (input instanceof IFileEditorInput) { // This is the existing workspace file final IFileEditorInput fileInput = (IFileEditorInput) input; resource = fileInput.getFile(); if (getRefactorableFileEditorInput() != null && ((RefactorableFileEditorInput) getRefactorableFileEditorInput()).isRefactor()) { getRefactorableFileEditorInput().setRefactor(false); if (getDocumentProvider() != null) { getDocumentProvider().disconnect(getRefactorableFileEditorInput()); } getRefactorableFileEditorInput().setFile(fileInput.getFile()); input = getRefactorableFileEditorInput(); } else { input = new RefactorableFileEditorInput(fileInput.getFile()); } } if (resource != null) { if (PHPToolkitUtil.isPHPFile((IFile) resource)) { PHPSourceParser.editFile.set(resource); super.doSetInput(input); // At this point new document content was set, maybe even // getProject() value was updated (for example when document // was moved from one PHP project to another). initPHPVersionsListener(); } else { super.doSetInput(input); } } else { isExternal = true; super.doSetInput(input); } ImageDescriptor imageDescriptor = input.getImageDescriptor(); if (imageDescriptor != null) { setTitleImage(JFaceResources.getResources().createImageWithDefault(imageDescriptor)); } if (isShowingOverrideIndicators()) { installOverrideIndicator(true); } if (fProjectionModelUpdater != null) updateProjectionSupport(); if (fPHPEditorErrorTickUpdater != null) fPHPEditorErrorTickUpdater.updateEditorImage(getModelElement()); } /** * Install everything necessary to get document folding working and enable * document folding */ private void updateProjectionSupport() { // dispose of previous document folding support if (fProjectionModelUpdater != null) { fProjectionModelUpdater.uninstall(); fProjectionModelUpdater = null; } ProjectionViewer projectionViewer = (ProjectionViewer) getSourceViewer(); if (projectionViewer == null) return; fProjectionModelUpdater = new PHPFoldingStructureProviderProxy(); if (fProjectionModelUpdater != null) { fProjectionModelUpdater.install(projectionViewer); fProjectionModelUpdater.initialize(); } } @Override protected boolean canHandleMove(IEditorInput originalElement, IEditorInput movedElement) { if (getRefactorableFileEditorInput() != null) { getRefactorableFileEditorInput().setRefactor(true); } return super.canHandleMove(originalElement, movedElement); } private RefactorableFileEditorInput getRefactorableFileEditorInput() { if (getEditorInput() instanceof RefactorableFileEditorInput) { return (RefactorableFileEditorInput) getEditorInput(); } return null; } OutlineSelectionChangedListener fPHPOutlinePageListener; @Override public Object getAdapter(Class required) { if (required == IFoldingStructureProviderExtension.class && fProjectionModelUpdater != null) { IStructuredTextFoldingProvider foldingProvider = fProjectionModelUpdater.getFoldingProvider(); if (foldingProvider instanceof IFoldingStructureProviderExtension) { return foldingProvider; } } if (required == IFoldingStructureProvider.class && fProjectionModelUpdater != null) { IStructuredTextFoldingProvider foldingProvider = fProjectionModelUpdater.getFoldingProvider(); if (foldingProvider instanceof IFoldingStructureProvider) { return foldingProvider; } } Object adapter = super.getAdapter(required); // add selection listener to outline page // so that if outline selects model element, editor selects correct item if (adapter instanceof ConfigurableContentOutlinePage && IContentOutlinePage.class.equals(required) && shouldOutlineViewBeLoaded()) { final ConfigurableContentOutlinePage outlinePage = (ConfigurableContentOutlinePage) adapter; if (fPHPOutlinePageListener == null) { fPHPOutlinePageListener = new OutlineSelectionChangedListener(outlinePage.getConfiguration()); outlinePage.addDoubleClickListener(fPHPOutlinePageListener); } fPHPOutlinePageListener.install(outlinePage); fPHPOutlinePage = outlinePage; outlinePage.setInput(getModelElement()); } return adapter; } private boolean shouldOutlineViewBeLoaded() { if (fPHPOutlinePage != null && fPHPOutlinePage.getControl() != null && !fPHPOutlinePage.getControl().isDisposed()) { return false; } return true; } protected void clearStatusLine() { setStatusLineErrorMessage(null); setStatusLineMessage(null); } public SourceViewerConfiguration getSourceViwerConfiguration() { return super.getSourceViewerConfiguration(); } /** * Returns the cached selected range, which allows to query it from a non-UI * thread. * <p> * The result might be outdated if queried from a non-UI thread.</em> * </p> * * @return the caret offset in the master document * @see ITextViewer#getSelectedRange() * @since 3.3 */ public Point getCachedSelectedRange() { return fCachedSelectedRange; } /* * (non-Javadoc) * * @see * org.eclipse.wst.sse.ui.StructuredTextEditor#handleCursorPositionChanged() */ @Override protected void handleCursorPositionChanged() { updateCursorDependentActions(); if (getTextViewer() != null) { fCachedSelectedRange = getTextViewer().getSelectedRange(); } else { fCachedSelectedRange = null; } super.handleCursorPositionChanged(); } @Override protected void handlePreferenceStoreChanged(final PropertyChangeEvent event) { final String property = event.getProperty(); try { if (AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH.equals(property)) { /* * Ignore tab setting since we rely on the formatter * preferences. We do this outside the try-finally block to * avoid that EDITOR_TAB_WIDTH is handled by the sub-class * (AbstractDecoratedTextEditor). */ return; } if (PreferenceConstants.EDITOR_TEXT_HOVER_MODIFIERS.equals(property) || PreferenceConstants.EDITOR_TEXT_HOVER_MODIFIER_MASKS.equals(property)) { updateHoverBehavior(); return; } boolean newBooleanValue = false; Object newValue = event.getNewValue(); if (newValue != null) { newBooleanValue = Boolean.valueOf(newValue.toString()).booleanValue(); } if (PreferenceConstants.EDITOR_MARK_OCCURRENCES.equals(property)) { if (newBooleanValue != fMarkOccurrenceAnnotations) { fMarkOccurrenceAnnotations = newBooleanValue; if (!fMarkOccurrenceAnnotations) uninstallOccurrencesFinder(); else installOccurrencesFinder(true); } return; } if (PreferenceConstants.EDITOR_CLOSE_BRACKETS.equals(property)) { fBracketInserter.setCloseBracketsEnabled(getPreferenceStore().getBoolean(property)); return; } if (PreferenceConstants.EDITOR_CLOSE_STRINGS.equals(property)) { fBracketInserter.setCloseStringsEnabled(getPreferenceStore().getBoolean(property)); return; } if (PreferenceConstants.EDITOR_MARK_TYPE_OCCURRENCES.equals(property)) { fMarkTypeOccurrences = newBooleanValue; return; } if (PreferenceConstants.EDITOR_MARK_METHOD_OCCURRENCES.equals(property)) { fMarkMethodOccurrences = newBooleanValue; return; } if (PreferenceConstants.EDITOR_MARK_FUNCTION_OCCURRENCES.equals(property)) { fMarkFunctionOccurrences = newBooleanValue; return; } if (PreferenceConstants.EDITOR_MARK_CONSTANT_OCCURRENCES.equals(property)) { fMarkConstantOccurrences = newBooleanValue; return; } if (PreferenceConstants.EDITOR_MARK_GLOBAL_VARIABLE_OCCURRENCES.equals(property)) { fMarkGlobalVariableOccurrences = newBooleanValue; return; } if (PreferenceConstants.EDITOR_MARK_LOCAL_VARIABLE_OCCURRENCES.equals(property)) { fMarkLocalVariableOccurrences = newBooleanValue; return; } if (PreferenceConstants.EDITOR_MARK_METHOD_EXIT_POINTS.equals(property)) { fMarkMethodExitPoints = newBooleanValue; return; } if (PreferenceConstants.EDITOR_MARK_BREAK_CONTINUE_TARGETS.equals(property)) { fMarkBreakContinueTargets = newBooleanValue; return; } if (PreferenceConstants.EDITOR_MARK_IMPLEMENTORS.equals(property)) { fMarkImplementors = newBooleanValue; return; } if (PreferenceConstants.EDITOR_STICKY_OCCURRENCES.equals(property)) { fStickyOccurrenceAnnotations = newBooleanValue; return; } if (affectsOverrideIndicatorAnnotations(event)) { if (isShowingOverrideIndicators()) { if (fOverrideIndicatorManager == null) installOverrideIndicator(true); } else { if (fOverrideIndicatorManager != null) uninstallOverrideIndicator(); } return; } } finally { super.handlePreferenceStoreChanged(event); } if (AbstractStructuredFoldingStrategy.FOLDING_ENABLED.equals(property)) { if (getSourceViewer() instanceof ProjectionViewer) { // install projection support if it has not even been // installed yet if (isFoldingEnabled() && !projectionSupportInstalled) { installProjectionSupport(); } } } } 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 character; case '\'': return character; case '`': return character; default: throw new IllegalArgumentException(); } } /** * Determines whether the preference change encoded by the given event * changes the override indication. * * @param event * the event to be investigated * @return <code>true</code> if event causes a change */ protected boolean affectsOverrideIndicatorAnnotations(PropertyChangeEvent event) { String key = event.getProperty(); AnnotationPreference preference = getAnnotationPreferenceLookup() .getAnnotationPreference(OverrideIndicatorManager.ANNOTATION_TYPE); if (key == null || preference == null) return false; return key.equals(preference.getHighlightPreferenceKey()) || key.equals(preference.getVerticalRulerPreferenceKey()) || key.equals(preference.getOverviewRulerPreferenceKey()) || key.equals(preference.getTextPreferenceKey()); } /** * Tells whether override indicators are shown. * * @return <code>true</code> if the override indicators are shown * @since 3.0 */ protected boolean isShowingOverrideIndicators() { AnnotationPreference preference = getAnnotationPreferenceLookup() .getAnnotationPreference(OverrideIndicatorManager.ANNOTATION_TYPE); IPreferenceStore store = getPreferenceStore(); return getBoolean(store, preference.getHighlightPreferenceKey()) || getBoolean(store, preference.getVerticalRulerPreferenceKey()) || getBoolean(store, preference.getOverviewRulerPreferenceKey()) || getBoolean(store, preference.getTextPreferenceKey()); } /** * Returns the boolean preference for the given key. * * @param store * the preference store * @param key * the preference key * @return <code>true</code> if the key exists in the store and its value is * <code>true</code> * @since 3.0 */ private boolean getBoolean(IPreferenceStore store, String key) { return key != null && store.getBoolean(key); } @Override protected void initializeKeyBindingScopes() { setKeyBindingScopes(new String[] { "org.eclipse.php.ui.phpEditorScope" }); //$NON-NLS-1$ } /** * Marks or unmarks the given action to be updated on text cursor position * changes. * * @param actionId * the action id * @param mark * <code>true</code> if the action is cursor position dependent */ public void markAsCursorDependentAction(final String actionId, final boolean mark) { assert actionId != null; if (mark) { if (!fCursorActions.contains(actionId)) fCursorActions.add(actionId); } else fCursorActions.remove(actionId); } public IDocument getDocument() { ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer != null) { return sourceViewer.getDocument(); } return null; } /** * Updates the specified action by calling <code>IUpdate.update</code> if * applicable. * * @param actionId * the action id */ private void updateAction(final String actionId) { assert actionId != null; final IAction action = getAction(actionId); if (action instanceof IUpdate) ((IUpdate) action).update(); } /** * Updates all cursor position dependent actions. */ protected void updateCursorDependentActions() { if (fCursorActions != null) { long currentTime = System.currentTimeMillis(); if (fLastActionsUpdate > currentTime - 1000) { // only allow updates // at most once per // second return; } fLastActionsUpdate = currentTime; final Iterator<String> e = fCursorActions.iterator(); while (e.hasNext()) updateAction(e.next()); } } /* * Update the hovering behavior depending on the preferences. */ private void updateHoverBehavior() { final SourceViewerConfiguration configuration = getSourceViewerConfiguration(); final String[] types = configuration.getConfiguredContentTypes(getSourceViewer()); for (final String t : types) { final ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer instanceof ITextViewerExtension2) { // Remove existing hovers ((ITextViewerExtension2) sourceViewer).removeTextHovers(t); final int[] stateMasks = configuration.getConfiguredTextHoverStateMasks(getSourceViewer(), t); if (stateMasks != null) for (final int stateMask : stateMasks) { final ITextHover textHover = configuration.getTextHover(sourceViewer, t, stateMask); ((ITextViewerExtension2) sourceViewer).setTextHover(textHover, t, stateMask); } else { final ITextHover textHover = configuration.getTextHover(sourceViewer, t); ((ITextViewerExtension2) sourceViewer).setTextHover(textHover, t, ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK); } } else sourceViewer.setTextHover(configuration.getTextHover(sourceViewer, t), t); } } @Override protected StructuredTextViewer createStructedTextViewer(Composite parent, IVerticalRuler verticalRuler, int styles) { if (getProject() != null) { fFormatterProfileListener = new IPreferencesPropagatorListener() { @Override public void preferencesEventOccured(PreferencesPropagatorEvent event) { SourceViewerConfiguration config = getSourceViewerConfiguration(); ISourceViewer sourceViewer = getSourceViewer(); if (config != null && sourceViewer != null) { StyledText textWidget = sourceViewer.getTextWidget(); int tabWidth = config.getTabWidth(sourceViewer); if (textWidget != null && textWidget.getTabs() != tabWidth) { textWidget.setTabs(tabWidth); } } } @Override public IProject getProject() { IScriptProject scriptProject = PHPStructuredEditor.this.getProject(); if (scriptProject != null) { return scriptProject.getProject(); } return null; } }; // workaround for bug 409116 PreferencesPropagator propagator = PreferencePropagatorFactory.getInstance() .getPreferencePropagator(FORMATTER_PLUGIN_ID); propagator.addPropagatorListener(fFormatterProfileListener, "org.eclipse.php.formatter.core.formatter.tabulation.size"); //$NON-NLS-1$ propagator.addPropagatorListener(fFormatterProfileListener, "formatterProfile"); //$NON-NLS-1$ } return new PHPStructuredTextViewer(this, parent, verticalRuler, getOverviewRuler(), isOverviewRulerVisible(), styles); } /** * Resets the foldings structure according to the folding preferences. */ public void resetProjection() { if (fProjectionModelUpdater != null) { fProjectionModelUpdater.initialize(); } } /* * @see org.eclipse.ui.texteditor.AbstractTextEditor#performSave(boolean, * org.eclipse.core.runtime.IProgressMonitor) */ @Override protected void performSave(boolean overwrite, IProgressMonitor progressMonitor) { IDocumentProvider p = getDocumentProvider(); if (p instanceof ISourceModuleDocumentProvider) { ISourceModuleDocumentProvider cp = (ISourceModuleDocumentProvider) p; cp.setSavePolicy(fSavePolicy); } try { super.performSave(overwrite, progressMonitor); } finally { if (p instanceof ISourceModuleDocumentProvider) { ISourceModuleDocumentProvider cp = (ISourceModuleDocumentProvider) p; cp.setSavePolicy(null); } } } @Override public IDocumentProvider getDocumentProvider() { if (getEditorInput() instanceof ExternalStorageEditorInput) { IDocumentProvider provider = LocalStorageModelProvider.getInstance(); if (provider != null) { return provider; } } if (getEditorInput() instanceof RefactorableFileEditorInput) { return super.getDocumentProvider(); } if (getEditorInput() instanceof IStorageEditorInput) { IDocumentProvider provider = StorageModelProvider.getInstance(); if (provider != null) { return provider; } } return super.getDocumentProvider(); } /** * IScriptReconcilingListener methods - reconcile listeners */ private ListenerList fReconcilingListeners = new ListenerList(ListenerList.IDENTITY); public void addReconcileListener(IPHPScriptReconcilingListener reconcileListener) { synchronized (fReconcilingListeners) { fReconcilingListeners.add(reconcileListener); } } public void removeReconcileListener(IPHPScriptReconcilingListener reconcileListener) { synchronized (fReconcilingListeners) { fReconcilingListeners.remove(reconcileListener); } } @Override public void aboutToBeReconciled() { fReconcileSelection = true; // Notify AST provider PHPUiPlugin.getDefault().getASTProvider().aboutToBeReconciled((ISourceModule) getModelElement()); // Notify listeners Object[] listeners = fReconcilingListeners.getListeners(); for (int i = 0, length = listeners.length; i < length; ++i) ((IPHPScriptReconcilingListener) listeners[i]).aboutToBeReconciled(); } /* * @see * org.eclipse.jdt.internal.ui.text.java.IJavaReconcilingListener#reconciled * (CompilationUnit, boolean, IProgressMonitor) * * @since 3.0 */ @Override public void reconciled(Program ast, boolean forced, IProgressMonitor progressMonitor) { // see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=58245 PHPUiPlugin phpPlugin = PHPUiPlugin.getDefault(); if (phpPlugin == null) return; // Always notify AST provider ISourceModule inputModelElement = (ISourceModule) getModelElement(); phpPlugin.getASTProvider().reconciled(ast, inputModelElement, progressMonitor); this.fReconcileSelection = false; // Notify listeners Object[] listeners = fReconcilingListeners.getListeners(); for (int i = 0, length = listeners.length; i < length; ++i) ((IPHPScriptReconcilingListener) listeners[i]).reconciled(ast, forced, progressMonitor); } /** * Returns the model element wrapped by this editors input. Most likely to * be the relevant source module * * @return the model element wrapped by this editors input. * */ public IModelElement getModelElement() { return EditorUtility.getEditorInputModelElement(this, false); } /** * Returns the most narrow model element including the given offset. * * @param offset * the offset inside of the requested element * @return the most narrow model element */ protected IModelElement getElementAt(int offset) { return getElementAt(offset, true); } /** * Returns the most narrow element including the given offset. If * <code>reconcile</code> is <code>true</code> the editor's input element is * reconciled in advance. If it is <code>false</code> this method only * returns a result if the editor's input element does not need to be * reconciled. * * @param offset * the offset included by the retrieved element * @param reconcile * <code>true</code> if working copy should be reconciled * @return the most narrow element which includes the given offset */ protected IModelElement getElementAt(int offset, boolean reconcile) { ISourceModule unit = (ISourceModule) getModelElement(); if (unit != null) { try { if (reconcile) { fReconcileSelection = false; ScriptModelUtil.reconcile(unit); return unit.getElementAt(offset); } else if (unit.isConsistent()) return unit.getElementAt(offset); } catch (ModelException x) { if (!x.isDoesNotExist()) // DLTKUIPlugin.log(x.getStatus()); System.err.println(x.getStatus()); // nothing found, be tolerant and go on } } return null; } /** * Returns project that holds the edited file (if any) * * @return project or <code>null</code> if there's no one */ public IScriptProject getProject() { IModelElement modelElement = getModelElement(); if (modelElement != null) { return modelElement.getScriptProject(); } return null; } /** * Support mark occurrences in PHP Editor */ /** * Returns the lock object for the given annotation model. * * @param annotationModel * the annotation model * @return the annotation model's lock object * @since 3.0 */ private Object getLockObject(IAnnotationModel annotationModel) { if (annotationModel instanceof ISynchronizable) { Object lock = ((ISynchronizable) annotationModel).getLockObject(); if (lock != null) return lock; } return annotationModel; } protected void updateOccurrenceAnnotations(ITextSelection selection, Program astRoot) { updateOccurrencesAnnotationsRunJob(selection, astRoot); } /** * Updates the occurrences annotations based on the current selection. * * TODO : since {@link PHPStructuredEditor#aboutToBeChangedEvent} currently * doesn't work, we check if " document.getLength() != astRoot.getEnd() " to * identify if the document was not already reconciled * * @param selection * the text selection * @param astRoot * the compilation unit AST * @since 3.0 */ protected void updateOccurrenceAnnotations(final ITextSelection selection, final IModelElement sourceModule) { if (fOccurrencesFinderJob != null) fOccurrencesFinderJob.cancel(); if (!fMarkOccurrenceAnnotations) return; String updatingOccurrencesJobName = "Updating occurrence annotations"; //$NON-NLS-1$ IJobManager jobManager = Job.getJobManager(); if (jobManager.find(updatingOccurrencesJobName).length > 0) { jobManager.cancel(updatingOccurrencesJobName); } Job job = new Job(updatingOccurrencesJobName) { @Override protected IStatus run(IProgressMonitor monitor) { Program astRoot = null; try { astRoot = SharedASTProvider.getAST((ISourceModule) sourceModule, SharedASTProvider.WAIT_ACTIVE_ONLY, new NullProgressMonitor()); } catch (ModelException e) { Logger.logException(e); } catch (IOException e) { Logger.logException(e); } if (!monitor.isCanceled() && astRoot != null) { updateOccurrencesAnnotationsRunJob(selection, astRoot); } return Status.OK_STATUS; } @Override public boolean belongsTo(Object family) { return getName().equals(family); } }; job.setSystem(true); job.setPriority(Job.DECORATE); job.schedule(); } private void updateOccurrencesAnnotationsRunJob(ITextSelection selection, Program astRoot) { if (astRoot == null || selection == null) return; ISourceViewer viewer = getSourceViewer(); if (viewer == null) return; IDocument document = viewer.getDocument(); if (document == null) return; // TODO: see the method comment, need to be removed once // PHPStructuredEditor#aboutToBeChangedEvent is used if (document.getLength() != astRoot.getEnd() || this.fReconcileSelection) { return; } boolean hasChanged = false; if (document instanceof IDocumentExtension4) { int offset = selection.getOffset(); long currentModificationStamp = ((IDocumentExtension4) document).getModificationStamp(); IRegion markOccurrenceTargetRegion = fMarkOccurrenceTargetRegion; hasChanged = currentModificationStamp != fMarkOccurrenceModificationStamp; if (markOccurrenceTargetRegion != null && !hasChanged) { if (markOccurrenceTargetRegion.getOffset() <= offset && offset <= markOccurrenceTargetRegion.getOffset() + markOccurrenceTargetRegion.getLength()) return; } fMarkOccurrenceTargetRegion = ScriptWordFinder.findWord(document, offset); fMarkOccurrenceModificationStamp = currentModificationStamp; } OccurrenceLocation[] locations = null; ASTNode selectedNode = NodeFinder.perform(astRoot, selection.getOffset(), selection.getLength()); if (locations == null && fMarkExceptions) { // TODO : Implement Me! // ExceptionOccurrencesFinder finder= new // ExceptionOccurrencesFinder(); // if (finder.initialize(astRoot, selectedNode) == null) { // locations= finder.getOccurrences(); // } } if (locations == null && fMarkMethodExitPoints) { IOccurrencesFinder finder = OccurrencesFinderFactory.createMethodExitsFinder(); if (finder.initialize(astRoot, selectedNode) == null) { locations = finder.getOccurrences(); } } if (locations == null && fMarkImplementors) { IOccurrencesFinder finder = OccurrencesFinderFactory.createIncludeFinder(); if (finder.initialize(astRoot, selectedNode) == null) { locations = finder.getOccurrences(); } } if (locations == null && fMarkBreakContinueTargets) { IOccurrencesFinder finder = OccurrencesFinderFactory.createBreakContinueTargetFinder(); if (finder.initialize(astRoot, selectedNode) == null) { locations = finder.getOccurrences(); } } if (locations == null && fMarkImplementors) { IOccurrencesFinder finder = OccurrencesFinderFactory.createImplementorsOccurrencesFinder(); if (finder.initialize(astRoot, selectedNode) == null) { locations = finder.getOccurrences(); } } if (selectedNode != null && selectedNode.getType() == ASTNode.VARIABLE) { final Expression name = ((Variable) selectedNode).getName(); if (name instanceof Identifier) { selectedNode = name; } } if (locations == null && selectedNode != null && (selectedNode instanceof Identifier || (isScalarButNotInString(selectedNode)))) { int type = PHPElementConciliator.concile(selectedNode); if (markOccurrencesOfType(type)) { IOccurrencesFinder finder = OccurrencesFinderFactory.getOccurrencesFinder(type); if (finder != null) { if (finder.initialize(astRoot, selectedNode) == null) { locations = finder.getOccurrences(); } } } } if (locations == null) { if (!fStickyOccurrenceAnnotations) removeOccurrenceAnnotations(); else if (hasChanged) // check consistency of current annotations removeOccurrenceAnnotations(); return; } fOccurrencesFinderJob = new OccurrencesFinderJob(document, locations, selection); // fOccurrencesFinderJob.setPriority(Job.DECORATE); // fOccurrencesFinderJob.setSystem(true); // fOccurrencesFinderJob.schedule(); fOccurrencesFinderJob.run(new NullProgressMonitor()); } /** * Checks whether or not the node is a scalar and return true only if the * scalar is not part of a string * * @param node * @return */ private boolean isScalarButNotInString(ASTNode node) { return (node.getType() == ASTNode.SCALAR) && (node.getParent().getType() != ASTNode.QUOTE); } protected void uninstallOverrideIndicator() { if (fOverrideIndicatorManager != null) { removeReconcileListener(fOverrideIndicatorManager); fOverrideIndicatorManager.removeAnnotations(); fOverrideIndicatorManager = null; } } protected void installOverrideIndicator(boolean provideAST) { uninstallOverrideIndicator(); if (getDocumentProvider() == null) { return; } IAnnotationModel model = getDocumentProvider().getAnnotationModel(getEditorInput()); final IModelElement inputElement = getModelElement(); if (model == null || inputElement == null || inputElement.getElementType() != IModelElement.SOURCE_MODULE) return; fOverrideIndicatorManager = new OverrideIndicatorManager(model); addReconcileListener(fOverrideIndicatorManager); if (provideAST) { Job job = new Job("Installing override indicator") { //$NON-NLS-1$ @Override protected IStatus run(IProgressMonitor monitor) { try { Program ast = SharedASTProvider.getAST((ISourceModule) inputElement, SharedASTProvider.WAIT_ACTIVE_ONLY, new NullProgressMonitor()); if (fOverrideIndicatorManager != null) { fOverrideIndicatorManager.reconciled(ast, true, getProgressMonitor()); } } catch (ModelException e) { Logger.logException(e); } catch (IOException e) { Logger.logException(e); } return Status.OK_STATUS; } }; job.setSystem(true); job.setPriority(Job.DECORATE); job.schedule(); } } protected void installOccurrencesFinder(boolean forceUpdate) { fMarkOccurrenceAnnotations = true; fPostSelectionListenerWithAST = new ISelectionListenerWithAST() { @Override public void selectionChanged(IEditorPart part, ITextSelection selection, Program astRoot) { updateOccurrenceAnnotations(selection, astRoot); } }; SelectionListenerWithASTManager.getDefault().addListener(this, fPostSelectionListenerWithAST); if (forceUpdate && getSelectionProvider() != null) { fForcedMarkOccurrencesSelection = getSelectionProvider().getSelection(); final IModelElement source = getModelElement(); if ((source != null) && source.getElementType() == IModelElement.SOURCE_MODULE) { updateOccurrenceAnnotations((ITextSelection) fForcedMarkOccurrencesSelection, (ISourceModule) source); } } if (fOccurrencesFinderJobCanceler == null) { fOccurrencesFinderJobCanceler = new OccurrencesFinderJobCanceler(); fOccurrencesFinderJobCanceler.install(); } } protected void uninstallOccurrencesFinder() { fMarkOccurrenceAnnotations = false; if (fOccurrencesFinderJob != null) { fOccurrencesFinderJob.cancel(); fOccurrencesFinderJob = null; } if (fOccurrencesFinderJobCanceler != null) { fOccurrencesFinderJobCanceler.uninstall(); fOccurrencesFinderJobCanceler = null; } if (fPostSelectionListenerWithAST != null) { SelectionListenerWithASTManager.getDefault().removeListener(this, fPostSelectionListenerWithAST); fPostSelectionListenerWithAST = null; } removeOccurrenceAnnotations(); } public boolean isMarkingOccurrences() { IPreferenceStore store = getPreferenceStore(); return store != null && store.getBoolean(PreferenceConstants.EDITOR_MARK_OCCURRENCES); } /** * Returns is the occurrences of the type should be marked. * * @param type * One of the {@link PHPElementConciliator} constants integer * type. * @return True, if the type occurrences should be marked; False, otherwise. */ boolean markOccurrencesOfType(int type) { switch (type) { case PHPElementConciliator.CONCILIATOR_GLOBAL_VARIABLE: return fMarkGlobalVariableOccurrences; case PHPElementConciliator.CONCILIATOR_LOCAL_VARIABLE: return fMarkLocalVariableOccurrences; case PHPElementConciliator.CONCILIATOR_FUNCTION: return fMarkFunctionOccurrences; case PHPElementConciliator.CONCILIATOR_CLASSNAME: case PHPElementConciliator.CONCILIATOR_TRAITNAME: return fMarkTypeOccurrences; case PHPElementConciliator.CONCILIATOR_CONSTANT: return fMarkConstantOccurrences; case PHPElementConciliator.CONCILIATOR_CLASS_MEMBER: return fMarkMethodOccurrences; case PHPElementConciliator.CONCILIATOR_UNKNOWN: case PHPElementConciliator.CONCILIATOR_PROGRAM: default: return false; } } void removeOccurrenceAnnotations() { fMarkOccurrenceModificationStamp = IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; fMarkOccurrenceTargetRegion = null; IDocumentProvider documentProvider = getDocumentProvider(); if (documentProvider == null) return; IAnnotationModel annotationModel = documentProvider.getAnnotationModel(getEditorInput()); if (annotationModel == null || fOccurrenceAnnotations == null) return; synchronized (getLockObject(annotationModel)) { if (annotationModel instanceof IAnnotationModelExtension) { ((IAnnotationModelExtension) annotationModel).replaceAnnotations(fOccurrenceAnnotations, null); } else { for (int i = 0, length = fOccurrenceAnnotations.length; i < length; i++) annotationModel.removeAnnotation(fOccurrenceAnnotations[i]); } fOccurrenceAnnotations = null; } } protected boolean isActivePart() { IWorkbenchPart part = getActivePart(); return part != null && part.equals(this); } private IWorkbenchPart getActivePart() { IWorkbenchWindow window = getSite().getWorkbenchWindow(); IPartService service = window.getPartService(); IWorkbenchPart part = service.getActivePart(); return part; } /** * React to changed selection. * * @since 3.0 */ protected void selectionChanged() { if (getSelectionProvider() == null) return; Job job = new Job("PHPStructuredEditor selection changed job") { //$NON-NLS-1$ @Override protected IStatus run(IProgressMonitor monitor) { ISourceReference element = computeHighlightRangeSourceReference(); setSelection(element, false); return Status.OK_STATUS; } }; job.setPriority(Job.DECORATE); job.setSystem(true); job.schedule(); } /** * Computes and returns the source reference that includes the caret and * serves as provider for the outline page selection and the editor range * indication. * * @return the computed source reference */ public ISourceReference computeHighlightRangeSourceReference() { ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer == null) return null; final StyledText styledText = sourceViewer.getTextWidget(); if (styledText == null) return null; final int[] caret = new int[1]; if (sourceViewer instanceof ITextViewerExtension5) { final ITextViewerExtension5 extension = (ITextViewerExtension5) sourceViewer; Display.getDefault().syncExec(new Runnable() { @Override public void run() { if (!styledText.isDisposed()) { caret[0] = extension.widgetOffset2ModelOffset(styledText.getCaretOffset()); } } }); } else { int offset = sourceViewer.getVisibleRegion().getOffset(); caret[0] = offset + styledText.getCaretOffset(); } IModelElement element = getElementAt(caret[0], false); // IModelElement element = getElementAt(caret[0], true); if (!(element instanceof ISourceReference)) return null; return (ISourceReference) element; } protected void setSelection(ISourceReference reference, boolean moveCursor) { if (getSelectionProvider() == null) return; final ISelection[] selection = new ISelection[1]; Display.getDefault().syncExec(new Runnable() { @Override public void run() { selection[0] = getSelectionProvider().getSelection(); } }); if (selection[0] == null) { return; } if (selection[0] instanceof TextSelection) { TextSelection textSelection = (TextSelection) selection[0]; if (textSelection instanceof IStructuredSelection) { Object firstElement = ((IStructuredSelection) textSelection).getFirstElement(); if (firstElement instanceof IImplForPHP) { ((IImplForPHP) firstElement).setModelElement(getModelElement()); } } // PR 39995: [navigation] Forward history cleared after going back // in navigation history: // mark only in navigation history if the cursor is being moved // (which it isn't if // this is called from a PostSelectionEvent that should only update // the magnet) if (moveCursor && (textSelection.getOffset() != 0 || textSelection.getLength() != 0)) markInNavigationHistory(); } if (reference != null) { StyledText textWidget = null; ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer != null) textWidget = sourceViewer.getTextWidget(); if (textWidget == null) return; try { ISourceRange range = null; range = reference.getSourceRange(); if (range == null) return; IDocument document = getDocument(); int offset = range.getOffset(); int length = range.getLength(); // avoid throwing BadLocationException in // GenericPositionManager#addPosition(String, Position) if (offset < 0 || length < 0 || (document != null && offset + length > document.getLength())) return; setHighlightRange(offset, length, moveCursor); if (!moveCursor) return; offset = -1; length = -1; if (reference instanceof IMember) { range = ((IMember) reference).getNameRange(); if (range != null) { offset = range.getOffset(); length = range.getLength(); } } if (offset > -1 && length > 0 && sourceViewer != null) { try { textWidget.setRedraw(false); sourceViewer.revealRange(offset, length); sourceViewer.setSelectedRange(offset, length); } finally { textWidget.setRedraw(true); } markInNavigationHistory(); } } catch (ModelException x) { } catch (IllegalArgumentException x) { } } else if (moveCursor) { resetHighlightRange(); markInNavigationHistory(); } } @Override public void doSave(IProgressMonitor progressMonitor) { if (getDocument() instanceof IStructuredDocument) { CommandStack commandStack = ((IStructuredDocument) getDocument()).getUndoManager().getCommandStack(); // https://bugs.eclipse.org/bugs/show_bug.cgi?id=322529 ((IStructuredDocument) getDocument()).getUndoManager().forceEndOfPendingCommand(null, getViewer().getSelectedRange().x, getViewer().getSelectedRange().y); if (commandStack instanceof BasicCommandStack) { ((BasicCommandStack) commandStack).saveIsDone(); } } super.doSave(progressMonitor); } public ISourceViewer getViewer() { return super.getSourceViewer(); } @Override public void update() { super.update(); if (fPHPOutlinePage != null && fPHPOutlinePage instanceof ConfigurableContentOutlinePage) { ((ConfigurableContentOutlinePage) fPHPOutlinePage).setInput(getModelElement()); } } /** * Initializes the drag and drop support for the given viewer based on * provided editor adapter for drop target listeners. * * @param viewer * the viewer * @since 3.0 */ @Override protected void initializeDragAndDrop(ISourceViewer viewer) { IDragAndDropService dndService = (IDragAndDropService) getSite().getService(IDragAndDropService.class); if (dndService == null) return; ITextEditorDropTargetListener listener = (ITextEditorDropTargetListener) getAdapter( ITextEditorDropTargetListener.class); if (listener == null) { Object object = Platform.getAdapterManager().loadAdapter(this, "org.eclipse.ui.texteditor.ITextEditorDropTargetListener"); //$NON-NLS-1$ if (object instanceof ITextEditorDropTargetListener) listener = (ITextEditorDropTargetListener) object; } if (listener != null) dndService.addMergedDropTarget(viewer.getTextWidget(), DND.DROP_MOVE | DND.DROP_COPY, listener.getTransfers(), listener); IPreferenceStore store = getPreferenceStore(); if (store != null && store.getBoolean(PREFERENCE_TEXT_DRAG_AND_DROP_ENABLED)) installTextDragAndDrop(viewer); } /** * Installs text drag and drop on the given source viewer. * * @param viewer * the viewer * @since 3.3 */ @Override protected void installTextDragAndDrop(final ISourceViewer viewer) { if (viewer == null || fIsTextDragAndDropInstalled) return; final IDragAndDropService dndService = (IDragAndDropService) getSite().getService(IDragAndDropService.class); if (dndService == null) return; final StyledText st = viewer.getTextWidget(); if (st == null) return; // Install drag source final ISelectionProvider selectionProvider = viewer.getSelectionProvider(); final DragSource source = new DragSource(st, DND.DROP_COPY | DND.DROP_MOVE); source.setTransfer(new Transfer[] { TextTransfer.getInstance() }); source.addDragListener(new DragSourceAdapter() { String fSelectedText; Point fSelection; @Override public void dragStart(DragSourceEvent event) { fTextDragAndDropToken = null; try { fSelection = st.getSelection(); event.doit = isLocationSelected(new Point(event.x, event.y)); ISelection selection = selectionProvider.getSelection(); if (selection instanceof ITextSelection) fSelectedText = ((ITextSelection) selection).getText(); else // fallback to widget fSelectedText = st.getSelectionText(); } catch (IllegalArgumentException ex) { event.doit = false; } } private boolean isLocationSelected(Point point) { // FIXME: https://bugs.eclipse.org/bugs/show_bug.cgi?id=260922 if (isBlockSelectionModeEnabled()) return false; int offset = st.getOffsetAtLocation(point); Point p = st.getLocationAtOffset(offset); if (p.x > point.x) offset--; return offset >= fSelection.x && offset < fSelection.y; } @Override public void dragSetData(DragSourceEvent event) { event.data = fSelectedText; fTextDragAndDropToken = this; // Can be any non-null object } @Override public void dragFinished(DragSourceEvent event) { try { if (event.detail == DND.DROP_MOVE && validateEditorInputState()) { Point newSelection = st.getSelection(); int length = fSelection.y - fSelection.x; int delta = 0; if (newSelection.x < fSelection.x) delta = length; st.replaceTextRange(fSelection.x + delta, length, ""); //$NON-NLS-1$ if (fTextDragAndDropToken == null) { // Move in same editor - end compound change IRewriteTarget target = (IRewriteTarget) getAdapter(IRewriteTarget.class); if (target != null) target.endCompoundChange(); } } } finally { fTextDragAndDropToken = null; } } }); // Install drag target DropTargetListener dropTargetListener = new DropTargetAdapter() { private Point fSelection; @Override public void dragEnter(DropTargetEvent event) { fTextDragAndDropToken = null; fSelection = st.getSelection(); if (event.detail == DND.DROP_DEFAULT) { if ((event.operations & DND.DROP_MOVE) != 0) { event.detail = DND.DROP_MOVE; } else if ((event.operations & DND.DROP_COPY) != 0) { event.detail = DND.DROP_COPY; } else { event.detail = DND.DROP_NONE; } } } @Override public void dragOperationChanged(DropTargetEvent event) { if (event.detail == DND.DROP_DEFAULT) { if ((event.operations & DND.DROP_MOVE) != 0) { event.detail = DND.DROP_MOVE; } else if ((event.operations & DND.DROP_COPY) != 0) { event.detail = DND.DROP_COPY; } else { event.detail = DND.DROP_NONE; } } } @Override public void dragOver(DropTargetEvent event) { event.feedback |= DND.FEEDBACK_SCROLL; } @Override public void drop(DropTargetEvent event) { try { if (fTextDragAndDropToken != null && event.detail == DND.DROP_MOVE) { // Move in same editor int caretOffset = st.getCaretOffset(); if (fSelection.x <= caretOffset && caretOffset <= fSelection.y) { event.detail = DND.DROP_NONE; return; } // Start compound change IRewriteTarget target = (IRewriteTarget) getAdapter(IRewriteTarget.class); if (target != null) target.beginCompoundChange(); } if (!validateEditorInputState()) { event.detail = DND.DROP_NONE; return; } String text = (String) event.data; if (isBlockSelectionModeEnabled()) { // FIXME fix block selection and DND // if (fTextDNDColumnSelection != null && // fTextDragAndDropToken != null && event.detail == // DND.DROP_MOVE) { // // DND_MOVE within same editor - remove origin before // inserting // Rectangle newSelection= st.getColumnSelection(); // st.replaceColumnSelection(fTextDNDColumnSelection, // ""); //$NON-NLS-1$ // st.replaceColumnSelection(newSelection, text); // st.setColumnSelection(newSelection.x, newSelection.y, // newSelection.x + fTextDNDColumnSelection.width - // fTextDNDColumnSelection.x, newSelection.y + // fTextDNDColumnSelection.height - // fTextDNDColumnSelection.y); // } else { // Point newSelection= st.getSelection(); // st.insert(text); // IDocument document= // getDocumentProvider().getDocument(getEditorInput()); // int startLine= st.getLineAtOffset(newSelection.x); // int startColumn= newSelection.x - // st.getOffsetAtLine(startLine); // int endLine= startLine + // document.computeNumberOfLines(text); // int endColumn= startColumn + // TextUtilities.indexOf(document.getLegalLineDelimiters(), // text, 0)[0]; // st.setColumnSelection(startColumn, startLine, // endColumn, endLine); // } } else { Point newSelection = st.getSelection(); try { int modelOffset = widgetOffset2ModelOffset(viewer, newSelection.x); viewer.getDocument().replace(modelOffset, 0, text); } catch (BadLocationException e) { return; } st.setSelectionRange(newSelection.x, text.length()); } } finally { fTextDragAndDropToken = null; } } }; dndService.addMergedDropTarget(st, DND.DROP_MOVE | DND.DROP_COPY, new Transfer[] { TextTransfer.getInstance() }, dropTargetListener); fIsTextDragAndDropInstalled = true; } /** * Uninstalls text drag and drop from the given source viewer. * * @param viewer * the viewer * @since 3.3 */ @Override protected void uninstallTextDragAndDrop(ISourceViewer viewer) { if (viewer == null || !fIsTextDragAndDropInstalled) return; final IDragAndDropService dndService = (IDragAndDropService) getSite().getService(IDragAndDropService.class); if (dndService == null) return; StyledText st = viewer.getTextWidget(); if (st != null) { dndService.removeMergedDropTarget(st); DragSource dragSource = (DragSource) st.getData(DND.DRAG_SOURCE_KEY); if (dragSource != null) { dragSource.dispose(); st.setData(DND.DRAG_SOURCE_KEY, null); } } fIsTextDragAndDropInstalled = false; } @Override public boolean isDirty() { // if super.isDirty() return false,it means this boolean result = super.isDirty(); if (!result) { return result; } if (getDocument() instanceof IStructuredDocument) { CommandStack commandStack = ((IStructuredDocument) getDocument()).getUndoManager().getCommandStack(); if (commandStack instanceof BasicCommandStack) { BasicCommandStack bcs = (BasicCommandStack) commandStack; return bcs.isSaveNeeded(); } } return result; } @Override public void firePropertyChange(int property) { super.firePropertyChange(property); } @Override protected boolean isTabsToSpacesConversionEnabled() { return true; } @Override protected void installTabsToSpacesConverter() { SourceViewerConfiguration config = getSourceViewerConfiguration(); ISourceViewer sourceViewer = getSourceViewer(); if (config != null && sourceViewer instanceof ITextViewerExtension7) { // NB: no need to reset the indentation object used by the // TabAutoEditStrategy object because installTabsToSpacesConverter() // is called each time the editor is bound to a new document. TabAutoEditStrategy strategy = new TabAutoEditStrategy(false); ((ITextViewerExtension7) sourceViewer).setTabsToSpacesConverter(strategy); updateIndentPrefixes(); } } /** * Installs a tabs to spaces converter. * * <p> * Subclasses may extend or override this method. * </p> * * @since 3.3 */ @Override protected void uninstallTabsToSpacesConverter() { } public void updatedTitleImage(Image image) { setTitleImage(image); } /** * Workaround for BracketInserterTest * * <p> * Don't call this inside PDT. * * TODO remove this once we find a better way to test it * </p> * * @return */ final public VerifyKeyListener getfBracketInserter() { return fBracketInserter; } }