[221762] GoTo Matching Tag throws NPE with mismatched tags.
diff --git a/bundles/org.eclipse.wst.xml.ui/plugin.properties b/bundles/org.eclipse.wst.xml.ui/plugin.properties
index 8007594..c529b7f 100644
--- a/bundles/org.eclipse.wst.xml.ui/plugin.properties
+++ b/bundles/org.eclipse.wst.xml.ui/plugin.properties
@@ -90,4 +90,5 @@
ActionDefinition.previousSibling.name=Go to Previous Sibling
ActionDefinition.previousSibling.description=Go to Previous Sibling
ActionDefinition.gotoMatchingTag.name=Go to Matching Tag
-ActionDefinition.gotoMatchingTag.description=Go to Matching Tag
\ No newline at end of file
+ActionDefinition.gotoMatchingTag.description=Go to Matching Tag
+MatchingTagPreference.label=Matching Tags
\ No newline at end of file
diff --git a/bundles/org.eclipse.wst.xml.ui/plugin.xml b/bundles/org.eclipse.wst.xml.ui/plugin.xml
index 73a7a58..0780f9d 100644
--- a/bundles/org.eclipse.wst.xml.ui/plugin.xml
+++ b/bundles/org.eclipse.wst.xml.ui/plugin.xml
@@ -387,6 +387,31 @@
</validator>
</extension>
+ <extension point="org.eclipse.ui.editors.annotationTypes">
+ <type name="org.eclipse.wst.xml.ui.matching.tag"/>
+ </extension>
+ <extension point="org.eclipse.ui.editors.markerAnnotationSpecification">
+ <specification
+ includeOnPreferencePage="false"
+ colorPreferenceValue="212,212,212"
+ annotationType="org.eclipse.wst.xml.ui.matching.tag"
+ colorPreferenceKey="matchingTagIndicationColor"
+ presentationLayer="4"
+ label="%MatchingTagPreference.label"
+ icon="icons/full/obj16/tag-generic.gif"
+ textPreferenceValue="false"
+ textPreferenceKey="matchingTagIndication"
+ highlightPreferenceKey="matchingTagHighlight"
+ highlightPreferenceValue="true"
+ verticalRulerPreferenceKey="matchingTagVerticalRuler"
+ verticalRulerPreferenceValue="false"
+ overviewRulerPreferenceKey="matchingTagIndicationInOverviewRuler"
+ overviewRulerPreferenceValue="true"
+ textStylePreferenceKey="matchingTagTextStyle"
+ textStylePreferenceValue="NONE">
+ </specification>
+ </extension>
+
<!--======================================================================================-->
<!-- Document provider for ExternalFileEditorInput -->
<!--======================================================================================-->
diff --git a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/actions/GoToMatchingTagAction.java b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/actions/GoToMatchingTagAction.java
index 3651e64..c0ae763 100644
--- a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/actions/GoToMatchingTagAction.java
+++ b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/actions/GoToMatchingTagAction.java
@@ -11,28 +11,57 @@
package org.eclipse.wst.xml.ui.internal.actions;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
import java.util.ResourceBundle;
import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.TextSelection;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.IAnnotationModelExtension;
+import org.eclipse.jface.viewers.IPostSelectionProvider;
import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.widgets.Event;
+import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.TextEditorAction;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.w3c.dom.Attr;
import org.w3c.dom.Node;
/**
* Moves the cursor to the end tag if it is in a start tag, and vice versa.
+ * Also updates the matching tag annotation using the active editor.
*
* @author nitin
*
*/
class GoToMatchingTagAction extends TextEditorAction {
+ private class UpdateListener implements ISelectionChangedListener {
+ public void selectionChanged(SelectionChangedEvent event) {
+ updateFor(event.getSelection());
+ }
+ }
+
+ /**
+ *
+ */
+ private static final String ANNOTATION_TYPE = "org.eclipse.wst.xml.ui.matching.tag";
+ private ISelectionChangedListener fUpdateListener = null;
+ boolean DEBUG = false;
+
/**
* @param bundle
* @param prefix
@@ -41,6 +70,45 @@
*/
GoToMatchingTagAction(ResourceBundle bundle, String prefix, ITextEditor editor) {
super(bundle, prefix, editor);
+ fUpdateListener = new UpdateListener();
+ }
+
+ void removeAnnotation(boolean allMatching) {
+ ITextEditor textEditor = getTextEditor();
+ if (textEditor == null) {
+ if (DEBUG) {
+ System.out.println("no editor");
+ }
+ return;
+ }
+ IDocumentProvider documentProvider = textEditor.getDocumentProvider();
+ if (documentProvider == null) {
+ if (DEBUG) {
+ System.out.println("no document provider");
+ }
+ return;
+ }
+ IAnnotationModel annotationModel = documentProvider.getAnnotationModel(textEditor.getEditorInput());
+ if (annotationModel == null) {
+ if (DEBUG) {
+ System.out.println("no annotation model");
+ }
+ return;
+ }
+
+ Iterator annotationIterator = annotationModel.getAnnotationIterator();
+ while (annotationIterator.hasNext()) {
+ Annotation annotation = (Annotation) annotationIterator.next();
+ if (ANNOTATION_TYPE.equals(annotation.getType())) {
+ annotation.markDeleted(true);
+ annotationIterator.remove();
+ if (DEBUG) {
+ System.out.println("removed " + annotation);
+ }
+ if (!allMatching)
+ break;
+ }
+ }
}
/*
@@ -66,15 +134,19 @@
int targetOffset = -1;
if (o instanceof IDOMNode) {
IDOMNode node = (IDOMNode) o;
- if (node.getStartStructuredDocumentRegion().containsOffset(offset)) {
+ IStructuredDocumentRegion startStructuredDocumentRegion = node.getStartStructuredDocumentRegion();
+ if (startStructuredDocumentRegion != null && startStructuredDocumentRegion.containsOffset(offset)) {
matchRegion = ((IDOMNode) o).getEndStructuredDocumentRegion();
if (matchRegion != null)
targetOffset = matchRegion.getStartOffset() + 2;
}
- else if (node.getEndStructuredDocumentRegion().containsOffset(offset)) {
- matchRegion = ((IDOMNode) o).getStartStructuredDocumentRegion();
- if (matchRegion != null)
- targetOffset = matchRegion.getStartOffset() + 1;
+ else {
+ IStructuredDocumentRegion endStructuredDocumentRegion = node.getEndStructuredDocumentRegion();
+ if (endStructuredDocumentRegion != null && endStructuredDocumentRegion.containsOffset(offset)) {
+ matchRegion = ((IDOMNode) o).getStartStructuredDocumentRegion();
+ if (matchRegion != null)
+ targetOffset = matchRegion.getStartOffset() + 1;
+ }
}
}
@@ -85,8 +157,131 @@
}
}
- public void update() {
- setEnabled(true);
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.texteditor.TextEditorAction#setEditor(org.eclipse.ui.texteditor.ITextEditor)
+ */
+ public void setEditor(ITextEditor editor) {
+ ITextEditor textEditor = getTextEditor();
+ if (textEditor != null) {
+ removeAnnotation(true);
+
+ ISelectionProvider selectionProvider = textEditor.getSelectionProvider();
+ if (selectionProvider instanceof IPostSelectionProvider) {
+ ((IPostSelectionProvider) selectionProvider).removePostSelectionChangedListener(fUpdateListener);
+ }
+ }
+ super.setEditor(editor);
+ if (editor != null) {
+ ISelectionProvider selectionProvider = editor.getSelectionProvider();
+ if (selectionProvider instanceof IPostSelectionProvider) {
+ ((IPostSelectionProvider) selectionProvider).addPostSelectionChangedListener(fUpdateListener);
+ }
+
+ updateFor(selectionProvider.getSelection());
+ }
}
+ public void update() {
+ setEnabled(true);
+
+ }
+
+ void updateFor(ISelection selection) {
+ ITextEditor textEditor = getTextEditor();
+ if (textEditor == null) {
+ if (DEBUG) {
+ System.out.println("no editor");
+ }
+ return;
+ }
+ IDocumentProvider documentProvider = textEditor.getDocumentProvider();
+ if (documentProvider == null) {
+ if (DEBUG) {
+ System.out.println("no document provider");
+ }
+ return;
+ }
+ IAnnotationModel annotationModel = documentProvider.getAnnotationModel(textEditor.getEditorInput());
+ if (annotationModel == null || !(annotationModel instanceof IAnnotationModelExtension)) {
+ if (DEBUG) {
+ System.out.println("no annotation model");
+ }
+ return;
+ }
+
+ List oldAnnotations = new ArrayList(2);
+ Iterator annotationIterator = annotationModel.getAnnotationIterator();
+ while (annotationIterator.hasNext()) {
+ Annotation annotation = (Annotation) annotationIterator.next();
+ if (ANNOTATION_TYPE.equals(annotation.getType())) {
+ annotation.markDeleted(true);
+ if (DEBUG) {
+ System.out.println("removing " + annotation);
+ }
+ oldAnnotations.add(annotation);
+ }
+ }
+
+ Map newAnnotations = new HashMap();
+ if (!selection.isEmpty() && selection instanceof IStructuredSelection && selection instanceof ITextSelection) {
+ Object o = ((IStructuredSelection) selection).getFirstElement();
+ if (o instanceof IDOMNode) {
+ int offset = ((ITextSelection) selection).getOffset();
+ IStructuredDocumentRegion matchRegion = null;
+ if (((Node) o).getNodeType() == Node.ATTRIBUTE_NODE) {
+ o = ((Attr) o).getOwnerElement();
+ }
+
+ Position pStart = null;
+ Position pEnd = null;
+ if (o instanceof IDOMNode) {
+ IDOMNode node = (IDOMNode) o;
+ IStructuredDocumentRegion startStructuredDocumentRegion = node.getStartStructuredDocumentRegion();
+ if (startStructuredDocumentRegion != null && startStructuredDocumentRegion.containsOffset(offset)) {
+ if (startStructuredDocumentRegion.getNumberOfRegions() > 1) {
+ ITextRegion nameRegion = startStructuredDocumentRegion.getRegions().get(1);
+ pStart = new Position(startStructuredDocumentRegion.getStartOffset(nameRegion), nameRegion.getTextLength());
+ }
+ matchRegion = ((IDOMNode) o).getEndStructuredDocumentRegion();
+ if (matchRegion != null && matchRegion.getNumberOfRegions() > 1) {
+ ITextRegion nameRegion = matchRegion.getRegions().get(1);
+ pEnd = new Position(matchRegion.getStartOffset(nameRegion), nameRegion.getTextLength());
+ }
+ }
+ else {
+ IStructuredDocumentRegion endStructuredDocumentRegion = node.getEndStructuredDocumentRegion();
+ if (endStructuredDocumentRegion != null && endStructuredDocumentRegion.containsOffset(offset)) {
+ if (endStructuredDocumentRegion.getNumberOfRegions() > 1) {
+ ITextRegion nameRegion = endStructuredDocumentRegion.getRegions().get(1);
+ pEnd = new Position(endStructuredDocumentRegion.getStartOffset(nameRegion), nameRegion.getTextLength());
+ }
+ matchRegion = ((IDOMNode) o).getStartStructuredDocumentRegion();
+ if (matchRegion != null && matchRegion.getNumberOfRegions() > 1) {
+ ITextRegion nameRegion = matchRegion.getRegions().get(1);
+ pStart = new Position(matchRegion.getStartOffset(nameRegion), nameRegion.getTextLength());
+ }
+ }
+ }
+ }
+ if (pStart != null && pEnd != null) {
+ Annotation annotation = new Annotation(false);
+ annotation.setType(ANNOTATION_TYPE);
+ newAnnotations.put(annotation, pStart);
+ if (DEBUG) {
+ System.out.println("adding " + annotation);
+ }
+
+ annotation = new Annotation(false);
+ annotation.setType(ANNOTATION_TYPE);
+ newAnnotations.put(annotation, pEnd);
+ if (DEBUG) {
+ System.out.println("adding " + annotation);
+ }
+ }
+ }
+ }
+ ((IAnnotationModelExtension) annotationModel).replaceAnnotations((Annotation[]) oldAnnotations.toArray(new Annotation[oldAnnotations.size()]), newAnnotations);
+ }
}