Lines 9-17
Link Here
|
9 |
* Christian Plesner Hansen (plesner@quenta.org) - initial API and implementation |
9 |
* Christian Plesner Hansen (plesner@quenta.org) - initial API and implementation |
10 |
*******************************************************************************/ |
10 |
*******************************************************************************/ |
11 |
package org.eclipse.jface.text.source; |
11 |
package org.eclipse.jface.text.source; |
12 |
import java.util.HashSet; |
|
|
13 |
import java.util.Set; |
14 |
|
15 |
import org.eclipse.core.runtime.Assert; |
12 |
import org.eclipse.core.runtime.Assert; |
16 |
|
13 |
|
17 |
import org.eclipse.jface.text.BadLocationException; |
14 |
import org.eclipse.jface.text.BadLocationException; |
Lines 97-102
Link Here
|
97 |
this(chars, IDocumentExtension3.DEFAULT_PARTITIONING); |
94 |
this(chars, IDocumentExtension3.DEFAULT_PARTITIONING); |
98 |
} |
95 |
} |
99 |
|
96 |
|
|
|
97 |
/** |
98 |
* @see org.eclipse.jface.text.source.ICharacterPairMatcherExtension#isMatchedChar(char) |
99 |
* @since 3.8 |
100 |
*/ |
101 |
public boolean isMatchedChar(char ch) { |
102 |
return fPairs.contains(ch); |
103 |
} |
104 |
|
100 |
/* @see ICharacterPairMatcher#match(IDocument, int) */ |
105 |
/* @see ICharacterPairMatcher#match(IDocument, int) */ |
101 |
public IRegion match(IDocument doc, int offset) { |
106 |
public IRegion match(IDocument doc, int offset) { |
102 |
if (doc == null || offset < 0 || offset > doc.getLength()) return null; |
107 |
if (doc == null || offset < 0 || offset > doc.getLength()) return null; |
Lines 132-169
Link Here
|
132 |
if (document == null || offset < 0 || offset > document.getLength()) |
137 |
if (document == null || offset < 0 || offset > document.getLength()) |
133 |
return null; |
138 |
return null; |
134 |
|
139 |
|
135 |
int start; |
140 |
//maybe a bracket is selected |
136 |
int end; |
141 |
IRegion region= match(document, offset, length); |
137 |
if (length >= 0) { |
142 |
fAnchor= ICharacterPairMatcher.LEFT; //always set the anchor to LEFT |
138 |
start= offset; |
143 |
if (region != null) { |
139 |
end= offset + length; |
144 |
return region; |
140 |
} else { |
|
|
141 |
end= offset; |
142 |
start= offset + length; |
143 |
} |
145 |
} |
144 |
|
146 |
|
145 |
int sourceCaretOffset= offset + length; |
147 |
//bracket is not selected |
146 |
int adjustment= getOffsetAdjustment(document, sourceCaretOffset, length); |
|
|
147 |
sourceCaretOffset+= adjustment; |
148 |
|
149 |
try { |
148 |
try { |
150 |
for (int offset1= sourceCaretOffset; offset1 >= 0; offset1--) { |
149 |
final String partition1= TextUtilities.getContentType(document, fPartitioning, offset, false); |
151 |
char prevChar= document.getChar(Math.max(offset1 - 1, 0)); |
150 |
final DocumentPartitionAccessor partDoc= new DocumentPartitionAccessor(document, fPartitioning, partition1); |
152 |
if (fPairs.contains(prevChar) && fPairs.isStartCharacter(prevChar)) { |
151 |
return findEnclosingPeers(document, partDoc, offset, length, 0, document.getLength()); |
153 |
IRegion match= performMatch(document, offset1); |
|
|
154 |
if (match != null) { |
155 |
int matchOffset= match.getOffset(); |
156 |
int matchLength= match.getLength(); |
157 |
if ((matchOffset <= start) && (matchOffset + matchLength > start) && (matchOffset < end) && (matchOffset + matchLength >= end)) { |
158 |
return match; |
159 |
} |
160 |
} |
161 |
} |
162 |
} |
163 |
} catch (BadLocationException ble) { |
152 |
} catch (BadLocationException ble) { |
|
|
153 |
fAnchor= -1; |
164 |
return null; |
154 |
return null; |
165 |
} |
155 |
} |
166 |
return null; |
156 |
} |
|
|
157 |
|
158 |
/** |
159 |
* @see org.eclipse.jface.text.source.ICharacterPairMatcherExtension#isRecomputationOfEnclosingPairRequired(org.eclipse.jface.text.IDocument, |
160 |
* org.eclipse.jface.text.IRegion, org.eclipse.jface.text.IRegion) |
161 |
* @since 3.8 |
162 |
*/ |
163 |
public boolean isRecomputationOfEnclosingPairRequired(IDocument document, IRegion currentSelection, IRegion previousSelection) { |
164 |
int previousCaretOffset= previousSelection.getOffset() + previousSelection.getLength(); |
165 |
int currentCaretOffset= currentSelection.getOffset() + currentSelection.getLength(); |
166 |
|
167 |
try { |
168 |
String prevContentType= TextUtilities.getContentType(document, fPartitioning, previousCaretOffset, false); |
169 |
String currContentType= TextUtilities.getContentType(document, fPartitioning, currentCaretOffset, false); |
170 |
|
171 |
if (!prevContentType.equals(currContentType)) |
172 |
return true; |
173 |
|
174 |
int start; |
175 |
int end; |
176 |
if (currentCaretOffset > previousCaretOffset) { |
177 |
start= previousCaretOffset; |
178 |
end= currentCaretOffset; |
179 |
} else { |
180 |
start= currentCaretOffset; |
181 |
end= previousCaretOffset; |
182 |
} |
183 |
for (int i= start; i < end; i++) { |
184 |
if (isMatchedChar(document.getChar(i))) { |
185 |
return true; |
186 |
} |
187 |
} |
188 |
} catch (BadLocationException e) { |
189 |
//do nothing |
190 |
} |
191 |
return false; |
167 |
} |
192 |
} |
168 |
|
193 |
|
169 |
/** |
194 |
/** |
Lines 261-266
Link Here
|
261 |
return -1; |
286 |
return -1; |
262 |
} |
287 |
} |
263 |
|
288 |
|
|
|
289 |
/* |
290 |
* Performs the actual work of finding enclosing peer characters for #findEnclosingPeerCharacters(IDocument, int, int). |
291 |
*/ |
292 |
private IRegion findEnclosingPeers(IDocument document, DocumentPartitionAccessor doc, int offset, int length, int lowerBoundary, int upperBoundary) throws BadLocationException { |
293 |
char[] pairs= fPairs.fPairs; |
294 |
|
295 |
int start; |
296 |
int end; |
297 |
if (length >= 0) { |
298 |
start= offset; |
299 |
end= offset + length; |
300 |
} else { |
301 |
end= offset; |
302 |
start= offset + length; |
303 |
} |
304 |
|
305 |
boolean lowerFound= false; |
306 |
boolean upperFound= false; |
307 |
int[][] counts= new int[pairs.length][2]; |
308 |
int pos1= doc.getNextPosition(start, false); |
309 |
int pos2= start; |
310 |
|
311 |
while ((pos1 >= lowerBoundary && !lowerFound) || (pos2 < upperBoundary && !upperFound)) { |
312 |
for (int i= 0; i < counts.length; i++) { |
313 |
counts[i][0]= counts[i][1]= 0; |
314 |
} |
315 |
|
316 |
outer1: while (pos1 >= lowerBoundary && !lowerFound) { |
317 |
final char c= doc.getChar(pos1); |
318 |
int i= getCharacterType(c, document, pos1); |
319 |
if (i != -1 && doc.inPartition(pos1)) { |
320 |
if (i % 2 == 0) { |
321 |
counts[i / 2][0]--; //start |
322 |
} else { |
323 |
counts[i / 2][0]++; //end |
324 |
} |
325 |
for (int j= 0; j < counts.length; j++) { |
326 |
if (counts[j][0] == -1) { |
327 |
lowerFound= true; |
328 |
break outer1; |
329 |
} |
330 |
} |
331 |
} |
332 |
pos1= doc.getNextPosition(pos1, false); |
333 |
} |
334 |
|
335 |
outer2: while (pos2 < upperBoundary && !upperFound) { |
336 |
final char c= doc.getChar(pos2); |
337 |
int i= getCharacterType(c, document, pos2); |
338 |
if (i != -1 && doc.inPartition(pos2)) { |
339 |
if (i % 2 == 0) { |
340 |
counts[i / 2][1]++; //start |
341 |
} else { |
342 |
counts[i / 2][1]--; //end |
343 |
} |
344 |
for (int j= 0; j < counts.length; j++) { |
345 |
if (counts[j][1] == -1 && counts[j][0] == -1) { |
346 |
upperFound= true; |
347 |
break outer2; |
348 |
} |
349 |
} |
350 |
} |
351 |
pos2= doc.getNextPosition(pos2, true); |
352 |
} |
353 |
|
354 |
if (pos1 > start || pos2 < end) { |
355 |
//match inside selection => discard |
356 |
pos1= doc.getNextPosition(pos1, false); |
357 |
pos2= doc.getNextPosition(pos2, true); |
358 |
lowerFound= false; |
359 |
upperFound= false; |
360 |
} |
361 |
} |
362 |
pos2++; |
363 |
if (pos1 < lowerBoundary || pos2 > upperBoundary) |
364 |
return null; |
365 |
return new Region(pos1, pos2 - pos1); |
366 |
} |
367 |
|
368 |
/** |
369 |
* Determines whether the given character is a start character or an end character or none of |
370 |
* these. |
371 |
* |
372 |
* <p> |
373 |
* Clients can override this method to handle characters which may have special meaning in some |
374 |
* situations. E.g. In Java '<' is used as an angular bracket and as well as less-than operator. |
375 |
* </p> |
376 |
* |
377 |
* @param ch the character |
378 |
* @param document the document |
379 |
* @param offset the offset in document |
380 |
* @return 0 if start character, 1 if end character and -1 if neither a start or an end character |
381 |
* @since 3.8 |
382 |
*/ |
383 |
protected int getCharacterType(char ch, IDocument document, int offset) { |
384 |
char[] pairs= fPairs.fPairs; |
385 |
for (int i= 0; i < pairs.length; i++) { |
386 |
if (pairs[i] == ch) { |
387 |
return i; |
388 |
} |
389 |
} |
390 |
return -1; |
391 |
} |
392 |
|
264 |
/* @see ICharacterPairMatcher#getAnchor() */ |
393 |
/* @see ICharacterPairMatcher#getAnchor() */ |
265 |
public int getAnchor() { |
394 |
public int getAnchor() { |
266 |
return fAnchor; |
395 |
return fAnchor; |
Lines 285-290
Link Here
|
285 |
private final IDocument fDocument; |
414 |
private final IDocument fDocument; |
286 |
private final String fPartitioning, fPartition; |
415 |
private final String fPartitioning, fPartition; |
287 |
private ITypedRegion fCachedPartition; |
416 |
private ITypedRegion fCachedPartition; |
|
|
417 |
private int fLength; |
288 |
|
418 |
|
289 |
/** |
419 |
/** |
290 |
* Creates a new partitioned document for the specified document. |
420 |
* Creates a new partitioned document for the specified document. |
Lines 298-303
Link Here
|
298 |
fDocument= doc; |
428 |
fDocument= doc; |
299 |
fPartitioning= partitioning; |
429 |
fPartitioning= partitioning; |
300 |
fPartition= partition; |
430 |
fPartition= partition; |
|
|
431 |
fLength= doc.getLength(); |
301 |
} |
432 |
} |
302 |
|
433 |
|
303 |
/** |
434 |
/** |
Lines 338-344
Link Here
|
338 |
} |
469 |
} |
339 |
|
470 |
|
340 |
/** |
471 |
/** |
341 |
* Returns the next position to query in the search. The position |
472 |
* Returns the next position to query in the search. The position |
342 |
* is not guaranteed to be in this document's partition. |
473 |
* is not guaranteed to be in this document's partition. |
343 |
* |
474 |
* |
344 |
* @param pos an offset within the document |
475 |
* @param pos an offset within the document |
Lines 347-354
Link Here
|
347 |
*/ |
478 |
*/ |
348 |
public int getNextPosition(int pos, boolean searchForward) { |
479 |
public int getNextPosition(int pos, boolean searchForward) { |
349 |
final ITypedRegion partition= getPartition(pos); |
480 |
final ITypedRegion partition= getPartition(pos); |
350 |
if (partition == null) return simpleIncrement(pos, searchForward); |
481 |
if (partition == null || fPartition.equals(partition.getType())) |
351 |
if (fPartition.equals(partition.getType())) |
|
|
352 |
return simpleIncrement(pos, searchForward); |
482 |
return simpleIncrement(pos, searchForward); |
353 |
if (searchForward) { |
483 |
if (searchForward) { |
354 |
int end= partition.getOffset() + partition.getLength(); |
484 |
int end= partition.getOffset() + partition.getLength(); |
Lines 376-382
Link Here
|
376 |
*/ |
506 |
*/ |
377 |
private ITypedRegion getPartition(int pos) { |
507 |
private ITypedRegion getPartition(int pos) { |
378 |
if (fCachedPartition == null || !contains(fCachedPartition, pos)) { |
508 |
if (fCachedPartition == null || !contains(fCachedPartition, pos)) { |
379 |
Assert.isTrue(pos >= 0 && pos <= fDocument.getLength()); |
509 |
Assert.isTrue(pos >= 0 && pos <= fLength); |
380 |
try { |
510 |
try { |
381 |
fCachedPartition= TextUtilities.getPartition(fDocument, fPartitioning, pos, false); |
511 |
fCachedPartition= TextUtilities.getPartition(fDocument, fPartitioning, pos, false); |
382 |
} catch (BadLocationException e) { |
512 |
} catch (BadLocationException e) { |
Lines 405-432
Link Here
|
405 |
} |
535 |
} |
406 |
|
536 |
|
407 |
/** |
537 |
/** |
408 |
* Returns true if the specified character pair occurs in one |
538 |
* Returns true if the specified character occurs in one of the character pairs. |
409 |
* of the character pairs. |
539 |
* |
410 |
* |
|
|
411 |
* @param c a character |
540 |
* @param c a character |
412 |
* @return true exactly if the character occurs in one of the pairs |
541 |
* @return true exactly if the character occurs in one of the pairs |
413 |
*/ |
542 |
*/ |
414 |
public boolean contains(char c) { |
543 |
public boolean contains(char c) { |
415 |
return getAllCharacters().contains(new Character(c)); |
544 |
char[] pairs= fPairs; |
416 |
} |
545 |
for (int i= 0, n= pairs.length; i < n; i++) { |
417 |
|
546 |
if (c == pairs[i]) |
418 |
private Set/*<Character>*/ fCharsCache= null; |
547 |
return true; |
419 |
/** |
|
|
420 |
* @return A set containing all characters occurring in character pairs. |
421 |
*/ |
422 |
private Set/*<Character>*/ getAllCharacters() { |
423 |
if (fCharsCache == null) { |
424 |
Set/*<Character>*/ set= new HashSet/*<Character>*/(); |
425 |
for (int i= 0; i < fPairs.length; i++) |
426 |
set.add(new Character(fPairs[i])); |
427 |
fCharsCache= set; |
428 |
} |
548 |
} |
429 |
return fCharsCache; |
549 |
return false; |
430 |
} |
550 |
} |
431 |
|
551 |
|
432 |
/** |
552 |
/** |