~ubuntu-branches/debian/sid/eclipse-cdt/sid

« back to all changes in this revision

Viewing changes to core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlightingPresenter.java

  • Committer: Package Import Robot
  • Author(s): Jakub Adam
  • Date: 2011-10-06 21:15:04 UTC
  • mfrom: (1.1.4)
  • Revision ID: package-import@ubuntu.com-20111006211504-8dutmljjih0zikfv
Tags: 8.0.1-1
* New upstream release.
* Split the JNI packages into a separate architecture dependent
  package and made eclipse-cdt architecture independent.
* Install JNI libraries into multiarch aware location
* Bumped Standards-Version to 3.9.2.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*******************************************************************************
 
2
 * Copyright (c) 2000, 2008 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
 *     Anton Leherbauer (Wind River Systems) - Adapted for CDT
 
11
 *******************************************************************************/
 
12
 
 
13
package org.eclipse.cdt.internal.ui.editor;
 
14
 
 
15
import java.util.ArrayList;
 
16
import java.util.Arrays;
 
17
import java.util.List;
 
18
 
 
19
import org.eclipse.jface.text.BadLocationException;
 
20
import org.eclipse.jface.text.BadPositionCategoryException;
 
21
import org.eclipse.jface.text.DocumentEvent;
 
22
import org.eclipse.jface.text.IDocument;
 
23
import org.eclipse.jface.text.IDocumentListener;
 
24
import org.eclipse.jface.text.IPositionUpdater;
 
25
import org.eclipse.jface.text.IRegion;
 
26
import org.eclipse.jface.text.ISynchronizable;
 
27
import org.eclipse.jface.text.ITextInputListener;
 
28
import org.eclipse.jface.text.ITextPresentationListener;
 
29
import org.eclipse.jface.text.Position;
 
30
import org.eclipse.jface.text.Region;
 
31
import org.eclipse.jface.text.TextPresentation;
 
32
import org.eclipse.swt.custom.StyleRange;
 
33
 
 
34
import org.eclipse.cdt.ui.CUIPlugin;
 
35
 
 
36
import org.eclipse.cdt.internal.ui.editor.SemanticHighlightingManager.HighlightedPosition;
 
37
import org.eclipse.cdt.internal.ui.editor.SemanticHighlightingManager.HighlightingStyle;
 
38
import org.eclipse.cdt.internal.ui.text.CPresentationReconciler;
 
39
 
 
40
 
 
41
/**
 
42
 * Semantic highlighting presenter - UI thread implementation.
 
43
 * Cloned from JDT.
 
44
 * 
 
45
 * @since 4.0
 
46
 */
 
