Skip to main content
summaryrefslogtreecommitdiffstats
blob: de1b4c93dad1a3adeb1ed53ed5a0ceb8e6983bf4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
/*******************************************************************************
 * Copyright (c) 2000, 2015 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.jface.text.formatter;


import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.Assert;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.BadPositionCategoryException;
import org.eclipse.jface.text.DefaultPositionUpdater;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension3;
import org.eclipse.jface.text.IPositionUpdater;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.jface.text.TypedPosition;


/**
 * Standard implementation of <code>IContentFormatter</code>.
 * The formatter supports two operation modes: partition aware and
 * partition unaware. <p>
 * In the partition aware mode, the formatter determines the
 * partitioning of the document region to be formatted. For each
 * partition it determines all document positions  which are affected
 * when text changes are applied to the partition. Those which overlap
 * with the partition are remembered as character positions. These
 * character positions are passed over to the formatting strategy
 * registered for the partition's content type. The formatting strategy
 * returns a string containing the formatted document partition as well
 * as the adapted character positions. The formatted partition replaces
 * the old content of the partition. The remembered document positions
 * are updated with the adapted character positions. In addition, all
 * other document positions are accordingly adapted to the formatting
 * changes.<p>
 * In the partition unaware mode, the document's partitioning is ignored
 * and the document is considered consisting of only one partition of
 * the content type <code>IDocument.DEFAULT_CONTENT_TYPE</code>. The
 * formatting process is similar to the partition aware mode, with the
 * exception of having only one partition.<p>
 * Usually, clients instantiate this class and configure it before using it.
 *
 * @see IContentFormatter
 * @see IDocument
 * @see ITypedRegion
 * @see Position
 */
public class ContentFormatter implements IContentFormatter {

	/**
	 * Defines a reference to either the offset or the end offset of
	 * a particular position.
	 */
	static class PositionReference implements Comparable<PositionReference> {

		/** The referenced position */
		protected Position fPosition;
		/** The reference to either the offset or the end offset */
		protected boolean fRefersToOffset;
		/** The original category of the referenced position */
		protected String fCategory;

		/**
		 * Creates a new position reference.
		 *
		 * @param position the position to be referenced
		 * @param refersToOffset <code>true</code> if position offset should be referenced
		 * @param category the category the given position belongs to
		 */
		protected PositionReference(Position position, boolean refersToOffset, String category) {
			fPosition= position;
			fRefersToOffset= refersToOffset;
			fCategory= category;
		}

		/**
		 * Returns the offset of the referenced position.
		 *
		 * @return the offset of the referenced position
		 */
		protected int getOffset() {
			return fPosition.getOffset();
		}

		/**
		 * Manipulates the offset of the referenced position.
		 *
		 * @param offset the new offset of the referenced position
		 */
		protected void setOffset(int offset) {
			fPosition.setOffset(offset);
		}

		/**
		 * Returns the length of the referenced position.
		 *
		 * @return the length of the referenced position
		 */
		protected int getLength() {
			return fPosition.getLength();
		}

		/**
		 * Manipulates the length of the referenced position.
		 *
		 * @param length the new length of the referenced position
		 */
		protected void setLength(int length) {
			fPosition.setLength(length);
		}

		/**
		 * Returns whether this reference points to the offset or end offset
		 * of the references position.
		 *
		 * @return <code>true</code> if the offset of the position is referenced, <code>false</code> otherwise
		 */
		protected boolean refersToOffset() {
			return fRefersToOffset;
		}

		/**
		 * Returns the category of the referenced position.
		 *
		 * @return the category of the referenced position
		 */
		protected String getCategory() {
			return fCategory;
		}

		/**
		 * Returns the referenced position.
		 *
		 * @return the referenced position
		 */
		protected Position getPosition() {
			return fPosition;
		}

		/**
		 * Returns the referenced character position
		 *
		 * @return the referenced character position
		 */
		protected int getCharacterPosition() {
			if (fRefersToOffset)
				return getOffset();
			return getOffset() + getLength();
		}

		@Override
		public int compareTo(PositionReference r) {
			return getCharacterPosition() - r.getCharacterPosition();
		}
	}

	/**
	 * The position updater used to update the remembered partitions.
	 *
	 * @see IPositionUpdater
	 * @see DefaultPositionUpdater
	 */
	class NonDeletingPositionUpdater extends DefaultPositionUpdater {

