~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/text/folding/DefaultCFoldingStructureProvider.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, 2010 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)
 
11
 *     Markus Schorn (Wind River Systems)
 
12
 *     Elazar Leibovich (IDF) - Code folding of compound statements (bug 174597)
 
13
 *     Andrew Ferguson (Symbian)
 
14
 *******************************************************************************/
 
15
 
 
16
package org.eclipse.cdt.internal.ui.text.folding;
 
17
 
 
18
import java.util.ArrayList;
 
19
import java.util.Collection;
 
20
import java.util.Collections;
 
21
import java.util.Comparator;
 
22
import java.util.HashMap;
 
23
import java.util.Iterator;
 
24
import java.util.LinkedHashMap;
 
25
import java.util.List;
 
26
import java.util.Map;
 
27
import java.util.Stack;
 
28
 
 
29
import org.eclipse.core.runtime.Assert;
 
30
import org.eclipse.core.runtime.IProgressMonitor;
 
31
import org.eclipse.core.runtime.IStatus;
 
32
import org.eclipse.core.runtime.Platform;
 
33
import org.eclipse.core.runtime.Status;
 
34
import org.eclipse.jface.preference.IPreferenceStore;
 
35
import org.eclipse.jface.text.BadLocationException;
 
36
import org.eclipse.jface.text.IDocument;
 
37
import org.eclipse.jface.text.IRegion;
 
38
import org.eclipse.jface.text.ITextSelection;
 
39
import org.eclipse.jface.text.ITypedRegion;
 
40
import org.eclipse.jface.text.Position;
 
41
import org.eclipse.jface.text.Region;
 
42
import org.eclipse.jface.text.TextUtilities;
 
43
import org.eclipse.jface.text.source.Annotation;
 
44
import org.eclipse.jface.text.source.projection.IProjectionListener;
 
45
import org.eclipse.jface.text.source.projection.IProjectionPosition;
 
46
import org.eclipse.jface.text.source.projection.ProjectionAnnotation;
 
47
import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel;
 
48
import org.eclipse.jface.text.source.projection.ProjectionViewer;
 
49
import org.eclipse.jface.viewers.ISelection;
 
50
import org.eclipse.jface.viewers.ISelectionChangedListener;
 
51
import org.eclipse.jface.viewers.SelectionChangedEvent;
 
52
import org.eclipse.ui.texteditor.IDocumentProvider;
 
53
import org.eclipse.ui.texteditor.ITextEditor;
 
54
 
 
55
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
 
56
import org.eclipse.cdt.core.dom.ast.IASTBreakStatement;
 
57
import org.eclipse.cdt.core.dom.ast.IASTCaseStatement;
 
58
import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement;
 
59
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
 
60
import org.eclipse.cdt.core.dom.ast.IASTDefaultStatement;
 
61
import org.eclipse.cdt.core.dom.ast.IASTDoStatement;
 
62
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
 
63
import org.eclipse.cdt.core.dom.ast.IASTForStatement;
 
64
import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator;
 
65
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
 
66
import org.eclipse.cdt.core.dom.ast.IASTIfStatement;
 
67
import org.eclipse.cdt.core.dom.ast.IASTNodeLocation;
 
68
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorElifStatement;
 
69
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorElseStatement;
 
70
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorEndifStatement;
 
71
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIfStatement;
 
72
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIfdefStatement;
 
73
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIfndefStatement;
 
74
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorStatement;
 
75
import org.eclipse.cdt.core.dom.ast.IASTProblem;
 
76
import org.eclipse.cdt.core.dom.ast.IASTStatement;
 
77
import org.eclipse.cdt.core.dom.ast.IASTSwitchStatement;
 
78
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
 
79
import org.eclipse.cdt.core.dom.ast.IASTWhileStatement;
 
80
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTRangeBasedForStatement;
 
81
import org.eclipse.cdt.core.model.CModelException;
 
82
import org.eclipse.cdt.core.model.ICElement;
 
83
import org.eclipse.cdt.core.model.ILanguage;
 
84
import org.eclipse.cdt.core.model.IParent;
 
85
import org.eclipse.cdt.core.model.ISourceRange;
 
86
import org.eclipse.cdt.core.model.ISourceReference;
 
87
import org.eclipse.cdt.core.model.ITranslationUnit;
 
88
import org.eclipse.cdt.core.parser.IProblem;
 
89
import org.eclipse.cdt.ui.CUIPlugin;
 
90
import org.eclipse.cdt.ui.PreferenceConstants;
 
91
import org.eclipse.cdt.ui.text.ICPartitions;
 
92
import org.eclipse.cdt.ui.text.folding.ICFoldingStructureProvider;
 
93
 
 
94
import org.eclipse.cdt.internal.core.dom.parser.ASTQueries;
 
95
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
 
96
import org.eclipse.cdt.internal.core.model.ASTCache;
 
97
 
 
98
import org.eclipse.cdt.internal.ui.editor.ASTProvider;
 
99
import org.eclipse.cdt.internal.ui.editor.CEditor;
 
100
import org.eclipse.cdt.internal.ui.editor.ASTProvider.WAIT_FLAG;
 
101
import org.eclipse.cdt.internal.ui.text.DocumentCharacterIterator;
 
102
import org.eclipse.cdt.internal.ui.text.ICReconcilingListener;
 
103
 
 
104
/**
 
105
 * Default implementation of a {@link ICFoldingStructureProvider}.
 
106
 * <p>
 
107
 * Derived from JDT counterpart.
 
108
 * </p>
 
109
 */
 