47
public class SemanticHighlightingPresenter implements ITextPresentationListener, ITextInputListener, IDocumentListener {
 
48
 
 
49
        /**
 
50
         * Semantic highlighting position updater.
 
51
         */
 
52
        private class HighlightingPositionUpdater implements IPositionUpdater {
 
53
 
 
54
                /** The position category. */
 
55
                private final String fCategory;
 
56
 
 
57
                /**
 
58
                 * Creates a new updater for the given <code>category</code>.
 
59
                 *
 
60
                 * @param category the new category.
 
61
                 */
 
62
                public HighlightingPositionUpdater(String category) {
 
63
                        fCategory= category;
 
64
                }
 
65
 
 
66
                /*
 
67
                 * @see org.eclipse.jface.text.IPositionUpdater#update(org.eclipse.jface.text.DocumentEvent)
 
68
                 */
 
69
                public void update(DocumentEvent event) {
 
70
 
 
71
                        int eventOffset= event.getOffset();
 
72
                        int eventOldLength= event.getLength();
 
73
                        int eventEnd= eventOffset + eventOldLength;
 
74
 
 
75
                        try {
 
76
                                Position[] positions= event.getDocument().getPositions(fCategory);
 
77
 
 
78
                                for (int i= 0; i != positions.length; i++) {
 
79
 
 
80
                                        HighlightedPosition position= (HighlightedPosition) positions[i];
 
81
 
 
82
                                        // Also update deleted positions because they get deleted by the background thread and removed/invalidated only in the UI runnable
 
83
//                                      if (position.isDeleted())
 
84
//                                              continue;
 
85
 
 
86
                                        int offset= position.getOffset();
 
87
                                        int length= position.getLength();
 
88
                                        int end= offset + length;
 
89
 
 
90
                                        if (offset > eventEnd)
 
91
                                                updateWithPrecedingEvent(position, event);
 
92
                                        else if (end < eventOffset)
 
93
                                                updateWithSucceedingEvent(position, event);
 
94
                                        else if (offset <= eventOffset && end >= eventEnd)
 
95
                                                updateWithIncludedEvent(position, event);
 
96
                                        else if (offset <= eventOffset)
 
97
                                                updateWithOverEndEvent(position, event);
 
98
                                        else if (end >= eventEnd)
 
99
                                                updateWithOverStartEvent(position, event);
 
100
                                        else
 
101
                                                updateWithIncludingEvent(position, event);
 
102
                                }
 
103
                        } catch (BadPositionCategoryException e) {
 
104
                                // ignore and return
 
105
                        }
 
106
                }
 
107
 
 
108
                /**
 
109
                 * Update the given position with the given event. The event precedes the position.
 
110
                 *
 
111
                 * @param position The position
 
112
                 * @param event The event
 
113
                 */
 
114
                private void updateWithPrecedingEvent(HighlightedPosition position, DocumentEvent event) {
 
115
                        String newText= event.getText();
 
116
                        int eventNewLength= newText != null ? newText.length() : 0;
 
117
                        int deltaLength= eventNewLength - event.getLength();
 
118
 
 
119
                        position.setOffset(position.getOffset() + deltaLength);
 
120
                }
 
121
 
 
122
                /**
 
123
                 * Update the given position with the given event. The event succeeds the position.
 
124
                 *
 
125
                 * @param position The position
 
126
                 * @param event The event
 
127
                 */
 
128
                private void updateWithSucceedingEvent(HighlightedPosition position, DocumentEvent event) {
 
129
                }
 
130
 
 
131
                /**
 
132
                 * Update the given position with the given event. The event is included by the position.
 
133
                 *
 
134
                 * @param position The position
 
135
                 * @param event The event
 
136
                 */
 
137
                private void updateWithIncludedEvent(HighlightedPosition position, DocumentEvent event) {
 
138
                        int eventOffset= event.getOffset();
 
139
                        String newText= event.getText();
 
140
                        if (newText == null)
 
141
                                newText= ""; //$NON-NLS-1$
 
142
                        int eventNewLength= newText.length();
 
143
 
 
144
                        int deltaLength= eventNewLength - event.getLength();
 
145
 
 
146
                        int offset= position.getOffset();
 
147
                        int length= position.getLength();
 
148
                        int end= offset + length;
 
149
 
 
150
                        int includedLength= 0;
 
151
                        while (includedLength < eventNewLength && Character.isJavaIdentifierPart(newText.charAt(includedLength)))
 
152
                                includedLength++;
 
153
                        if (includedLength == eventNewLength)
 
154
                                position.setLength(length + deltaLength);
 
155
                        else {
 
156
                                int newLeftLength= eventOffset - offset + includedLength;
 
157
 
 
158
                                int excludedLength= eventNewLength;
 
159
                                while (excludedLength > 0 && Character.isJavaIdentifierPart(newText.charAt(excludedLength - 1)))
 
160
                                        excludedLength--;
 
161
                                int newRightOffset= eventOffset + excludedLength;
 
162
                                int newRightLength= end + deltaLength - newRightOffset;
 
163
 
 
164
                                if (newRightLength == 0) {
 
165
                                        position.setLength(newLeftLength);
 
166
                                } else {
 
167
                                        if (newLeftLength == 0) {
 
168
                                                position.update(newRightOffset, newRightLength);
 
169
                                        } else {
 
170
                                                position.setLength(newLeftLength);
 
171
                                                addPositionFromUI(newRightOffset, newRightLength, position.getHighlighting());
 
172
                                        }
 
173
                                }
 
174
                        }
 
175
                }
 
176
 
 
177
                /**
 
178
                 * Update the given position with the given event. The event overlaps with the end of the position.
 
179
                 *
 
180
                 * @param position The position
 
181
                 * @param event The event
 
182
                 */
 
183
                private void updateWithOverEndEvent(HighlightedPosition position, DocumentEvent event) {
 
184
                        String newText= event.getText();
 
185
                        if (newText == null)
 
186
                                newText= ""; //$NON-NLS-1$
 
187
                        int eventNewLength= newText.length();
 
188
 
 
189
                        int includedLength= 0;
 
190
                        while (includedLength < eventNewLength && Character.isJavaIdentifierPart(newText.charAt(includedLength)))
 
191
                                includedLength++;
 
192
                        position.setLength(event.getOffset() - position.getOffset() + includedLength);
 
193
                }
 
194
 
 
195
                /**
 
196
                 * Update the given position with the given event. The event overlaps with the start of the position.
 
197
                 *
 
198
                 * @param position The position
 
199
                 * @param event The event
 
200
                 */
 
201
                private void updateWithOverStartEvent(HighlightedPosition position, DocumentEvent event) {
 
202
                        int eventOffset= event.getOffset();
 
203
                        int eventEnd= eventOffset + event.getLength();
 
204
 
 
205
                        String newText= event.getText();
 
206
                        if (newText == null)
 
207
                                newText= ""; //$NON-NLS-1$
 
208
                        int eventNewLength= newText.length();
 
209
 
 
210
                        int excludedLength= eventNewLength;
 
211
                        while (excludedLength > 0 && Character.isJavaIdentifierPart(newText.charAt(excludedLength - 1)))
 
212
                                excludedLength--;
 
213
                        int deleted= eventEnd - position.getOffset();
 
214
                        int inserted= eventNewLength - excludedLength;
 
215
                        position.update(eventOffset + excludedLength, position.getLength() - deleted + inserted);
 
216
                }
 
217
 
 
218
                /**
 
219
                 * Update the given position with the given event. The event includes the position.
 
220
                 *
 
221
                 * @param position The position
 
222
                 * @param event The event
 
223
                 */
 
224
                private void updateWithIncludingEvent(HighlightedPosition position, DocumentEvent event) {
 
225
                        position.delete();
 
226
                        position.update(event.getOffset(), 0);
 
227
                }
 
228
        }
 
229
 
 
230
        /** Position updater */
 
231
        private IPositionUpdater fPositionUpdater= new HighlightingPositionUpdater(getPositionCategory());
 
232
 
 
233
        /** The source viewer this semantic highlighting reconciler is installed on */
 
234
        private CSourceViewer fSourceViewer;
 
235
        /** The background presentation reconciler */
 
236
        private CPresentationReconciler fPresentationReconciler;
 
237
 
 
238
        /** UI's current highlighted positions - can contain <code>null</code> elements */
 
239
        private List<HighlightedPosition> fPositions= new ArrayList<HighlightedPosition>();
 
240
        /** UI position lock */
 
241
        private Object fPositionLock= new Object();
 
242
 
 
243
        /** <code>true</code> iff the current reconcile is canceled. */
 
244
        private boolean fIsCanceled= false;
 
245
 
 
246
        /**
 
247
         * Creates and returns a new highlighted position with the given offset, length and highlighting.
 
248
         * <p>
 
249
         * NOTE: Also called from background thread.
 
250
         * </p>
 
251
         *
 
252
         * @param offset The offset
 
253
         * @param length The length
 
254
         * @param highlighting The highlighting
 
255
         * @return The new highlighted position
 
256
         */
 
257
        public HighlightedPosition createHighlightedPosition(int offset, int length, HighlightingStyle highlighting) {
 
258
                // TODO: reuse deleted positions
 
259
                return new HighlightedPosition(offset, length, highlighting, fPositionUpdater);
 
260
        }
 
261
 
 
262
        /**
 
263
         * Adds all current positions to the given list.
 
264
         * <p>
 
265
         * NOTE: Called from background thread.
 
266
         * </p>
 
267
         *
 
268
         * @param list The list
 
269
         */
 
270
        public void addAllPositions(List<? super HighlightedPosition> list) {
 
271
                synchronized (fPositionLock) {
 
272
                        list.addAll(fPositions);
 
273
                }
 
274
        }
 
275
 
 
276
        /**
 
277
         * Create a text presentation in the background.
 
278
         * <p>
 
279
         * NOTE: Called from background thread.
 
280
         * </p>
 
281
         *
 
282
         * @param addedPositions the added positions
 
283
         * @param removedPositions the removed positions
 
284
         * @return the text presentation or <code>null</code>, if reconciliation should be canceled
 
285
         */
 
286
        public TextPresentation createPresentation(List<? extends Position> addedPositions, List<? extends Position> removedPositions) {
 
287
                CSourceViewer sourceViewer= fSourceViewer;
 
288
                CPresentationReconciler presentationReconciler= fPresentationReconciler;
 
289
                if (sourceViewer == null || presentationReconciler == null)
 
290
                        return null;
 
291
 
 
292
                if (isCanceled())
 
293
                        return null;
 
294
 
 
295
                IDocument document= sourceViewer.getDocument();
 
296
                if (document == null)
 
297
                        return null;
 
298
 
 
299
                int minStart= Integer.MAX_VALUE;
 
300
                int maxEnd= Integer.MIN_VALUE;
 
301
                for (int i= 0, n= removedPositions.size(); i < n; i++) {
 
302
                        Position position= removedPositions.get(i);
 
303
                        int offset= position.getOffset();
 
304
                        minStart= Math.min(minStart, offset);
 
305
                        maxEnd= Math.max(maxEnd, offset + position.getLength());
 
306
                }
 
307
                for (int i= 0, n= addedPositions.size(); i < n; i++) {
 
308
                        Position position= addedPositions.get(i);
 
309
                        int offset= position.getOffset();
 
310
                        minStart= Math.min(minStart, offset);
 
311
                        maxEnd= Math.max(maxEnd, offset + position.getLength());
 
312
                }
 
313
 
 
314
                if (minStart < maxEnd)
 
315
                        try {
 
316
                                return presentationReconciler.createRepairDescription(new Region(minStart, maxEnd - minStart), document);
 
317
                        } catch (RuntimeException e) {
 
318
                                // Assume concurrent modification from UI thread
 
319
                        }
 
320
 
 
321
                return null;
 
322
        }
 
323
 
 
324
        /**
 
325
         * Create a runnable for updating the presentation.
 
326
         * <p>
 
327
         * NOTE: Called from background thread.
 
328
         * </p>
 
329
         * @param textPresentation the text presentation
 
330
         * @param addedPositions the added positions
 
331
         * @param removedPositions the removed positions
 
332
         * @return the runnable or <code>null</code>, if reconciliation should be canceled
 
333
         */
 
334
        public Runnable createUpdateRunnable(final TextPresentation textPresentation, List<HighlightedPosition> addedPositions, List<HighlightedPosition> removedPositions) {
 
335
                if (fSourceViewer == null || textPresentation == null)
 
336
                        return null;
 
337
 
 
338
                // TODO: do clustering of positions and post multiple fast runnables
 
339
                final HighlightedPosition[] added= addedPositions.toArray(new HighlightedPosition[addedPositions.size()]);
 
340
                final HighlightedPosition[] removed= removedPositions.toArray(new HighlightedPosition[removedPositions.size()]);
 
341
 
 
342
                if (isCanceled())
 
343
                        return null;
 
344
 
 
345
                Runnable runnable= new Runnable() {
 
346
                        public void run() {
 
347
                                updatePresentation(textPresentation, added, removed);
 
348
                        }
 
349
                };
 
350
                return runnable;
 
351
        }
 
352
 
 
353
        /**
 
354
         * Invalidate the presentation of the positions based on the given added positions and the existing deleted positions.
 
355
         * Also unregisters the deleted positions from the document and patches the positions of this presenter.
 
356
         * <p>
 
357
         * NOTE: Indirectly called from background thread by UI runnable.
 
358
         * </p>
 
359
         * @param textPresentation the text presentation or <code>null</code>, if the presentation should computed in the UI thread
 
360
         * @param addedPositions the added positions
 
361
         * @param removedPositions the removed positions
 
362
         */
 
363
        public void updatePresentation(TextPresentation textPresentation, HighlightedPosition[] addedPositions, HighlightedPosition[] removedPositions) {
 
364
                if (fSourceViewer == null)
 
365
                        return;
 
366
 
 
367
//              checkOrdering("added positions: ", Arrays.asList(addedPositions)); //$NON-NLS-1$
 
368
//              checkOrdering("removed positions: ", Arrays.asList(removedPositions)); //$NON-NLS-1$
 
369
//              checkOrdering("old positions: ", fPositions); //$NON-NLS-1$
 
370
 
 
371
                // TODO: double-check consistency with document.getPositions(...)
 
372
                // TODO: reuse removed positions
 
373
                if (isCanceled())
 
374
                        return;
 
375
 
 
376
                IDocument document= fSourceViewer.getDocument();
 
377
                if (document == null)
 
378
                        return;
 
379
 
 
380
                String positionCategory= getPositionCategory();
 
381
 
 
382
                List<HighlightedPosition> removedPositionsList= Arrays.asList(removedPositions);
 
383
 
 
384
                try {
 
385
                        synchronized (fPositionLock) {
 
386
                                List<HighlightedPosition> oldPositions= fPositions;
 
387
                                int newSize= Math.max(fPositions.size() + addedPositions.length - removedPositions.length, 10);
 
388
                                
 
389
                                /*
 
390
                                 * The following loop is a kind of merge sort: it merges two List<Position>, each
 
391
                                 * sorted by position.offset, into one new list. The first of the two is the
 
392
                                 * previous list of positions (oldPositions), from which any deleted positions get
 
393
                                 * removed on the fly. The second of two is the list of added positions. The result
 
394
                                 * is stored in newPositions.
 
395
                                 */
 
396
                                List<HighlightedPosition> newPositions= new ArrayList<HighlightedPosition>(newSize);
 
397
                                HighlightedPosition position= null;
 
398
                                HighlightedPosition addedPosition= null;
 
399
                                for (int i= 0, j= 0, n= oldPositions.size(), m= addedPositions.length; i < n || position != null || j < m || addedPosition != null;) {
 
400
                                        // loop variant: i + j < old(i + j)
 
401
                                        
 
402
                                        // a) find the next non-deleted Position from the old list
 
403
                                        while (position == null && i < n) {
 
404
                                                position= oldPositions.get(i++);
 
405
                                                if (position.isDeleted() || contain(removedPositionsList, position)) {
 
406
                                                        document.removePosition(positionCategory, position);
 
407
                                                        position= null;
 
408
                                                }
 
409
                                        }
 
410
                                        
 
411
                                        // b) find the next Position from the added list
 
412
                                        if (addedPosition == null && j < m) {
 
413
                                                addedPosition= addedPositions[j++];
 
414
                                                document.addPosition(positionCategory, addedPosition);
 
415
                                        }
 
416
                                        
 
417
                                        // c) merge: add the next of position/addedPosition with the lower offset
 
418
                                        if (position != null) {
 
419
                                                if (addedPosition != null)
 
420
                                                        if (position.getOffset() <= addedPosition.getOffset()) {
 
421
                                                                newPositions.add(position);
 
422
                                                                position= null;
 
423
                                                        } else {
 
424
                                                                newPositions.add(addedPosition);
 
425
                                                                addedPosition= null;
 
426
                                                        }
 
427
                                                else {
 
428
                                                        newPositions.add(position);
 
429
                                                        position= null;
 
430
                                                }
 
431
                                        } else if (addedPosition != null) {
 
432
                                                newPositions.add(addedPosition);
 
433
                                                addedPosition= null;
 
434
                                        }
 
435
                                }
 
436
                                fPositions= newPositions;
 
437
                        }
 
438
                } catch (BadPositionCategoryException e) {
 
439
                        // Should not happen
 
440
                        CUIPlugin.log(e);
 
441
                } catch (BadLocationException e) {
 
442
                        // Should not happen
 
443
                        CUIPlugin.log(e);
 
444
                }
 
445
//              checkOrdering("new positions: ", fPositions); //$NON-NLS-1$
 
446
 
 
447
                if (textPresentation != null)
 
448
                        fSourceViewer.changeTextPresentation(textPresentation, false);
 
449
                else
 
450
                        fSourceViewer.invalidateTextPresentation();
 
451
        }
 
452
 
 
453
//      private void checkOrdering(String s, List positions) {
 
454
//              Position previous= null;
 
455
//              for (int i= 0, n= positions.size(); i < n; i++) {
 
456
//                      Position current= (Position) positions.get(i);
 
457
//                      if (previous != null && previous.getOffset() + previous.getLength() > current.getOffset())
 
458
//                              return;
 
459
//              }
 
460
//      }
 
461
 
 
462
        /**
 
463
         * Returns <code>true</code> iff the positions contain the position.
 
464
         * @param positions the positions, must be ordered by offset but may overlap
 
465
         * @param position the position
 
466
         * @return <code>true</code> iff the positions contain the position
 
467
         */
 
468
        private boolean contain(List<? extends Position> positions, Position position) {
 
469
                return indexOf(positions, position) != -1;
 
470
        }
 
471
 
 
472
        /**
 
473
         * Returns index of the position in the positions, <code>-1</code> if not found.
 
474
         * @param positions the positions, must be ordered by offset but may overlap
 
475
         * @param position the position
 
476
         * @return the index
 
477
         */
 
478
        private int indexOf(List<? extends Position> positions, Position position) {
 
479
                int index= computeIndexAtOffset(positions, position.getOffset());
 
480
                int size= positions.size();
 
481
                while (index < size) {
 
482
                        if (positions.get(index) == position) 
 
483
                                return index;
 
484
                        index++;
 
485
                }
 
486
                return -1;
 
487
        }
 
488
 
 
489
        /**
 
490
         * Insert the given position in <code>fPositions</code>, s.t. the offsets remain in linear order.
 
491
         *
 
492
         * @param position The position for insertion
 
493
         */
 
494
        private void insertPosition(HighlightedPosition position) {
 
495
                int i= computeIndexAfterOffset(fPositions, position.getOffset());
 
496
                fPositions.add(i, position);
 
497
        }
 
498
 
 
499
        /**
 
500
         * Returns the index of the first position with an offset greater than the given offset.
 
501
         *
 
502
         * @param positions the positions, must be ordered by offset and must not overlap
 
503
         * @param offset the offset
 
504
         * @return the index of the last position with an offset greater than the given offset
 
505
         */
 
506
        private int computeIndexAfterOffset(List<? extends Position> positions, int offset) {
 
507
                int i= -1;
 
508
                int j= positions.size();
 
509
                while (j - i > 1) {
 
510
                        int k= (i + j) >> 1;
 
511
                        Position position= positions.get(k);
 
512
                        if (position.getOffset() > offset)
 
513
                                j= k;
 
514
                        else
 
515
                                i= k;
 
516
                }
 
517
                return j;
 
518
        }
 
519
 
 
520
        /**
 
521
         * Returns the index of the first position with an offset equal or greater than the given offset.
 
522
         *
 
523
         * @param positions the positions, must be ordered by offset and must not overlap
 
524
         * @param offset the offset
 
525
         * @return the index of the last position with an offset equal or greater than the given offset
 
526
         */
 
527
        private int computeIndexAtOffset(List<? extends Position> positions, int offset) {
 
528
                int i= -1;
 
529
                int j= positions.size();
 
530
                while (j - i > 1) {
 
531
                        int k= (i + j) >> 1;
 
532
                        Position position= positions.get(k);
 
533
                        if (position.getOffset() >= offset)
 
534
                                j= k;
 
535
                        else
 
536
                                i= k;
 
537
                }
 
538
                return j;
 
539
        }
 
540
 
 
541
        /*
 
542
         * @see org.eclipse.jface.text.ITextPresentationListener#applyTextPresentation(org.eclipse.jface.text.TextPresentation)
 
543
         */
 
544
        public void applyTextPresentation(TextPresentation textPresentation) {
 
545
                IRegion region= textPresentation.getExtent();
 
546
                int i= computeIndexAtOffset(fPositions, region.getOffset()), n= computeIndexAtOffset(fPositions, region.getOffset() + region.getLength());
 
547
                if (n - i > 2) {
 
548
                        List<StyleRange> ranges= new ArrayList<StyleRange>(n - i);
 
549
                        for (; i < n; i++) {
 
550
                                HighlightedPosition position= fPositions.get(i);
 
551
                                if (!position.isDeleted())
 
552
                                        ranges.add(position.createStyleRange());
 
553
                        }
 
554
                        StyleRange[] array= new StyleRange[ranges.size()];
 
555
                        array= ranges.toArray(array);
 
556
                        textPresentation.replaceStyleRanges(array);
 
557
                } else {
 
558
                        for (; i < n; i++) {
 
559
                                HighlightedPosition position= fPositions.get(i);
 
560
                                if (!position.isDeleted())
 
561
                                        textPresentation.replaceStyleRange(position.createStyleRange());
 
562
                        }
 
563
                }
 
564
        }
 
565
 
 
566
        /*
 
567
         * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
 
568
         */
 
569
        public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
 
570
                setCanceled(true);
 
571
                releaseDocument(oldInput);
 
572
                resetState();
 
573
        }
 