		/**
		 * Creates a new updater for the given category.
		 *
		 * @param category the category
		 */
		protected NonDeletingPositionUpdater(String category) {
			super(category);
		}

		@Override
		protected boolean notDeleted() {
			return true;
		}
	}

	/**
	 * The position updater which runs as first updater on the document's positions.
	 * Used to remove all affected positions from their categories to avoid them
	 * from being regularly updated.
	 *
	 * @see IPositionUpdater
	 */
	class RemoveAffectedPositions implements IPositionUpdater {
		@Override
		public void update(DocumentEvent event) {
			removeAffectedPositions(event.getDocument());
		}
	}

	/**
	 * The position updater which runs as last updater on the document's positions.
	 * Used to update all affected positions and adding them back to their
	 * original categories.
	 *
	 * @see IPositionUpdater
	 */
	class UpdateAffectedPositions implements IPositionUpdater {

		/** The affected positions */
		private int[] fPositions;
		/** The offset */
		private int fOffset;

		/**
		 * Creates a new updater.
		 *
		 * @param positions the affected positions
		 * @param offset the offset
		 */
		public UpdateAffectedPositions(int[] positions, int offset) {
			fPositions= positions;
			fOffset= offset;
		}

		@Override
		public void update(DocumentEvent event) {
			updateAffectedPositions(event.getDocument(), fPositions, fOffset);
		}
	}


	/** Internal position category used for the formatter partitioning */
	private final static String PARTITIONING= "__formatter_partitioning"; //$NON-NLS-1$

	/** The map of <code>IFormattingStrategy</code> objects */
	private Map<String, IFormattingStrategy> fStrategies;
	/** The indicator of whether the formatter operates in partition aware mode or not */
	private boolean fIsPartitionAware= true;

	/** The partition information managing document position categories */
	private String[] fPartitionManagingCategories;
	/** The list of references to offset and end offset of all overlapping positions */
	private List<PositionReference> fOverlappingPositionReferences;
	/** Position updater used for partitioning positions */
	private IPositionUpdater fPartitioningUpdater;
	/**
	 * The document partitioning used by this formatter.
	 * @since 3.0
	 */
	private String fPartitioning;
	/**
	 * The document this formatter works on.
	 * @since 3.0
	 */
	private IDocument fDocument;
	/**
	 * The external partition managing categories.
	 * @since 3.0
	 */
	private String[] fExternalPartitonManagingCategories;
	/**
	 * Indicates whether <code>fPartitionManagingCategories</code> must be computed.
	 * @since 3.0
	 */
	private boolean fNeedsComputation= true;


	/**
	 * Creates a new content formatter. The content formatter operates by default
	 * in the partition-aware mode. There are no preconfigured formatting strategies.
	 * Will use the default document partitioning if not further configured.
	 */
	public ContentFormatter() {
		fPartitioning= IDocumentExtension3.DEFAULT_PARTITIONING;
	}

	/**
	 * Registers a strategy for a particular content type. If there is already a strategy
	 * registered for this type, the new strategy is registered instead of the old one.
	 * If the given content type is <code>null</code> the given strategy is registered for
	 * all content types as is called only once per formatting session.
	 *
	 * @param strategy the formatting strategy to register, or <code>null</code> to remove an existing one
	 * @param contentType the content type under which to register
	 */
	public void setFormattingStrategy(IFormattingStrategy strategy, String contentType) {

		Assert.isNotNull(contentType);

		if (fStrategies == null)
			fStrategies= new HashMap<>();

		if (strategy == null)
			fStrategies.remove(contentType);
		else
			fStrategies.put(contentType, strategy);
	}

	/**
	 * Informs this content formatter about the names of those position categories
	 * which are used to manage the document's partitioning information and thus should
	 * be ignored when this formatter updates positions.
	 *
	 * @param categories the categories to be ignored
	 * @deprecated incompatible with an open set of document partitionings. The provided information is only used
	 * 		if this formatter can not compute the partition managing position categories.
	 */
	@Deprecated
	public void setPartitionManagingPositionCategories(String[] categories) {
		fExternalPartitonManagingCategories= TextUtilities.copy(categories);
	}