110
public class DefaultCFoldingStructureProvider implements ICFoldingStructureProvider {
 
111
 
 
112
        /**
 
113
         * A visitor to collect compound statement positions.
 
114
         *
 
115
         * @since 5.0
 
116
         */
 
117
        private final class StatementVisitor extends ASTVisitor {
 
118
                {
 
119
                        shouldVisitStatements = true;
 
120
                        shouldVisitDeclarations = true;
 
121
                }
 
122
                private final Stack<StatementRegion> fStatements;
 
123
                int fLevel= 0;
 
124
                String fFunction= ""; //$NON-NLS-1$
 
125
 
 
126
                private StatementVisitor(Stack<StatementRegion> statements) {
 
127
                        fStatements = statements;
 
128
                }
 
129
 
 
130
                @Override
 
131
                public int visit(IASTStatement statement) {
 
132
                        ++fLevel;
 
133
                        // if it's not part of the displayed - file, we don't need it
 
134
                        if (!statement.isPartOfTranslationUnitFile())
 
135
                                return PROCESS_SKIP;// we neither need its descendants
 
136
                        try {
 
137
                                StatementRegion mr;
 
138
                                IASTFileLocation fl;
 
139
                                if (statement instanceof IASTIfStatement) {
 
140
                                        IASTIfStatement ifstmt = (IASTIfStatement) statement;
 
141
                                        fl = ifstmt.getFileLocation();
 
142
                                        if (fl==null) return PROCESS_CONTINUE;
 
143
                                        int ifOffset= fl.getNodeOffset();
 
144
                                        IASTStatement thenStmt;
 
145
                                        mr = createRegion();
 
146
                                        thenStmt = ifstmt.getThenClause();
 
147
                                        if (thenStmt==null) return PROCESS_CONTINUE;
 
148
                                        fl = thenStmt.getFileLocation();
 
149
                                        mr.setLength(fl.getNodeOffset() + fl.getNodeLength() - ifOffset);
 
150
                                        mr.setOffset(ifOffset);
 
151
                                        mr.inclusive = !(thenStmt instanceof IASTCompoundStatement);
 
152
                                        IASTStatement elseStmt;
 
153
                                        elseStmt = ifstmt.getElseClause();
 
154
                                        if (elseStmt == null) {
 
155
                                                mr.inclusive = true;
 
156
                                                fStatements.push(mr);
 
157
                                                return PROCESS_CONTINUE;
 
158
                                        }
 
159
                                        IASTFileLocation elseStmtLocation = elseStmt.getFileLocation();
 
160
                                        mr.inclusive = mr.inclusive || fl.getEndingLineNumber() < elseStmtLocation.getStartingLineNumber();
 
161
                                        if (elseStmt instanceof IASTIfStatement) {
 
162
                                                fStatements.push(mr);
 
163
                                                return PROCESS_CONTINUE;
 
164
                                        }
 
165
                                        fStatements.push(mr);
 
166
                                        mr = createRegion();
 
167
                                        mr.setLength(elseStmtLocation.getNodeLength());
 
168
                                        mr.setOffset(elseStmtLocation.getNodeOffset());
 
169
                                        mr.inclusive = true;
 
170
                                        fStatements.push(mr);
 
171
                                        return PROCESS_CONTINUE;
 
172
                                }
 
173
                                mr = createRegion();
 
174
                                mr.inclusive = true;
 
175
                                if (statement instanceof IASTDoStatement)
 
176
                                        mr.inclusive = false;
 
177
                                if (statement instanceof IASTSwitchStatement) {
 
178
                                        IASTStatement switchstmt = ((IASTSwitchStatement)statement).getBody();
 
179
                                        if (switchstmt instanceof IASTCompoundStatement) {
 
180
                                                IASTStatement[] stmts = ((IASTCompoundStatement)switchstmt).getStatements();
 
181
                                                boolean pushedMR = false;
 
182
                                                for (IASTStatement tmpstmt : stmts) {
 
183
                                                        StatementRegion tmpmr;
 
184
                                                        if (!(tmpstmt instanceof IASTCaseStatement || tmpstmt instanceof IASTDefaultStatement)) {
 
185
                                                                if (!pushedMR) return PROCESS_SKIP;
 
186
                                                                IASTFileLocation tmpfl = tmpstmt.getFileLocation();
 
187
                                                                tmpmr = fStatements.peek();
 
188
                                                                tmpmr.setLength(tmpfl.getNodeLength()+tmpfl.getNodeOffset()-tmpmr.getOffset());
 
189
                                                                if (tmpstmt instanceof IASTBreakStatement) pushedMR = false;
 
190
                                                                continue;
 
191
                                                        }
 
192
                                                        IASTFileLocation tmpfl;
 
193
                                                        tmpmr = createRegion();
 
194
                                                        tmpmr.level= fLevel+1;
 
195
                                                        tmpmr.inclusive = true;
 
196
                                                        if (tmpstmt instanceof IASTCaseStatement) {
 
197
                                                                IASTCaseStatement casestmt = (IASTCaseStatement) tmpstmt;
 
198
                                                                tmpfl = casestmt.getExpression().getFileLocation();
 
199
                                                                tmpmr.setOffset(tmpfl.getNodeOffset());
 
200
                                                                tmpmr.setLength(tmpfl.getNodeLength());
 
201
                                                        } else if (tmpstmt instanceof IASTDefaultStatement) {
 
202
                                                                IASTDefaultStatement defstmt = (IASTDefaultStatement) tmpstmt;
 
203
                                                                tmpfl = defstmt.getFileLocation();
 
204
                                                                tmpmr.setOffset(tmpfl.getNodeOffset()+tmpfl.getNodeLength());
 
205
                                                                tmpmr.setLength(0);
 
206
                                                        }
 
207
                                                        fStatements.push(tmpmr);
 
208
                                                        pushedMR = true;
 
209
                                                }
 
210
                                        }
 
211
                                }
 
212
                                if (statement instanceof IASTForStatement
 
213
                                                || statement instanceof IASTWhileStatement
 
214
                                                || statement instanceof IASTDoStatement
 
215
                                                || statement instanceof IASTSwitchStatement
 
216
                                                || statement instanceof ICPPASTRangeBasedForStatement) {
 
217
                                        fl = statement.getFileLocation();
 
218
                                        mr.setLength(fl.getNodeLength());
 
219
                                        mr.setOffset(fl.getNodeOffset());
 
220
                                        fStatements.push(mr);
 
221
                                }
 
222
                                return PROCESS_CONTINUE;
 
223
                        } catch (Exception e) {
 
224
                                CUIPlugin.log(e);
 
225
                                return PROCESS_ABORT;
 
226
                        }
 
227
                }
 
228
 
 
229
                @Override
 
230
                public int leave(IASTStatement statement) {
 
231
                        --fLevel;
 
232
                        return PROCESS_CONTINUE;
 
233
                }
 
234
 
 
235
                @Override
 
236
                public int visit(IASTDeclaration declaration) {
 
237
                        if (!declaration.isPartOfTranslationUnitFile())
 
238
                                return PROCESS_SKIP;// we neither need its descendants
 
239
                        if (declaration instanceof IASTFunctionDefinition) {
 
240
                                final IASTFunctionDeclarator declarator = ((IASTFunctionDefinition)declaration).getDeclarator();
 
241
                                if (declarator != null) {
 
242
                                        fFunction= new String(ASTQueries.findInnermostDeclarator(declarator).getName().toCharArray());
 
243
                                        fLevel= 0;
 
244
                                }
 
245
                        }
 
246
                        return PROCESS_CONTINUE;
 
247
                }
 
248
 
 
249
                @Override
 
250
                public int leave(IASTDeclaration declaration) {
 
251
                        if (declaration instanceof IASTFunctionDefinition) {
 
252
                                fFunction= ""; //$NON-NLS-1$
 
253
                        }
 
254
                        return PROCESS_CONTINUE;
 
255
                }
 
256
 
 
257
                private StatementRegion createRegion() {
 
258
                        return new StatementRegion(fFunction, fLevel);
 
259
                }
 
260
        }
 
261
 
 
262
        /**
 
263
         * Listen to cursor position changes.
 
264
         */
 
265
        private final class SelectionListener implements ISelectionChangedListener {
 
266
                public void selectionChanged(SelectionChangedEvent event) {
 
267
                        ISelection s= event.getSelection();
 
268
                        if (s instanceof ITextSelection) {
 
269
                                ITextSelection selection= (ITextSelection)event.getSelection();
 
270
                                fCursorPosition= selection.getOffset();
 
271
                        }
 
272
                }
 
273
        }
 
274
 
 
275
        /**
 
276
         * Update folding positions triggered by reconciler.
 
277
         */
 
278
        private class FoldingStructureReconciler implements ICReconcilingListener {
 
279
                private volatile boolean fReconciling;
 
280
 
 
281
                /*
 
282
                 * @see org.eclipse.cdt.internal.ui.text.ICReconcilingListener#aboutToBeReconciled()
 
283
                 */
 
284
                public void aboutToBeReconciled() {
 
285
                        
 
286
                }
 
287
 
 
288
                /*
 
289
                 * @see org.eclipse.cdt.internal.ui.text.ICReconcilingListener#reconciled(IASTTranslationUnit, boolean, IProgressMonitor)
 
290
                 */
 
291
                public void reconciled(IASTTranslationUnit ast, boolean force, IProgressMonitor progressMonitor) {
 
292
                        if (fInput == null || progressMonitor.isCanceled()) {
 
293
                                return;
 
294
                        }
 
295
                        synchronized (this) {
 
296
                                if (fReconciling) {
 
297
                                        return;
 
298
                                }
 
299
                                fReconciling= true;
 
300
                        }
 
301
                        try {
 
302
                                final boolean initialReconcile= fInitialReconcilePending;
 
303
                                fInitialReconcilePending= false;
 
304
                                FoldingStructureComputationContext ctx= createContext(initialReconcile);
 
305
                                if (ctx != null) {
 
306
                                        if (initialReconcile || !hasSyntaxError(ast)) {
 
307
                                                ctx.fAST= ast;
 
308
                                        }
 
309
                                        update(ctx);
 
310
                                }
 
311
                        } finally {
 
312
                                fReconciling= false;
 
313
                        }
 
314
                }
 
315
 
 
316
                /**
 
317
                 * Test whether the given ast contains one or more syntax errors.
 
318
                 * 
 
319
                 * @param ast
 
320
                 * @return <code>true</code> if the ast contains a syntax error
 
321
                 */
 
322
                private boolean hasSyntaxError(IASTTranslationUnit ast) {
 
323
                        if (ast == null) {
 
324
                                return false;
 
325
                        }
 
326
                        IASTProblem[] problems= ast.getPreprocessorProblems();
 
327
                        for (IASTProblem problem : problems) {
 
328
                                if ((problem.getID() & (IProblem.SYNTAX_ERROR | IProblem.SCANNER_RELATED)) != 0) {
 
329
                                        return true;
 
330
                                }
 
331
                        }
 
332
                        problems= CPPVisitor.getProblems(ast);
 
333
                        for (IASTProblem problem : problems) {
 
334
                                if ((problem.getID() & (IProblem.SYNTAX_ERROR | IProblem.SCANNER_RELATED)) != 0) {
 
335
                                        return true;
 
336
                                }
 
337
                        }
 
338
                        return false;
 
339
                }
 
340
 
 
341
        }
 
342
 
 
343
 
 
344
        /**
 
345
         * A context that contains the information needed to compute the folding structure of an
 
346
         * {@link ITranslationUnit}. Computed folding regions are collected via
 
347
         * {@linkplain #addProjectionRange(DefaultCFoldingStructureProvider.CProjectionAnnotation, Position) addProjectionRange}.
 
348
         */
 
349
        protected final class FoldingStructureComputationContext {
 
350
                private final ProjectionAnnotationModel fModel;
 
351
                private final IDocument fDocument;
 
352
                private final boolean fAllowCollapsing;
 
353
 
 
354
                private ISourceReference fFirstType;
 
355
                private boolean fHasHeaderComment;
 
356
                private LinkedHashMap<CProjectionAnnotation,Position> fMap= new LinkedHashMap<CProjectionAnnotation,Position>();
 
357
                private IASTTranslationUnit fAST;
 
358
 
 
359
                FoldingStructureComputationContext(IDocument document, ProjectionAnnotationModel model, boolean allowCollapsing) {
 
360
                        Assert.isNotNull(document);
 
361
                        Assert.isNotNull(model);
 
362
                        fDocument= document;
 
363
                        fModel= model;
 
364
                        fAllowCollapsing= allowCollapsing;
 
365
                }
 
366
                
 
367
                void setFirstType(ISourceReference reference) {
 
368
                        if (hasFirstType())
 
369
                                throw new IllegalStateException();
 
370
                        fFirstType= reference;
 
371
                }
 
372
                
 
373
                boolean hasFirstType() {
 
374
                        return fFirstType != null;
 
375
                }
 
376
                
 
377
                ISourceReference getFirstType() {
 
378
                        return fFirstType;
 
379
                }
 
380
 
 
381
                boolean hasHeaderComment() {
 
382
                        return fHasHeaderComment;
 
383
                }
 
384
 
 
385
                void setHasHeaderComment() {
 
386
                        fHasHeaderComment= true;
 
387
                }
 
388
                
 
389
                /**
 
390
                 * Returns <code>true</code> if newly created folding regions may be collapsed,
 
391
                 * <code>false</code> if not. This is usually <code>false</code> when updating the
 
392
                 * folding structure while typing; it may be <code>true</code> when computing or restoring
 
393
                 * the initial folding structure.
 
394
                 * 
 
395
                 * @return <code>true</code> if newly created folding regions may be collapsed,
 
396
                 *         <code>false</code> if not
 
397
                 */
 
398
                public boolean allowCollapsing() {
 
399
                        return fAllowCollapsing;
 
400
                }
 
401
 
 
402
                /**
 
403
                 * Returns the document which contains the code being folded.
 
404
                 * 
 
405
                 * @return the document which contains the code being folded
 
406
                 */
 
407
                IDocument getDocument() {
 
408
                        return fDocument;
 
409
                }
 
410
 
 
411
                ProjectionAnnotationModel getModel() {
 
412
                        return fModel;
 
413
                }
 
414
                
 
415
                /**
 
416
                 * Adds a projection (folding) region to this context. The created annotation / position
 
417
                 * pair will be added to the {@link ProjectionAnnotationModel} of the
 
418
                 * {@link ProjectionViewer} of the editor.
 
419
                 * 
 
420
                 * @param annotation the annotation to add
 
421
                 * @param position the corresponding position
 
422
                 */
 
423
                public void addProjectionRange(CProjectionAnnotation annotation, Position position) {
 
424
                        fMap.put(annotation, position);
 
425
                }
 
426
 
 
427
                /**
 
428
                 * Returns <code>true</code> if header comments should be collapsed.
 
429
                 * 
 
430
                 * @return <code>true</code> if header comments should be collapsed
 
431
                 */
 
432
                public boolean collapseHeaderComments() {
 
433
                        return fAllowCollapsing && fCollapseHeaderComments;
 
434
                }
 
435
 
 
436
                /**
 
437
                 * Returns <code>true</code> if comments should be collapsed.
 
438
                 * 
 
439
                 * @return <code>true</code> if comments should be collapsed
 
440
                 */
 
441
                public boolean collapseComments() {
 
442
                        return fAllowCollapsing && fCollapseComments;
 
443
                }
 
444
 
 
445
                /**
 
446
                 * Returns <code>true</code> if functions should be collapsed.
 
447
                 * 
 
448
                 * @return <code>true</code> if functions should be collapsed
 
449
                 */
 
450
                public boolean collapseFunctions() {
 
451
                        return fAllowCollapsing && fCollapseFunctions;
 
452
                }
 
453
 
 
454
                /**
 
455
                 * Returns <code>true</code> if macros should be collapsed.
 
456
                 * 
 
457
                 * @return <code>true</code> if macros should be collapsed
 
458
                 */
 
459
                public boolean collapseMacros() {
 
460
                        return fAllowCollapsing && fCollapseMacros;
 
461
                }
 
462
 
 
463
                /**
 
464
                 * Returns <code>true</code> if methods should be collapsed.
 
465
                 * 
 
466
                 * @return <code>true</code> if methods should be collapsed
 
467
                 */
 
468
                public boolean collapseMethods() {
 
469
                        return fAllowCollapsing && fCollapseMethods;
 
470
                }
 
471
 
 
472
                /**
 
473
                 * Returns <code>true</code> if structures should be collapsed.
 
474
                 * 
 
475
                 * @return <code>true</code> if structures should be collapsed
 
476
                 */
 
477
                public boolean collapseStructures() {
 
478
                        return fAllowCollapsing && fCollapseStructures;
 
479
                }
 
480
 
 
481
                /**
 
482
                 * Returns <code>true</code> if inactive code should be collapsed.
 
483
                 * 
 
484
                 * @return <code>true</code> if inactive code should be collapsed
 
485
                 */
 
486
                public boolean collapseInactiveCode() {
 
487
                        return fAllowCollapsing && fCollapseInactiveCode;
 
488
                }
 
489
 
 
490
                /**
 
491
                 * @return the current AST or <code>null</code>
 
492
                 */
 
493
                public IASTTranslationUnit getAST() {
 
494
                        return fAST;
 
495
                }
 
496
        }
 
497
        
 
498
 
 
499
        private static class CProjectionAnnotation extends ProjectionAnnotation {
 
500
 
 
501
                public final static int CMODEL= 0;
 
502
                public final static int COMMENT= 1;
 
503
                public final static int BRANCH= 2;
 
504
                public final static int STATEMENT= 3;
 
505
                
 
506
                private Object fKey;
 
507
                private int fCategory;
 
508
                
 
509
                public CProjectionAnnotation(boolean isCollapsed, Object key, boolean isComment) {
 
510
                        this(isCollapsed, key, isComment ? COMMENT : 0);
 
511
                }
 
512
                
 
513
                public CProjectionAnnotation(boolean isCollapsed, Object key, int category) {
 
514
                        super(isCollapsed);
 
515
                        fKey= key;
 
516
                        fCategory= category;
 
517
                }
 
518
                
 
519
                public Object getElement() {
 
520
                        return fKey;
 
521
                }
 
522
                
 
523
                public void setElement(Object element) {
 
524
                        fKey= element;
 
525
                }
 
526
                
 
527
                public int getCategory() {
 
528
                        return fCategory;
 
529
                }
 
530
 
 
531
//              public void setCategory(int category) {
 
532
//                      fCategory = category;
 
533
//              }
 
534
//
 
535
//              public boolean isComment() {
 
536
//                      return fCategory == COMMENT;
 
537
//              }
 
538
//              
 
539
//              public void setIsComment(boolean isComment) {
 
540
//                      fCategory= isComment ? COMMENT : 0;
 
541
//              }
 
542
                /*
 
543
                 * @see java.lang.Object#toString()
 
544
                 */
 
545
                @Override
 
546
                public String toString() {
 
547
                        return "CProjectionAnnotation:\n" + //$NON-NLS-1$
 
548
                                        "\tkey: \t"+ fKey + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
 
549
                                        "\tcollapsed: \t" + isCollapsed() + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
 
550
                                        "\tcategory: \t" + getCategory() + "\n"; //$NON-NLS-1$ //$NON-NLS-2$
 
551
                }
 
552
        }
 
553
        
 
554
 
 
555
        private static final class Tuple {
 
556
                CProjectionAnnotation annotation;
 
557
                Position position;
 
558
                Tuple(CProjectionAnnotation annotation, Position position) {
 
559
                        this.annotation= annotation;
 
560
                        this.position= position;
 
561
                }
 
562
        }
 
563
 
 
564
        private static final class Counter {
 
565
                int fCount;
 
566
        }
 
567
        
 
568
        /**
 
569
         * Projection position that will return two foldable regions: one folding away
 
570
         * the region from after the '/*' to the beginning of the content, the other
 
571
         * from after the first content line until after the comment.
 
572
         */
 
573
        private static final class CommentPosition extends Position implements IProjectionPosition {
 
574
                CommentPosition(int offset, int length) {
 
575
                        super(offset, length);
 
576
                }
 
577
 
 
578
                /*
 
579
                 * @see org.eclipse.jface.text.source.projection.IProjectionPosition#computeFoldingRegions(org.eclipse.jface.text.IDocument)
 
580
                 */
 
581
                public IRegion[] computeProjectionRegions(IDocument document) throws BadLocationException {
 
582
                        DocumentCharacterIterator sequence= new DocumentCharacterIterator(document, offset, offset + length);
 
583
                        int prefixEnd= 0;
 
584
                        int contentStart= findFirstContent(sequence, prefixEnd);
 
585
 
 
586
                        int firstLine= document.getLineOfOffset(offset + prefixEnd);
 
587
                        int captionLine= document.getLineOfOffset(offset + contentStart);
 
588
                        int lastLine= document.getLineOfOffset(offset + length);
 
589
 
 
590
                        Assert.isTrue(firstLine <= captionLine, "first folded line is greater than the caption line"); //$NON-NLS-1$
 
591
                        Assert.isTrue(captionLine <= lastLine, "caption line is greater than the last folded line"); //$NON-NLS-1$
 
592
 
 
593
                        IRegion preRegion;
 
594
                        if (firstLine < captionLine) {
 
595
                                int preOffset= document.getLineOffset(firstLine);
 
596
                                IRegion preEndLineInfo= document.getLineInformation(captionLine);
 
597
                                int preEnd= preEndLineInfo.getOffset();
 
598
                                preRegion= new Region(preOffset, preEnd - preOffset);
 
599
                        } else {
 
600
                                preRegion= null;
 
601
                        }
 
602
 
 
603
                        if (captionLine < lastLine) {
 
604
                                int postOffset= document.getLineOffset(captionLine + 1);
 
605
                                IRegion postRegion= new Region(postOffset, offset + length - postOffset);
 
606
 
 
607
                                if (preRegion == null)
 
608
                                        return new IRegion[] { postRegion };
 
609
 
 
610
                                return new IRegion[] { preRegion, postRegion };
 
611
                        }
 
612
 
 
613
                        if (preRegion != null)
 
614
                                return new IRegion[] { preRegion };
 
615
 
 
616
                        return null;
 
617
                }
 
618
 
 
619
                /**
 
620
                 * Finds the offset of the first identifier part within <code>content</code>.
 
621
                 * Returns 0 if none is found.
 
622
                 *
 
623
                 * @param content the content to search
 
624
                 * @return the first index of a unicode identifier part, or zero if none can
 
625
                 *         be found
 
626
                 */
 
627
                private int findFirstContent(final CharSequence content, int prefixEnd) {
 
628
                        int lenght= content.length();
 
629
                        for (int i= prefixEnd; i < lenght; i++) {
 
630
                                if (Character.isUnicodeIdentifierPart(content.charAt(i)))
 
631
                                        return i;
 
632
                        }
 
633
                        return 0;
 
634
                }
 
635
 
 
636
                /*
 
637
                 * @see org.eclipse.jface.text.source.projection.IProjectionPosition#computeCaptionOffset(org.eclipse.jface.text.IDocument)
 
638
                 */
 
639
                public int computeCaptionOffset(IDocument document) {
 
640
                        DocumentCharacterIterator sequence= new DocumentCharacterIterator(document, offset, offset + length);
 
641
                        return findFirstContent(sequence, 0);
 
642
                }
 
643
        }
 
644
 
 
645
        /**
 
646
         * Projection position that will return two foldable regions: one folding away
 
647
         * the lines before the one containing the simple name of the C element, one
 
648
         * folding away any lines after the caption.
 
649
         */
 
650
        private static final class CElementPosition extends Position implements IProjectionPosition {
 
651
 
 
652
                private ICElement fElement;
 
653
 
 
654
                public CElementPosition(int offset, int length, ICElement element) {
 
655
                        super(offset, length);
 
656
                        Assert.isNotNull(element);
 
657
                        fElement= element;
 
658
                }
 
659
                
 
660
                public void setElement(ICElement member) {
 
661
                        Assert.isNotNull(member);
 
662
                        fElement= member;
 
663
                }
 
664
                
 
665
                /*
 
666
                 * @see org.eclipse.jface.text.source.projection.IProjectionPosition#computeFoldingRegions(org.eclipse.jface.text.IDocument)
 
667
                 */
 
668
                public IRegion[] computeProjectionRegions(IDocument document) throws BadLocationException {
 
669
                        int captionOffset= offset;
 
670
                        try {
 
671
                                /* The member's name range may not be correct. However,
 
672
                                 * reconciling would trigger another element delta which would
 
673
                                 * lead to reentrant situations. Therefore, we optimistically
 
674
                                 * assume that the name range is correct, but double check the
 
675
                                 * received lines below. */
 
676
                                if (fElement instanceof ISourceReference) {
 
677
                                        ISourceRange sourceRange= ((ISourceReference) fElement).getSourceRange();
 
678
                                        if (sourceRange != null) {
 
679
                                                // Use end of name range for the caption offset
 
680
                                                // in case a qualified name is split on multiple lines (bug 248613).
 
681
                                                captionOffset= sourceRange.getIdStartPos() + sourceRange.getIdLength() - 1;
 
682
                                        }
 
683
                                }
 
684
                        } catch (CModelException e) {
 
685
                                // ignore and use default
 
686
                        }
 
687
 
 
688
                        int firstLine= document.getLineOfOffset(offset);
 
689
                        int captionLine= document.getLineOfOffset(captionOffset);
 
690
                        int lastLine= document.getLineOfOffset(offset + length);
 
691
 
 
692
                        /* see comment above - adjust the caption line to be inside the
 
693
                         * entire folded region, and rely on later element deltas to correct
 
694
                         * the name range. */
 
695
                        if (captionLine < firstLine)
 
696
                                captionLine= firstLine;
 
697
                        if (captionLine > lastLine)
 
698
                                captionLine= lastLine;
 
699
 
 
700
                        IRegion preRegion;
 
701
                        if (firstLine < captionLine) {
 
702
                                int preOffset= document.getLineOffset(firstLine);
 
703
                                IRegion preEndLineInfo= document.getLineInformation(captionLine);
 
704
                                int preEnd= preEndLineInfo.getOffset();
 
705
                                preRegion= new Region(preOffset, preEnd - preOffset);
 
706
                        } else {
 
707
                                preRegion= null;
 
708
                        }
 
709
 
 
710
                        if (captionLine < lastLine) {
 
711
                                int postOffset= document.getLineOffset(captionLine + 1);
 
712
                                IRegion postRegion= new Region(postOffset, offset + length - postOffset);
 
713
 
 
714
                                if (preRegion == null)
 
715
                                        return new IRegion[] { postRegion };
 
716
 
 
717
                                return new IRegion[] { preRegion, postRegion };
 
718
                        }
 
719
 
 
720
                        if (preRegion != null)
 
721
                                return new IRegion[] { preRegion };
 
722
 
 
723
                        return null;
 
724
                }
 
725
 
 
726
                /*
 
727
                 * @see org.eclipse.jface.text.source.projection.IProjectionPosition#computeCaptionOffset(org.eclipse.jface.text.IDocument)
 
728
                 */
 
729
                public int computeCaptionOffset(IDocument document) throws BadLocationException {
 
730
                        int captionOffset= offset;
 
731
                        try {
 
732
                                // need a reconcile here?
 
733
                                if (fElement instanceof ISourceReference) {
 
734
                                        ISourceRange sourceRange= ((ISourceReference) fElement).getSourceRange();
 
735
                                        if (sourceRange != null) {
 
736
                                                captionOffset= sourceRange.getIdStartPos() + sourceRange.getIdLength() - 1;
 
737
                                                if (captionOffset < offset) {
 
738
                                                        captionOffset= offset;
 
739
                                                }
 
740
                                        }
 
741
                                }
 
742
                        } catch (CModelException e) {
 
743
                                // ignore and use default
 
744
                        }
 
745
 
 
746
                        return captionOffset - offset;
 
747
                }
 
748
 
 
749
        }
 
750
        
 
751
        /**
 
752
         * Internal projection listener.
 
753
         */
 
754
        private final class ProjectionListener implements IProjectionListener {
 
755
                private ProjectionViewer fViewer;
 
756
 
 
757
                /**
 
758
                 * Registers the listener with the viewer.
 
759
                 * 
 
760
                 * @param viewer the viewer to register a listener with
 
761
                 */
 
762
                public ProjectionListener(ProjectionViewer viewer) {
 
763
                        Assert.isLegal(viewer != null);
 
764
                        fViewer= viewer;
 
765
                        fViewer.addProjectionListener(this);
 
766
                }
 
767
                
 
768
                /**
 
769
                 * Disposes of this listener and removes the projection listener from the viewer.
 
770
                 */
 
771
                public void dispose() {
 
772
                        if (fViewer != null) {
 
773
                                fViewer.removeProjectionListener(this);
 
774
                                fViewer= null;
 
775
                        }
 
776
                }
 
777
                
 
778
                /*
 
779
                 * @see org.eclipse.jface.text.source.projection.IProjectionListener#projectionEnabled()
 
780
                 */
 
781
                public void projectionEnabled() {
 
782
                        handleProjectionEnabled();
 
783
                }
 
784
 
 
785
                /*
 
786
                 * @see org.eclipse.jface.text.source.projection.IProjectionListener#projectionDisabled()
 
787
                 */
 
788
                public void projectionDisabled() {
 
789
                        handleProjectionDisabled();
 
790
                }
 
791
        }
 
792
 
 
793
        /**
 
794
         * Implementation of <code>IRegion</code> that can be reused
 
795
         * by setting the offset and the length.
 
796
         */
 
797
        private static class ModifiableRegion extends Position implements IRegion {
 
798
                ModifiableRegion() {
 
799
                        super();
 
800
                }
 
801
                ModifiableRegion(int offset, int length) {
 
802
                        super(offset, length);
 
803
                }
 
804
        }
 
805
 
 
806
        /**
 
807
         * Representation of a preprocessor code branch.
 
808
         */
 
809
        private static class Branch extends ModifiableRegion {
 
810
 
 
811
                private final boolean fTaken;
 
812
                public final String fCondition;
 
813
                public boolean fInclusive;
 
814
 
 
815
                Branch(int offset, boolean taken, String key) {
 
816
                        this(offset, 0, taken, key);
 
817
                }
 
818
 
 
819
                Branch(int offset, int length, boolean taken, String key) {
 
820
                        super(offset, length);
 
821
                        fTaken= taken;
 
822
                        fCondition= key;
 
823
                }
 
824
 
 
825
                public void setEndOffset(int endOffset) {
 
826
                        setLength(endOffset - getOffset());
 
827
                }
 
828
 
 
829
                public boolean taken() {
 
830
                        return fTaken;
 
831
                }
 
832
 
 
833
                public void setInclusive(boolean inclusive) {
 
834
                        fInclusive= inclusive;
 
835
                }
 
836
        }
 
837
 
 
838
        private final static boolean DEBUG= "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.cdt.ui/debug/folding"));  //$NON-NLS-1$//$NON-NLS-2$;
 
