Mercurial > projects > dwt-addons
annotate dwtx/jface/text/rules/FastPartitioner.d @ 150:5cf141e43417
...
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sun, 24 Aug 2008 23:05:26 +0200 |
parents | 000f9136b8f7 |
children | f70d9508c95c |
rev | line source |
---|---|
129 | 1 /******************************************************************************* |
2 * Copyright (c) 2000, 2006 IBM Corporation and others. | |
3 * All rights reserved. This program and the accompanying materials | |
4 * are made available under the terms of the Eclipse Public License v1.0 | |
5 * which accompanies this distribution, and is available at | |
6 * http://www.eclipse.org/legal/epl-v10.html | |
7 * | |
8 * Contributors: | |
9 * IBM Corporation - initial API and implementation | |
10 * Port to the D programming language: | |
11 * Frank Benoit <benoit@tionex.de> | |
12 *******************************************************************************/ | |
13 module dwtx.jface.text.rules.FastPartitioner; | |
14 | |
131 | 15 import dwtx.jface.text.rules.ITokenScanner; // packageimport |
16 import dwtx.jface.text.rules.Token; // packageimport | |
17 import dwtx.jface.text.rules.RuleBasedScanner; // packageimport | |
18 import dwtx.jface.text.rules.EndOfLineRule; // packageimport | |
19 import dwtx.jface.text.rules.WordRule; // packageimport | |
20 import dwtx.jface.text.rules.WhitespaceRule; // packageimport | |
21 import dwtx.jface.text.rules.WordPatternRule; // packageimport | |
22 import dwtx.jface.text.rules.IPredicateRule; // packageimport | |
23 import dwtx.jface.text.rules.DefaultPartitioner; // packageimport | |
24 import dwtx.jface.text.rules.NumberRule; // packageimport | |
25 import dwtx.jface.text.rules.SingleLineRule; // packageimport | |
26 import dwtx.jface.text.rules.PatternRule; // packageimport | |
27 import dwtx.jface.text.rules.IWordDetector; // packageimport | |
28 import dwtx.jface.text.rules.RuleBasedDamagerRepairer; // packageimport | |
29 import dwtx.jface.text.rules.ICharacterScanner; // packageimport | |
30 import dwtx.jface.text.rules.IRule; // packageimport | |
31 import dwtx.jface.text.rules.DefaultDamagerRepairer; // packageimport | |
32 import dwtx.jface.text.rules.IToken; // packageimport | |
33 import dwtx.jface.text.rules.IPartitionTokenScanner; // packageimport | |
34 import dwtx.jface.text.rules.MultiLineRule; // packageimport | |
35 import dwtx.jface.text.rules.RuleBasedPartitioner; // packageimport | |
36 import dwtx.jface.text.rules.RuleBasedPartitionScanner; // packageimport | |
37 import dwtx.jface.text.rules.BufferedRuleBasedScanner; // packageimport | |
38 import dwtx.jface.text.rules.IWhitespaceDetector; // packageimport | |
39 | |
40 | |
129 | 41 import dwt.dwthelper.utils; |
42 | |
43 | |
44 import java.util.ArrayList; | |
45 import java.util.List; | |
46 | |
47 import dwtx.core.runtime.Assert; | |
48 import dwtx.core.runtime.Platform; | |
49 import dwtx.jface.text.BadLocationException; | |
50 import dwtx.jface.text.BadPositionCategoryException; | |
51 import dwtx.jface.text.DefaultPositionUpdater; | |
52 import dwtx.jface.text.DocumentEvent; | |
53 import dwtx.jface.text.DocumentRewriteSession; | |
54 import dwtx.jface.text.IDocument; | |
55 import dwtx.jface.text.IDocumentPartitioner; | |
56 import dwtx.jface.text.IDocumentPartitionerExtension; | |
57 import dwtx.jface.text.IDocumentPartitionerExtension2; | |
58 import dwtx.jface.text.IDocumentPartitionerExtension3; | |
59 import dwtx.jface.text.IRegion; | |
60 import dwtx.jface.text.ITypedRegion; | |
61 import dwtx.jface.text.Position; | |
62 import dwtx.jface.text.Region; | |
63 import dwtx.jface.text.TextUtilities; | |
64 import dwtx.jface.text.TypedPosition; | |
65 import dwtx.jface.text.TypedRegion; | |
66 | |
67 | |
68 | |
69 /** | |
70 * A standard implementation of a document partitioner. It uses an | |
71 * {@link IPartitionTokenScanner} to scan the document and to determine the | |
72 * document's partitioning. The tokens returned by the scanner must return the | |
73 * partition type as their data. The partitioner remembers the document's | |
74 * partitions in the document itself rather than maintaining its own data | |
75 * structure. | |
76 * <p> | |
77 * To reduce array creations in {@link IDocument#getPositions(String)}, the | |
78 * positions get cached. The cache is cleared after updating the positions in | |
79 * {@link #documentChanged2(DocumentEvent)}. Subclasses need to call | |
80 * {@link #clearPositionCache()} after modifying the partitioner's positions. | |
81 * The cached positions may be accessed through {@link #getPositions()}. | |
82 * </p> | |
83 * | |
84 * @see IPartitionTokenScanner | |
85 * @since 3.1 | |
86 */ | |
87 public class FastPartitioner : IDocumentPartitioner, IDocumentPartitionerExtension, IDocumentPartitionerExtension2, IDocumentPartitionerExtension3 { | |
88 | |
89 /** | |
90 * The position category this partitioner uses to store the document's partitioning information. | |
91 */ | |
147 | 92 private static const String CONTENT_TYPES_CATEGORY= "__content_types_category"; //$NON-NLS-1$ |
129 | 93 /** The partitioner's scanner */ |
94 protected final IPartitionTokenScanner fScanner; | |
95 /** The legal content types of this partitioner */ | |
96 protected final String[] fLegalContentTypes; | |
97 /** The partitioner's document */ | |
98 protected IDocument fDocument; | |
99 /** The document length before a document change occurred */ | |
100 protected int fPreviousDocumentLength; | |
101 /** The position updater used to for the default updating of partitions */ | |
102 protected final DefaultPositionUpdater fPositionUpdater; | |
103 /** The offset at which the first changed partition starts */ | |
104 protected int fStartOffset; | |
105 /** The offset at which the last changed partition ends */ | |
106 protected int fEndOffset; | |
107 /**The offset at which a partition has been deleted */ | |
108 protected int fDeleteOffset; | |
109 /** | |
110 * The position category this partitioner uses to store the document's partitioning information. | |
111 */ | |
146 | 112 private const String fPositionCategory; |
129 | 113 /** |
114 * The active document rewrite session. | |
115 */ | |
116 private DocumentRewriteSession fActiveRewriteSession; | |
117 /** | |
118 * Flag indicating whether this partitioner has been initialized. | |
119 */ | |
120 private bool fIsInitialized= false; | |
121 /** | |
122 * The cached positions from our document, so we don't create a new array every time | |
123 * someone requests partition information. | |
124 */ | |
125 private Position[] fCachedPositions= null; | |
126 /** Debug option for cache consistency checking. */ | |
147 | 127 private static const bool CHECK_CACHE_CONSISTENCY= "true".equalsIgnoreCase(Platform.getDebugOption("dwtx.jface.text/debug/FastPartitioner/PositionCache")); //$NON-NLS-1$//$NON-NLS-2$; |
129 | 128 |
129 /** | |
130 * Creates a new partitioner that uses the given scanner and may return | |
131 * partitions of the given legal content types. | |
132 * | |
133 * @param scanner the scanner this partitioner is supposed to use | |
134 * @param legalContentTypes the legal content types of this partitioner | |
135 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
136 public this(IPartitionTokenScanner scanner, String[] legalContentTypes) { |
129 | 137 fScanner= scanner; |
138 fLegalContentTypes= TextUtilities.copy(legalContentTypes); | |
139 fPositionCategory= CONTENT_TYPES_CATEGORY + hashCode(); | |
140 fPositionUpdater= new DefaultPositionUpdater(fPositionCategory); | |
141 } | |
142 | |
143 /* | |
144 * @see dwtx.jface.text.IDocumentPartitionerExtension2#getManagingPositionCategories() | |
145 */ | |
146 public String[] getManagingPositionCategories() { | |
150 | 147 return [ fPositionCategory ]; |
129 | 148 } |
149 | |
150 /* | |
151 * @see dwtx.jface.text.IDocumentPartitioner#connect(dwtx.jface.text.IDocument) | |
152 */ | |
153 public final void connect(IDocument document) { | |
154 connect(document, false); | |
155 } | |
156 | |
157 /** | |
158 * {@inheritDoc} | |
159 * <p> | |
160 * May be extended by subclasses. | |
161 * </p> | |
162 */ | |
163 public void connect(IDocument document, bool delayInitialization) { | |
164 Assert.isNotNull(document); | |
165 Assert.isTrue(!document.containsPositionCategory(fPositionCategory)); | |
166 | |
167 fDocument= document; | |
168 fDocument.addPositionCategory(fPositionCategory); | |
169 | |
170 fIsInitialized= false; | |
171 if (!delayInitialization) | |
172 checkInitialization(); | |
173 } | |
174 | |
175 /** | |
176 * Calls {@link #initialize()} if the receiver is not yet initialized. | |
177 */ | |
178 protected final void checkInitialization() { | |
179 if (!fIsInitialized) | |
180 initialize(); | |
181 } | |
182 | |
183 /** | |
184 * Performs the initial partitioning of the partitioner's document. | |
185 * <p> | |
186 * May be extended by subclasses. | |
187 * </p> | |
188 */ | |
189 protected void initialize() { | |
190 fIsInitialized= true; | |
191 clearPositionCache(); | |
192 fScanner.setRange(fDocument, 0, fDocument.getLength()); | |
193 | |
194 try { | |
195 IToken token= fScanner.nextToken(); | |
196 while (!token.isEOF()) { | |
197 | |
198 String contentType= getTokenContentType(token); | |
199 | |
200 if (isSupportedContentType(contentType)) { | |
201 TypedPosition p= new TypedPosition(fScanner.getTokenOffset(), fScanner.getTokenLength(), contentType); | |
202 fDocument.addPosition(fPositionCategory, p); | |
203 } | |
204 | |
205 token= fScanner.nextToken(); | |
206 } | |
207 } catch (BadLocationException x) { | |
208 // cannot happen as offsets come from scanner | |
209 } catch (BadPositionCategoryException x) { | |
210 // cannot happen if document has been connected before | |
211 } | |
212 } | |
213 | |
214 /** | |
215 * {@inheritDoc} | |
216 * <p> | |
217 * May be extended by subclasses. | |
218 * </p> | |
219 */ | |
220 public void disconnect() { | |
221 | |
222 Assert.isTrue(fDocument.containsPositionCategory(fPositionCategory)); | |
223 | |
224 try { | |
225 fDocument.removePositionCategory(fPositionCategory); | |
226 } catch (BadPositionCategoryException x) { | |
227 // can not happen because of Assert | |
228 } | |
229 } | |
230 | |
231 /** | |
232 * {@inheritDoc} | |
233 * <p> | |
234 * May be extended by subclasses. | |
235 * </p> | |
236 */ | |
237 public void documentAboutToBeChanged(DocumentEvent e) { | |
238 if (fIsInitialized) { | |
239 | |
240 Assert.isTrue(e.getDocument() is fDocument); | |
241 | |
242 fPreviousDocumentLength= e.getDocument().getLength(); | |
243 fStartOffset= -1; | |
244 fEndOffset= -1; | |
245 fDeleteOffset= -1; | |
246 } | |
247 } | |
248 | |
249 /* | |
250 * @see IDocumentPartitioner#documentChanged(DocumentEvent) | |
251 */ | |
252 public final bool documentChanged(DocumentEvent e) { | |
253 if (fIsInitialized) { | |
254 IRegion region= documentChanged2(e); | |
255 return (region !is null); | |
256 } | |
257 return false; | |
258 } | |
259 | |
260 /** | |
261 * Helper method for tracking the minimal region containing all partition changes. | |
262 * If <code>offset</code> is smaller than the remembered offset, <code>offset</code> | |
263 * will from now on be remembered. If <code>offset + length</code> is greater than | |
264 * the remembered end offset, it will be remembered from now on. | |
265 * | |
266 * @param offset the offset | |
267 * @param length the length | |
268 */ | |
269 private void rememberRegion(int offset, int length) { | |
270 // remember start offset | |
271 if (fStartOffset is -1) | |
272 fStartOffset= offset; | |
273 else if (offset < fStartOffset) | |
274 fStartOffset= offset; | |
275 | |
276 // remember end offset | |
277 int endOffset= offset + length; | |
278 if (fEndOffset is -1) | |
279 fEndOffset= endOffset; | |
280 else if (endOffset > fEndOffset) | |
281 fEndOffset= endOffset; | |
282 } | |
283 | |
284 /** | |
285 * Remembers the given offset as the deletion offset. | |
286 * | |
287 * @param offset the offset | |
288 */ | |
289 private void rememberDeletedOffset(int offset) { | |
290 fDeleteOffset= offset; | |
291 } | |
292 | |
293 /** | |
294 * Creates the minimal region containing all partition changes using the | |
295 * remembered offset, end offset, and deletion offset. | |
296 * | |
297 * @return the minimal region containing all the partition changes | |
298 */ | |
299 private IRegion createRegion() { | |
300 if (fDeleteOffset is -1) { | |
301 if (fStartOffset is -1 || fEndOffset is -1) | |
302 return null; | |
303 return new Region(fStartOffset, fEndOffset - fStartOffset); | |
304 } else if (fStartOffset is -1 || fEndOffset is -1) { | |
305 return new Region(fDeleteOffset, 0); | |
306 } else { | |
307 int offset= Math.min(fDeleteOffset, fStartOffset); | |
308 int endOffset= Math.max(fDeleteOffset, fEndOffset); | |
309 return new Region(offset, endOffset - offset); | |
310 } | |
311 } | |
312 | |
313 /** | |
314 * {@inheritDoc} | |
315 * <p> | |
316 * May be extended by subclasses. | |
317 * </p> | |
318 */ | |
319 public IRegion documentChanged2(DocumentEvent e) { | |
320 | |
321 if (!fIsInitialized) | |
322 return null; | |
323 | |
324 try { | |
325 Assert.isTrue(e.getDocument() is fDocument); | |
326 | |
327 Position[] category= getPositions(); | |
328 IRegion line= fDocument.getLineInformationOfOffset(e.getOffset()); | |
329 int reparseStart= line.getOffset(); | |
330 int partitionStart= -1; | |
331 String contentType= null; | |
332 int newLength= e.getText() is null ? 0 : e.getText().length(); | |
333 | |
334 int first= fDocument.computeIndexInCategory(fPositionCategory, reparseStart); | |
335 if (first > 0) { | |
134 | 336 TypedPosition partition= cast(TypedPosition) category[first - 1]; |
129 | 337 if (partition.includes(reparseStart)) { |
338 partitionStart= partition.getOffset(); | |
339 contentType= partition.getType(); | |
340 if (e.getOffset() is partition.getOffset() + partition.getLength()) | |
341 reparseStart= partitionStart; | |
342 -- first; | |
343 } else if (reparseStart is e.getOffset() && reparseStart is partition.getOffset() + partition.getLength()) { | |
344 partitionStart= partition.getOffset(); | |
345 contentType= partition.getType(); | |
346 reparseStart= partitionStart; | |
347 -- first; | |
348 } else { | |
349 partitionStart= partition.getOffset() + partition.getLength(); | |
350 contentType= IDocument.DEFAULT_CONTENT_TYPE; | |
351 } | |
352 } | |
353 | |
354 fPositionUpdater.update(e); | |
355 for (int i= first; i < category.length; i++) { | |
356 Position p= category[i]; | |
357 if (p.isDeleted) { | |
358 rememberDeletedOffset(e.getOffset()); | |
359 break; | |
360 } | |
361 } | |
362 clearPositionCache(); | |
363 category= getPositions(); | |
364 | |
365 fScanner.setPartialRange(fDocument, reparseStart, fDocument.getLength() - reparseStart, contentType, partitionStart); | |
366 | |
367 int behindLastScannedPosition= reparseStart; | |
368 IToken token= fScanner.nextToken(); | |
369 | |
370 while (!token.isEOF()) { | |
371 | |
372 contentType= getTokenContentType(token); | |
373 | |
374 if (!isSupportedContentType(contentType)) { | |
375 token= fScanner.nextToken(); | |
376 continue; | |
377 } | |
378 | |
379 int start= fScanner.getTokenOffset(); | |
380 int length= fScanner.getTokenLength(); | |
381 | |
382 behindLastScannedPosition= start + length; | |
383 int lastScannedPosition= behindLastScannedPosition - 1; | |
384 | |
385 // remove all affected positions | |
386 while (first < category.length) { | |
134 | 387 TypedPosition p= cast(TypedPosition) category[first]; |
129 | 388 if (lastScannedPosition >= p.offset + p.length || |
389 (p.overlapsWith(start, length) && | |
390 (!fDocument.containsPosition(fPositionCategory, start, length) || | |
391 !contentType.equals(p.getType())))) { | |
392 | |
393 rememberRegion(p.offset, p.length); | |
394 fDocument.removePosition(fPositionCategory, p); | |
395 ++ first; | |
396 | |
397 } else | |
398 break; | |
399 } | |
400 | |
401 // if position already exists and we have scanned at least the | |
402 // area covered by the event, we are done | |
403 if (fDocument.containsPosition(fPositionCategory, start, length)) { | |
404 if (lastScannedPosition >= e.getOffset() + newLength) | |
405 return createRegion(); | |
406 ++ first; | |
407 } else { | |
408 // insert the new type position | |
409 try { | |
410 fDocument.addPosition(fPositionCategory, new TypedPosition(start, length, contentType)); | |
411 rememberRegion(start, length); | |
412 } catch (BadPositionCategoryException x) { | |
413 } catch (BadLocationException x) { | |
414 } | |
415 } | |
416 | |
417 token= fScanner.nextToken(); | |
418 } | |
419 | |
420 first= fDocument.computeIndexInCategory(fPositionCategory, behindLastScannedPosition); | |
421 | |
422 clearPositionCache(); | |
423 category= getPositions(); | |
424 TypedPosition p; | |
425 while (first < category.length) { | |
134 | 426 p= cast(TypedPosition) category[first++]; |
129 | 427 fDocument.removePosition(fPositionCategory, p); |
428 rememberRegion(p.offset, p.length); | |
429 } | |
430 | |
431 } catch (BadPositionCategoryException x) { | |
432 // should never happen on connected documents | |
433 } catch (BadLocationException x) { | |
434 } finally { | |
435 clearPositionCache(); | |
436 } | |
437 | |
438 return createRegion(); | |
439 } | |
440 | |
441 /** | |
442 * Returns the position in the partitoner's position category which is | |
443 * close to the given offset. This is, the position has either an offset which | |
444 * is the same as the given offset or an offset which is smaller than the given | |
445 * offset. This method profits from the knowledge that a partitioning is | |
446 * a ordered set of disjoint position. | |
447 * <p> | |
448 * May be extended or replaced by subclasses. | |
449 * </p> | |
450 * @param offset the offset for which to search the closest position | |
451 * @return the closest position in the partitioner's category | |
452 */ | |
453 protected TypedPosition findClosestPosition(int offset) { | |
454 | |
455 try { | |
456 | |
457 int index= fDocument.computeIndexInCategory(fPositionCategory, offset); | |
458 Position[] category= getPositions(); | |
459 | |
460 if (category.length is 0) | |
461 return null; | |
462 | |
463 if (index < category.length) { | |
464 if (offset is category[index].offset) | |
134 | 465 return cast(TypedPosition) category[index]; |
129 | 466 } |
467 | |
468 if (index > 0) | |
469 index--; | |
470 | |
134 | 471 return cast(TypedPosition) category[index]; |
129 | 472 |
473 } catch (BadPositionCategoryException x) { | |
474 } catch (BadLocationException x) { | |
475 } | |
476 | |
477 return null; | |
478 } | |
479 | |
480 | |
481 /** | |
482 * {@inheritDoc} | |
483 * <p> | |
484 * May be replaced or extended by subclasses. | |
485 * </p> | |
486 */ | |
487 public String getContentType(int offset) { | |
488 checkInitialization(); | |
489 | |
490 TypedPosition p= findClosestPosition(offset); | |
491 if (p !is null && p.includes(offset)) | |
492 return p.getType(); | |
493 | |
494 return IDocument.DEFAULT_CONTENT_TYPE; | |
495 } | |
496 | |
497 /** | |
498 * {@inheritDoc} | |
499 * <p> | |
500 * May be replaced or extended by subclasses. | |
501 * </p> | |
502 */ | |
503 public ITypedRegion getPartition(int offset) { | |
504 checkInitialization(); | |
505 | |
506 try { | |
507 | |
508 Position[] category = getPositions(); | |
509 | |
510 if (category is null || category.length is 0) | |
511 return new TypedRegion(0, fDocument.getLength(), IDocument.DEFAULT_CONTENT_TYPE); | |
512 | |
513 int index= fDocument.computeIndexInCategory(fPositionCategory, offset); | |
514 | |
515 if (index < category.length) { | |
516 | |
134 | 517 TypedPosition next= cast(TypedPosition) category[index]; |
129 | 518 |
519 if (offset is next.offset) | |
520 return new TypedRegion(next.getOffset(), next.getLength(), next.getType()); | |
521 | |
522 if (index is 0) | |
523 return new TypedRegion(0, next.offset, IDocument.DEFAULT_CONTENT_TYPE); | |
524 | |
134 | 525 TypedPosition previous= cast(TypedPosition) category[index - 1]; |
129 | 526 if (previous.includes(offset)) |
527 return new TypedRegion(previous.getOffset(), previous.getLength(), previous.getType()); | |
528 | |
529 int endOffset= previous.getOffset() + previous.getLength(); | |
530 return new TypedRegion(endOffset, next.getOffset() - endOffset, IDocument.DEFAULT_CONTENT_TYPE); | |
531 } | |
532 | |
134 | 533 TypedPosition previous= cast(TypedPosition) category[category.length - 1]; |
129 | 534 if (previous.includes(offset)) |
535 return new TypedRegion(previous.getOffset(), previous.getLength(), previous.getType()); | |
536 | |
537 int endOffset= previous.getOffset() + previous.getLength(); | |
538 return new TypedRegion(endOffset, fDocument.getLength() - endOffset, IDocument.DEFAULT_CONTENT_TYPE); | |
539 | |
540 } catch (BadPositionCategoryException x) { | |
541 } catch (BadLocationException x) { | |
542 } | |
543 | |
544 return new TypedRegion(0, fDocument.getLength(), IDocument.DEFAULT_CONTENT_TYPE); | |
545 } | |
546 | |
547 /* | |
548 * @see IDocumentPartitioner#computePartitioning(int, int) | |
549 */ | |
550 public final ITypedRegion[] computePartitioning(int offset, int length) { | |
551 return computePartitioning(offset, length, false); | |
552 } | |
553 | |
554 /** | |
555 * {@inheritDoc} | |
556 * <p> | |
557 * May be replaced or extended by subclasses. | |
558 * </p> | |
559 */ | |
560 public String[] getLegalContentTypes() { | |
561 return TextUtilities.copy(fLegalContentTypes); | |
562 } | |
563 | |
564 /** | |
565 * Returns whether the given type is one of the legal content types. | |
566 * <p> | |
567 * May be extended by subclasses. | |
568 * </p> | |
569 * | |
570 * @param contentType the content type to check | |
571 * @return <code>true</code> if the content type is a legal content type | |
572 */ | |
573 protected bool isSupportedContentType(String contentType) { | |
574 if (contentType !is null) { | |
575 for (int i= 0; i < fLegalContentTypes.length; i++) { | |
576 if (fLegalContentTypes[i].equals(contentType)) | |
577 return true; | |
578 } | |
579 } | |
580 | |
581 return false; | |
582 } | |
583 | |
584 /** | |
585 * Returns a content type encoded in the given token. If the token's | |
586 * data is not <code>null</code> and a string it is assumed that | |
587 * it is the encoded content type. | |
588 * <p> | |
589 * May be replaced or extended by subclasses. | |
590 * </p> | |
591 * | |
592 * @param token the token whose content type is to be determined | |
593 * @return the token's content type | |
594 */ | |
595 protected String getTokenContentType(IToken token) { | |
596 Object data= token.getData(); | |
138 | 597 if ( cast(String)data ) |
134 | 598 return cast(String) data; |
129 | 599 return null; |
600 } | |
601 | |
602 /* zero-length partition support */ | |
603 | |
604 /** | |
605 * {@inheritDoc} | |
606 * <p> | |
607 * May be replaced or extended by subclasses. | |
608 * </p> | |
609 */ | |
610 public String getContentType(int offset, bool preferOpenPartitions) { | |
611 return getPartition(offset, preferOpenPartitions).getType(); | |
612 } | |
613 | |
614 /** | |
615 * {@inheritDoc} | |
616 * <p> | |
617 * May be replaced or extended by subclasses. | |
618 * </p> | |
619 */ | |
620 public ITypedRegion getPartition(int offset, bool preferOpenPartitions) { | |
621 ITypedRegion region= getPartition(offset); | |
622 if (preferOpenPartitions) { | |
623 if (region.getOffset() is offset && !region.getType().equals(IDocument.DEFAULT_CONTENT_TYPE)) { | |
624 if (offset > 0) { | |
625 region= getPartition(offset - 1); | |
626 if (region.getType().equals(IDocument.DEFAULT_CONTENT_TYPE)) | |
627 return region; | |
628 } | |
629 return new TypedRegion(offset, 0, IDocument.DEFAULT_CONTENT_TYPE); | |
630 } | |
631 } | |
632 return region; | |
633 } | |
634 | |
635 /** | |
636 * {@inheritDoc} | |
637 * <p> | |
638 * May be replaced or extended by subclasses. | |
639 * </p> | |
640 */ | |
641 public ITypedRegion[] computePartitioning(int offset, int length, bool includeZeroLengthPartitions) { | |
642 checkInitialization(); | |
643 List list= new ArrayList(); | |
644 | |
645 try { | |
646 | |
647 int endOffset= offset + length; | |
648 | |
649 Position[] category= getPositions(); | |
650 | |
651 TypedPosition previous= null, current= null; | |
652 int start, end, gapOffset; | |
653 Position gap= new Position(0); | |
654 | |
655 int startIndex= getFirstIndexEndingAfterOffset(category, offset); | |
656 int endIndex= getFirstIndexStartingAfterOffset(category, endOffset); | |
657 for (int i= startIndex; i < endIndex; i++) { | |
658 | |
134 | 659 current= cast(TypedPosition) category[i]; |
129 | 660 |
661 gapOffset= (previous !is null) ? previous.getOffset() + previous.getLength() : 0; | |
662 gap.setOffset(gapOffset); | |
663 gap.setLength(current.getOffset() - gapOffset); | |
664 if ((includeZeroLengthPartitions && overlapsOrTouches(gap, offset, length)) || | |
665 (gap.getLength() > 0 && gap.overlapsWith(offset, length))) { | |
666 start= Math.max(offset, gapOffset); | |
667 end= Math.min(endOffset, gap.getOffset() + gap.getLength()); | |
668 list.add(new TypedRegion(start, end - start, IDocument.DEFAULT_CONTENT_TYPE)); | |
669 } | |
670 | |
671 if (current.overlapsWith(offset, length)) { | |
672 start= Math.max(offset, current.getOffset()); | |
673 end= Math.min(endOffset, current.getOffset() + current.getLength()); | |
674 list.add(new TypedRegion(start, end - start, current.getType())); | |
675 } | |
676 | |
677 previous= current; | |
678 } | |
679 | |
680 if (previous !is null) { | |
681 gapOffset= previous.getOffset() + previous.getLength(); | |
682 gap.setOffset(gapOffset); | |
683 gap.setLength(fDocument.getLength() - gapOffset); | |
684 if ((includeZeroLengthPartitions && overlapsOrTouches(gap, offset, length)) || | |
685 (gap.getLength() > 0 && gap.overlapsWith(offset, length))) { | |
686 start= Math.max(offset, gapOffset); | |
687 end= Math.min(endOffset, fDocument.getLength()); | |
688 list.add(new TypedRegion(start, end - start, IDocument.DEFAULT_CONTENT_TYPE)); | |
689 } | |
690 } | |
691 | |
692 if (list.isEmpty()) | |
693 list.add(new TypedRegion(offset, length, IDocument.DEFAULT_CONTENT_TYPE)); | |
694 | |
695 } catch (BadPositionCategoryException ex) { | |
696 // Make sure we clear the cache | |
697 clearPositionCache(); | |
698 } catch (RuntimeException ex) { | |
699 // Make sure we clear the cache | |
700 clearPositionCache(); | |
701 throw ex; | |
702 } | |
703 | |
704 TypedRegion[] result= new TypedRegion[list.size()]; | |
705 list.toArray(result); | |
706 return result; | |
707 } | |
708 | |
709 /** | |
710 * Returns <code>true</code> if the given ranges overlap with or touch each other. | |
711 * | |
712 * @param gap the first range | |
713 * @param offset the offset of the second range | |
714 * @param length the length of the second range | |
715 * @return <code>true</code> if the given ranges overlap with or touch each other | |
716 */ | |
717 private bool overlapsOrTouches(Position gap, int offset, int length) { | |
718 return gap.getOffset() <= offset + length && offset <= gap.getOffset() + gap.getLength(); | |
719 } | |
720 | |
721 /** | |
722 * Returns the index of the first position which ends after the given offset. | |
723 * | |
724 * @param positions the positions in linear order | |
725 * @param offset the offset | |
726 * @return the index of the first position which ends after the offset | |
727 */ | |
728 private int getFirstIndexEndingAfterOffset(Position[] positions, int offset) { | |
729 int i= -1, j= positions.length; | |
730 while (j - i > 1) { | |
731 int k= (i + j) >> 1; | |
732 Position p= positions[k]; | |
733 if (p.getOffset() + p.getLength() > offset) | |
734 j= k; | |
735 else | |
736 i= k; | |
737 } | |
738 return j; | |
739 } | |
740 | |
741 /** | |
742 * Returns the index of the first position which starts at or after the given offset. | |
743 * | |
744 * @param positions the positions in linear order | |
745 * @param offset the offset | |
746 * @return the index of the first position which starts after the offset | |
747 */ | |
748 private int getFirstIndexStartingAfterOffset(Position[] positions, int offset) { | |
749 int i= -1, j= positions.length; | |
750 while (j - i > 1) { | |
751 int k= (i + j) >> 1; | |
752 Position p= positions[k]; | |
753 if (p.getOffset() >= offset) | |
754 j= k; | |
755 else | |
756 i= k; | |
757 } | |
758 return j; | |
759 } | |
760 | |
761 /* | |
762 * @see dwtx.jface.text.IDocumentPartitionerExtension3#startRewriteSession(dwtx.jface.text.DocumentRewriteSession) | |
763 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
764 public void startRewriteSession(DocumentRewriteSession session) { |
129 | 765 if (fActiveRewriteSession !is null) |
766 throw new IllegalStateException(); | |
767 fActiveRewriteSession= session; | |
768 } | |
769 | |
770 /** | |
771 * {@inheritDoc} | |
772 * <p> | |
773 * May be extended by subclasses. | |
774 * </p> | |
775 */ | |
776 public void stopRewriteSession(DocumentRewriteSession session) { | |
777 if (fActiveRewriteSession is session) | |
778 flushRewriteSession(); | |
779 } | |
780 | |
781 /** | |
782 * {@inheritDoc} | |
783 * <p> | |
784 * May be extended by subclasses. | |
785 * </p> | |
786 */ | |
787 public DocumentRewriteSession getActiveRewriteSession() { | |
788 return fActiveRewriteSession; | |
789 } | |
790 | |
791 /** | |
792 * Flushes the active rewrite session. | |
793 */ | |
794 protected final void flushRewriteSession() { | |
795 fActiveRewriteSession= null; | |
796 | |
797 // remove all position belonging to the partitioner position category | |
798 try { | |
799 fDocument.removePositionCategory(fPositionCategory); | |
800 } catch (BadPositionCategoryException x) { | |
801 } | |
802 fDocument.addPositionCategory(fPositionCategory); | |
803 | |
804 fIsInitialized= false; | |
805 } | |
806 | |
807 /** | |
808 * Clears the position cache. Needs to be called whenever the positions have | |
809 * been updated. | |
810 */ | |
811 protected final void clearPositionCache() { | |
812 if (fCachedPositions !is null) { | |
813 fCachedPositions= null; | |
814 } | |
815 } | |
816 | |
817 /** | |
818 * Returns the partitioners positions. | |
819 * | |
820 * @return the partitioners positions | |
821 * @throws BadPositionCategoryException if getting the positions from the | |
822 * document fails | |
823 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
824 protected final Position[] getPositions() { |
129 | 825 if (fCachedPositions is null) { |
826 fCachedPositions= fDocument.getPositions(fPositionCategory); | |
150 | 827 } else if (CHECK_CACHE_CONSISTENCY) { |
129 | 828 Position[] positions= fDocument.getPositions(fPositionCategory); |
829 int len= Math.min(positions.length, fCachedPositions.length); | |
830 for (int i= 0; i < len; i++) { | |
831 if (!positions[i].equals(fCachedPositions[i])) | |
832 System.err.println("FastPartitioner.getPositions(): cached position is not up to date: from document: " + toString(positions[i]) + " in cache: " + toString(fCachedPositions[i])); //$NON-NLS-1$ //$NON-NLS-2$ | |
833 } | |
834 for (int i= len; i < positions.length; i++) | |
835 System.err.println("FastPartitioner.getPositions(): new position in document: " + toString(positions[i])); //$NON-NLS-1$ | |
836 for (int i= len; i < fCachedPositions.length; i++) | |
837 System.err.println("FastPartitioner.getPositions(): stale position in cache: " + toString(fCachedPositions[i])); //$NON-NLS-1$ | |
838 } | |
839 return fCachedPositions; | |
840 } | |
841 | |
842 /** | |
843 * Pretty print a <code>Position</code>. | |
844 * | |
845 * @param position the position to format | |
846 * @return a formatted string | |
847 */ | |
848 private String toString(Position position) { | |
849 return "P[" + position.getOffset() + "+" + position.getLength() + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ | |
850 } | |
851 } |