	/**
	 * Sets the document partitioning to be used by this formatter.
	 *
	 * @param partitioning the document partitioning
	 * @since 3.0
	 */
	public void setDocumentPartitioning(String partitioning) {
		fPartitioning= partitioning;
	}

	/**
	 * Sets the formatter's operation mode.
	 *
	 * @param enable indicates whether the formatting process should be partition ware
	 */
	public void enablePartitionAwareFormatting(boolean enable) {
		fIsPartitionAware= enable;
	}

	@Override
	public IFormattingStrategy getFormattingStrategy(String contentType) {

		Assert.isNotNull(contentType);

		if (fStrategies == null)
			return null;

		return fStrategies.get(contentType);
	}

	@Override
	public void format(IDocument document, IRegion region) {
		fNeedsComputation= true;
		fDocument= document;
		try {

			if (fIsPartitionAware)
				formatPartitions(region);
			else
				formatRegion(region);

		} finally {
			fNeedsComputation= true;
			fDocument= null;
		}
	}

	/**
	 * Determines the partitioning of the given region of the document.
	 * Informs the formatting strategies of each partition about the start,
	 * the process, and the termination of the formatting session.
	 *
	 * @param region the document region to be formatted
	 * @since 3.0
	 */
	private void formatPartitions(IRegion region) {

		addPartitioningUpdater();

		try {

			TypedPosition[] ranges= getPartitioning(region);
			if (ranges != null) {
				start(ranges, getIndentation(region.getOffset()));
				format(ranges);
				stop(ranges);
			}

		} catch (BadLocationException x) {
		}

		removePartitioningUpdater();
	}

	/**
	 * Formats the given region with the strategy registered for the default
	 * content type. The strategy is informed about the start, the process, and
	 * the termination of the formatting session.
	 *
	 * @param region the region to be formatted
	 * @since 3.0
	 */
	private void formatRegion(IRegion region) {

		IFormattingStrategy strategy= getFormattingStrategy(IDocument.DEFAULT_CONTENT_TYPE);
		if (strategy != null) {
			strategy.formatterStarts(getIndentation(region.getOffset()));
			format(strategy, new TypedPosition(region.getOffset(), region.getLength(), IDocument.DEFAULT_CONTENT_TYPE));
			strategy.formatterStops();
		}
	}

	/**
	 * Returns the partitioning of the given region of the document to be formatted.
	 * As one partition after the other will be formatted and formatting will
	 * probably change the length of the formatted partition, it must be kept
	 * track of the modifications in order to submit the correct partition to all
	 * formatting strategies. For this, all partitions are remembered as positions
	 * in a dedicated position category. (As formatting strategies might rely on each
	 * other, calling them in reversed order is not an option.)
	 *
	 * @param region the region for which the partitioning must be determined
	 * @return the partitioning of the specified region
	 * @exception BadLocationException of region is invalid in the document
	 * @since 3.0
	 */
	private TypedPosition[] getPartitioning(IRegion region) throws BadLocationException {

		ITypedRegion[] regions= TextUtilities.computePartitioning(fDocument, fPartitioning, region.getOffset(), region.getLength(), false);
		TypedPosition[] positions= new TypedPosition[regions.length];

		for (int i= 0; i < regions.length; i++) {
			positions[i]= new TypedPosition(regions[i]);
			try {
				fDocument.addPosition(PARTITIONING, positions[i]);
			} catch (BadPositionCategoryException x) {
				// should not happen
			}
		}

		return positions;
	}

	/**
	 * Fires <code>formatterStarts</code> to all formatter strategies
	 * which will be involved in the forthcoming formatting process.
	 *
	 * @param regions the partitioning of the document to be formatted
	 * @param indentation the initial indentation
	 */
	private void start(TypedPosition[] regions, String indentation) {
		for (TypedPosition region : regions) {
			IFormattingStrategy s= getFormattingStrategy(region.getType());
			if (s != null)
				s.formatterStarts(indentation);
		}
	}

	/**
	 * Formats one partition after the other using the formatter strategy registered for
	 * the partition's content type.
	 *
	 * @param ranges the partitioning of the document region to be formatted
	 * @since 3.0
	 */
	private void format(TypedPosition[] ranges) {
		for (TypedPosition range : ranges) {
			IFormattingStrategy s= getFormattingStrategy(range.getType());
			if (s != null) {
				format(s, range);
			}
		}
	}