839
 
 
840
        private ITextEditor fEditor;
 
841
        private ProjectionListener fProjectionListener;
 
842
        protected ICElement fInput;
 
843
        
 
844
        private boolean fCollapseHeaderComments= true;
 
845
        private boolean fCollapseComments= false;
 
846
        private boolean fCollapseMacros= false;
 
847
        private boolean fCollapseFunctions= true;
 
848
        private boolean fCollapseStructures= true;
 
849
        private boolean fCollapseMethods= false;
 
850
        private boolean fCollapseInactiveCode= true;
 
851
        
 
852
        private int fMinCommentLines= 1;
 
853
        private boolean fPreprocessorBranchFoldingEnabled= true;
 
854
        private boolean fStatementsFoldingEnabled= false;
 
855
        private boolean fCommentFoldingEnabled= true;
 
856
 
 
857
        private ICReconcilingListener fReconilingListener;
 
858
        private volatile boolean fInitialReconcilePending= true;
 
859
 
 
860
        private int fCursorPosition;
 
861
 
 
862
        private SelectionListener fSelectionListener;
 
863
 
 
864
 
 
865
        /**
 
866
         * Creates a new folding provider. It must be
 
867
         * {@link #install(ITextEditor, ProjectionViewer) installed} on an editor/viewer pair before it
 
868
         * can be used, and {@link #uninstall() uninstalled} when not used any longer.
 
869
         * <p>
 
870
         * The projection state may be reset by calling {@link #initialize()}.
 
871
         * </p>
 
872
         */
 