574
 
 
575
        /*
 
576
         * @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
 
577
         */
 
578
        public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
 
579
                manageDocument(newInput);
 
580
        }
 
581
 
 
582
        /*
 
583
         * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
 
584
         */
 
585
        public void documentAboutToBeChanged(DocumentEvent event) {
 
586
                setCanceled(true);
 
587
        }
 
588
 
 
589
        /*
 
590
         * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
 
591
         */
 
592
        public void documentChanged(DocumentEvent event) {
 
593
        }
 
594
 
 
595
        /**
 
596
         * @return Returns <code>true</code> iff the current reconcile is canceled.
 
597
         * <p>
 
598
         * NOTE: Also called from background thread.
 
599
         * </p>
 
600
         */
 
601
        public boolean isCanceled() {
 
602
                IDocument document= fSourceViewer != null ? fSourceViewer.getDocument() : null;
 
603
                if (document == null)
 
604
                        return fIsCanceled;
 
605
 
 
606
                synchronized (getLockObject(document)) {
 
607
                        return fIsCanceled;
 
608
                }
 
609
        }
 
610
 
 
611
        /**
 
612
         * Set whether or not the current reconcile is canceled.
 
613
         *
 
614
         * @param isCanceled <code>true</code> iff the current reconcile is canceled
 
615
         */
 
