diff options
author | Deepak Azad | 2012-02-26 08:17:36 +0000 |
---|---|---|
committer | Dani Megert | 2012-02-26 08:17:36 +0000 |
commit | 1942dbddd460925c0dd3cbed02bad82b548af896 (patch) | |
tree | 5259505fe4170d94bc068e75106dca5153a41440 | |
parent | db449c047ea409b35e73b1cf7094d38610826800 (diff) | |
download | eclipse.platform.text-1942dbddd460925c0dd3cbed02bad82b548af896.tar.gz eclipse.platform.text-1942dbddd460925c0dd3cbed02bad82b548af896.tar.xz eclipse.platform.text-1942dbddd460925c0dd3cbed02bad82b548af896.zip |
Fixed bug 40580: [navigation] Matching brace (Ctrl+Shift+P) should workv20120226-0817
anywhere in the file
6 files changed, 252 insertions, 53 deletions
diff --git a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/AbstractPairMatcherTest.java b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/AbstractPairMatcherTest.java index 474363f2db1..00166bc67cc 100644 --- a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/AbstractPairMatcherTest.java +++ b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/AbstractPairMatcherTest.java @@ -85,10 +85,21 @@ public abstract class AbstractPairMatcherTest extends TestCase { performMatch(matcher, "[% ]#"); performMatch(matcher, "{% }#"); + performMatch(matcher, "#( %)%"); + performMatch(matcher, "#[ %]%"); + performMatch(matcher, "#{ %}%"); + performMatch(matcher, "%(% )#"); + performMatch(matcher, "%[% ]#"); + performMatch(matcher, "%{% }#"); + performMatch(matcher, "#( % )#"); performMatch(matcher, "#[ % ]#"); performMatch(matcher, "#{ % }#"); + performMatch(matcher, "#( % % )#"); + performMatch(matcher, "#[ % % ]#"); + performMatch(matcher, "#{ % % }#"); + matcher.dispose(); } @@ -122,9 +133,17 @@ public abstract class AbstractPairMatcherTest extends TestCase { performMatch(matcher, "(%())#"); performMatch(matcher, "((%)#)"); + performMatch(matcher, "%(%)#"); + performMatch(matcher, "#(()%)%"); + performMatch(matcher, "%(%())#"); + performMatch(matcher, "(%(%)#)"); + performMatch(matcher, "#(%)#"); performMatch(matcher, "#(%())#"); + performMatch(matcher, "#(% %)#"); + performMatch(matcher, "#(% %())#"); + matcher.dispose(); } @@ -167,6 +186,13 @@ public abstract class AbstractPairMatcherTest extends TestCase { performMatch(matcher, "|c #( c| ) ( |c % )# c|"); performMatch(matcher, "#( % |a ) a| |b ) b| |c ) c| )#"); + performMatch(matcher, "#( % % |a a| )#"); + performMatch(matcher, "|b #( % % )# b|"); + performMatch(matcher, "|c #( % % c| ) ( |c )# c|"); + performMatch(matcher, "|c #( c| ) ( |c % % )# c|"); + performMatch(matcher, "#( % % |a ) a| |b ) b| |c ) c| )#"); +// performMatch(matcher, " #( |c ( c| % % |c ) c| )#"); + matcher.dispose(); } @@ -259,7 +285,7 @@ public abstract class AbstractPairMatcherTest extends TestCase { */ protected void performReaderTest(String testString, int expectedPos, int expectedMatch, String expectedString) { TestCase t0= createTestCase(testString); - assertEquals(expectedPos, t0.fPos); + assertEquals(expectedPos, t0.fPos1); assertEquals(expectedMatch, t0.fMatch2); assertEquals(expectedString, t0.fString); } @@ -275,12 +301,22 @@ public abstract class AbstractPairMatcherTest extends TestCase { matcher.clear(); final IRegion region; - if (test.isEnclosingTestCase()) { + if (test.isSelectionTestCase()) { assertTrue((matcher instanceof ICharacterPairMatcherExtension)); ICharacterPairMatcherExtension matcherExtension= (ICharacterPairMatcherExtension)matcher; - region= matcherExtension.findEnclosingPeerCharacters(test.getDocument(), test.fPos); + if (test.isEnclosingTestCase()) { + region= matcherExtension.findEnclosingPeerCharacters(test.getDocument(), test.fPos1, test.fPos2 - test.fPos1); + } else { + region= matcherExtension.match(test.getDocument(), test.fPos1, test.fPos2 - test.fPos1); + } } else { - region= matcher.match(test.getDocument(), test.fPos); + if (test.isEnclosingTestCase()) { + assertTrue((matcher instanceof ICharacterPairMatcherExtension)); + ICharacterPairMatcherExtension matcherExtension= (ICharacterPairMatcherExtension)matcher; + region= matcherExtension.findEnclosingPeerCharacters(test.getDocument(), test.fPos1, test.fPos2 - test.fPos1); + } else { + region= matcher.match(test.getDocument(), test.fPos1); + } } if (test.fMatch2 == -1) { @@ -289,11 +325,13 @@ public abstract class AbstractPairMatcherTest extends TestCase { assertNull(region); } else { assertNotNull(region); - final boolean isForward= test.isEnclosingTestCase() ? false : test.fPos > test.fMatch2; + final boolean isForward= test.isEnclosingTestCase() ? false : test.fPos1 > test.fMatch2; assertEquals(isForward, matcher.getAnchor() == ICharacterPairMatcher.RIGHT); final int offset= (isForward || test.isEnclosingTestCase()) ? test.getOffset() : test.getOffset() - 1; - final int length= ((isForward && !fCaretInsideMatchedPair) || test.isEnclosingTestCase()) ? test.getLength() : test.getLength() + 1; + final int length= ((isForward && (!fCaretInsideMatchedPair || test.isSelectionTestCase())) || test.isEnclosingTestCase()) + ? test.getLength() + : test.getLength() + 1; assertEquals(length, region.getLength()); assertEquals(offset, region.getOffset()); } @@ -313,49 +351,77 @@ public abstract class AbstractPairMatcherTest extends TestCase { * @return the created test case */ public TestCase createTestCase(String str) { - int pos= str.indexOf("%"); - assertFalse(pos == -1); + int pos1= str.indexOf("%"); + assertFalse(pos1 == -1); + int pos2= str.lastIndexOf("%"); + boolean selectionTest= pos1 != pos2; + int match1= str.indexOf("#"); int match2= str.lastIndexOf("#"); boolean enclosingTest= match1 != match2; - if (fCaretInsideMatchedPair) { - if (pos - 1 >= 0) { - char ch= str.charAt(pos - 1); + + if (!selectionTest && fCaretInsideMatchedPair) { + if (pos1 - 1 >= 0) { + char ch= str.charAt(pos1 - 1); if ("()[]{}<>".indexOf(ch) % 2 == 1) { - pos-= 1; + pos1-= 1; } } } // account for the length of marker characters - if (!enclosingTest) { - if (match1 != -1 && match1 < pos) - pos-= 1; - if (pos < match1) - match1-= 1; + if (selectionTest) { + if (!enclosingTest) { + assertTrue(pos2 - pos1 == 2); + if (match1 != -1 && match1 < pos1) { + pos1-= 1; + pos2-= 2; + } + if (pos1 < match1) { + pos2-= 1; + match1-= 2; + } + } else { + pos1-= 1; + pos2-= 2; + match2-= 3; + } } else { - pos-= 1; - match2-= 2; + if (!enclosingTest) { + if (match1 != -1 && match1 < pos1) + pos1-= 1; + if (pos1 < match1) + match1-= 1; + } else { + pos1-= 1; + match2-= 2; + } + pos2= pos1; } final String stripped= str.replaceAll("%", "").replaceAll("#", ""); - return enclosingTest ? new TestCase(stripped, pos, match1, match2) : new TestCase(stripped, pos, match1); + + if (enclosingTest) + return new TestCase(stripped, pos1, pos2, match1, match2); + else { + if (selectionTest) + return new TestCase(stripped, pos1, pos2, pos2, match1); + else + return new TestCase(stripped, pos1, pos2, pos1, match1); + } } private class TestCase { public final String fString; - public final int fPos, fMatch1, fMatch2; + public final int fPos1, fPos2, fMatch1, fMatch2; - public TestCase(String string, int pos, int match) { - this(string, pos, pos, match); - } - - public TestCase(String string, int pos, int match1, int match2) { + public TestCase(String string, int pos1, int pos2, int match1, int match2) { fString= string; - fPos= pos; + fPos1= pos1; + fPos2= pos2; fMatch1= match1; fMatch2= match2; } @@ -375,7 +441,11 @@ public abstract class AbstractPairMatcherTest extends TestCase { } public boolean isEnclosingTestCase() { - return fPos != fMatch1; + return fPos1 != fMatch1 && fPos2 != fMatch1; + } + + public boolean isSelectionTestCase() { + return fPos1 != fPos2; } } diff --git a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/JFaceTextTestSuite.java b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/JFaceTextTestSuite.java index 40f17217f61..e8dae5280fb 100644 --- a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/JFaceTextTestSuite.java +++ b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/JFaceTextTestSuite.java @@ -34,6 +34,7 @@ public class JFaceTextTestSuite extends TestSuite { suite.addTest(RulesTestSuite.suite()); suite.addTest(ReconcilerTestSuite.suite()); suite.addTest(DefaultPairMatcherTest.suite()); + suite.addTest(DefaultPairMatcherTest2.suite()); return suite; } 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 64bd558085b..1327c8a0340 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 @@ -108,21 +108,53 @@ public class DefaultCharacterPairMatcher implements ICharacterPairMatcher, IChar } /** - * @see org.eclipse.jface.text.source.ICharacterPairMatcherExtension#findEnclosingPeerCharacters(org.eclipse.jface.text.IDocument, int) + * @see org.eclipse.jface.text.source.ICharacterPairMatcherExtension#match(org.eclipse.jface.text.IDocument, + * int, int) * @since 3.8 */ - public IRegion findEnclosingPeerCharacters(IDocument document, int offset) { + public IRegion match(IDocument document, int offset, int length) { + if (document == null || offset < 0 || offset > document.getLength() || Math.abs(length) > 1) + return null; + + int sourceCaretOffset= offset + length; + int adjustment= getOffsetAdjustment(document, sourceCaretOffset, length); + sourceCaretOffset+= adjustment; + + return match(document, sourceCaretOffset); + } + + /** + * @see org.eclipse.jface.text.source.ICharacterPairMatcherExtension#findEnclosingPeerCharacters(org.eclipse.jface.text.IDocument, + * int, int) + * @since 3.8 + */ + public IRegion findEnclosingPeerCharacters(IDocument document, int offset, int length) { 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; + } + + int sourceCaretOffset= offset + length; + int adjustment= getOffsetAdjustment(document, sourceCaretOffset, length); + sourceCaretOffset+= adjustment; + try { - for (int offset1= offset; offset1 >= 0; offset1--) { + 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 <= offset) && (matchOffset + matchLength > offset)) { + if ((matchOffset <= start) && (matchOffset + matchLength > start) && (matchOffset < end) && (matchOffset + matchLength >= end)) { return match; } } @@ -134,6 +166,36 @@ public class DefaultCharacterPairMatcher implements ICharacterPairMatcher, IChar return null; } + /** + * Computes the adjustment in the start offset for the purpose of finding a matching peer. The + * adjustment is non-zero only when the selection length is one and the selection covers a + * character matched by the matcher. + * + * @param document the document to work on + * @param offset the start offset + * @param length the selection length + * @return the start offset adjustment + * @since 3.8 + */ + private int getOffsetAdjustment(IDocument document, int offset, int length) { + if (length == 0 || Math.abs(length) > 1) + return 0; + try { + if (length < 0) { + if (fPairs.isStartCharacter(document.getChar(offset))) { + return 1; + } + } else { + if (fCaretInsideMatchedPair && fPairs.isEndCharacter(document.getChar(offset - 1))) { + return -1; + } + } + } catch (BadLocationException e) { + //do nothing + } + return 0; + } + /* * Performs the actual work of matching for #match(IDocument, int). */ @@ -385,8 +447,8 @@ public class DefaultCharacterPairMatcher implements ICharacterPairMatcher, IChar } /** - * Returns true of the specified character is a start character. - * + * Returns true if the specified character is a start character. + * * @param c a character * @return true exactly if the character is a start character */ @@ -395,8 +457,19 @@ public class DefaultCharacterPairMatcher implements ICharacterPairMatcher, IChar } /** + * Returns true if the specified character is an end character. + * + * @param c a character + * @return true exactly if the character is an end character + * @since 3.8 + */ + public boolean isEndCharacter(char c) { + return this.isOpeningCharacter(c, false); + } + + /** * Returns the matching character for the specified character. - * + * * @param c a character occurring in a character pair * @return the matching character */ diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ICharacterPairMatcher.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ICharacterPairMatcher.java index 6a7e8d9202a..5e09b636e29 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ICharacterPairMatcher.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ICharacterPairMatcher.java @@ -28,7 +28,8 @@ import org.eclipse.jface.text.IRegion; * exists: * <ul> * <li>{@link org.eclipse.jface.text.source.ICharacterPairMatcherExtension} since version 3.8 - * introducing the concept of enclosing peer characters at a caret offset.</li> + * introducing the concept of matching peer character and enclosing peer characters for a given + * selection.</li> * </ul> * </p> * <p> @@ -66,13 +67,18 @@ public interface ICharacterPairMatcher { /** * Starting at the given offset, the matcher chooses a character close to this offset. The * matcher then searches for the matching peer character of the chosen character and if it finds - * one, returns the minimal region of the document that contains both characters. It returns - * <code>null</code> if there is no peer character. + * one, returns the minimal region of the document that contains both characters. + * + * <p> + * Since version 3.8 the recommended way for finding matching peers is to use + * {@link org.eclipse.jface.text.source.ICharacterPairMatcherExtension#match(IDocument, int, int)} + * . + * </p> * * @param document the document to work on * @param offset the start offset - * @return the minimal region containing the peer characters and <code>null</code> if there is - * no peer character. + * @return the minimal region containing the peer characters or <code>null</code> if there is no + * peer character. */ IRegion match(IDocument document, int offset); diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ICharacterPairMatcherExtension.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ICharacterPairMatcherExtension.java index 1c03e232317..32ceac7fe9d 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ICharacterPairMatcherExtension.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ICharacterPairMatcherExtension.java @@ -16,8 +16,8 @@ import org.eclipse.jface.text.IRegion; /** * Extension interface {@link org.eclipse.jface.text.source.ICharacterPairMatcher}. * <p> - * Extends the character pair matcher with the concept of enclosing peer characters at a caret - * offset. + * Extends the character pair matcher with the concept of matching peer character and enclosing peer + * characters for a given selection. * * @see org.eclipse.jface.text.source.ICharacterPairMatcher * @since 3.8 @@ -25,13 +25,30 @@ import org.eclipse.jface.text.IRegion; public interface ICharacterPairMatcherExtension { /** - * Starting at the given offset, the matcher searches for a pair of enclosing peer characters + * Starting with the selected character or a character close to the given <code>offset</code>, + * the matcher searches for the matching peer character and if it finds one, returns the minimal + * region of the document that contains both characters. + * + * @param document the document to work on + * @param offset the start offset + * @param length the selection length. The length can be negative indicating right-to-left + * selection. + * @return the minimal region containing the peer characters or <code>null</code> if there is no + * peer character. + */ + IRegion match(IDocument document, int offset, int length); + + + /** + * Starting at the given selection, the matcher searches for a pair of enclosing peer characters * and if it finds one, returns the minimal region of the document that contains the pair. * * @param document the document to work on * @param offset the start offset + * @param length the selection length. The length can be negative indicating right-to-left + * selection. * @return the minimal region containing the peer characters or <code>null</code> if there is no * enclosing pair */ - IRegion findEnclosingPeerCharacters(IDocument document, int offset); + IRegion findEnclosingPeerCharacters(IDocument document, int offset, int length); } 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 783bd0699e7..f32f1a0b7fc 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 @@ -232,6 +232,31 @@ public final class MatchingCharacterPainter implements IPainter, PaintListener { } } + /** + * Returns the signed current selection. The length will be negative if the resulting selection + * is right-to-left. + * <p> + * The selection offset is model based. + * </p> + * + * @param sourceViewer the source viewer + * @return a region denoting the current signed selection, for a resulting RtoL selections + * length is < 0 + * @since 3.8 + */ + private static final IRegion getSignedSelection(ISourceViewer sourceViewer) { + Point viewerSelection= sourceViewer.getSelectedRange(); + + StyledText text= sourceViewer.getTextWidget(); + Point selection= text.getSelectionRange(); + if (text.getCaretOffset() == selection.x) { + viewerSelection.x= viewerSelection.x + viewerSelection.y; + viewerSelection.y= -viewerSelection.y; + } + + return new Region(viewerSelection.x, viewerSelection.y); + } + /* * @see org.eclipse.jface.text.IPainter#paint(int) */ @@ -243,17 +268,24 @@ public final class MatchingCharacterPainter implements IPainter, PaintListener { return; } - Point selection= fSourceViewer.getSelectedRange(); - if (selection.y > 0) { - deactivate(true); - return; - } + IRegion selection= getSignedSelection(fSourceViewer); + IRegion pair; + boolean characterPresentAtCaretLocation; - IRegion pair= fMatcher.match(document, selection.x); - boolean characterPresentAtCaretLocation= (pair != null); - if (pair == null && fHighlightEnclosingPeerCharcters && fMatcher instanceof ICharacterPairMatcherExtension) { + if (fMatcher instanceof ICharacterPairMatcherExtension) { ICharacterPairMatcherExtension matcher= (ICharacterPairMatcherExtension)fMatcher; - pair= matcher.findEnclosingPeerCharacters(document, selection.x); + pair= matcher.match(document, selection.getOffset(), selection.getLength()); + characterPresentAtCaretLocation= (pair != null); + if (pair == null && fHighlightEnclosingPeerCharcters) { + pair= matcher.findEnclosingPeerCharacters(document, selection.getOffset(), selection.getLength()); + } + } else { + if (Math.abs(selection.getLength()) > 0) { + deactivate(true); + return; + } + pair= fMatcher.match(document, selection.getOffset()); + characterPresentAtCaretLocation= (pair != null); } if (pair == null) { |