Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton Leherbauer2008-02-08 10:17:46 +0000
committerAnton Leherbauer2008-02-08 10:17:46 +0000
commite192d34edc2b0788bb50e857169056330825e18c (patch)
treeff1c2bff1859f60d291d6fb4a2393bec8e3df69f
parentba72e09e05d685197c6d86f806de6ac152e98f0d (diff)
downloadorg.eclipse.cdt-e192d34edc2b0788bb50e857169056330825e18c.tar.gz
org.eclipse.cdt-e192d34edc2b0788bb50e857169056330825e18c.tar.xz
org.eclipse.cdt-e192d34edc2b0788bb50e857169056330825e18c.zip
Fix for 110222: Improve pair matcher for angle brackets
-rw-r--r--core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/PairMatcherTest.java86
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CHeuristicScanner.java23
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CPairMatcher.java197
3 files changed, 298 insertions, 8 deletions
diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/PairMatcherTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/PairMatcherTest.java
index 332b2511271..b7bc2434610 100644
--- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/PairMatcherTest.java
+++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/PairMatcherTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * Copyright (c) 2000, 2008 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
@@ -51,7 +51,7 @@ public class PairMatcherTest extends TestCase {
document.setDocumentPartitioner(ICPartitions.C_PARTITIONING, partitioner);
fDocument= document;
- fPairMatcher= new CPairMatcher(new char[] { '(', ')' });
+ fPairMatcher= new CPairMatcher(new char[] { '(', ')', '<', '>' });
}
public static Test suite() {
@@ -146,12 +146,92 @@ public class PairMatcherTest extends TestCase {
assertNotNull(match);
assertTrue(match.getOffset() == 3 && match.getLength() == 12);
}
- }
+ }
public void testAfterClosingMatchWithNLAndSingleLineComment() {
fDocument.set("x\nx(y\nx //(x\ny)x");
IRegion match= fPairMatcher.match(fDocument, 15);
assertNotNull(match);
assertTrue(match.getOffset() == 3 && match.getLength() == 12);
+ }
+
+ public void testAngleBracketsAsOperators() {
+ fDocument.set("void f(){ \n\tif (x<y);\n\twhile(x>y)\n\t\tx << 2; y >> 1;\n}");
+ int idx= fDocument.get().indexOf('<', 0);
+ while (idx >= 0) {
+ IRegion match= fPairMatcher.match(fDocument, idx + 1);
+ assertNull(match);
+ idx= fDocument.get().indexOf('<', idx + 1);
+ }
+ idx= fDocument.get().indexOf('>', 0);
+ while (idx >= 0) {
+ IRegion match= fPairMatcher.match(fDocument, idx + 1);
+ assertNull(match);
+ idx= fDocument.get().indexOf('>', idx + 1);
+ }
+ }
+
+ public void testAngleBracketsAsPairs() {
+ fDocument.set("template < class X > class A {};}");
+ int idx= fDocument.get().indexOf('<', 0);
+ IRegion match= fPairMatcher.match(fDocument, idx + 1);
+ assertNotNull(match);
+ int otherIdx= fDocument.get().indexOf('>');
+ assertEquals(otherIdx, match.getOffset() + match.getLength() - 1);
+
+ match= fPairMatcher.match(fDocument, otherIdx + 1);
+ assertNotNull(match);
+ assertEquals(idx, match.getOffset());
+ }
+
+ public void testAngleBracketsAsPairs2() {
+ fDocument.set("ConstTemplate c<5>;");
+ int idx= fDocument.get().indexOf('<', 0);
+ IRegion match= fPairMatcher.match(fDocument, idx + 1);
+ assertNotNull(match);
+ int otherIdx= fDocument.get().indexOf('>');
+ assertEquals(otherIdx, match.getOffset() + match.getLength() - 1);
+
+ match= fPairMatcher.match(fDocument, otherIdx + 1);
+ assertNotNull(match);
+ assertEquals(idx, match.getOffset());
+ }
+
+ public void testAngleBracketsAsPairsNested() {
+ fDocument.set("OtherTemplate nested<map<int,int>,Y>;");
+ int idx= fDocument.get().indexOf('<', 0);
+ IRegion match= fPairMatcher.match(fDocument, idx + 1);
+ assertNotNull(match);
+ int otherIdx= fDocument.get().lastIndexOf('>');
+ assertEquals(otherIdx, match.getOffset() + match.getLength() - 1);
+
+ match= fPairMatcher.match(fDocument, otherIdx + 1);
+ assertNotNull(match);
+ assertEquals(idx, match.getOffset());
+
+ idx= fDocument.get().indexOf('<', idx+1);
+ match= fPairMatcher.match(fDocument, idx + 1);
+ assertNotNull(match);
+ otherIdx= fDocument.get().indexOf('>', idx + 1);
+ assertEquals(otherIdx, match.getOffset() + match.getLength() - 1);
+ }
+
+ public void testAngleBracketsAsPairsMultiline() {
+ fDocument.set("OtherTemplate nested<\n\tmap<int,int>,Y\n>;");
+ int idx= fDocument.get().indexOf('<', 0);
+ IRegion match= fPairMatcher.match(fDocument, idx + 1);
+ assertNotNull(match);
+ int otherIdx= fDocument.get().lastIndexOf('>');
+ assertEquals(otherIdx, match.getOffset() + match.getLength() - 1);
+
+ match= fPairMatcher.match(fDocument, otherIdx + 1);
+ assertNotNull(match);
+ assertEquals(idx, match.getOffset());
+
+ idx= fDocument.get().indexOf('<', idx+1);
+ match= fPairMatcher.match(fDocument, idx + 1);
+ assertNotNull(match);
+ otherIdx= fDocument.get().indexOf('>', idx + 1);
+ assertEquals(otherIdx, match.getOffset() + match.getLength() - 1);
}
}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CHeuristicScanner.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CHeuristicScanner.java
index 8114fea5b2a..c7a7a22a1a5 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CHeuristicScanner.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CHeuristicScanner.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * Copyright (c) 2000, 2008 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
@@ -634,13 +634,30 @@ public final class CHeuristicScanner implements Symbols {
* @return the matching peer character position, or <code>NOT_FOUND</code>
*/
public int findClosingPeer(int start, final char openingPeer, final char closingPeer) {
+ return findClosingPeer(start, UNBOUND, openingPeer, closingPeer);
+ }
+
+ /**
+ * Returns the position of the closing peer character (forward search). Any scopes introduced by opening peers
+ * are skipped. All peers accounted for must reside in the default partition.
+ *
+ * <p>Note that <code>start</code> must not point to the opening peer, but to the first
+ * character being searched.</p>
+ *
+ * @param start the start position
+ * @param bound the bound
+ * @param openingPeer the opening peer character (e.g. '{')
+ * @param closingPeer the closing peer character (e.g. '}')
+ * @return the matching peer character position, or <code>NOT_FOUND</code>
+ */
+ public int findClosingPeer(int start, int bound, final char openingPeer, final char closingPeer) {
Assert.isLegal(start >= 0);
try {
int depth= 1;
start -= 1;
while (true) {
- start= scanForward(start + 1, UNBOUND, new CharacterMatch(new char[] {openingPeer, closingPeer}));
+ start= scanForward(start + 1, bound, new CharacterMatch(new char[] {openingPeer, closingPeer}));
if (start == NOT_FOUND)
return NOT_FOUND;
@@ -726,7 +743,7 @@ public final class CHeuristicScanner implements Symbols {
return null;
int begin= findOpeningPeer(offset - 1, CHeuristicScanner.UNBOUND, LBRACE, RBRACE);
- int end= findClosingPeer(offset, LBRACE, RBRACE);
+ int end= findClosingPeer(offset, UNBOUND, LBRACE, RBRACE);
if (begin == NOT_FOUND || end == NOT_FOUND)
return null;
return new Region(begin, end + 1 - begin);
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CPairMatcher.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CPairMatcher.java
index 4444f1d69b1..82732ab9a1b 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CPairMatcher.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CPairMatcher.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * Copyright (c) 2000, 2008 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
@@ -12,17 +12,210 @@
*******************************************************************************/
package org.eclipse.cdt.internal.ui.text;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextUtilities;
import org.eclipse.jface.text.source.DefaultCharacterPairMatcher;
+import org.eclipse.jface.text.source.ICharacterPairMatcher;
import org.eclipse.cdt.ui.text.ICPartitions;
/**
- * Helper class for match pairs of characters.
+ * Helper class to match pairs of characters.
*/
public class CPairMatcher extends DefaultCharacterPairMatcher {
+ private static final int ANGLE_BRACKETS_SEARCH_BOUND = 200;
+
+ private boolean fMatchAngularBrackets= true;
+ private int fAnchor= -1;
+
public CPairMatcher(char[] pairs) {
super(pairs, ICPartitions.C_PARTITIONING);
}
+ /* @see ICharacterPairMatcher#match(IDocument, int) */
+ public IRegion match(IDocument document, int offset) {
+ try {
+ return performMatch(document, offset);
+ } catch (BadLocationException ble) {
+ return null;
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.text.source.DefaultCharacterPairMatcher#getAnchor()
+ */
+ public int getAnchor() {
+ if (fAnchor < 0) {
+ return super.getAnchor();
+ }
+ return fAnchor;
+ }
+
+ /*
+ * Performs the actual work of matching for #match(IDocument, int).
+ */
+ private IRegion performMatch(IDocument document, int offset) throws BadLocationException {
+ if (offset < 0 || document == null) return null;
+ final char prevChar= document.getChar(Math.max(offset - 1, 0));
+ if ((prevChar == '<' || prevChar == '>') && !fMatchAngularBrackets)
+ return null;
+ final IRegion region;
+ if (prevChar == '<') {
+ region= findClosingAngleBracket(document, offset - 1);
+ fAnchor= ICharacterPairMatcher.LEFT;
+ } else if (prevChar == '>') {
+ region= findOpeningAngleBracket(document, offset - 1);
+ fAnchor= ICharacterPairMatcher.RIGHT;
+ } else {
+ region= super.match(document, offset);
+ fAnchor= -1;
+ }
+ if (region != null) {
+ if (prevChar == '>') {
+ final int peer= region.getOffset();
+ if (isLessThanOperator(document, peer))
+ return null;
+ } else if (prevChar == '<') {
+ final int peer= region.getOffset() + region.getLength() - 1;
+ if (isGreaterThanOperator(document, peer))
+ return null;
+ }
+ }
+ return region;
+ }
+
+ /**
+ * Returns the region enclosing the matching angle brackets.
+ *
+ * @param document a document
+ * @param offset an offset within the document pointing after the closing angle bracket
+ * @return the matching region or {@link NullPointerException} if no match could be found
+ * @throws BadLocationException
+ */
+ private IRegion findOpeningAngleBracket(IDocument document, int offset) throws BadLocationException {
+ if (offset < 0) return null;
+ final String contentType= TextUtilities.getContentType(document, ICPartitions.C_PARTITIONING, offset, false);
+ CHeuristicScanner scanner= new CHeuristicScanner(document, ICPartitions.C_PARTITIONING, contentType);
+ if (isTemplateParameterCloseBracket(offset, document, scanner)) {
+ int pos= scanner.findOpeningPeer(offset - 1, Math.max(0, offset - ANGLE_BRACKETS_SEARCH_BOUND), '<', '>');
+ if (pos != CHeuristicScanner.NOT_FOUND) {
+ return new Region(pos, offset - pos);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the region enclosing the matching angle brackets.
+ *
+ * @param document a document
+ * @param offset an offset within the document pointing after the opening angle bracket
+ * @return the matching region or {@link NullPointerException} if no match could be found
+ * @throws BadLocationException
+ */
+ private IRegion findClosingAngleBracket(IDocument document, int offset) throws BadLocationException {
+ if (offset < 0) return null;
+ final String contentType= TextUtilities.getContentType(document, ICPartitions.C_PARTITIONING, offset, false);
+ CHeuristicScanner scanner= new CHeuristicScanner(document, ICPartitions.C_PARTITIONING, contentType);
+ if (isTemplateParameterOpenBracket(offset, document, scanner)) {
+ int pos= scanner.findClosingPeer(offset + 1, Math.min(document.getLength(), offset + ANGLE_BRACKETS_SEARCH_BOUND), '<', '>');
+ if (pos != CHeuristicScanner.NOT_FOUND) {
+ return new Region(offset, pos - offset + 1);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if the character at the specified offset is a
+ * less-than sign, rather than an template parameter list open
+ * angle bracket.
+ *
+ * @param document a document
+ * @param offset an offset within the document
+ * @return true if the character at the specified offset is not
+ * a template parameter start bracket
+ * @throws BadLocationException
+ */
+ private boolean isLessThanOperator(IDocument document, int offset) throws BadLocationException {
+ if (offset < 0) return false;
+ final String contentType= TextUtilities.getContentType(document, ICPartitions.C_PARTITIONING, offset, false);
+ CHeuristicScanner scanner= new CHeuristicScanner(document, ICPartitions.C_PARTITIONING, contentType);
+ return !isTemplateParameterOpenBracket(offset, document, scanner);
+ }
+
+ /**
+ * Returns true if the character at the specified offset is a
+ * greater-than sign, rather than an template parameter list close
+ * angle bracket.
+ *
+ * @param document a document
+ * @param offset an offset within the document
+ * @return true if the character at the specified offset is not
+ * a template parameter end bracket
+ * @throws BadLocationException
+ */
+ private boolean isGreaterThanOperator(IDocument document, int offset) throws BadLocationException {
+ if (offset < 0) return false;
+ final String contentType= TextUtilities.getContentType(document, ICPartitions.C_PARTITIONING, offset, false);
+ CHeuristicScanner scanner= new CHeuristicScanner(document, ICPartitions.C_PARTITIONING, contentType);
+ return !isTemplateParameterCloseBracket(offset, document, scanner);
+ }
+
+ /**
+ * Checks if the angular bracket at <code>offset</code> is a template
+ * parameter bracket.
+ *
+ * @param offset the offset of the opening bracket
+ * @param document the document
+ * @param scanner a heuristic scanner on <code>document</code>
+ * @return <code>true</code> if the bracket is part of a template parameter,
+ * <code>false</code> otherwise
+ */
+ private boolean isTemplateParameterOpenBracket(int offset, IDocument document, CHeuristicScanner scanner) {
+ int prevToken= scanner.previousToken(offset - 1, Math.max(0, offset - ANGLE_BRACKETS_SEARCH_BOUND));
+ if (prevToken == Symbols.TokenIDENT) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if the angular bracket at <code>offset</code> is a template
+ * parameter bracket.
+ *
+ * @param offset the offset of the closing bracket
+ * @param document the document
+ * @param scanner a heuristic scanner on <code>document</code>
+ * @return <code>true</code> if the bracket is part of a template parameter,
+ * <code>false</code> otherwise
+ */
+ private boolean isTemplateParameterCloseBracket(int offset, IDocument document, CHeuristicScanner scanner) {
+ if (offset >= document.getLength() - 1)
+ return true;
+ int thisToken= scanner.previousToken(offset, Math.max(0, offset - ANGLE_BRACKETS_SEARCH_BOUND));
+ if (thisToken != Symbols.TokenGREATERTHAN)
+ return false;
+ int prevToken= scanner.previousToken(scanner.getPosition(), Math.max(0, offset - ANGLE_BRACKETS_SEARCH_BOUND));
+ if (prevToken == Symbols.TokenGREATERTHAN)
+ return true;
+ int nextToken= scanner.nextToken(offset + 1, Math.min(document.getLength(), offset + ANGLE_BRACKETS_SEARCH_BOUND));
+
+ switch (nextToken) {
+ case Symbols.TokenGREATERTHAN:
+ case Symbols.TokenCOMMA:
+ case Symbols.TokenSEMICOLON:
+ case Symbols.TokenCLASS:
+ case Symbols.TokenSTRUCT:
+ case Symbols.TokenUNION:
+ return true;
+ }
+ return false;
+ }
+
}

Back to the top