616
        public void setCanceled(boolean isCanceled) {
 
617
                IDocument document= fSourceViewer != null ? fSourceViewer.getDocument() : null;
 
618
                if (document == null) {
 
619
                        fIsCanceled= isCanceled;
 
620
                        return;
 
621
                }
 
622
 
 
623
                synchronized (getLockObject(document)) {
 
624
                        fIsCanceled= isCanceled;
 
625
                }
 
626
        }
 
627
 
 
628
        /**
 
629
         * @param document the document
 
630
         * @return the document's lock object
 
631
         */
 
632
        private Object getLockObject(IDocument document) {
 
633
                if (document instanceof ISynchronizable) {
 
634
                        Object lock= ((ISynchronizable)document).getLockObject();
 
635
                        if (lock != null)
 
636
                                return lock;
 
637
                }
 
638
                return document;
 
639
        }
 
640
 
 
641
        /**
 
642
         * Install this presenter on the given source viewer and background presentation
 
643
         * reconciler.
 
644
         *
 
645
         * @param sourceViewer the source viewer
 
646
         * @param backgroundPresentationReconciler the background presentation reconciler,
 
647
         *      can be <code>null</code>, in that case {@link SemanticHighlightingPresenter#createPresentation(List, List)}
 
648
         *      should not be called
 
649
         */
 