	/**
	 * Formats the given region of the document using the specified formatting
	 * strategy. In order to maintain positions correctly, first all affected
	 * positions determined, after all document listeners have been informed about
	 * the coming change, the affected positions are removed to avoid that they
	 * are regularly updated. After all position updaters have run, the affected
	 * positions are updated with the formatter's information and added back to
	 * their categories, right before the first document listener is informed about
	 * that a change happened.
	 *
	 * @param strategy the strategy to be used
	 * @param region the region to be formatted
	 * @since 3.0
	 */
	private void format(IFormattingStrategy strategy, TypedPosition region) {
		try {

			final int offset= region.getOffset();
			int length= region.getLength();

			String content= fDocument.get(offset, length);
			final int[] positions= getAffectedPositions(offset, length);
			String formatted= strategy.format(content, isLineStart(offset), getIndentation(offset), positions);

			if (formatted != null && !formatted.equals(content)) {

				IPositionUpdater first= new RemoveAffectedPositions();
				fDocument.insertPositionUpdater(first, 0);
				IPositionUpdater last= new UpdateAffectedPositions(positions, offset);
				fDocument.addPositionUpdater(last);

				fDocument.replace(offset, length, formatted);

				fDocument.removePositionUpdater(first);
				fDocument.removePositionUpdater(last);
			}

		} catch (BadLocationException x) {
			// should not happen
		}
	}

	/**
	 * Fires <code>formatterStops</code> to all formatter strategies which were
	 * involved in the formatting process which is about to terminate.
	 *
	 * @param regions the partitioning of the document which has been formatted
	 */
	private void stop(TypedPosition[] regions) {
		for (TypedPosition region : regions) {
			IFormattingStrategy s= getFormattingStrategy(region.getType());
			if (s != null)
				s.formatterStops();
		}
	}

	/**
	 * Installs those updaters which the formatter needs to keep track of the partitions.
	 * @since 3.0
	 */
	private void addPartitioningUpdater() {
		fPartitioningUpdater= new NonDeletingPositionUpdater(PARTITIONING);
		fDocument.addPositionCategory(PARTITIONING);
		fDocument.addPositionUpdater(fPartitioningUpdater);
	}

	/**
	 * Removes the formatter's internal position updater and category.
	 *
	 * @since 3.0
	 */
	private void removePartitioningUpdater() {

		try {

			fDocument.removePositionUpdater(fPartitioningUpdater);
			fDocument.removePositionCategory(PARTITIONING);
			fPartitioningUpdater= null;

		} catch (BadPositionCategoryException x) {
			// should not happen
		}
	}

	/**
	 * Returns the partition managing position categories for the formatted document.
	 *
	 * @return the position managing position categories
	 * @since 3.0
	 */
	private String[] getPartitionManagingCategories() {
		if (fNeedsComputation) {
			fNeedsComputation= false;
			fPartitionManagingCategories= TextUtilities.computePartitionManagingCategories(fDocument);
			if (fPartitionManagingCategories == null)
				fPartitionManagingCategories= fExternalPartitonManagingCategories;
		}
		return fPartitionManagingCategories;
	}