873
        public DefaultCFoldingStructureProvider() {
 
874
        }
 
875
        
 
876
        /*
 
877
         * @see org.eclipse.cdt.ui.text.folding.ICFoldingStructureProvider#install(org.eclipse.ui.texteditor.ITextEditor, org.eclipse.jface.text.source.projection.ProjectionViewer)
 
878
         */
 
879
        public void install(ITextEditor editor, ProjectionViewer viewer) {
 
880
                Assert.isLegal(editor != null);
 
881
                Assert.isLegal(viewer != null);
 
882
 
 
883
                internalUninstall();
 
884
                
 
885
                if (editor instanceof CEditor) {
 
886
                        fEditor= editor;
 
887
                        fProjectionListener= new ProjectionListener(viewer);
 
888
                }
 
889
        }
 
890
 
 
891
        /*
 
892
         * @see org.eclipse.cdt.ui.text.folding.ICFoldingStructureProvider#uninstall()
 
893
         */
 
894
        public void uninstall() {
 
895
                internalUninstall();
 
896
        }
 
897
        
 
898
        /**
 
899
         * Internal implementation of {@link #uninstall()}.
 
900
         */
 
901
        private void internalUninstall() {
 
902
                if (isInstalled()) {
 
903
                        handleProjectionDisabled();
 
904
                        fProjectionListener.dispose();
 
905
                        fProjectionListener= null;
 
906
                        fEditor= null;
 
907
                }
 
908
        }
 
909
 
 
910
        /**
 
911
         * Returns <code>true</code> if the provider is installed, <code>false</code> otherwise.
 
912
         * 
 
913
         * @return <code>true</code> if the provider is installed, <code>false</code> otherwise
 
914
         */
 
915
        protected final boolean isInstalled() {
 
916
                return fEditor != null;
 
917
        }
 
918
                
 
919
        /**
 
920
         * Called whenever projection is enabled, for example when the viewer issues a
 
921
         * {@link IProjectionListener#projectionEnabled() projectionEnabled} message. When the provider
 
922
         * is already enabled when this method is called, it is first
 
923
         * {@link #handleProjectionDisabled() disabled}.
 
924
         * <p>
 
925
         * Subclasses may extend.
 
926
         * </p>
 
927
         */
 
928
        protected void handleProjectionEnabled() {
 
929
                if (DEBUG) System.out.println("DefaultCFoldingStructureProvider.handleProjectionEnabled()"); //$NON-NLS-1$
 
930
                // projectionEnabled messages are not always paired with projectionDisabled
 
931
                // i.e. multiple enabled messages may be sent out.
 
932
                // we have to make sure that we disable first when getting an enable
 
933
                // message.
 
934
                handleProjectionDisabled();
 
935
 
 
936
                if (fEditor instanceof CEditor) {
 
937
                        initialize();
 
938
                        fReconilingListener= new FoldingStructureReconciler();
 
939
                        ((CEditor)fEditor).addReconcileListener(fReconilingListener);
 
940
                        fSelectionListener= new SelectionListener();
 
941
                        fEditor.getSelectionProvider().addSelectionChangedListener(fSelectionListener);
 
942
                }
 
943
        }
 
944
 
 
945
        /**
 
946
         * Called whenever projection is disabled, for example when the provider is
 
947
         * {@link #uninstall() uninstalled}, when the viewer issues a
 
948
         * {@link IProjectionListener#projectionDisabled() projectionDisabled} message and before
 
949
         * {@link #handleProjectionEnabled() enabling} the provider. Implementations must be prepared to
 
950
         * handle multiple calls to this method even if the provider is already disabled.
 
951
         * <p>
 
952
         * Subclasses may extend.
 
953
         * </p>
 
954
         */
 
