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