	/**
	 * Determines whether the given document position category should be ignored
	 * by this formatter's position updating.
	 *
	 * @param category the category to check
	 * @return <code>true</code> if the category should be ignored, <code>false</code> otherwise
	 */
	private boolean ignoreCategory(String category) {

		if (PARTITIONING.equals(category)) {
			return true;
		}

		String[] categories= getPartitionManagingCategories();
		if (categories != null) {
			for (String cat : categories) {
				if (cat.equals(category)) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * Determines all embracing, overlapping, and follow up positions
	 * for the given region of the document.
	 *
	 * @param offset the offset of the document region to be formatted
	 * @param length the length of the document to be formatted
	 * @since 3.0
	 */
	private void determinePositionsToUpdate(int offset, int length) {

		String[] categories= fDocument.getPositionCategories();
		if (categories != null) {
			for (String cat : categories) {

				if (ignoreCategory(cat))
					continue;

				try {

					Position[] positions= fDocument.getPositions(cat);

					for (Position p : positions) {

						if (p.overlapsWith(offset, length)) {

							if (offset < p.getOffset())
								fOverlappingPositionReferences.add(new PositionReference(p, true, cat));

							if (p.getOffset() + p.getLength() < offset + length)
								fOverlappingPositionReferences.add(new PositionReference(p, false, cat));
						}
					}

				} catch (BadPositionCategoryException x) {
					// can not happen
				}
			}
		}
	}

	/**
	 * Returns all offset and the end offset of all positions overlapping with the
	 * specified document range.
	 *
	 * @param offset the offset of the document region to be formatted
	 * @param length the length of the document to be formatted
	 * @return all character positions of the interleaving positions
	 * @since 3.0
	 */
	private int[] getAffectedPositions(int offset, int length) {

		fOverlappingPositionReferences= new ArrayList<>();

		determinePositionsToUpdate(offset, length);

		Collections.sort(fOverlappingPositionReferences);

		int[] positions= new int[fOverlappingPositionReferences.size()];
		for (int i= 0; i < positions.length; i++) {
			PositionReference r= fOverlappingPositionReferences.get(i);
			positions[i]= r.getCharacterPosition() - offset;
		}

		return positions;
	}

	/**
	 * Removes the affected positions from their categories to avoid
	 * that they are invalidly updated.
	 *
	 * @param document the document
	 */
	private void removeAffectedPositions(IDocument document) {
		int size= fOverlappingPositionReferences.size();
		for (int i= 0; i < size; i++) {
			PositionReference r= fOverlappingPositionReferences.get(i);
			try {
				document.removePosition(r.getCategory(), r.getPosition());
			} catch (BadPositionCategoryException x) {
				// can not happen
			}
		}
	}

	/**
	 * Updates all the overlapping positions. Note, all other positions are
	 * automatically updated by their document position updaters.
	 *
	 * @param document the document to has been formatted
	 * @param positions the adapted character positions to be used to update the document positions
	 * @param offset the offset of the document region that has been formatted
	 */
	protected void updateAffectedPositions(IDocument document, int[] positions, int offset) {

		if (document != fDocument)
			return;

		if (positions.length == 0)
			return;

		for (int i= 0; i < positions.length; i++) {

			PositionReference r= fOverlappingPositionReferences.get(i);

			if (r.refersToOffset())
				r.setOffset(offset + positions[i]);
			else
				r.setLength((offset + positions[i]) - r.getOffset());

			Position p= r.getPosition();
			String category= r.getCategory();
			if (!document.containsPosition(category, p.offset, p.length)) {
				try {
					if (positionAboutToBeAdded(document, category, p))
						document.addPosition(r.getCategory(), p);
				} catch (BadPositionCategoryException x) {
					// can not happen
				} catch (BadLocationException x) {
					// should not happen
				}
			}

		}

		fOverlappingPositionReferences= null;
	}

	/**
	 * The given position is about to be added to the given position category of the given document. <p>
	 * This default implementation return <code>true</code>.
	 *
	 * @param document the document
	 * @param category the position category
	 * @param position the position that will be added
	 * @return <code>true</code> if the position can be added, <code>false</code> if it should be ignored
	 */
	protected boolean positionAboutToBeAdded(IDocument document, String category, Position position) {
		return true;
	}

	/**
	 * Returns the indentation of the line of the given offset.
	 *
	 * @param offset the offset
	 * @return the indentation of the line of the offset
	 * @since 3.0
	 */
	private String getIndentation(int offset) {

		try {
			int start= fDocument.getLineOfOffset(offset);
			start= fDocument.getLineOffset(start);

			int end= start;
			char c= fDocument.getChar(end);
			while ('\t' == c || ' ' == c)
				c= fDocument.getChar(++end);

			return fDocument.get(start, end - start);
		} catch (BadLocationException x) {
		}

		return ""; //$NON-NLS-1$
	}

	/**
	 * Determines whether the offset is the beginning of a line in the given document.
	 *
	 * @param offset the offset
	 * @return <code>true</code> if offset is the beginning of a line
	 * @exception BadLocationException if offset is invalid in document
	 * @since 3.0
	 */
	private boolean isLineStart(int offset) throws BadLocationException {
		int start= fDocument.getLineOfOffset(offset);
		start= fDocument.getLineOffset(start);
		return (start == offset);
	}
}

Back to the top