955
        protected void handleProjectionDisabled() {
 
956
                if (fReconilingListener != null) {
 
957
                        ((CEditor)fEditor).removeReconcileListener(fReconilingListener);
 
958
                        fReconilingListener= null;
 
959
                }
 
960
                if (fSelectionListener != null) {
 
961
                        fEditor.getSelectionProvider().removeSelectionChangedListener(fSelectionListener);
 
962
                        fSelectionListener= null;
 
963
                }
 
964
        }
 
965
 
 
966
        /*
 
967
         * @see org.eclipse.cdt.ui.text.folding.ICFoldingStructureProvider#initialize()
 
968
         */
 
969
        public final void initialize() {
 
970
                if (DEBUG) System.out.println("DefaultCFoldingStructureProvider.initialize()"); //$NON-NLS-1$
 
971
                fInitialReconcilePending= true;
 
972
                fCursorPosition= -1;
 
973
                update(createInitialContext());
 
974
        }
 
975
 
 
976
        private FoldingStructureComputationContext createInitialContext() {
 
977
                initializePreferences();
 
978
                fInput= getInputElement();
 
979
                if (fInput == null)
 
980
                        return null;
 
981
                
 
982
                return createContext(true);
 
983
        }
 
984
 
 
985
        private FoldingStructureComputationContext createContext(boolean allowCollapse) {
 
986
                if (!isInstalled())
 
987
                        return null;
 
988
                ProjectionAnnotationModel model= getModel();
 
989
                if (model == null)
 
990
                        return null;
 
991
                IDocument doc= getDocument();
 
992
                if (doc == null)
 
993
                        return null;
 
994
                
 
995
                return new FoldingStructureComputationContext(doc, model, allowCollapse);
 
996
        }
 
997
        
 
998
        private ICElement getInputElement() {
 
999
                if (fEditor instanceof CEditor) {
 
1000
                        return ((CEditor)fEditor).getInputCElement();
 
1001
                }
 
1002
                return null;
 
1003
        }
 
1004
 
 
1005
        private void initializePreferences() {
 
1006
                IPreferenceStore store= CUIPlugin.getDefault().getPreferenceStore();
 
1007
                fCollapseFunctions= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_FUNCTIONS);
 
1008
                fCollapseStructures= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_STRUCTURES);
 
1009
                fCollapseMacros= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_MACROS);
 
1010
                fCollapseMethods= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_METHODS);
 
1011
                fCollapseHeaderComments= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_HEADERS);
 
1012
                fCollapseComments= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_COMMENTS);
 
1013
                fCollapseInactiveCode= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_INACTIVE_CODE);
 
1014
                fPreprocessorBranchFoldingEnabled= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_PREPROCESSOR_BRANCHES_ENABLED);
 
1015
                fStatementsFoldingEnabled= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_STATEMENTS);
 
1016
                fCommentFoldingEnabled = true;
 
1017
        }
 
1018
 
 
1019
        private void update(FoldingStructureComputationContext ctx) {
 
1020
            if (ctx == null || !isConsistent(fInput))
 
1021
                        return;
 
1022
 
 
1023
                if (!fInitialReconcilePending && fSelectionListener != null) {
 
1024
                        fEditor.getSelectionProvider().removeSelectionChangedListener(fSelectionListener);
 
1025
                        fSelectionListener= null;
 
1026
                }
 
1027
 
 
1028
                Map<CProjectionAnnotation,Position> additions= new HashMap<CProjectionAnnotation,Position>();
 
1029
                List<CProjectionAnnotation> deletions= new ArrayList<CProjectionAnnotation>();
 
1030
                List<CProjectionAnnotation> updates= new ArrayList<CProjectionAnnotation>();
 
1031
                
 
1032
                computeFoldingStructure(ctx);
 
1033
                Map<CProjectionAnnotation,Position> updated= ctx.fMap;
 
1034
                Map<Object, List<Tuple>> previous= computeCurrentStructure(ctx);
 
1035
 
 
1036
                Iterator<CProjectionAnnotation> e= updated.keySet().iterator();
 
1037
                while (e.hasNext()) {
 
1038
                        CProjectionAnnotation newAnnotation= e.next();
 
1039
                        Object key= newAnnotation.getElement();
 
1040
                        Position newPosition= updated.get(newAnnotation);
 
1041
 
 
1042
                        List<Tuple> annotations= previous.get(key);
 
1043
                        if (annotations == null) {
 
1044
                                if (DEBUG) System.out.println("DefaultCFoldingStructureProvider.update() new annotation " + newAnnotation); //$NON-NLS-1$
 
1045
 
 
1046
                                additions.put(newAnnotation, newPosition);
 
1047
 
 
1048
                        } else {
 
1049
                                Iterator<Tuple> x= annotations.iterator();
 
1050
                                boolean matched= false;
 
1051
                                while (x.hasNext()) {
 
1052
                                        Tuple tuple= x.next();
 
1053
                                        CProjectionAnnotation existingAnnotation= tuple.annotation;
 
1054
                                        Position existingPosition= tuple.position;
 
1055
                                        if (newAnnotation.getCategory() == existingAnnotation.getCategory()) {
 
1056
                                                final boolean collapseChanged = ctx.allowCollapsing() && existingAnnotation.isCollapsed() != newAnnotation.isCollapsed();
 
1057
                                                if (existingPosition != null && (collapseChanged || !newPosition.equals(existingPosition))) {
 
1058
                                                        existingPosition.setOffset(newPosition.getOffset());
 
1059
                                                        existingPosition.setLength(newPosition.getLength());
 
1060
                                                        if (collapseChanged) {
 
1061
                                                                if (DEBUG) System.out.println("DefaultCFoldingStructureProvider.update() change annotation " + newAnnotation); //$NON-NLS-1$
 
1062
                                                                if (newAnnotation.isCollapsed())
 
1063
                                                                        existingAnnotation.markCollapsed();
 
1064
                                                                else
 
1065
                                                                        existingAnnotation.markExpanded();
 
1066
                                                        }
 
1067
                                                        updates.add(existingAnnotation);
 
1068
                                                }
 
1069
                                                matched= true;
 
1070
                                                x.remove();
 
1071
                                                break;
 
1072
                                        }
 
1073
                                }
 
1074
                                if (!matched) {
 
1075
                                        if (DEBUG) System.out.println("DefaultCFoldingStructureProvider.update() new annotation " + newAnnotation); //$NON-NLS-1$
 
1076
 
 
1077
                                        additions.put(newAnnotation, newPosition);
 
1078
                                }
 
1079
                                if (annotations.isEmpty())
 
1080
                                        previous.remove(key);
 
1081
                        }
 
1082
                }
 
1083
 
 
1084
                Iterator<List<Tuple>> e2= previous.values().iterator();
 
1085
                while (e2.hasNext()) {
 
1086
                        List<Tuple> list= e2.next();
 
1087
                        int size= list.size();
 
1088
                        for (int i= 0; i < size; i++) {
 
1089
                                CProjectionAnnotation annotation= list.get(i).annotation;
 
1090
                                if (DEBUG) System.out.println("DefaultCFoldingStructureProvider.update() deleted annotation " + annotation); //$NON-NLS-1$
 
1091
                                deletions.add(annotation);
 
1092
                        }
 
1093
                }
 
1094
 
 
1095
                match(deletions, additions, updates, ctx);
 
1096
 
 
1097
                Annotation[] removals= new Annotation[deletions.size()];
 
1098
                deletions.toArray(removals);
 
1099
                Annotation[] changes= new Annotation[updates.size()];
 
1100
                updates.toArray(changes);
 
1101
                if (DEBUG) System.out.println("DefaultCFoldingStructureProvider.update() "+removals.length+" deleted, "+additions.size()+" added, "+changes.length+" changed"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
 
1102
                ctx.getModel().modifyAnnotations(removals, additions, changes);
 
1103
    }
 
1104
 
 
1105
        /**
 
1106
         * Matches deleted annotations to changed or added ones. A deleted
 
1107
         * annotation/position tuple that has a matching addition / change
 
1108
         * is updated and marked as changed. The matching tuple is not added
 
1109
         * (for additions) or marked as deletion instead (for changes). The
 
1110
         * result is that more annotations are changed and fewer get
 
1111
         * deleted/re-added.
 
1112
         */
 
1113
        private void match(List<CProjectionAnnotation> deletions, Map<CProjectionAnnotation,Position> additions,
 
1114
                        List<CProjectionAnnotation> changes, FoldingStructureComputationContext ctx) {
 
1115
                if (deletions.isEmpty() || (additions.isEmpty() && changes.isEmpty()))
 
1116
                        return;
 
1117
 
 
1118
                List<CProjectionAnnotation> newDeletions= new ArrayList<CProjectionAnnotation>();
 
1119
                List<CProjectionAnnotation> newChanges= new ArrayList<CProjectionAnnotation>();
 
1120
 
 
1121
                Iterator<CProjectionAnnotation> deletionIterator= deletions.iterator();
 
1122
                while (deletionIterator.hasNext()) {
 
1123
                        CProjectionAnnotation deleted= deletionIterator.next();
 
1124
                        Position deletedPosition= ctx.getModel().getPosition(deleted);
 
1125
                        if (deletedPosition == null || deletedPosition.length < 5)
 
1126
                                continue;
 
1127
                        
 
1128
                        Tuple deletedTuple= new Tuple(deleted, deletedPosition);
 
1129
 
 
1130
                        Tuple match= findMatch(deletedTuple, changes, null, ctx);
 
1131
                        boolean addToDeletions= true; 
 
1132
                        if (match == null) {
 
1133
                                match= findMatch(deletedTuple, additions.keySet(), additions, ctx);
 
1134
                                addToDeletions= false;
 
1135
                        }
 
1136
                        
 
1137
                        if (match != null) {
 
1138
                                Object element= match.annotation.getElement();
 
1139
                                deleted.setElement(element);
 
1140
                                deletedPosition.setLength(match.position.getLength());
 
1141
                                if (deletedPosition instanceof CElementPosition && element instanceof ICElement) {
 
1142
                                        CElementPosition cep= (CElementPosition) deletedPosition;
 
1143
                                        cep.setElement((ICElement) element);
 
1144
                                }
 
1145
 
 
1146
                                deletionIterator.remove();
 
1147
                                if (DEBUG) System.out.println("DefaultCFoldingStructureProvider.update() changed annotation " + deleted); //$NON-NLS-1$
 
1148
                                newChanges.add(deleted);
 
1149
 
 
1150
                                if (addToDeletions) {
 
1151
                                        if (DEBUG) System.out.println("DefaultCFoldingStructureProvider.update() deleted annotation " + match.annotation); //$NON-NLS-1$
 
1152
                                        newDeletions.add(match.annotation);
 
1153
                                }
 
1154
                        }
 
1155
                }
 
1156
 
 
1157
                deletions.addAll(newDeletions);
 
1158
                changes.addAll(newChanges);
 
1159
        }
 
1160
 
 
1161
        /**
 
1162
         * Finds a match for <code>tuple</code> in a collection of
 
1163
         * annotations. The positions for the
 
1164
         * <code>CProjectionAnnotation</code> instances in
 
1165
         * <code>annotations</code> can be found in the passed
 
1166
         * <code>positionMap</code> or in the model if
 
1167
         * <code>positionMap</code> is <code>null</code>.
 
1168
         * <p>
 
1169
         * A tuple is said to match another if their annotations have the
 
1170
         * same category and their position offsets are equal.
 
1171
         * </p>
 
1172
         * <p>
 
1173
         * If a match is found, the annotation gets removed from
 
1174
         * <code>annotations</code>.
 
1175
         * </p>
 
1176
         * 
 
1177
         * @param tuple the tuple for which we want to find a match
 
1178
         * @param annotations collection of
 
1179
         *        <code>CProjectionAnnotation</code>
 
1180
         * @param positionMap a <code>Map&lt;Annotation, Position&gt;</code>
 
1181
         *        or <code>null</code>
 
1182
         * @return a matching tuple or <code>null</code> for no match
 
1183
         */
 
1184
        private Tuple findMatch(Tuple tuple, Collection<CProjectionAnnotation> annotations, Map<CProjectionAnnotation,Position> positionMap, FoldingStructureComputationContext ctx) {
 
1185
                Iterator<CProjectionAnnotation> it= annotations.iterator();
 
1186
                while (it.hasNext()) {
 
1187
                        CProjectionAnnotation annotation= it.next();
 
1188
                        if (tuple.annotation.getCategory() == annotation.getCategory()) {
 
1189
                                Position position= positionMap == null ? ctx.getModel().getPosition(annotation) : positionMap.get(annotation);
 
1190
                                if (position == null)
 
1191
                                        continue;
 
1192
 
 
1193
                                if (tuple.position.getOffset() == position.getOffset()) {
 
1194
                                        it.remove();
 
1195
                                        return new Tuple(annotation, position);
 
1196
                                }
 
1197
                        }
 
1198
                }
 
1199
                
 
1200
                return null;
 
1201
        }
 
1202
 
 
1203
        private Map<Object, List<Tuple>> computeCurrentStructure(FoldingStructureComputationContext ctx) {
 
1204
                boolean includeBranches= fPreprocessorBranchFoldingEnabled && ctx.fAST != null;
 
1205
                boolean includeStmts= fStatementsFoldingEnabled && ctx.fAST != null;
 
1206
                boolean includeCModel= ctx.fAST != null || !(fPreprocessorBranchFoldingEnabled || fStatementsFoldingEnabled);
 
1207
                Map<Object, List<Tuple>> map= new HashMap<Object, List<Tuple>>();
 
1208
                ProjectionAnnotationModel model= ctx.getModel();
 
1209
                Iterator<?> e= model.getAnnotationIterator();
 
1210
                while (e.hasNext()) {
 
1211
                        Object annotation= e.next();
 
1212
                        if (annotation instanceof CProjectionAnnotation) {
 
1213
                                CProjectionAnnotation cAnnotation= (CProjectionAnnotation) annotation;
 
1214
                                final boolean include;
 
1215
                                switch (cAnnotation.getCategory()) {
 
1216
                                case CProjectionAnnotation.BRANCH:
 
1217
                                        include= includeBranches;
 
1218
                                        break;
 
1219
                                case CProjectionAnnotation.STATEMENT:
 
1220
                                        include= includeStmts;
 
1221
                                        break;
 
1222
                                case CProjectionAnnotation.CMODEL:
 
1223
                                        include= includeCModel;
 
1224
                                        break;
 
1225
                                default:
 
1226
                                        include= true;
 
1227
                                        break;
 
1228
                                }
 
1229
                                Position position= model.getPosition(cAnnotation);
 
1230
                                assert position != null;
 
1231
                                if (include || position.length < 5) {
 
1232
                                        List<Tuple> list= map.get(cAnnotation.getElement());
 
1233
                                        if (list == null) {
 
1234
                                                list= new ArrayList<Tuple>(2);
 
1235
                                                map.put(cAnnotation.getElement(), list);
 
1236
                                        }
 
1237
                                        list.add(new Tuple(cAnnotation, position));
 
1238
                                }
 
1239
                        }
 
1240
                }
 
1241
 
 
1242
                Comparator<Tuple> comparator= new Comparator<Tuple>() {
 
1243
                        public int compare(Tuple t1, Tuple t2) {
 
1244
                                return t1.position.getOffset() - t2.position.getOffset();
 
1245
                        }
 
1246
                };
 
1247
                for(List<Tuple> list : map.values()) {
 
1248
                        Collections.sort(list, comparator);
 
1249
                }
 
1250
                return map;
 
1251
        }
 
1252
        
 
1253
 
 
1254
        private void computeFoldingStructure(final FoldingStructureComputationContext ctx) {
 
1255
                if (fCommentFoldingEnabled) {
 
1256
                        // compute comment positions from partitioning
 
1257
                        try {
 
1258
                                IDocument doc= ctx.getDocument();
 
1259
                                ITypedRegion[] partitions = TextUtilities.computePartitioning(doc, ICPartitions.C_PARTITIONING, 0, doc.getLength(), false);
 
1260
                                computeFoldingStructure(partitions, ctx);
 
1261
                        } catch (BadLocationException e) {
 
1262
                                // ignore
 
1263
                        }
 
1264
                }
 
1265
                final boolean needAST= fPreprocessorBranchFoldingEnabled || fStatementsFoldingEnabled;
 
1266
                if (needAST) {
 
1267
                        IASTTranslationUnit ast= ctx.getAST();
 
1268
                        if (ast != null) {
 
1269
                                computeFoldingStructure(ast, ctx);
 
1270
                        } else if (fInitialReconcilePending) {
 
1271
                                final WAIT_FLAG waitFlag= ASTProvider.WAIT_ACTIVE_ONLY;
 
1272
                                final ASTProvider astProvider= CUIPlugin.getDefault().getASTProvider();
 
1273
                                IStatus status= astProvider.runOnAST(getInputElement(), waitFlag, null, new ASTCache.ASTRunnable() {
 
1274
                                        public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) {
 
1275
                                                if (ast != null) {
 
1276
                                                        ctx.fAST= ast;
 
1277
                                                        computeFoldingStructure(ast, ctx);
 
1278
                                                }
 
1279
                                                return Status.OK_STATUS;
 
1280
                                        }
 
1281
                                });
 
1282
                                if (status.matches(IStatus.ERROR)) {
 
1283
                                        CUIPlugin.log(status);
 
1284
                                }
 
1285
                        }
 
1286
                }
 
1287
                if (!needAST || ctx.getAST() != null) {
 
1288
                        fInitialReconcilePending= false;
 
1289
                        IParent parent= (IParent) fInput;
 
1290
                        try {
 
1291
                                computeFoldingStructure(parent.getChildren(), ctx);
 
1292
                        } catch (CModelException x) {
 
1293
                        }
 
1294
                }
 
1295
        }
 