650
        public void install(CSourceViewer sourceViewer, CPresentationReconciler backgroundPresentationReconciler) {
 
651
                fSourceViewer= sourceViewer;
 
652
                fPresentationReconciler= backgroundPresentationReconciler;
 
653
 
 
654
                fSourceViewer.prependTextPresentationListener(this);
 
655
                fSourceViewer.addTextInputListener(this);
 
656
                manageDocument(fSourceViewer.getDocument());
 
657
        }
 
658
 
 
659
        /**
 
660
         * Uninstall this presenter.
 
661
         */
 
662
        public void uninstall() {
 
663
                setCanceled(true);
 
664
 
 
665
                if (fSourceViewer != null) {
 
666
                        fSourceViewer.removeTextPresentationListener(this);
 
667
                        releaseDocument(fSourceViewer.getDocument());
 
668
                        invalidateTextPresentation();
 
669
                        resetState();
 
670
 
 
671
                        fSourceViewer.removeTextInputListener(this);
 
672
                        fSourceViewer= null;
 
673
                }
 
674
        }
 
675
 
 
676
        /**
 
677
         * Invalidate text presentation of positions with the given highlighting.
 
678
         *
 
679
         * @param highlighting The highlighting
 
680
         */
 
681
        public void highlightingStyleChanged(HighlightingStyle highlighting) {
 
682
                for (int i= 0, n= fPositions.size(); i < n; i++) {
 
683
                        HighlightedPosition position= fPositions.get(i);
 
684
                        if (position.getHighlighting() == highlighting)
 
685
                                fSourceViewer.invalidateTextPresentation(position.getOffset(), position.getLength());
 
686
                }
 
687
        }
 
