aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorzzhongwei2013-03-13 03:58:59 (EDT)
committerzzhongwei2013-03-13 03:58:59 (EDT)
commita319c42a668002443a965898ae8a64891c00b032 (patch)
treee53ff71c0d569ee77380a6ca2d6ed266d3125746
parent70ddddd01d8115bd3665e5edf86251ed67dc4e0e (diff)
downloadorg.eclipse.pdt-a319c42a668002443a965898ae8a64891c00b032.zip
org.eclipse.pdt-a319c42a668002443a965898ae8a64891c00b032.tar.gz
org.eclipse.pdt-a319c42a668002443a965898ae8a64891c00b032.tar.bz2
379509: Expand selection to include previous sibling
https://bugs.eclipse.org/bugs/show_bug.cgi?id=379509
-rw-r--r--plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/actions/IPHPEditorActionDefinitionIds.java25
-rw-r--r--plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/PHPStructuredEditor.java28
-rw-r--r--plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/SelectionHistory.java85
-rw-r--r--plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/StructureSelectEnclosingAction.java46
-rw-r--r--plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/StructureSelectHistoryAction.java51
-rw-r--r--plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/StructureSelectNextAction.java134
-rw-r--r--plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/StructureSelectPreviousAction.java123
-rw-r--r--plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/StructureSelectionAction.java208
8 files changed, 699 insertions, 1 deletions
diff --git a/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/actions/IPHPEditorActionDefinitionIds.java b/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/actions/IPHPEditorActionDefinitionIds.java
index 4e08857..d300e15 100644
--- a/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/actions/IPHPEditorActionDefinitionIds.java
+++ b/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/actions/IPHPEditorActionDefinitionIds.java
@@ -225,4 +225,29 @@ public interface IPHPEditorActionDefinitionIds extends
* <code>"org.eclipse.php.ui.edit.text.php.open.call.hierarchy"</code>).
*/
public static final String OPEN_PHP_MANUAL = "org.eclipse.php.ui.edit.text.php.open.manual"; //$NON-NLS-1$
+
+ /**
+ * Action definition ID of the edit -> select enclosing action (value
+ * <code>"org.eclipse.pdt.ui.edit.text.select.enclosing"</code>).
+ */
+ public static final String SELECT_ENCLOSING = "org.eclipse.pdt.ui.edit.text.select.enclosing"; //$NON-NLS-1$
+
+ /**
+ * Action definition ID of the edit -> select next action (value
+ * <code>"org.eclipse.pdt.ui.edit.text.select.next"</code>).
+ */
+ public static final String SELECT_NEXT = "org.eclipse.pdt.ui.edit.text.select.next"; //$NON-NLS-1$
+
+ /**
+ * Action definition ID of the edit -> select previous action (value
+ * <code>"org.eclipse.pdt.ui.edit.text.select.previous"</code>).
+ */
+ public static final String SELECT_PREVIOUS = "org.eclipse.pdt.ui.edit.text.select.previous"; //$NON-NLS-1$
+
+ /**
+ * Action definition ID of the edit -> select restore last action (value
+ * <code>"org.eclipse.pdt.ui.edit.text.select.last"</code>).
+ */
+ public static final String SELECT_LAST = "org.eclipse.pdt.ui.edit.text.select.last"; //$NON-NLS-1$
+
}
diff --git a/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/PHPStructuredEditor.java b/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/PHPStructuredEditor.java
index 3cb248b..4e2656e 100644
--- a/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/PHPStructuredEditor.java
+++ b/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/PHPStructuredEditor.java
@@ -87,6 +87,7 @@ import org.eclipse.php.internal.ui.actions.GotoMatchingBracketAction;
import org.eclipse.php.internal.ui.autoEdit.TabAutoEditStrategy;
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.PHPFoldingStructureProviderProxy;
import org.eclipse.php.internal.ui.preferences.PreferenceConstants;
import org.eclipse.php.internal.ui.text.DocumentCharacterIterator;
@@ -321,6 +322,9 @@ public class PHPStructuredEditor extends StructuredTextEditor implements
*/
private boolean projectionSupportInstalled = false;;
+ /** The selection history of the editor */
+ private SelectionHistory fSelectionHistory;
+
/**
* Internal implementation class for a change listener.
*
@@ -1971,9 +1975,31 @@ public class PHPStructuredEditor extends StructuredTextEditor implements
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 action = new GotoMatchingBracketAction(this);
+ action = new GotoMatchingBracketAction(this);
action.setActionDefinitionId(IPHPEditorActionDefinitionIds.GOTO_MATCHING_BRACKET);
setAction(GotoMatchingBracketAction.GOTO_MATCHING_BRACKET, action);
diff --git a/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/SelectionHistory.java b/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/SelectionHistory.java
new file mode 100644
index 0000000..a124c3e
--- /dev/null
+++ b/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/SelectionHistory.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ *
+
+ *******************************************************************************/
+package org.eclipse.php.internal.ui.editor.selectionactions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.dltk.core.ISourceRange;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.php.internal.ui.editor.PHPStructuredEditor;
+
+public class SelectionHistory {
+
+ private List fHistory;
+ private PHPStructuredEditor fEditor;
+ private ISelectionChangedListener fSelectionListener;
+ private int fSelectionChangeListenerCounter;
+ private StructureSelectHistoryAction fHistoryAction;
+
+ public SelectionHistory(PHPStructuredEditor editor) {
+ Assert.isNotNull(editor);
+ fEditor = editor;
+ fHistory = new ArrayList(3);
+ fSelectionListener = new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ if (fSelectionChangeListenerCounter == 0)
+ flush();
+ }
+ };
+ fEditor.getSelectionProvider().addSelectionChangedListener(
+ fSelectionListener);
+ }
+
+ public void setHistoryAction(StructureSelectHistoryAction action) {
+ Assert.isNotNull(action);
+ fHistoryAction = action;
+ }
+
+ public boolean isEmpty() {
+ return fHistory.isEmpty();
+ }
+
+ public void remember(ISourceRange range) {
+ fHistory.add(range);
+ fHistoryAction.update();
+ }
+
+ public ISourceRange getLast() {
+ if (isEmpty())
+ return null;
+ int size = fHistory.size();
+ ISourceRange result = (ISourceRange) fHistory.remove(size - 1);
+ fHistoryAction.update();
+ return result;
+ }
+
+ public void flush() {
+ if (fHistory.isEmpty())
+ return;
+ fHistory.clear();
+ fHistoryAction.update();
+ }
+
+ public void ignoreSelectionChanges() {
+ fSelectionChangeListenerCounter++;
+ }
+
+ public void listenToSelectionChanges() {
+ fSelectionChangeListenerCounter--;
+ }
+
+ public void dispose() {
+ fEditor.getSelectionProvider().removeSelectionChangedListener(
+ fSelectionListener);
+ }
+}
diff --git a/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/StructureSelectEnclosingAction.java b/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/StructureSelectEnclosingAction.java
new file mode 100644
index 0000000..7a639f1
--- /dev/null
+++ b/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/StructureSelectEnclosingAction.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ *
+
+ *******************************************************************************/
+package org.eclipse.php.internal.ui.editor.selectionactions;
+
+import org.eclipse.dltk.core.ISourceRange;
+import org.eclipse.dltk.core.ISourceReference;
+import org.eclipse.dltk.core.ModelException;
+import org.eclipse.php.internal.core.ast.nodes.ASTNode;
+import org.eclipse.php.internal.core.corext.dom.SelectionAnalyzer;
+import org.eclipse.php.internal.ui.editor.PHPStructuredEditor;
+
+public class StructureSelectEnclosingAction extends StructureSelectionAction {
+
+ public StructureSelectEnclosingAction(PHPStructuredEditor editor,
+ SelectionHistory history) {
+ super("&Enclosing Element", editor, history);
+ setToolTipText("Expand Selection to Include Enclosing Element");
+ setDescription("Expand selection to include enclosing element");
+ // PlatformUI.getWorkbench().getHelpSystem().setHelp(this,
+ // IJavaHelpContextIds.STRUCTURED_SELECT_ENCLOSING_ACTION);
+ }
+
+ /*
+ * This constructor is for testing purpose only.
+ */
+ public StructureSelectEnclosingAction() {
+ }
+
+ @Override
+ ISourceRange internalGetNewSelectionRange(ISourceRange oldSourceRange,
+ ISourceReference sr, SelectionAnalyzer selAnalyzer)
+ throws ModelException {
+ ASTNode first = selAnalyzer.getFirstSelectedNode();
+ if (first == null || first.getParent() == null)
+ return getLastCoveringNodeRange(oldSourceRange, sr, selAnalyzer);
+
+ return getSelectedNodeSourceRange(sr, first.getParent());
+ }
+}
diff --git a/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/StructureSelectHistoryAction.java b/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/StructureSelectHistoryAction.java
new file mode 100644
index 0000000..3a6c014
--- /dev/null
+++ b/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/StructureSelectHistoryAction.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ *
+
+ *******************************************************************************/
+package org.eclipse.php.internal.ui.editor.selectionactions;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.dltk.core.ISourceRange;
+import org.eclipse.jface.action.Action;
+import org.eclipse.php.internal.ui.editor.PHPStructuredEditor;
+import org.eclipse.ui.texteditor.IUpdate;
+
+public class StructureSelectHistoryAction extends Action implements IUpdate {
+ private PHPStructuredEditor fEditor;
+ private SelectionHistory fHistory;
+
+ public StructureSelectHistoryAction(PHPStructuredEditor editor,
+ SelectionHistory history) {
+ super("&Restore Last Selection");
+ setToolTipText("Restore Last Selection");
+ setDescription("Restore last selection");
+ Assert.isNotNull(history);
+ Assert.isNotNull(editor);
+ fHistory = history;
+ fEditor = editor;
+ update();
+ // PlatformUI.getWorkbench().getHelpSystem().setHelp(this,
+ // IJavaHelpContextIds.STRUCTURED_SELECTION_HISTORY_ACTION);
+ }
+
+ public void update() {
+ setEnabled(!fHistory.isEmpty());
+ }
+
+ public void run() {
+ ISourceRange old = fHistory.getLast();
+ if (old != null) {
+ try {
+ fHistory.ignoreSelectionChanges();
+ fEditor.selectAndReveal(old.getOffset(), old.getLength());
+ } finally {
+ fHistory.listenToSelectionChanges();
+ }
+ }
+ }
+}
diff --git a/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/StructureSelectNextAction.java b/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/StructureSelectNextAction.java
new file mode 100644
index 0000000..ca73f07
--- /dev/null
+++ b/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/StructureSelectNextAction.java
@@ -0,0 +1,134 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.php.internal.ui.editor.selectionactions;
+
+import org.eclipse.dltk.core.ISourceRange;
+import org.eclipse.dltk.core.ISourceReference;
+import org.eclipse.dltk.core.ModelException;
+import org.eclipse.php.internal.core.ast.nodes.ASTNode;
+import org.eclipse.php.internal.core.ast.visitor.ApplyAll;
+import org.eclipse.php.internal.core.corext.dom.SelectionAnalyzer;
+import org.eclipse.php.internal.ui.editor.PHPStructuredEditor;
+
+public class StructureSelectNextAction extends StructureSelectionAction {
+
+ private static class NextNodeAnalyzer extends ApplyAll {
+ private final int fOffset;
+ private ASTNode fNextNode;
+
+ private NextNodeAnalyzer(int offset) {
+ // super(true);
+ fOffset = offset;
+ }
+
+ public static ASTNode perform(int offset, ASTNode lastCoveringNode) {
+ NextNodeAnalyzer analyzer = new NextNodeAnalyzer(offset);
+ lastCoveringNode.accept(analyzer);
+ return analyzer.fNextNode;
+ }
+
+ @Override
+ public boolean visit(ASTNode node) {
+ int start = node.getStart();
+ int end = node.getEnd();
+ if (start == fOffset) {
+ fNextNode = node;
+ return true;
+ } else {
+ return (start < fOffset && fOffset < end);
+ }
+ }
+
+ @Override
+ protected boolean apply(ASTNode node) {
+ int start = node.getStart();
+ int end = node.getEnd();
+ if (start == fOffset) {
+ fNextNode = node;
+ return true;
+ } else {
+ return (start < fOffset && fOffset < end);
+ }
+ }
+ }
+
+ public StructureSelectNextAction(PHPStructuredEditor editor,
+ SelectionHistory history) {
+ super("&Next Element", editor, history);
+ setToolTipText("Expand Selection to Include Next Sibling");
+ setDescription("Expand selection to include next sibling");
+ // PlatformUI
+ // .getWorkbench()
+ // .getHelpSystem()
+ // .setHelp(this,
+ // IJavaHelpContextIds.STRUCTURED_SELECT_NEXT_ACTION);
+ }
+
+ /*
+ * This constructor is for testing purpose only.
+ */
+ public StructureSelectNextAction() {
+ }
+
+ /*
+ * non java doc
+ *
+ * @see StructureSelectionAction#internalGetNewSelectionRange(ISourceRange,
+ * ICompilationUnit, SelectionAnalyzer)
+ */
+ public ISourceRange internalGetNewSelectionRange(
+ ISourceRange oldSourceRange, ISourceReference sr,
+ SelectionAnalyzer selAnalyzer) throws ModelException {
+ if (oldSourceRange.getLength() == 0
+ && selAnalyzer.getLastCoveringNode() != null) {
+ ASTNode previousNode = NextNodeAnalyzer.perform(
+ oldSourceRange.getOffset(),
+ selAnalyzer.getLastCoveringNode());
+ if (previousNode != null)
+ return getSelectedNodeSourceRange(sr, previousNode);
+ }
+ org.eclipse.php.internal.core.ast.nodes.ASTNode first = selAnalyzer
+ .getFirstSelectedNode();
+ if (first == null)
+ return getLastCoveringNodeRange(oldSourceRange, sr, selAnalyzer);
+
+ org.eclipse.php.internal.core.ast.nodes.ASTNode parent = first
+ .getParent();
+ if (parent == null)
+ return getLastCoveringNodeRange(oldSourceRange, sr, selAnalyzer);
+
+ org.eclipse.php.internal.core.ast.nodes.ASTNode lastSelectedNode = selAnalyzer
+ .getSelectedNodes()[selAnalyzer.getSelectedNodes().length - 1];
+ org.eclipse.php.internal.core.ast.nodes.ASTNode nextNode = getNextNode(
+ parent, lastSelectedNode);
+ if (nextNode == parent)
+ return getSelectedNodeSourceRange(sr, first.getParent());
+ int offset = oldSourceRange.getOffset();
+ int end = Math.min(sr.getSourceRange().getLength(), nextNode.getStart()
+ + nextNode.getLength() - 1);
+ return createSourceRange(offset, end);
+ }
+
+ // -- helper methods for this class and subclasses
+
+ private static org.eclipse.php.internal.core.ast.nodes.ASTNode getNextNode(
+ org.eclipse.php.internal.core.ast.nodes.ASTNode parent,
+ org.eclipse.php.internal.core.ast.nodes.ASTNode node) {
+ org.eclipse.php.internal.core.ast.nodes.ASTNode[] siblingNodes = getSiblingNodes(node);
+ if (siblingNodes == null || siblingNodes.length == 0)
+ return parent;
+ if (node == siblingNodes[siblingNodes.length - 1])
+ return parent;
+ else
+ return siblingNodes[findIndex(siblingNodes, node) + 1];
+ }
+
+}
diff --git a/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/StructureSelectPreviousAction.java b/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/StructureSelectPreviousAction.java
new file mode 100644
index 0000000..f38dc3d
--- /dev/null
+++ b/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/StructureSelectPreviousAction.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.php.internal.ui.editor.selectionactions;
+
+import org.eclipse.dltk.core.ISourceRange;
+import org.eclipse.dltk.core.ISourceReference;
+import org.eclipse.dltk.core.ModelException;
+import org.eclipse.php.internal.core.ast.nodes.ASTNode;
+import org.eclipse.php.internal.core.ast.visitor.ApplyAll;
+import org.eclipse.php.internal.core.corext.dom.SelectionAnalyzer;
+import org.eclipse.php.internal.ui.editor.PHPStructuredEditor;
+
+public class StructureSelectPreviousAction extends StructureSelectionAction {
+
+ private static class PreviousNodeAnalyzer extends ApplyAll {
+ private final int fOffset;
+ private ASTNode fPreviousNode;
+
+ private PreviousNodeAnalyzer(int offset) {
+ // super(true);
+ fOffset = offset;
+ }
+
+ public static ASTNode perform(int offset, ASTNode lastCoveringNode) {
+ PreviousNodeAnalyzer analyzer = new PreviousNodeAnalyzer(offset);
+ lastCoveringNode.accept(analyzer);
+ return analyzer.fPreviousNode;
+ }
+
+ @Override
+ public boolean visit(ASTNode node) {
+ int start = node.getStart();
+ int end = start + node.getLength();
+ if (end == fOffset) {
+ fPreviousNode = node;
+ return true;
+ } else {
+ return (start < fOffset && fOffset < end);
+ }
+ }
+
+ @Override
+ protected boolean apply(ASTNode node) {
+ return visit(node);
+ }
+ }
+
+ public StructureSelectPreviousAction(PHPStructuredEditor editor,
+ SelectionHistory history) {
+ super("&Next Element", editor, history);
+ setToolTipText("Expand Selection to Include Next Sibling");
+ setDescription("Expand selection to include next sibling");
+ // PlatformUI
+ // .getWorkbench()
+ // .getHelpSystem()
+ // .setHelp(this,
+ // IJavaHelpContextIds.STRUCTURED_SELECT_PREVIOUS_ACTION);
+ }
+
+ /*
+ * This constructor is for testing purpose only.
+ */
+ public StructureSelectPreviousAction() {
+ }
+
+ /*
+ * non java doc
+ *
+ * @see StructureSelectionAction#internalGetNewSelectionRange(ISourceRange,
+ * ICompilationUnit, SelectionAnalyzer)
+ */
+ @Override
+ ISourceRange internalGetNewSelectionRange(ISourceRange oldSourceRange,
+ ISourceReference sr, SelectionAnalyzer selAnalyzer)
+ throws ModelException {
+ if (oldSourceRange.getLength() == 0
+ && selAnalyzer.getLastCoveringNode() != null) {
+ ASTNode previousNode = PreviousNodeAnalyzer.perform(
+ oldSourceRange.getOffset(),
+ selAnalyzer.getLastCoveringNode());
+ if (previousNode != null)
+ return getSelectedNodeSourceRange(sr, previousNode);
+ }
+ ASTNode first = selAnalyzer.getFirstSelectedNode();
+ if (first == null)
+ return getLastCoveringNodeRange(oldSourceRange, sr, selAnalyzer);
+
+ ASTNode parent = first.getParent();
+ if (parent == null)
+ return getLastCoveringNodeRange(oldSourceRange, sr, selAnalyzer);
+
+ ASTNode previousNode = getPreviousNode(parent,
+ selAnalyzer.getSelectedNodes()[0]);
+ if (previousNode == parent)
+ return getSelectedNodeSourceRange(sr, parent);
+
+ int offset = previousNode.getStart();
+ int end = oldSourceRange.getOffset() + oldSourceRange.getLength() - 1;
+ return StructureSelectionAction.createSourceRange(offset, end);
+ }
+
+ private static ASTNode getPreviousNode(ASTNode parent, ASTNode node) {
+ ASTNode[] siblingNodes = StructureSelectionAction.getSiblingNodes(node);
+ if (siblingNodes == null || siblingNodes.length == 0)
+ return parent;
+ if (node == siblingNodes[0]) {
+ return parent;
+ } else {
+ int index = StructureSelectionAction.findIndex(siblingNodes, node);
+ if (index < 1)
+ return parent;
+ return siblingNodes[index - 1];
+ }
+ }
+}
diff --git a/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/StructureSelectionAction.java b/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/StructureSelectionAction.java
new file mode 100644
index 0000000..400ef7f
--- /dev/null
+++ b/plugins/org.eclipse.php.ui/src/org/eclipse/php/internal/ui/editor/selectionactions/StructureSelectionAction.java
@@ -0,0 +1,208 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.php.internal.ui.editor.selectionactions;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.dltk.core.ISourceModule;
+import org.eclipse.dltk.core.ISourceRange;
+import org.eclipse.dltk.core.ISourceReference;
+import org.eclipse.dltk.core.ModelException;
+import org.eclipse.dltk.internal.core.SourceRange;
+import org.eclipse.dltk.internal.ui.editor.EditorUtility;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.php.internal.core.ast.nodes.ASTNode;
+import org.eclipse.php.internal.core.ast.nodes.Program;
+import org.eclipse.php.internal.core.ast.nodes.StructuralPropertyDescriptor;
+import org.eclipse.php.internal.core.corext.dom.Selection;
+import org.eclipse.php.internal.core.corext.dom.SelectionAnalyzer;
+import org.eclipse.php.internal.ui.editor.PHPStructuredEditor;
+import org.eclipse.php.ui.editor.SharedASTProvider;
+
+public abstract class StructureSelectionAction extends Action {
+
+ public static final String NEXT = "SelectNextElement"; //$NON-NLS-1$
+ public static final String PREVIOUS = "SelectPreviousElement"; //$NON-NLS-1$
+ public static final String ENCLOSING = "SelectEnclosingElement"; //$NON-NLS-1$
+ public static final String HISTORY = "RestoreLastSelection"; //$NON-NLS-1$
+
+ private PHPStructuredEditor fEditor;
+ private SelectionHistory fSelectionHistory;
+
+ protected StructureSelectionAction(String text, PHPStructuredEditor editor,
+ SelectionHistory history) {
+ super(text);
+ Assert.isNotNull(editor);
+ Assert.isNotNull(history);
+ fEditor = editor;
+ fSelectionHistory = history;
+ }
+
+ /*
+ * This constructor is for testing purpose only.
+ */
+ protected StructureSelectionAction() {
+ super(""); //$NON-NLS-1$
+ }
+
+ /*
+ * Method declared in IAction.
+ */
+ @Override
+ public final void run() {
+ ISourceModule inputElement = EditorUtility.getEditorInputModelElement(
+ fEditor, false);
+ if (!(inputElement instanceof ISourceModule && inputElement.exists()))
+ return;
+
+ // ITypeRoot typeRoot= (ITypeRoot) inputElement;
+ ISourceRange sourceRange;
+ try {
+ sourceRange = inputElement.getSourceRange();
+ if (sourceRange == null || sourceRange.getLength() == 0) {
+ MessageDialog
+ .openInformation(
+ fEditor.getEditorSite().getShell(),
+ "Expand Selection To",
+ "No source code attached to class file. To perform this operation you will need to attach source.");
+ return;
+ }
+ } catch (ModelException e) {
+ }
+ ITextSelection selection = getTextSelection();
+ ISourceRange newRange = getNewSelectionRange(
+ createSourceRange(selection), inputElement);
+ // Check if new selection differs from current selection
+ if (selection.getOffset() == newRange.getOffset()
+ && selection.getLength() == newRange.getLength())
+ return;
+ fSelectionHistory.remember(new SourceRange(selection.getOffset(),
+ selection.getLength()));
+ try {
+ fSelectionHistory.ignoreSelectionChanges();
+ fEditor.selectAndReveal(newRange.getOffset(), newRange.getLength());
+ } finally {
+ fSelectionHistory.listenToSelectionChanges();
+ }
+ }
+
+ public final ISourceRange getNewSelectionRange(ISourceRange oldSourceRange,
+ ISourceModule typeRoot) {
+ try {
+ Program root = getAST(typeRoot);
+ if (root == null)
+ return oldSourceRange;
+ Selection selection = Selection.createFromStartLength(
+ oldSourceRange.getOffset(), oldSourceRange.getLength());
+ SelectionAnalyzer selAnalyzer = new SelectionAnalyzer(selection,
+ true);
+ root.accept(selAnalyzer);
+ return internalGetNewSelectionRange(oldSourceRange, typeRoot,
+ selAnalyzer);
+ } catch (ModelException e) {
+ return new SourceRange(oldSourceRange.getOffset(),
+ oldSourceRange.getLength());
+ }
+ }
+
+ /**
+ * Subclasses determine the actual new selection.
+ *
+ * @param oldSourceRange
+ * the selected range
+ * @param sr
+ * the current type root
+ * @param selAnalyzer
+ * the selection analyzer
+ * @return return the new selection range
+ * @throws JavaModelException
+ * if getting the source range fails
+ */
+ abstract ISourceRange internalGetNewSelectionRange(
+ ISourceRange oldSourceRange, ISourceReference sr,
+ SelectionAnalyzer selAnalyzer) throws ModelException;
+
+ protected final ITextSelection getTextSelection() {
+ return (ITextSelection) fEditor.getSelectionProvider().getSelection();
+ }
+
+ // -- helper methods for subclasses to fit a node range into the source
+ // range
+
+ protected static ISourceRange getLastCoveringNodeRange(
+ ISourceRange oldSourceRange, ISourceReference sr,
+ SelectionAnalyzer selAnalyzer) throws ModelException {
+ if (selAnalyzer.getLastCoveringNode() == null)
+ return oldSourceRange;
+ else
+ return getSelectedNodeSourceRange(sr,
+ selAnalyzer.getLastCoveringNode());
+ }
+
+ protected static ISourceRange getSelectedNodeSourceRange(
+ ISourceReference sr, ASTNode nodeToSelect) throws ModelException {
+ int offset = nodeToSelect.getStart();
+ int end = Math.min(sr.getSourceRange().getLength(),
+ nodeToSelect.getStart() + nodeToSelect.getLength() - 1);
+ return createSourceRange(offset, end);
+ }
+
+ // -- private helper methods
+
+ private static ISourceRange createSourceRange(ITextSelection ts) {
+ return new SourceRange(ts.getOffset(), ts.getLength());
+ }
+
+ private static Program getAST(ISourceModule sr) {
+ try {
+ return SharedASTProvider.getAST(sr, SharedASTProvider.WAIT_YES,
+ null);
+ } catch (ModelException e) {
+ } catch (IOException e) {
+ }
+ return null;
+ }
+
+ // -- helper methods for this class and subclasses
+
+ static ISourceRange createSourceRange(int offset, int end) {
+ int length = end - offset + 1;
+ if (length == 0) // to allow 0-length selection
+ length = 1;
+ return new SourceRange(Math.max(0, offset), length);
+ }
+
+ static ASTNode[] getSiblingNodes(ASTNode node) {
+ ASTNode parent = node.getParent();
+ StructuralPropertyDescriptor locationInParent = node
+ .getLocationInParent();
+ if (locationInParent.isChildListProperty()) {
+ List<? extends ASTNode> siblings = (List<? extends ASTNode>) parent
+ .getStructuralProperty(locationInParent);
+ return siblings.toArray(new ASTNode[siblings.size()]);
+ }
+ return null;
+ }
+
+ static int findIndex(Object[] array, Object o) {
+ for (int i = 0; i < array.length; i++) {
+ Object object = array[i];
+ if (object == o)
+ return i;
+ }
+ return -1;
+ }
+
+}