1296
 
 
1297
        static boolean isConsistent(ICElement element) {
 
1298
                if (element instanceof ITranslationUnit) {
 
1299
                        try {
 
1300
                                return ((ITranslationUnit)element).isConsistent();
 
1301
                        } catch (CModelException exc) {
 
1302
                        }
 
1303
                }
 
1304
                return false;
 
1305
        }
 
1306
 
 
1307
        /**
 
1308
         * A modifiable region with extra information about the region it holds.
 
1309
         * It tells us whether or not to include the last line of the region
 
1310
         */
 
1311
        private static class StatementRegion extends ModifiableRegion {
 
1312
                public final String function;
 
1313
                public int level;
 
1314
                public boolean inclusive;
 
1315
                public StatementRegion(String function, int level) {
 
1316
                        this.function= function; 
 
1317
                        this.level= level;
 
1318
                }
 
1319
        }
 
1320
 
 
1321
        /**
 
1322
         * Computes folding structure of statements for the given AST.
 
1323
         * 
 
1324
         * @param ast
 
1325
         * @param ctx
 
1326
         */
 
1327
        private void computeStatementFoldingStructure(IASTTranslationUnit ast, FoldingStructureComputationContext ctx) {
 
1328
                final Stack<StatementRegion> iral= new Stack<StatementRegion>();
 
1329
                ast.accept(new StatementVisitor(iral));
 
1330
                while (!iral.empty()) {
 
1331
                        StatementRegion mr = iral.pop();
 
1332
                        IRegion aligned = alignRegion(mr, ctx,mr.inclusive);
 
1333
                        if (aligned != null) {
 
1334
                                Position alignedPos= new Position(aligned.getOffset(), aligned.getLength());
 
1335
                                ctx.addProjectionRange(new CProjectionAnnotation(
 
1336
                                                false, mr.function + mr.level + computeKey(mr, ctx), CProjectionAnnotation.STATEMENT), alignedPos);
 
1337
                        }
 
1338
                }
 
1339
        }
 
1340
 
 
1341
        /**
 
1342
         * Compute folding structure of things related to the AST tree. Currently it
 
1343
         * computes the folding structure for: preprocessor branches for the given
 
1344
         * AST. Also, it computes statements folding (if/else do/while for and
 
1345
         * switch)
 
1346
         * 
 
1347
         * @param ast
 
1348
         * @param ctx
 
1349
         */
 
1350
        private void computeFoldingStructure(IASTTranslationUnit ast, FoldingStructureComputationContext ctx) {
 
1351
                if (ast == null) {
 
1352
                        return;
 
1353
                }
 
1354
                String fileName = ast.getFilePath();
 
1355
                if (fileName == null) {
 
1356
                        return;
 
1357
                }
 
1358
 
 
1359
                if (fStatementsFoldingEnabled)
 
1360
                        computeStatementFoldingStructure(ast, ctx);
 
1361
 
 
1362
                if (fPreprocessorBranchFoldingEnabled)
 
1363
                        computePreprocessorFoldingStructure(ast, ctx);
 
1364
        }
 
1365
 
 
1366
        /**
 
1367
         * Computes folding structure for preprocessor branches for the given AST.
 
1368
         * 
 
1369
         * @param ast
 
1370
         * @param ctx
 
1371
         */
 