688
 
 
689
        /**
 
690
         * Invalidate text presentation of all positions.
 
691
         */
 
692
        private void invalidateTextPresentation() {
 
693
                if (fPositions.size() > 1000) {
 
694
                        fSourceViewer.invalidateTextPresentation();
 
695
                } else {
 
696
                        for (int i= 0, n= fPositions.size(); i < n; i++) {
 
697
                                Position position= fPositions.get(i);
 
698
                                fSourceViewer.invalidateTextPresentation(position.getOffset(), position.getLength());
 
699
                        }
 
700
                }
 
701
        }
 
702
 
 
703
        /**
 
704
         * Add a position with the given range and highlighting unconditionally, only from UI thread.
 
705
         * The position will also be registered on the document. The text presentation is not invalidated.
 
706
         *
 
707
         * @param offset The range offset
 
708
         * @param length The range length
 
709
         * @param highlighting
 
710
         */
 
711
        private void addPositionFromUI(int offset, int length, HighlightingStyle highlighting) {
 
712
                HighlightedPosition position= createHighlightedPosition(offset, length, highlighting);
 
713
                synchronized (fPositionLock) {
 
714
                        insertPosition(position);
 
715
                }
 
716
 
 
717
                IDocument document= fSourceViewer.getDocument();
 
718
                if (document == null)
 
719
                        return;
 
720
                String positionCategory= getPositionCategory();
 
721
                try {
 
722
                        document.addPosition(positionCategory, position);
 
723
                } catch (BadLocationException e) {
 
724
                        // Should not happen
 
725
                        CUIPlugin.log(e);
 
726
                } catch (BadPositionCategoryException e) {
 
727
                        // Should not happen
 
728
                        CUIPlugin.log(e);
 
729
                }
 
730
        }
 
