diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/DefaultCharacterPairMatcher.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/DefaultCharacterPairMatcher.java
index 4983562..9dd7a01 100644
--- a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/DefaultCharacterPairMatcher.java
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/DefaultCharacterPairMatcher.java
@@ -9,9 +9,6 @@
* Christian Plesner Hansen (plesner@quenta.org) - initial API and implementation
*******************************************************************************/
package org.eclipse.jface.text.source;
-import java.util.HashSet;
-import java.util.Set;
-
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.text.BadLocationException;
@@ -132,38 +129,75 @@
if (document == null || offset < 0 || offset > document.getLength())
return null;
- int start;
- int end;
- if (length >= 0) {
- start= offset;
- end= offset + length;
- } else {
- end= offset;
- start= offset + length;
+ //maybe a bracket is selected
+ IRegion region= match(document, offset, length);
+ fAnchor= ICharacterPairMatcher.LEFT; //always set the anchor to LEFT
+ if (region != null) {
+ return region;
}
- int sourceCaretOffset= offset + length;
- int adjustment= getOffsetAdjustment(document, sourceCaretOffset, length);
- sourceCaretOffset+= adjustment;
-
+ //bracket is not selected
try {
- for (int offset1= sourceCaretOffset; offset1 >= 0; offset1--) {
- char prevChar= document.getChar(Math.max(offset1 - 1, 0));
- if (fPairs.contains(prevChar) && fPairs.isStartCharacter(prevChar)) {
- IRegion match= performMatch(document, offset1);
- if (match != null) {
- int matchOffset= match.getOffset();
- int matchLength= match.getLength();
- if ((matchOffset <= start) && (matchOffset + matchLength > start) && (matchOffset < end) && (matchOffset + matchLength >= end)) {
- return match;
- }
- }
- }
- }
+ final String partition1= TextUtilities.getContentType(document, fPartitioning, offset, false);
+ final DocumentPartitionAccessor partDoc= new DocumentPartitionAccessor(document, fPartitioning, partition1);
+ return findEnclosingPeers(document, partDoc, offset, length, 0, document.getLength());
} catch (BadLocationException ble) {
+ fAnchor= -1;
return null;
}
- return null;
+ }
+
+ /**
+ * @see org.eclipse.jface.text.source.ICharacterPairMatcherExtension#isMatchedChar(char)
+ * @since 3.8
+ */
+ public boolean isMatchedChar(char ch) {
+ return fPairs.contains(ch);
+ }
+
+ /**
+ * @see org.eclipse.jface.text.source.ICharacterPairMatcherExtension#isMatchedChar(char,
+ * org.eclipse.jface.text.IDocument, int)
+ * @since 3.8
+ */
+ public boolean isMatchedChar(char ch, IDocument document, int offset) {
+ return isMatchedChar(ch);
+ }
+
+ /**
+ * @see org.eclipse.jface.text.source.ICharacterPairMatcherExtension#isRecomputationOfEnclosingPairRequired(org.eclipse.jface.text.IDocument,
+ * org.eclipse.jface.text.IRegion, org.eclipse.jface.text.IRegion)
+ * @since 3.8
+ */
+ public boolean isRecomputationOfEnclosingPairRequired(IDocument document, IRegion currentSelection, IRegion previousSelection) {
+ int previousCaretOffset= previousSelection.getOffset() + previousSelection.getLength();
+ int currentCaretOffset= currentSelection.getOffset() + currentSelection.getLength();
+
+ try {
+ String prevContentType= TextUtilities.getContentType(document, fPartitioning, previousCaretOffset, false);
+ String currContentType= TextUtilities.getContentType(document, fPartitioning, currentCaretOffset, false);
+
+ if (!prevContentType.equals(currContentType))
+ return true;
+
+ int start;
+ int end;
+ if (currentCaretOffset > previousCaretOffset) {
+ start= previousCaretOffset;
+ end= currentCaretOffset;
+ } else {
+ start= currentCaretOffset;
+ end= previousCaretOffset;
+ }
+ for (int i= start; i < end; i++) {
+ if (isMatchedChar(document.getChar(i))) {
+ return true;
+ }
+ }
+ } catch (BadLocationException e) {
+ //do nothing
+ }
+ return false;
}
/**
@@ -261,6 +295,106 @@
return -1;
}
+ /*
+ * Performs the actual work of finding enclosing peer characters for #findEnclosingPeerCharacters(IDocument, int, int).
+ */
+ private IRegion findEnclosingPeers(IDocument document, DocumentPartitionAccessor doc, int offset, int length, int lowerBoundary, int upperBoundary) throws BadLocationException {
+ char[] pairs= fPairs.fPairs;
+
+ int start;
+ int end;
+ if (length >= 0) {
+ start= offset;
+ end= offset + length;
+ } else {
+ end= offset;
+ start= offset + length;
+ }
+
+ boolean lowerFound= false;
+ boolean upperFound= false;
+ int[][] counts= new int[pairs.length][2];
+ int pos1= doc.getNextPosition(start, false);
+ int pos2= start;
+
+ while ((pos1 >= lowerBoundary && !lowerFound) || (pos2 < upperBoundary && !upperFound)) {
+ for (int i= 0; i < counts.length; i++) {
+ counts[i][0]= counts[i][1]= 0;
+ }
+
+ outer1: while (pos1 >= lowerBoundary && !lowerFound) {
+ final char c= doc.getChar(pos1);
+ int i= getCharacterIndex(c, document, pos1);
+ if (i != -1 && doc.inPartition(pos1)) {
+ if (i % 2 == 0) {
+ counts[i / 2][0]--; //start
+ } else {
+ counts[i / 2][0]++; //end
+ }
+ for (int j= 0; j < counts.length; j++) {
+ if (counts[j][0] == -1) {
+ lowerFound= true;
+ break outer1;
+ }
+ }
+ }
+ pos1= doc.getNextPosition(pos1, false);
+ }
+
+ outer2: while (pos2 < upperBoundary && !upperFound) {
+ final char c= doc.getChar(pos2);
+ int i= getCharacterIndex(c, document, pos2);
+ if (i != -1 && doc.inPartition(pos2)) {
+ if (i % 2 == 0) {
+ counts[i / 2][1]++; //start
+ } else {
+ counts[i / 2][1]--; //end
+ }
+ for (int j= 0; j < counts.length; j++) {
+ if (counts[j][1] == -1 && counts[j][0] == -1) {
+ upperFound= true;
+ break outer2;
+ }
+ }
+ }
+ pos2= doc.getNextPosition(pos2, true);
+ }
+
+ if (pos1 > start || pos2 < end) {
+ //match inside selection => discard
+ pos1= doc.getNextPosition(pos1, false);
+ pos2= doc.getNextPosition(pos2, true);
+ lowerFound= false;
+ upperFound= false;
+ }
+ }
+ pos2++;
+ if (pos1 < lowerBoundary || pos2 > upperBoundary)
+ return null;
+ return new Region(pos1, pos2 - pos1);
+ }
+
+ /**
+ * Determines the index of the character in the char array passed to the constructor of the pair
+ * matcher.
+ *
+ * @param ch the character
+ * @param document the document
+ * @param offset the offset in document
+ * @return the index of the character in the char array passed to the constructor of the pair
+ * matcher, and -1 if the character is not one of the matched characters.
+ * @since 3.8
+ */
+ private int getCharacterIndex(char ch, IDocument document, int offset) {
+ char[] pairs= fPairs.fPairs;
+ for (int i= 0; i < pairs.length; i++) {
+ if (pairs[i] == ch && isMatchedChar(ch, document, offset)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
/* @see ICharacterPairMatcher#getAnchor() */
public int getAnchor() {
return fAnchor;
@@ -285,6 +419,7 @@
private final IDocument fDocument;
private final String fPartitioning, fPartition;
private ITypedRegion fCachedPartition;
+ private int fLength;
/**
* Creates a new partitioned document for the specified document.
@@ -298,6 +433,7 @@
fDocument= doc;
fPartitioning= partitioning;
fPartition= partition;
+ fLength= doc.getLength();
}
/**
@@ -338,7 +474,7 @@
}
/**
- * Returns the next position to query in the search. The position
+ * Returns the next position to query in the search. The position
* is not guaranteed to be in this document's partition.
*
* @param pos an offset within the document
@@ -347,8 +483,7 @@
*/
public int getNextPosition(int pos, boolean searchForward) {
final ITypedRegion partition= getPartition(pos);
- if (partition == null) return simpleIncrement(pos, searchForward);
- if (fPartition.equals(partition.getType()))
+ if (partition == null || fPartition.equals(partition.getType()))
return simpleIncrement(pos, searchForward);
if (searchForward) {
int end= partition.getOffset() + partition.getLength();
@@ -376,7 +511,7 @@
*/
private ITypedRegion getPartition(int pos) {
if (fCachedPartition == null || !contains(fCachedPartition, pos)) {
- Assert.isTrue(pos >= 0 && pos <= fDocument.getLength());
+ Assert.isTrue(pos >= 0 && pos <= fLength);
try {
fCachedPartition= TextUtilities.getPartition(fDocument, fPartitioning, pos, false);
} catch (BadLocationException e) {
@@ -405,28 +540,18 @@
}
/**
- * Returns true if the specified character pair occurs in one
- * of the character pairs.
- *
+ * Returns true if the specified character occurs in one of the character pairs.
+ *
* @param c a character
* @return true exactly if the character occurs in one of the pairs
*/
public boolean contains(char c) {
- return getAllCharacters().contains(new Character(c));
- }
-
- private Set/*
+ * Clients can use this method to handle characters which may have special meaning in some
+ * situations. E.g. in Java, '<' is used as an angular bracket and as well as less-than operator.
+ *
+ * This is intended to be quick test to determine whether a re-computation of enclosing pair is
+ * required, as the re-computation after each selection change via a
+ * {@link #findEnclosingPeerCharacters(IDocument, int, int)} call can be expensive for some
+ * clients.
+ *
- * Clients instantiate and configure object of this class.true
if the the character is one of the characters matched by the pair
+ * matcher, and false
otherwise
+ */
+ boolean isMatchedChar(char ch);
+
+ /**
+ * Checks whether the character is one of the characters matched by the pair matcher.
+ *
+ * true
if the the character is one of the characters matched by the pair
+ * matcher, and false
otherwise
+ */
+ boolean isMatchedChar(char ch, IDocument document, int offset);
+
+ /**
+ * Computes whether a client needs to recompute the enclosing pair after a selection change in
+ * the document.
+ *
+ * true
if the enclosing pair needs to be recomputed, false
+ * otherwise
+ */
+ boolean isRecomputationOfEnclosingPairRequired(IDocument document, IRegion currentSelection, IRegion previousSelection);
}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/MatchingCharacterPainter.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/MatchingCharacterPainter.java
index f32f1a0..e6cfbda 100644
--- a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/MatchingCharacterPainter.java
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/MatchingCharacterPainter.java
@@ -20,21 +20,27 @@
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IPaintPositionManager;
import org.eclipse.jface.text.IPainter;
import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextInputListener;
+import org.eclipse.jface.text.ITextListener;
import org.eclipse.jface.text.ITextViewerExtension5;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextEvent;
/**
- * Highlights the peer character matching the character near the caret position.
- * This painter can be configured with an
- * {@link org.eclipse.jface.text.source.ICharacterPairMatcher}.
+ * Highlights the peer character matching the character near the caret position, or a pair of peer
+ * characters enclosing the caret position. This painter can be configured with an
+ * {@link org.eclipse.jface.text.source.ICharacterPairMatcher} or an
+ * {@link org.eclipse.jface.text.source.ICharacterPairMatcherExtension}.
*
true
if a matched character is found, false
otherwise
+ *
+ * @since 3.8
+ */
+ private boolean searchForCharacters(String text, ICharacterPairMatcherExtension matcher) {
+ if (text == null)
+ return false;
+ for (int i= 0; i < text.length(); i++) {
+ if (matcher.isMatchedChar(text.charAt(i))) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
}