1372
        private void computePreprocessorFoldingStructure(IASTTranslationUnit ast, FoldingStructureComputationContext ctx) {
 
1373
                List<Branch> branches = new ArrayList<Branch>();
 
1374
                Stack<Branch> branchStack = new Stack<Branch>();
 
1375
 
 
1376
                IASTPreprocessorStatement[] preprocStmts = ast.getAllPreprocessorStatements();
 
1377
 
 
1378
                for (IASTPreprocessorStatement statement : preprocStmts) {
 
1379
                        if (!statement.isPartOfTranslationUnitFile()) {
 
1380
                                // preprocessor directive is from a different file
 
1381
                                continue;
 
1382
                        }
 
1383
                        IASTNodeLocation stmtLocation= statement.getFileLocation();
 
1384
                        if (stmtLocation == null) {
 
1385
                                continue;
 
1386
                        }
 
1387
                        if (statement instanceof IASTPreprocessorIfStatement) {
 
1388
                                IASTPreprocessorIfStatement ifStmt = (IASTPreprocessorIfStatement)statement;
 
1389
                                branchStack.push(new Branch(stmtLocation.getNodeOffset(), ifStmt.taken(), "#if " + new String(ifStmt.getCondition()))); //$NON-NLS-1$
 
1390
                        } else if (statement instanceof IASTPreprocessorIfdefStatement) {
 
1391
                                IASTPreprocessorIfdefStatement ifdefStmt = (IASTPreprocessorIfdefStatement)statement;
 
1392
                                branchStack.push(new Branch(stmtLocation.getNodeOffset(), ifdefStmt.taken(), "#ifdef " + new String(ifdefStmt.getCondition()))); //$NON-NLS-1$
 
1393
                        } else if (statement instanceof IASTPreprocessorIfndefStatement) {
 
1394
                                IASTPreprocessorIfndefStatement ifndefStmt = (IASTPreprocessorIfndefStatement)statement;
 
1395
                                branchStack.push(new Branch(stmtLocation.getNodeOffset(), ifndefStmt.taken(), "#ifndef " + new String(ifndefStmt.getCondition()))); //$NON-NLS-1$
 
1396
                        } else if (statement instanceof IASTPreprocessorElseStatement) {
 
1397
                                if (branchStack.isEmpty()) {
 
1398
                                        // #else without #if
 
1399
                                        continue;
 
1400
                                }
 
1401
                                Branch branch= branchStack.pop();
 
1402
                                IASTPreprocessorElseStatement elseStmt = (IASTPreprocessorElseStatement)statement;
 
1403
                                branchStack.push(new Branch(stmtLocation.getNodeOffset(), elseStmt.taken(), branch.fCondition));
 
1404
                                branch.setEndOffset(stmtLocation.getNodeOffset());
 
1405
                                branches.add(branch);
 
1406
                        } else if (statement instanceof IASTPreprocessorElifStatement) {
 
1407
                                if (branchStack.isEmpty()) {
 
1408
                                        // #elif without #if
 
1409
                                        continue;
 
1410
                                }
 
1411
                                Branch branch= branchStack.pop();
 
1412
                                IASTPreprocessorElifStatement elifStmt = (IASTPreprocessorElifStatement) statement;
 
1413
                                branchStack.push(new Branch(stmtLocation.getNodeOffset(), elifStmt.taken(), branch.fCondition));
 
1414
                                branch.setEndOffset(stmtLocation.getNodeOffset());
 
1415
                                branches.add(branch);
 
1416
                        } else if (statement instanceof IASTPreprocessorEndifStatement) {
 
1417
                                if (branchStack.isEmpty()) {
 
1418
                                        // #endif without #if
 
1419
                                        continue;
 
1420
                                }
 
1421
                                Branch branch= branchStack.pop();
 
1422
                                branch.setEndOffset(stmtLocation.getNodeOffset() + stmtLocation.getNodeLength());
 
1423
                                branch.setInclusive(true);
 
1424
                                branches.add(branch);
 
1425
                        }
 
1426
                }
 
1427
 
 
1428
                if (!branchStack.isEmpty()) {
 
1429
                        // unterminated #if
 
1430
                        Branch branch= branchStack.pop();
 
1431
                        branch.setEndOffset(getDocument().getLength());
 
1432
                        branch.setInclusive(true);
 
1433
                        branches.add(branch);
 
1434
                }
 
1435
                
 
1436
                Map<String, Counter> keys= new HashMap<String, Counter>(branches.size());
 
1437
                for (Branch branch : branches) {
 
1438
                        IRegion aligned = alignRegion(branch, ctx, branch.fInclusive);
 
1439
                        if (aligned != null) {
 
1440
                                Position alignedPos= new Position(aligned.getOffset(), aligned.getLength());
 
1441
                                final boolean collapse= !branch.taken() && ctx.collapseInactiveCode() && !alignedPos.includes(fCursorPosition);
 
1442
                                // compute a stable key
 
1443
                                String key = branch.fCondition;
 
1444
                                Counter counter= keys.get(key);
 
1445
                                if (counter == null) {
 
1446
                                        keys.put(key, new Counter());
 
1447
                                } else {
 
1448
                                        key= Integer.toString(counter.fCount++) + key;
 
1449
                                }
 
1450
                                ctx.addProjectionRange(new CProjectionAnnotation(collapse, key, CProjectionAnnotation.BRANCH), alignedPos);
 
1451
                        }
 
1452
                }
 
1453
        }
 
1454
 
 
1455
        /**
 
1456
         * Compute a key for recognizing an annotation based on the given position.
 
1457
         * 
 
1458
         * @param pos
 
1459
         * @param ctx
 
1460
         * @return a key to recognize an annotation position
 
1461
         */
 
1462
        private Object computeKey(Position pos, FoldingStructureComputationContext ctx) {
 
1463
                try {
 
1464
                        final IDocument document= ctx.getDocument();
 
1465
                        IRegion line= document.getLineInformationOfOffset(pos.offset);
 
1466
                        return document.get(pos.offset, Math.min(32, line.getOffset() + line.getLength() - pos.offset));
 
1467
                } catch (BadLocationException exc) {
 
1468
                        return exc;
 
1469
                }
 
1470
        }
 
1471
 
 
1472
        /**
 
1473
         * Compute folding structure based on partioning information.
 
1474
         * 
 
1475
         * @param partitions  array of document partitions
 
1476
         * @param ctx  the folding structure context
 
1477
         * @throws BadLocationException 
 
1478
         */
 
1479
        private void computeFoldingStructure(ITypedRegion[] partitions, FoldingStructureComputationContext ctx) throws BadLocationException {
 
1480
                boolean collapse = ctx.collapseComments();
 
1481
                IDocument doc= ctx.getDocument();
 
1482
                int startLine = -1;
 
1483
                int endLine = -1;
 
1484
                List<Tuple> comments= new ArrayList<Tuple>();
 
1485
                ModifiableRegion commentRange = new ModifiableRegion();
 
1486
                for (ITypedRegion partition : partitions) {
 
1487
                        boolean singleLine= false;
 
1488
                        if (ICPartitions.C_MULTI_LINE_COMMENT.equals(partition.getType())
 
1489
                                || ICPartitions.C_MULTI_LINE_DOC_COMMENT.equals(partition.getType())) {
 
1490
                                Position position= createCommentPosition(alignRegion(partition, ctx, true));
 
1491
                                if (position != null) {
 
1492
                                        if (startLine >= 0 && endLine - startLine >= fMinCommentLines) {
 
1493
                                                Position projection = createCommentPosition(alignRegion(commentRange, ctx, true));
 
1494
                                                if (projection != null) {
 
1495
                                                        comments.add(new Tuple(new CProjectionAnnotation(collapse, doc.get(projection.offset, Math.min(16, projection.length)), true), projection));
 
1496
                                                }
 
1497
                                                startLine= -1;
 
1498
                                        }
 
1499
                                        comments.add(new Tuple(new CProjectionAnnotation(collapse, doc.get(position.offset, Math.min(16, position.length)), true), position));
 
1500
                                } else {
 
1501
                                        singleLine= true;
 
1502
                                }
 
1503
                        } else {
 
1504
                                singleLine= ICPartitions.C_SINGLE_LINE_COMMENT.equals(partition.getType());
 
1505
                        }
 
1506
                        if (singleLine) {
 
1507
                                // if comment starts at column 0 and spans only one line
 
1508
                                // and is adjacent to a previous line comment, add it
 
1509
                                // to the commentRange
 
1510
                                int lineNr = doc.getLineOfOffset(partition.getOffset());
 
1511
                                IRegion lineRegion = doc.getLineInformation(lineNr);
 
1512
                                boolean isLineStart = partition.getOffset() == lineRegion.getOffset();
 
1513
                                if (!isLineStart) {
 
1514
                                        continue;
 
1515
                                }
 
1516
                                if (!singleLine) {
 
1517
                                        singleLine = lineRegion.getOffset() + lineRegion.getLength() >= partition.getOffset() + partition.getLength();
 
1518
                                        if (!singleLine) {
 
1519
                                                continue;
 
1520
                                        }
 
1521
                                }
 
1522
                                if (startLine < 0 || lineNr - endLine > 1) {
 
1523
                                        if (startLine >= 0 && endLine - startLine >= fMinCommentLines) {
 
1524
                                                Position projection = createCommentPosition(alignRegion(commentRange, ctx, true));
 
1525
                                                if (projection != null) {
 
1526
                                                        comments.add(new Tuple(new CProjectionAnnotation(collapse, doc.get(projection.offset, Math.min(16, projection.length)), true), projection));
 
1527
                                                }
 
1528
                                        }
 
1529
                                        startLine = lineNr;
 
1530
                                        endLine = lineNr;
 
1531
                                        commentRange.offset = lineRegion.getOffset();
 
1532
                                        commentRange.length = lineRegion.getLength();
 
1533
                                } else {
 
1534
                                        endLine = lineNr;
 
1535
                                        int delta = lineRegion.getOffset() + lineRegion.getLength() - commentRange.offset - commentRange.length;
 
1536
                                        commentRange.length += delta;
 
1537
                                }
 
1538
                        }
 
1539
                }
 
1540
                if (startLine >= 0 && endLine - startLine >= fMinCommentLines) {
 
1541
                        Position projection = createCommentPosition(alignRegion(commentRange, ctx, true));
 
1542
                        if (projection != null) {
 
1543
                                comments.add(new Tuple(new CProjectionAnnotation(collapse, doc.get(projection.offset, Math.min(16, projection.length)), true), projection));
 
1544
                        }
 
1545
                }
 
1546
                if (!comments.isEmpty()) {
 
1547
                        // first comment starting before line 10 is considered the header comment
 
1548
                        Iterator<Tuple> iter = comments.iterator();
 
1549
                        Tuple tuple = iter.next();
 
1550
                        int lineNr = doc.getLineOfOffset(tuple.position.getOffset());
 
1551
                        if (lineNr < 10) {
 
1552
                                if (ctx.collapseHeaderComments()) {
 
1553
                                        tuple.annotation.markCollapsed();
 
1554
                                } else {
 
1555
                                        tuple.annotation.markExpanded();
 
1556
                                }
 
1557
                        }
 
1558
                        ctx.addProjectionRange(tuple.annotation, tuple.position);
 
1559
                        while (iter.hasNext()) {
 
1560
                                tuple = iter.next();
 
1561
                                ctx.addProjectionRange(tuple.annotation, tuple.position);
 
1562
                        }
 
1563
                }
 
1564
        }
 
1565
 
 
1566
        private void computeFoldingStructure(ICElement[] elements, FoldingStructureComputationContext ctx) throws CModelException {
 
1567
                for (ICElement element : elements) {
 
1568
                        computeFoldingStructure(element, ctx);
 
1569
 
 
1570
                        if (element instanceof IParent) {
 
1571
                                IParent parent= (IParent) element;
 
1572
                                computeFoldingStructure(parent.getChildren(), ctx);
 
1573
                        }
 
1574
                }
 
1575
        }
 
1576
 
 
1577
        /**
 
1578
         * Computes the folding structure for a given {@link ICElement C element}. Computed
 
1579
         * projection annotations are
 
1580
         * {@link DefaultCFoldingStructureProvider.FoldingStructureComputationContext#addProjectionRange(DefaultCFoldingStructureProvider.CProjectionAnnotation, Position) added}
 
1581
         * to the computation context.
 
1582
         * <p>
 
1583
         * Subclasses may extend or replace. The default implementation creates projection annotations
 
1584
         * for the following elements:
 
1585
         * <ul>
 
1586
         * <li>structs, unions, classes</li>
 
1587
         * <li>functions</li>
 
1588
         * <li>methods</li>
 
1589
         * <li>multiline macro definitions</li>
 
1590
         * </ul>
 
1591
         * </p>
 
1592
         * 
 
1593
         * @param element the C element to compute the folding structure for
 
1594
         * @param ctx the computation context
 
1595
         */
 
1596
        protected void computeFoldingStructure(ICElement element, FoldingStructureComputationContext ctx) {
 
1597
 
 
1598
                boolean collapse= false;
 
1599
                switch (element.getElementType()) {
 
1600
 
 
1601
                case ICElement.C_STRUCT:
 
1602
                case ICElement.C_CLASS:
 
1603
                case ICElement.C_UNION:
 
1604
                case ICElement.C_ENUMERATION:
 
1605
                case ICElement.C_TEMPLATE_STRUCT:
 
1606
                case ICElement.C_TEMPLATE_CLASS:
 
1607
                case ICElement.C_TEMPLATE_UNION:
 
1608
                        collapse= ctx.collapseStructures();
 
1609
                        break;
 
1610
                case ICElement.C_MACRO:
 
1611
                        collapse= ctx.collapseMacros();
 
1612
                        break;
 
1613
                case ICElement.C_FUNCTION:
 
1614
                case ICElement.C_TEMPLATE_FUNCTION:
 
1615
                        collapse= ctx.collapseFunctions();
 
1616
                        break;
 
1617
                case ICElement.C_METHOD:
 
1618
                case ICElement.C_TEMPLATE_METHOD:
 
1619
                        collapse= ctx.collapseMethods();
 
1620
                        break;
 
1621
                case ICElement.C_NAMESPACE:
 
1622
                        break;
 
1623
                default:
 
1624
                        return;
 
1625
                }
 
1626
 
 
1627
                IRegion[] regions= computeProjectionRanges((ISourceReference) element, ctx);
 
1628
                if (regions.length > 0) {
 
1629
                        IRegion normalized= alignRegion(regions[regions.length - 1], ctx, true);
 
1630
                        if (normalized != null) {
 
1631
                                Position position= createElementPosition(normalized, element);
 
1632
                                if (position != null) {
 
1633
                                        collapse= collapse && !position.includes(fCursorPosition);
 
1634
                                        ctx.addProjectionRange(new CProjectionAnnotation(collapse, element, false), position);
 
1635
                                }
 
1636
                        }
 
1637
                }
 
1638
        }
 
1639
 
 
1640
        /**
 
1641
         * Computes the projection ranges for a given <code>ISourceReference</code>. More than one
 
1642
         * range or none at all may be returned. If there are no foldable regions, an empty array is
 
1643
         * returned.
 
1644
         * <p>
 
1645
         * The last region in the returned array (if not empty) describes the region for the C
 
1646
         * element that implements the source reference. Any preceding regions describe comments
 
1647
         * of that element.
 
1648
         * </p>
 
1649
         * 
 
1650
         * @param reference a C element that is a source reference
 
1651
         * @param ctx the folding context
 
1652
         * @return the regions to be folded
 
1653
         */
 
1654
        protected final IRegion[] computeProjectionRanges(ISourceReference reference, FoldingStructureComputationContext ctx) {
 
1655
                try {
 
1656
                        ISourceRange range= reference.getSourceRange();
 
1657
                        return new IRegion[] {
 
1658
                                new Region(range.getStartPos(), range.getLength())
 
1659
                        };
 
1660
                } catch (CModelException e) {
 
1661
                }
 
1662
 
 
1663
                return new IRegion[0];
 
1664
        }
 
1665
 
 
1666
        /**
 
1667
         * Creates a comment folding position from an
 
1668
         * {@link #alignRegion(IRegion, DefaultCFoldingStructureProvider.FoldingStructureComputationContext, boolean) aligned}
 
1669
         * region.
 
1670
         * 
 
1671
         * @param aligned an aligned region
 
1672
         * @return a folding position corresponding to <code>aligned</code>
 
1673
         */
 
1674
        protected final Position createCommentPosition(IRegion aligned) {
 
1675
                if (aligned == null) {
 
1676
                        return null;
 
1677
                }
 
1678
                return new CommentPosition(aligned.getOffset(), aligned.getLength());
 
1679
        }
 
1680
 
 
1681
        /**
 
1682
         * Creates a folding position that remembers its element from an
 
1683
         * {@link #alignRegion(IRegion, DefaultCFoldingStructureProvider.FoldingStructureComputationContext, boolean) aligned}
 
1684
         * region.
 
1685
         * 
 
1686
         * @param aligned an aligned region
 
1687
         * @param element the element to remember
 
1688
         * @return a folding position corresponding to <code>aligned</code>
 
1689
         */
 
1690
        protected final Position createElementPosition(IRegion aligned, ICElement element) {
 
1691
                return new CElementPosition(aligned.getOffset(), aligned.getLength(), element);
 
1692
        }
 
1693
 
 
1694
        /**
 
1695
         * Aligns <code>region</code> to start and end at a line offset. The region's start is
 
1696
         * decreased to the next line offset, and the end offset increased to the next line start or the
 
1697
         * end of the document. <code>null</code> is returned if <code>region</code> is
 
1698
         * <code>null</code> itself or does not comprise at least one line delimiter, as a single line
 
1699
         * cannot be folded.
 
1700
         * 
 
1701
         * @param region the region to align, may be <code>null</code>
 
1702
         * @param ctx the folding context
 
1703
         * @return a region equal or greater than <code>region</code> that is aligned with line
 
1704
         *         offsets, <code>null</code> if the region is too small to be foldable (e.g. covers
 
1705
         *         only one line)
 
1706
         */
 
1707
        protected final IRegion alignRegion(IRegion region, FoldingStructureComputationContext ctx) {
 
1708
                return alignRegion(region, ctx, true);
 
1709
        }
 
1710
 
 
1711
        /**
 
1712
         * Aligns <code>region</code> to start and end at a line offset. The region's start is
 
1713
         * decreased to the next line offset, and the end offset increased to the next line start or the
 
1714
         * end of the document. <code>null</code> is returned if <code>region</code> is
 
1715
         * <code>null</code> itself or does not comprise at least one line delimiter, as a single line
 
1716
         * cannot be folded.
 
1717
         * 
 
1718
         * @param region the region to align, may be <code>null</code>
 
1719
         * @param ctx the folding context
 
1720
         * @param inclusive include line of end offset
 
1721
         * @return a region equal or greater than <code>region</code> that is aligned with line
 
1722
         *         offsets, <code>null</code> if the region is too small to be foldable (e.g. covers
 
1723
         *         only one line)
 
1724
         */
 
1725
        protected final IRegion alignRegion(IRegion region, FoldingStructureComputationContext ctx, boolean inclusive) {
 
1726
                if (region == null)
 
1727
                        return null;
 
1728
                
 
1729
                IDocument document= ctx.getDocument();
 
1730
                
 
1731
                try {
 
1732
                        
 
1733
                        int start= document.getLineOfOffset(region.getOffset());
 
1734
                        int end= document.getLineOfOffset(region.getOffset() + region.getLength());
 
1735
                        if (start >= end)
 
1736
                                return null;
 
1737
                        
 
1738
                        int offset= document.getLineOffset(start);
 
1739
                        int endOffset;
 
1740
                        if (inclusive) {
 
1741
                                if (document.getNumberOfLines() > end + 1)
 
1742
                                        endOffset= document.getLineOffset(end + 1);
 
1743
                                else
 
1744
                                        endOffset= document.getLineOffset(end) + document.getLineLength(end);
 
1745
                        } else {
 
1746
                                endOffset= document.getLineOffset(end);
 
1747
                        }
 
1748
                        return new Region(offset, endOffset - offset);
 
1749
                        
 
1750
                } catch (BadLocationException x) {
 
1751
                        // concurrent modification
 
1752
                        return null;
 
1753
                }
 
1754
        }
 
1755
        
 
1756
        private ProjectionAnnotationModel getModel() {
 
1757
                return (ProjectionAnnotationModel) fEditor.getAdapter(ProjectionAnnotationModel.class);
 
1758
        }
 
1759
 
 
1760
        private IDocument getDocument() {
 
1761
                IDocumentProvider provider= fEditor.getDocumentProvider();
 
1762
                return provider.getDocument(fEditor.getEditorInput());
 
1763
        }
 
1764
 
 
1765
}