731
 
 
732
        /**
 
733
         * Reset to initial state.
 
734
         */
 
735
        private void resetState() {
 
736
                synchronized (fPositionLock) {
 
737
                        fPositions.clear();
 
738
                }
 
739
        }
 
740
 
 
741
        /**
 
742
         * Start managing the given document.
 
743
         *
 
744
         * @param document The document
 
745
         */
 
746
        private void manageDocument(IDocument document) {
 
747
                if (document != null) {
 
748
                        document.addPositionCategory(getPositionCategory());
 
749
                        document.addPositionUpdater(fPositionUpdater);
 
750
                        document.addDocumentListener(this);
 
751
                }
 
752
        }
 
753
 
 
754
        /**
 
755
         * Stop managing the given document.
 
756
         *
 
757
         * @param document The document
 
758
         */
 
759
        private void releaseDocument(IDocument document) {
 
760
                if (document != null) {
 
761
                        document.removeDocumentListener(this);
 
762
                        document.removePositionUpdater(fPositionUpdater);
 
763
                        try {
 
764
                                document.removePositionCategory(getPositionCategory());
 
765
                        } catch (BadPositionCategoryException e) {
 
766
                                // Should not happen
 
767
                                CUIPlugin.log(e);
 
768
                        }
 
769
                }
 
770
        }
 
771
 
 
772
        /**
 
773
         * @return The semantic reconciler position's category.
 
774
         */
 
775
        private String getPositionCategory() {
 
776
                return toString();
 
777
        }
 
778
}