mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-07-23 17:05:26 +02:00
Fix for 110222: Improve pair matcher for angle brackets
This commit is contained in:
parent
ecf9c5833d
commit
4c7161361e
3 changed files with 309 additions and 8 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,221 @@
|
|||
*******************************************************************************/
|
||||
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.core.dom.ILinkage;
|
||||
import org.eclipse.cdt.core.model.ILanguage;
|
||||
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()
|
||||
*/
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure this bracket matcher for the given language.
|
||||
* @param language
|
||||
*/
|
||||
public void configure(ILanguage language) {
|
||||
fMatchAngularBrackets= language != null && language.getLinkageID() == ILinkage.CPP_LINKAGE_ID;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue