From a9f3514fa013cc5d3253e21dea17917dd7e22a9a Mon Sep 17 00:00:00 2001 From: Anton Leherbauer Date: Fri, 8 Sep 2006 14:11:06 +0000 Subject: [PATCH] 84019, 103196 - Folding support for comments and preprocessor branches (work in progress) --- core/org.eclipse.cdt.ui/.options | 3 + .../cdt/internal/ui/editor/ASTProvider.java | 47 +- .../cdt/internal/ui/editor/CEditor.java | 14 +- .../WorkInProgressPreferencePage.java | 7 +- .../DefaultCFoldingPreferenceBlock.java | 7 + .../DefaultCFoldingStructureProvider.java | 1597 +++++++++++++---- .../text/folding/FoldingMessages.properties | 4 + .../src/org/eclipse/cdt/ui/CUIPlugin.java | 4 + .../eclipse/cdt/ui/PreferenceConstants.java | 56 + 9 files changed, 1390 insertions(+), 349 deletions(-) diff --git a/core/org.eclipse.cdt.ui/.options b/core/org.eclipse.cdt.ui/.options index 4917fd522fb..ed41271de7b 100644 --- a/core/org.eclipse.cdt.ui/.options +++ b/core/org.eclipse.cdt.ui/.options @@ -8,3 +8,6 @@ org.eclipse.cdt.ui/debug/ASTProvider=false # Enables all semantic highlighting types org.eclipse.cdt.ui/debug/SemanticHighlighting=false + +# Enables debug information related to folding +org.eclipse.cdt.ui/debug/folding=false diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ASTProvider.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ASTProvider.java index de841841a41..5094f08c59e 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ASTProvider.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ASTProvider.java @@ -28,6 +28,7 @@ import org.eclipse.ui.IWorkbenchPartReference; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; +import org.eclipse.cdt.core.IPositionConverter; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.model.ICElement; @@ -93,12 +94,10 @@ public final class ASTProvider { */ public static final WAIT_FLAG WAIT_NO= new WAIT_FLAG("don't wait"); //$NON-NLS-1$ - public static int PARSE_MODE_FULL= ILanguage.AST_SKIP_IF_NO_BUILD_INFO; - public static int PARSE_MODE_FAST= ILanguage.AST_SKIP_IF_NO_BUILD_INFO | ILanguage.AST_SKIP_INDEXED_HEADERS; - public static int PARSE_MODE_FAST_INDEX= ILanguage.AST_SKIP_IF_NO_BUILD_INFO | ILanguage.AST_SKIP_INDEXED_HEADERS | ILanguage.AST_USE_INDEX; - public static int PARSE_MODE_INDEX= ILanguage.AST_SKIP_IF_NO_BUILD_INFO | ILanguage.AST_USE_INDEX; - - public static int PARSE_MODE= PARSE_MODE_FULL; + /** Full parse mode (no PDOM) */ + public static int PARSE_MODE_FULL= 0; + /** Fast parse mode (use PDOM) */ + public static int PARSE_MODE_FAST= ILanguage.AST_SKIP_INDEXED_HEADERS | ILanguage.AST_USE_INDEX; /** * Tells whether this class is in debug mode. @@ -232,6 +231,7 @@ public final class ASTProvider { private ICElement fReconcilingCElement; private ICElement fActiveCElement; + private IPositionConverter fActivePositionConverter; private IASTTranslationUnit fAST; private ActivationListener fActivationListener; private Object fReconcileLock= new Object(); @@ -239,7 +239,8 @@ public final class ASTProvider { private boolean fIsReconciling; private IWorkbenchPart fActiveEditor; - + protected int fParseMode= PARSE_MODE_FAST; + /** * Returns the C plug-in's AST provider. * @@ -280,7 +281,7 @@ public final class ASTProvider { synchronized (this) { fActiveEditor= editor; fActiveCElement= cElement; - cache(null, cElement); + cache(null, null, cElement); } if (DEBUG) @@ -337,7 +338,7 @@ public final class ASTProvider { fIsReconciling= true; fReconcilingCElement= cElement; } - cache(null, cElement); + cache(null, null, cElement); } /** @@ -353,7 +354,7 @@ public final class ASTProvider { fAST= null; - cache(null, null); + cache(null, null, null); } /** @@ -393,7 +394,7 @@ public final class ASTProvider { * @param ast * @param cElement */ - private synchronized void cache(IASTTranslationUnit ast, ICElement cElement) { + private synchronized void cache(IASTTranslationUnit ast, IPositionConverter converter, ICElement cElement) { if (fActiveCElement != null && !fActiveCElement.equals(cElement)) { if (DEBUG && cElement != null) // don't report call from disposeAST() @@ -408,6 +409,7 @@ public final class ASTProvider { disposeAST(); fAST= ast; + fActivePositionConverter= converter; // Signal AST change synchronized (fWaitLock) { @@ -502,9 +504,9 @@ public final class ASTProvider { if (fAST != null) { if (DEBUG) System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "Ignore created AST for " + cElement.getElementName() + "- AST from reconciler is newer"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - reconciled(fAST, cElement, null); + reconciled(fAST, fActivePositionConverter, cElement, null); } else - reconciled(ast, cElement, null); + reconciled(ast, null, cElement, null); } } @@ -550,7 +552,7 @@ public final class ASTProvider { if (progressMonitor != null && progressMonitor.isCanceled()) { root[0]= null; } else { - root[0]= tu.getLanguage().getASTTranslationUnit(tu, PARSE_MODE); + root[0]= tu.getLanguage().getASTTranslationUnit(tu, fParseMode); } } catch (OperationCanceledException ex) { root[0]= null; @@ -601,9 +603,9 @@ public final class ASTProvider { } /* - * @see org.eclipse.cdt.internal.ui.text.ICReconcilingListener#reconciled(org.eclipse.cdt.core.dom.IASTTranslationUnit) + * @see org.eclipse.cdt.internal.ui.text.ICReconcilingListener#reconciled() */ - void reconciled(IASTTranslationUnit ast, ICElement cElement, IProgressMonitor progressMonitor) { + void reconciled(IASTTranslationUnit ast, IPositionConverter converter, ICElement cElement, IProgressMonitor progressMonitor) { if (DEBUG) System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "reconciled: " + toString(cElement) + ", AST: " + toString(ast)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ @@ -624,7 +626,7 @@ public final class ASTProvider { return; } - cache(ast, cElement); + cache(ast, converter, cElement); } } @@ -635,6 +637,17 @@ public final class ASTProvider { else return Thread.currentThread().toString(); } + + /** + * @param element + * @return the position converter for the AST of the active element or null + */ + public IPositionConverter getActivePositionConverter(ICElement element) { + if (fActiveCElement == element) { + return fActivePositionConverter; + } + return null; + } } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java index 16ddfde2a8b..7e025fee866 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java @@ -2183,7 +2183,7 @@ public class CEditor extends TextEditor implements ISelectionChangedListener, IS return; // Always notify AST provider - cuiPlugin.getASTProvider().reconciled(ast, getInputCElement(), progressMonitor); + cuiPlugin.getASTProvider().reconciled(ast, positionTracker, getInputCElement(), progressMonitor); // Notify listeners Object[] listeners = fReconcilingListeners.getListeners(); @@ -2200,10 +2200,8 @@ public class CEditor extends TextEditor implements ISelectionChangedListener, IS * @param listener The reconcile listener to be added * @since 4.0 */ - final void addReconcileListener(ICReconcilingListener listener) { - synchronized (fReconcilingListeners) { - fReconcilingListeners.add(listener); - } + final public void addReconcileListener(ICReconcilingListener listener) { + fReconcilingListeners.add(listener); } /** @@ -2213,10 +2211,8 @@ public class CEditor extends TextEditor implements ISelectionChangedListener, IS * @param listener the reconcile listener to be removed * @since 4.0 */ - final void removeReconcileListener(ICReconcilingListener listener) { - synchronized (fReconcilingListeners) { - fReconcilingListeners.remove(listener); - } + final public void removeReconcileListener(ICReconcilingListener listener) { + fReconcilingListeners.remove(listener); } /** diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/WorkInProgressPreferencePage.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/WorkInProgressPreferencePage.java index f931265a16c..43617cf9564 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/WorkInProgressPreferencePage.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/WorkInProgressPreferencePage.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2005 IBM Corporation and others. + * Copyright (c) 2000, 2006 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -84,7 +84,10 @@ public class WorkInProgressPreferencePage extends PreferencePage implements IWor result.setLayout(layout); // Add your controls here - addCheckBox(result, "Semantic Highlighting", PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_ENABLED); //$NON-NLS-1$ + addCheckBox(result, "Enable semantic highlighting", PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_ENABLED); //$NON-NLS-1$ + addCheckBox(result, "Enable folding of preprocessor branches (#if/#endif)", PreferenceConstants.EDITOR_FOLDING_PREPROCESSOR_BRANCHES_ENABLED); //$NON-NLS-1$ + addCheckBox(result, "Enable comment folding", PreferenceConstants.EDITOR_FOLDING_COMMENTS_ENABLED); //$NON-NLS-1$ + applyDialogFont(result); return result; } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/folding/DefaultCFoldingPreferenceBlock.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/folding/DefaultCFoldingPreferenceBlock.java index a3831f3f2cf..bcc4c0c4580 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/folding/DefaultCFoldingPreferenceBlock.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/folding/DefaultCFoldingPreferenceBlock.java @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Anton Leherbauer (Wind River Systems) *******************************************************************************/ package org.eclipse.cdt.internal.ui.text.folding; @@ -64,6 +65,9 @@ public class DefaultCFoldingPreferenceBlock implements ICFoldingPreferenceBlock overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, PreferenceConstants.EDITOR_FOLDING_FUNCTIONS)); overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, PreferenceConstants.EDITOR_FOLDING_METHODS)); overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, PreferenceConstants.EDITOR_FOLDING_STRUCTURES)); + overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, PreferenceConstants.EDITOR_FOLDING_COMMENTS)); + overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, PreferenceConstants.EDITOR_FOLDING_HEADERS)); + overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, PreferenceConstants.EDITOR_FOLDING_INACTIVE_CODE)); return (OverlayKey[]) overlayKeys.toArray(new OverlayKey[overlayKeys.size()]); } @@ -88,6 +92,9 @@ public class DefaultCFoldingPreferenceBlock implements ICFoldingPreferenceBlock addCheckBox(inner, FoldingMessages.getString("DefaultCFoldingPreferenceBlock.functions"), PreferenceConstants.EDITOR_FOLDING_FUNCTIONS, 0); //$NON-NLS-1$ addCheckBox(inner, FoldingMessages.getString("DefaultCFoldingPreferenceBlock.methods"), PreferenceConstants.EDITOR_FOLDING_METHODS, 0); //$NON-NLS-1$ addCheckBox(inner, FoldingMessages.getString("DefaultCFoldingPreferenceBlock.structures"), PreferenceConstants.EDITOR_FOLDING_STRUCTURES, 0); //$NON-NLS-1$ + addCheckBox(inner, FoldingMessages.getString("DefaultCFoldingPreferenceBlock.comments"), PreferenceConstants.EDITOR_FOLDING_COMMENTS, 0); //$NON-NLS-1$ + addCheckBox(inner, FoldingMessages.getString("DefaultCFoldingPreferenceBlock.headers"), PreferenceConstants.EDITOR_FOLDING_HEADERS, 0); //$NON-NLS-1$ + addCheckBox(inner, FoldingMessages.getString("DefaultCFoldingPreferenceBlock.inactive_code"), PreferenceConstants.EDITOR_FOLDING_INACTIVE_CODE, 0); //$NON-NLS-1$ return inner; } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/folding/DefaultCFoldingStructureProvider.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/folding/DefaultCFoldingStructureProvider.java index 9c732199450..7d93f398e07 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/folding/DefaultCFoldingStructureProvider.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/folding/DefaultCFoldingStructureProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2005 QNX Software Systems and others. + * Copyright (c) 2000, 2006 QNX Software Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -7,85 +7,344 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Anton Leherbauer (Wind River Systems) *******************************************************************************/ package org.eclipse.cdt.internal.ui.text.folding; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Stack; -import org.eclipse.cdt.core.model.CModelException; -import org.eclipse.cdt.core.model.CoreModel; -import org.eclipse.cdt.core.model.ElementChangedEvent; -import org.eclipse.cdt.core.model.ICElement; -import org.eclipse.cdt.core.model.ICElementDelta; -import org.eclipse.cdt.core.model.IElementChangedListener; -import org.eclipse.cdt.core.model.IParent; -import org.eclipse.cdt.core.model.ISourceRange; -import org.eclipse.cdt.core.model.ISourceReference; -import org.eclipse.cdt.core.model.IWorkingCopy; -import org.eclipse.cdt.internal.ui.editor.CEditor; -import org.eclipse.cdt.ui.CUIPlugin; -import org.eclipse.cdt.ui.IWorkingCopyManager; -import org.eclipse.cdt.ui.PreferenceConstants; -import org.eclipse.cdt.ui.text.folding.ICFoldingStructureProvider; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Platform; import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.text.Assert; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITypedRegion; import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.TextUtilities; import org.eclipse.jface.text.source.Annotation; -import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.jface.text.source.projection.IProjectionListener; +import org.eclipse.jface.text.source.projection.IProjectionPosition; import org.eclipse.jface.text.source.projection.ProjectionAnnotation; import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; import org.eclipse.jface.text.source.projection.ProjectionViewer; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.ITextEditor; +import org.eclipse.cdt.core.IPositionConverter; +import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorElifStatement; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorElseStatement; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorEndifStatement; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIfStatement; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIfdefStatement; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIfndefStatement; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorStatement; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.model.CModelException; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.model.ElementChangedEvent; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICElementDelta; +import org.eclipse.cdt.core.model.IElementChangedListener; +import org.eclipse.cdt.core.model.IMember; +import org.eclipse.cdt.core.model.IParent; +import org.eclipse.cdt.core.model.ISourceRange; +import org.eclipse.cdt.core.model.ISourceReference; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.PreferenceConstants; +import org.eclipse.cdt.ui.text.ICPartitions; +import org.eclipse.cdt.ui.text.folding.ICFoldingStructureProvider; + +import org.eclipse.cdt.internal.ui.editor.ASTProvider; +import org.eclipse.cdt.internal.ui.editor.CEditor; +import org.eclipse.cdt.internal.ui.text.DocumentCharacterIterator; +import org.eclipse.cdt.internal.ui.text.ICReconcilingListener; + /** + * Default implementation of a {@link ICFoldingStructureProvider}. + *

+ * Derived from JDT counterpart. + *

*/ -public class DefaultCFoldingStructureProvider implements IProjectionListener, ICFoldingStructureProvider { +public class DefaultCFoldingStructureProvider implements ICFoldingStructureProvider { + + /** + * Reconcile annotation positions from preprocessor branches. + */ + private class PreprocessorBranchesReconciler implements ICReconcilingListener { + + /* + * @see org.eclipse.cdt.internal.ui.text.ICReconcilingListener#aboutToBeReconciled() + */ + public void aboutToBeReconciled() { + + } + + /* + * @see org.eclipse.cdt.internal.ui.text.ICReconcilingListener#reconciled(org.eclipse.cdt.core.dom.ast.IASTTranslationUnit, org.eclipse.cdt.core.IPositionConverter, org.eclipse.core.runtime.IProgressMonitor) + */ + public void reconciled(IASTTranslationUnit ast, IPositionConverter positionTracker, IProgressMonitor progressMonitor) { + FoldingStructureComputationContext ctx= createContext(fInitialASTReconcile); + fInitialASTReconcile= false; + if (fPreprocessorBranchFoldingEnabled) { + ctx.fAST= ast; + ctx.fASTPositionConverter= positionTracker; + } + update(ctx); + } + + } + + + /** + * A context that contains the information needed to compute the folding structure of an + * {@link ITranslationUnit}. Computed folding regions are collected via + * {@linkplain #addProjectionRange(DefaultCFoldingStructureProvider.CProjectionAnnotation, Position) addProjectionRange}. + */ + protected final class FoldingStructureComputationContext { + private final ProjectionAnnotationModel fModel; + private final IDocument fDocument; + private final boolean fAllowCollapsing; + + private ISourceReference fFirstType; + private boolean fHasHeaderComment; + private LinkedHashMap fMap= new LinkedHashMap(); + private IASTTranslationUnit fAST; + private IPositionConverter fASTPositionConverter; + + FoldingStructureComputationContext(IDocument document, ProjectionAnnotationModel model, boolean allowCollapsing) { + Assert.isNotNull(document); + Assert.isNotNull(model); + fDocument= document; + fModel= model; + fAllowCollapsing= allowCollapsing; + } + + void setFirstType(ISourceReference reference) { + if (hasFirstType()) + throw new IllegalStateException(); + fFirstType= reference; + } + + boolean hasFirstType() { + return fFirstType != null; + } + + ISourceReference getFirstType() { + return fFirstType; + } + + boolean hasHeaderComment() { + return fHasHeaderComment; + } + + void setHasHeaderComment() { + fHasHeaderComment= true; + } + + /** + * Returns true if newly created folding regions may be collapsed, + * false if not. This is usually false when updating the + * folding structure while typing; it may be true when computing or restoring + * the initial folding structure. + * + * @return true if newly created folding regions may be collapsed, + * false if not + */ + public boolean allowCollapsing() { + return fAllowCollapsing; + } + + /** + * Returns the document which contains the code being folded. + * + * @return the document which contains the code being folded + */ + IDocument getDocument() { + return fDocument; + } + + ProjectionAnnotationModel getModel() { + return fModel; + } + + /** + * Adds a projection (folding) region to this context. The created annotation / position + * pair will be added to the {@link ProjectionAnnotationModel} of the + * {@link ProjectionViewer} of the editor. + * + * @param annotation the annotation to add + * @param position the corresponding position + */ + public void addProjectionRange(CProjectionAnnotation annotation, Position position) { + fMap.put(annotation, position); + } + + /** + * Returns true if header comments should be collapsed. + * + * @return true if header comments should be collapsed + */ + public boolean collapseHeaderComments() { + return fAllowCollapsing && fCollapseHeaderComments; + } + + /** + * Returns true if comments should be collapsed. + * + * @return true if comments should be collapsed + */ + public boolean collapseComments() { + return fAllowCollapsing && fCollapseComments; + } + + /** + * Returns true if functions should be collapsed. + * + * @return true if functions should be collapsed + */ + public boolean collapseFunctions() { + return fAllowCollapsing && fCollapseFunctions; + } + + /** + * Returns true if macros should be collapsed. + * + * @return true if macros should be collapsed + */ + public boolean collapseMacros() { + return fAllowCollapsing && fCollapseMacros; + } + + /** + * Returns true if methods should be collapsed. + * + * @return true if methods should be collapsed + */ + public boolean collapseMethods() { + return fAllowCollapsing && fCollapseMethods; + } + + /** + * Returns true if structures should be collapsed. + * + * @return true if structures should be collapsed + */ + public boolean collapseStructures() { + return fAllowCollapsing && fCollapseStructures; + } + + /** + * Returns true if inactive code should be collapsed. + * + * @return true if inactive code should be collapsed + */ + public boolean collapseInactiveCode() { + return fAllowCollapsing && fCollapseInactiveCode; + } + + /** + * @return the converter for the current AST or null + */ + public IPositionConverter getASTPositionConverter() { + return fASTPositionConverter; + } + + /** + * @return the current AST or null + */ + public IASTTranslationUnit getAST() { + return fAST; + } + } + private static class CProjectionAnnotation extends ProjectionAnnotation { + + public final static int COMMENT= 1; + public final static int BRANCH= 2; - private ICElement fCElement; - private boolean fIsComment; + private Object fKey; + private int fCategory; - public CProjectionAnnotation(ICElement element, boolean isCollapsed, boolean isComment) { + public CProjectionAnnotation(boolean isCollapsed, Object key, boolean isComment) { + this(isCollapsed, key, isComment ? COMMENT : 0); + } + + public CProjectionAnnotation(boolean isCollapsed, Object key, int category) { super(isCollapsed); - fCElement= element; - fIsComment= isComment; + fKey= key; + fCategory= category; } - public ICElement getElement() { - return fCElement; + public Object getElement() { + return fKey; } - public void setElement(ICElement element) { - fCElement= element; + public void setElement(Object element) { + fKey= element; } + public int getCategory() { + return fCategory; + } + + public void setCategory(int category) { + fCategory = category; + } + public boolean isComment() { - return fIsComment; + return fCategory == COMMENT; } public void setIsComment(boolean isComment) { - fIsComment= isComment; + fCategory= isComment ? COMMENT : 0; + } + /* + * @see java.lang.Object#toString() + */ + public String toString() { + return "CProjectionAnnotation:\n" + //$NON-NLS-1$ + "\tkey: \t"+ fKey + "\n" + //$NON-NLS-1$ //$NON-NLS-2$ + "\tcollapsed: \t" + isCollapsed() + "\n" + //$NON-NLS-1$ //$NON-NLS-2$ + "\tcomment: \t" + isComment() + "\n"; //$NON-NLS-1$ //$NON-NLS-2$ } } + + private static final class Tuple { + CProjectionAnnotation annotation; + Position position; + Tuple(CProjectionAnnotation annotation, Position position) { + this.annotation= annotation; + this.position= position; + } + } + + private class ElementChangedListener implements IElementChangedListener { /* - * @see org.eclipse.jdt.core.IElementChangedListener#elementChanged(org.eclipse.jdt.core.ElementChangedEvent) + * @see org.eclipse.cdt.core.IElementChangedListener#elementChanged(org.eclipse.cdt.core.ElementChangedEvent) */ public void elementChanged(ElementChangedEvent e) { ICElementDelta delta= findElement(fInput, e.getDelta()); if (delta != null) - processDelta(delta); + update(createContext(false)); } private ICElementDelta findElement(ICElement target, ICElementDelta delta) { @@ -115,371 +374,1067 @@ public class DefaultCFoldingStructureProvider implements IProjectionListener, IC } } + /** + * Projection position that will return two foldable regions: one folding away + * the region from after the '/*' to the beginning of the content, the other + * from after the first content line until after the comment. + */ + private static final class CommentPosition extends Position implements IProjectionPosition { + CommentPosition(int offset, int length) { + super(offset, length); + } + + /* + * @see org.eclipse.jface.text.source.projection.IProjectionPosition#computeFoldingRegions(org.eclipse.jface.text.IDocument) + */ + public IRegion[] computeProjectionRegions(IDocument document) throws BadLocationException { + DocumentCharacterIterator sequence= new DocumentCharacterIterator(document, offset, offset + length); + int prefixEnd= 0; + int contentStart= findFirstContent(sequence, prefixEnd); + + int firstLine= document.getLineOfOffset(offset + prefixEnd); + int captionLine= document.getLineOfOffset(offset + contentStart); + int lastLine= document.getLineOfOffset(offset + length); + + Assert.isTrue(firstLine <= captionLine, "first folded line is greater than the caption line"); //$NON-NLS-1$ + Assert.isTrue(captionLine <= lastLine, "caption line is greater than the last folded line"); //$NON-NLS-1$ + + IRegion preRegion; + if (firstLine < captionLine) { +// preRegion= new Region(offset + prefixEnd, contentStart - prefixEnd); + int preOffset= document.getLineOffset(firstLine); + IRegion preEndLineInfo= document.getLineInformation(captionLine); + int preEnd= preEndLineInfo.getOffset(); + preRegion= new Region(preOffset, preEnd - preOffset); + } else { + preRegion= null; + } + + if (captionLine < lastLine) { + int postOffset= document.getLineOffset(captionLine + 1); + IRegion postRegion= new Region(postOffset, offset + length - postOffset); + + if (preRegion == null) + return new IRegion[] { postRegion }; + + return new IRegion[] { preRegion, postRegion }; + } + + if (preRegion != null) + return new IRegion[] { preRegion }; + + return null; + } + + /** + * Finds the offset of the first identifier part within content. + * Returns 0 if none is found. + * + * @param content the content to search + * @return the first index of a unicode identifier part, or zero if none can + * be found + */ + private int findFirstContent(final CharSequence content, int prefixEnd) { + int lenght= content.length(); + for (int i= prefixEnd; i < lenght; i++) { + if (Character.isUnicodeIdentifierPart(content.charAt(i))) + return i; + } + return 0; + } + +// /** +// * Finds the offset of the first identifier part within content. +// * Returns 0 if none is found. +// * +// * @param content the content to search +// * @return the first index of a unicode identifier part, or zero if none can +// * be found +// */ +// private int findPrefixEnd(final CharSequence content) { +// // return the index after the leading '/*' or '/**' +// int len= content.length(); +// int i= 0; +// while (i < len && isWhiteSpace(content.charAt(i))) +// i++; +// if (len >= i + 2 && content.charAt(i) == '/' && content.charAt(i + 1) == '*') +// if (len >= i + 3 && content.charAt(i + 2) == '*') +// return i + 3; +// else +// return i + 2; +// else +// return i; +// } +// +// private boolean isWhiteSpace(char c) { +// return c == ' ' || c == '\t'; +// } + + /* + * @see org.eclipse.jface.text.source.projection.IProjectionPosition#computeCaptionOffset(org.eclipse.jface.text.IDocument) + */ + public int computeCaptionOffset(IDocument document) { +// return 0; + DocumentCharacterIterator sequence= new DocumentCharacterIterator(document, offset, offset + length); + return findFirstContent(sequence, 0); + } + } + + /** + * Projection position that will return two foldable regions: one folding away + * the lines before the one containing the simple name of the C element, one + * folding away any lines after the caption. + */ + private static final class CElementPosition extends Position implements IProjectionPosition { + + private IMember fMember; + + public CElementPosition(int offset, int length, IMember member) { + super(offset, length); + Assert.isNotNull(member); + fMember= member; + } + + public void setMember(IMember member) { + Assert.isNotNull(member); + fMember= member; + } + + /* + * @see org.eclipse.jface.text.source.projection.IProjectionPosition#computeFoldingRegions(org.eclipse.jface.text.IDocument) + */ + public IRegion[] computeProjectionRegions(IDocument document) throws BadLocationException { + int nameStart= offset; + try { + /* The member's name range may not be correct. However, + * reconciling would trigger another element delta which would + * lead to reentrant situations. Therefore, we optimistically + * assume that the name range is correct, but double check the + * received lines below. */ + ISourceRange sourceRange= fMember.getSourceRange(); + if (sourceRange != null) + nameStart= sourceRange.getIdStartPos(); + + } catch (CModelException e) { + // ignore and use default + } + + int firstLine= document.getLineOfOffset(offset); + int captionLine= document.getLineOfOffset(nameStart); + int lastLine= document.getLineOfOffset(offset + length); + + /* see comment above - adjust the caption line to be inside the + * entire folded region, and rely on later element deltas to correct + * the name range. */ + if (captionLine < firstLine) + captionLine= firstLine; + if (captionLine > lastLine) + captionLine= lastLine; + + IRegion preRegion; + if (firstLine < captionLine) { + int preOffset= document.getLineOffset(firstLine); + IRegion preEndLineInfo= document.getLineInformation(captionLine); + int preEnd= preEndLineInfo.getOffset(); + preRegion= new Region(preOffset, preEnd - preOffset); + } else { + preRegion= null; + } + + if (captionLine < lastLine) { + int postOffset= document.getLineOffset(captionLine + 1); + IRegion postRegion= new Region(postOffset, offset + length - postOffset); + + if (preRegion == null) + return new IRegion[] { postRegion }; + + return new IRegion[] { preRegion, postRegion }; + } + + if (preRegion != null) + return new IRegion[] { preRegion }; + + return null; + } + + /* + * @see org.eclipse.jface.text.source.projection.IProjectionPosition#computeCaptionOffset(org.eclipse.jface.text.IDocument) + */ + public int computeCaptionOffset(IDocument document) throws BadLocationException { + int nameStart= offset; + try { + // need a reconcile here? + ISourceRange sourceRange= fMember.getSourceRange(); + if (sourceRange != null) + nameStart= sourceRange.getIdStartPos(); + } catch (CModelException e) { + // ignore and use default + } + + return nameStart - offset; + } + + } - private IDocument fCachedDocument; - + /** + * Internal projection listener. + */ + private final class ProjectionListener implements IProjectionListener { + private ProjectionViewer fViewer; + + /** + * Registers the listener with the viewer. + * + * @param viewer the viewer to register a listener with + */ + public ProjectionListener(ProjectionViewer viewer) { + Assert.isLegal(viewer != null); + fViewer= viewer; + fViewer.addProjectionListener(this); + } + + /** + * Disposes of this listener and removes the projection listener from the viewer. + */ + public void dispose() { + if (fViewer != null) { + fViewer.removeProjectionListener(this); + fViewer= null; + } + } + + /* + * @see org.eclipse.jface.text.source.projection.IProjectionListener#projectionEnabled() + */ + public void projectionEnabled() { + handleProjectionEnabled(); + } + + /* + * @see org.eclipse.jface.text.source.projection.IProjectionListener#projectionDisabled() + */ + public void projectionDisabled() { + handleProjectionDisabled(); + } + } + + /** + * Implementation of IRegion that can be reused + * by setting the offset and the length. + */ + private static class ModifiableRegion extends Position implements IRegion { + ModifiableRegion() { + super(); + } + ModifiableRegion(int offset, int length) { + super(offset, length); + } + } + + /** + * Representation of a preprocessor code branch. + */ + private static class Branch extends ModifiableRegion { + + private boolean fTaken; + + /** + * @param offset + * @param taken + */ + Branch(int offset, boolean taken) { + super(offset, 0); + fTaken= taken; + } + + /** + * @param offset + * @param length + * @param taken + */ + Branch(int offset, int length, boolean taken) { + super(offset, length); + fTaken= taken; + } + + /** + * @param endOffset + */ + public void setEndOffset(int endOffset) { + setLength(endOffset - getOffset()); + } + + public boolean taken() { + return fTaken; + } + } + + private final static boolean DEBUG= "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.cdt.ui/debug/folding")); //$NON-NLS-1$//$NON-NLS-2$; + private ITextEditor fEditor; - private ProjectionViewer fViewer; + private ProjectionListener fProjectionListener; protected ICElement fInput; private IElementChangedListener fElementListener; - private boolean fAllowCollapsing= false; + private boolean fCollapseHeaderComments= true; + private boolean fCollapseComments= false; private boolean fCollapseMacros= false; private boolean fCollapseFunctions= true; private boolean fCollapseStructures= true; private boolean fCollapseMethods= false; + private boolean fCollapseInactiveCode= true; + private int fMinCommentLines= 1; + private boolean fPreprocessorBranchFoldingEnabled= true; + private boolean fCommentFoldingEnabled= true; + + private ICReconcilingListener fReconilingListener; + boolean fInitialASTReconcile= true; + + + /** + * Creates a new folding provider. It must be + * {@link #install(ITextEditor, ProjectionViewer) installed} on an editor/viewer pair before it + * can be used, and {@link #uninstall() uninstalled} when not used any longer. + *

+ * The projection state may be reset by calling {@link #initialize()}. + *

+ */ public DefaultCFoldingStructureProvider() { } + /* + * @see org.eclipse.cdt.ui.text.folding.ICFoldingStructureProvider#install(org.eclipse.ui.texteditor.ITextEditor, org.eclipse.jface.text.source.projection.ProjectionViewer) + */ public void install(ITextEditor editor, ProjectionViewer viewer) { + Assert.isLegal(editor != null); + Assert.isLegal(viewer != null); + + internalUninstall(); + if (editor instanceof CEditor) { fEditor= editor; - fViewer= viewer; - fViewer.addProjectionListener(this); + fProjectionListener= new ProjectionListener(viewer); } } - + + /* + * @see org.eclipse.cdt.ui.text.folding.ICFoldingStructureProvider#uninstall() + */ public void uninstall() { + internalUninstall(); + } + + /** + * Internal implementation of {@link #uninstall()}. + */ + private void internalUninstall() { if (isInstalled()) { - projectionDisabled(); - fViewer.removeProjectionListener(this); - fViewer= null; + handleProjectionDisabled(); + fProjectionListener.dispose(); + fProjectionListener= null; fEditor= null; } } - - protected boolean isInstalled() { + + /** + * Returns true if the provider is installed, false otherwise. + * + * @return true if the provider is installed, false otherwise + */ + protected final boolean isInstalled() { return fEditor != null; } - /* - * @see org.eclipse.jface.text.source.projection.IProjectionListener#projectionEnabled() + /** + * Called whenever projection is enabled, for example when the viewer issues a + * {@link IProjectionListener#projectionEnabled() projectionEnabled} message. When the provider + * is already enabled when this method is called, it is first + * {@link #handleProjectionDisabled() disabled}. + *

+ * Subclasses may extend. + *

*/ - public void projectionEnabled() { + protected void handleProjectionEnabled() { + if (DEBUG) System.out.println("DefaultCFoldingStructureProvider.handleProjectionEnabled()"); //$NON-NLS-1$ // http://home.ott.oti.com/teams/wswb/anon/out/vms/index.html // projectionEnabled messages are not always paired with projectionDisabled // i.e. multiple enabled messages may be sent out. // we have to make sure that we disable first when getting an enable // message. - projectionDisabled(); - + handleProjectionDisabled(); + if (fEditor instanceof CEditor) { + fInitialASTReconcile= true; initialize(); - fElementListener= new ElementChangedListener(); - CoreModel.getDefault().addElementChangedListener(fElementListener); + if (fPreprocessorBranchFoldingEnabled || fCommentFoldingEnabled) { + fReconilingListener= new PreprocessorBranchesReconciler(); + ((CEditor)fEditor).addReconcileListener(fReconilingListener); + } else { + fElementListener= new ElementChangedListener(); + CoreModel.getDefault().addElementChangedListener(fElementListener); + } } } - - /* - * @see org.eclipse.jface.text.source.projection.IProjectionListener#projectionDisabled() + + /** + * Called whenever projection is disabled, for example when the provider is + * {@link #uninstall() uninstalled}, when the viewer issues a + * {@link IProjectionListener#projectionDisabled() projectionDisabled} message and before + * {@link #handleProjectionEnabled() enabling} the provider. Implementations must be prepared to + * handle multiple calls to this method even if the provider is already disabled. + *

+ * Subclasses may extend. + *

*/ - public void projectionDisabled() { - fCachedDocument= null; + protected void handleProjectionDisabled() { if (fElementListener != null) { CoreModel.getDefault().removeElementChangedListener(fElementListener); fElementListener= null; } - } - - public void initialize() { - - if (!isInstalled()) - return; - - initializePreferences(); - - try { - - IDocumentProvider provider= fEditor.getDocumentProvider(); - fCachedDocument= provider.getDocument(fEditor.getEditorInput()); - fAllowCollapsing= true; - - if (fEditor instanceof CEditor) { - IWorkingCopyManager manager= CUIPlugin.getDefault().getWorkingCopyManager(); - fInput= manager.getWorkingCopy(fEditor.getEditorInput()); - } - - if (fInput != null) { - ProjectionAnnotationModel model= (ProjectionAnnotationModel) fEditor.getAdapter(ProjectionAnnotationModel.class); - if (model != null) { - - if (fInput instanceof IWorkingCopy) { - IWorkingCopy unit= (IWorkingCopy) fInput; - synchronized (unit) { - try { - unit.reconcile(); - } catch (CModelException x) { - } - } - } - - Map additions= computeAdditions((IParent) fInput); - model.removeAllAnnotations(); - model.replaceAnnotations(null, additions); - } - } - } catch (Exception e) { - e.printStackTrace(); - } finally { - fCachedDocument= null; - fAllowCollapsing= false; + if (fReconilingListener != null) { + ((CEditor)fEditor).removeReconcileListener(fReconilingListener); + fReconilingListener= null; } } + /* + * @see org.eclipse.cdt.ui.text.folding.ICFoldingStructureProvider#initialize() + */ + public final void initialize() { + if (DEBUG) System.out.println("DefaultCFoldingStructureProvider.initialize()"); //$NON-NLS-1$ + update(createInitialContext()); + } + + private FoldingStructureComputationContext createInitialContext() { + initializePreferences(); + fInput= getInputElement(); + if (fInput == null) + return null; + + return createContext(true); + } + + private FoldingStructureComputationContext createContext(boolean allowCollapse) { + if (!isInstalled()) + return null; + ProjectionAnnotationModel model= getModel(); + if (model == null) + return null; + IDocument doc= getDocument(); + if (doc == null) + return null; + + return new FoldingStructureComputationContext(doc, model, allowCollapse); + } + + private ICElement getInputElement() { + if (fEditor instanceof CEditor) { + return ((CEditor)fEditor).getInputCElement(); + } + return null; + } + private void initializePreferences() { IPreferenceStore store= CUIPlugin.getDefault().getPreferenceStore(); fCollapseFunctions= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_FUNCTIONS); fCollapseStructures= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_STRUCTURES); fCollapseMacros= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_MACROS); fCollapseMethods= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_METHODS); + fCollapseHeaderComments= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_HEADERS); + fCollapseComments= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_COMMENTS); + fCollapseInactiveCode= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_INACTIVE_CODE); + fPreprocessorBranchFoldingEnabled= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_PREPROCESSOR_BRANCHES_ENABLED); + fCommentFoldingEnabled= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_COMMENTS_ENABLED); } - private Map computeAdditions(IParent parent) { - Map map= new HashMap(); - try { - computeAdditions(parent.getChildren(), map); - } catch (CModelException x) { - } - return map; - } - - private void computeAdditions(ICElement[] elements, Map map) throws CModelException { - for (int i= 0; i < elements.length; i++) { - ICElement element= elements[i]; - - computeAdditions(element, map); - - if (element instanceof IParent) { - IParent parent= (IParent) element; - computeAdditions(parent.getChildren(), map); - } - } - } - - private void computeAdditions(ICElement element, Map map) { - - boolean createProjection= false; - - boolean collapse= false; - switch (element.getElementType()) { - - case ICElement.C_STRUCT: - case ICElement.C_CLASS: - case ICElement.C_UNION: - collapse= fAllowCollapsing && fCollapseStructures; - createProjection= true; - break; - case ICElement.C_MACRO: - collapse= fAllowCollapsing && fCollapseMacros; - createProjection= true; - break; - case ICElement.C_FUNCTION: - collapse= fAllowCollapsing && fCollapseFunctions; - createProjection= true; - break; - case ICElement.C_METHOD: - collapse= fAllowCollapsing && fCollapseMethods; - createProjection= true; - break; - } - - if (createProjection) { - Position position= createProjectionPosition(element); - if (position != null) { - map.put(new CProjectionAnnotation(element, collapse, false), position); - } - } - } - - private Position createProjectionPosition(ICElement element) { - - if (fCachedDocument == null) - return null; - - try { - if (element instanceof ISourceReference) { - ISourceReference reference= (ISourceReference) element; - ISourceRange range= reference.getSourceRange(); - - // We need to start at the id position if not code like this - // static int - // foo() - // { } - // will be fold at the wrong line. - - int start= fCachedDocument.getLineOfOffset(range.getIdStartPos()); - int end= fCachedDocument.getLineOfOffset(range.getStartPos() + range.getLength()); - if (start < end) { - int offset= fCachedDocument.getLineOffset(start); - int endOffset= fCachedDocument.getLineOffset(end + 1); - return new Position(offset, endOffset - offset); - } - } - } catch (BadLocationException x) { - } catch (CModelException e) { - } - return null; - } - - protected void processDelta(ICElementDelta delta) { - - if (!isInstalled()) + private void update(FoldingStructureComputationContext ctx) { + if (ctx == null) return; + + Map additions= new HashMap(); + List deletions= new ArrayList(); + List updates= new ArrayList(); - ProjectionAnnotationModel model= (ProjectionAnnotationModel) fEditor.getAdapter(ProjectionAnnotationModel.class); - if (model == null) - return; - - try { - - IDocumentProvider provider= fEditor.getDocumentProvider(); - fCachedDocument= provider.getDocument(fEditor.getEditorInput()); - fAllowCollapsing= false; - - Map additions= new HashMap(); - List deletions= new ArrayList(); - List updates= new ArrayList(); - - Map updated= computeAdditions((IParent) fInput); - Map previous= createAnnotationMap(model); - - - Iterator e= updated.keySet().iterator(); - while (e.hasNext()) { - CProjectionAnnotation annotation= (CProjectionAnnotation) e.next(); - ICElement element= annotation.getElement(); - Position position= (Position) updated.get(annotation); - - List annotations= (List) previous.get(element); - if (annotations == null) { - - additions.put(annotation, position); - - } else { - - Iterator x= annotations.iterator(); - while (x.hasNext()) { - CProjectionAnnotation a= (CProjectionAnnotation) x.next(); - if (annotation.isComment() == a.isComment()) { - Position p= model.getPosition(a); - if (p != null && !position.equals(p)) { - p.setOffset(position.getOffset()); - p.setLength(position.getLength()); - updates.add(a); - } - x.remove(); - break; + computeFoldingStructure(ctx); + Map updated= ctx.fMap; + Map previous= computeCurrentStructure(ctx); + + Iterator e= updated.keySet().iterator(); + while (e.hasNext()) { + CProjectionAnnotation newAnnotation= (CProjectionAnnotation) e.next(); + Object key= newAnnotation.getElement(); + Position newPosition= (Position) updated.get(newAnnotation); + + List annotations= (List) previous.get(key); + if (annotations == null) { + if (DEBUG) System.out.println("DefaultCFoldingStructureProvider.update() new annotation " + newAnnotation); //$NON-NLS-1$ + + additions.put(newAnnotation, newPosition); + + } else { + Iterator x= annotations.iterator(); + boolean matched= false; + while (x.hasNext()) { + Tuple tuple= (Tuple) x.next(); + CProjectionAnnotation existingAnnotation= tuple.annotation; + Position existingPosition= tuple.position; + if (newAnnotation.isComment() == existingAnnotation.isComment()) { + if (existingPosition != null && (!newPosition.equals(existingPosition) || ctx.allowCollapsing() && existingAnnotation.isCollapsed() != newAnnotation.isCollapsed())) { + existingPosition.setOffset(newPosition.getOffset()); + existingPosition.setLength(newPosition.getLength()); + if (ctx.allowCollapsing() && existingAnnotation.isCollapsed() != newAnnotation.isCollapsed()) + if (DEBUG) System.out.println("DefaultCFoldingStructureProvider.update() change annotation " + newAnnotation); //$NON-NLS-1$ + if (newAnnotation.isCollapsed()) + existingAnnotation.markCollapsed(); + else + existingAnnotation.markExpanded(); + updates.add(existingAnnotation); } - } - - if (annotations.isEmpty()) - previous.remove(element); - } - } - - e= previous.values().iterator(); - while (e.hasNext()) { - List list= (List) e.next(); - int size= list.size(); - for (int i= 0; i < size; i++) - deletions.add(list.get(i)); - } - - match(model, deletions, additions, updates); - - Annotation[] removals= new Annotation[deletions.size()]; - deletions.toArray(removals); - Annotation[] changes= new Annotation[updates.size()]; - updates.toArray(changes); - model.modifyAnnotations(removals, additions, changes); - - } finally { - fCachedDocument= null; - fAllowCollapsing= true; - } - } - - private void match(ProjectionAnnotationModel model, List deletions, Map additions, List changes) { - if (deletions.isEmpty() || (additions.isEmpty() && changes.isEmpty())) - return; - - List newDeletions= new ArrayList(); - List newChanges= new ArrayList(); - - Iterator deletionIterator= deletions.iterator(); - outer: while (deletionIterator.hasNext()) { - CProjectionAnnotation deleted= (CProjectionAnnotation) deletionIterator.next(); - Position deletedPosition= model.getPosition(deleted); - if (deletedPosition == null) - continue; - - Iterator changesIterator= changes.iterator(); - while (changesIterator.hasNext()) { - CProjectionAnnotation changed= (CProjectionAnnotation) changesIterator.next(); - if (deleted.isComment() == changed.isComment()) { - Position changedPosition= model.getPosition(changed); - if (changedPosition == null) - continue; - - if (deletedPosition.getOffset() == changedPosition.getOffset()) { - - deletedPosition.setLength(changedPosition.getLength()); - deleted.setElement(changed.getElement()); - - deletionIterator.remove(); - newChanges.add(deleted); - - changesIterator.remove(); - newDeletions.add(changed); - - continue outer; - } - } - } - - Iterator additionsIterator= additions.keySet().iterator(); - while (additionsIterator.hasNext()) { - CProjectionAnnotation added= (CProjectionAnnotation) additionsIterator.next(); - if (deleted.isComment() == added.isComment()) { - Position addedPosition= (Position) additions.get(added); - - if (deletedPosition.getOffset() == addedPosition.getOffset()) { - - deletedPosition.setLength(addedPosition.getLength()); - deleted.setElement(added.getElement()); - - deletionIterator.remove(); - newChanges.add(deleted); - - additionsIterator.remove(); - + matched= true; + x.remove(); break; } } + if (!matched) { + if (DEBUG) System.out.println("DefaultCFoldingStructureProvider.update() new annotation " + newAnnotation); //$NON-NLS-1$ + + additions.put(newAnnotation, newPosition); + } + if (annotations.isEmpty()) + previous.remove(key); } } - + + e= previous.values().iterator(); + while (e.hasNext()) { + List list= (List) e.next(); + int size= list.size(); + for (int i= 0; i < size; i++) { + CProjectionAnnotation annotation= ((Tuple) list.get(i)).annotation; + if (DEBUG) System.out.println("DefaultCFoldingStructureProvider.update() deleted annotation " + annotation); //$NON-NLS-1$ + deletions.add(annotation); + } + } + + match(deletions, additions, updates, ctx); + + Annotation[] removals= new Annotation[deletions.size()]; + deletions.toArray(removals); + Annotation[] changes= new Annotation[updates.size()]; + updates.toArray(changes); + 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$ + ctx.getModel().modifyAnnotations(removals, additions, changes); + } + + /** + * Matches deleted annotations to changed or added ones. A deleted + * annotation/position tuple that has a matching addition / change + * is updated and marked as changed. The matching tuple is not added + * (for additions) or marked as deletion instead (for changes). The + * result is that more annotations are changed and fewer get + * deleted/re-added. + */ + private void match(List deletions, Map additions, List changes, FoldingStructureComputationContext ctx) { + if (deletions.isEmpty() || (additions.isEmpty() && changes.isEmpty())) + return; + + List newDeletions= new ArrayList(); + List newChanges= new ArrayList(); + + Iterator deletionIterator= deletions.iterator(); + while (deletionIterator.hasNext()) { + CProjectionAnnotation deleted= (CProjectionAnnotation) deletionIterator.next(); + Position deletedPosition= ctx.getModel().getPosition(deleted); + if (deletedPosition == null) + continue; + + Tuple deletedTuple= new Tuple(deleted, deletedPosition); + + Tuple match= findMatch(deletedTuple, changes, null, ctx); + boolean addToDeletions= true; + if (match == null) { + match= findMatch(deletedTuple, additions.keySet(), additions, ctx); + addToDeletions= false; + } + + if (match != null) { + Object element= match.annotation.getElement(); + deleted.setElement(element); + deletedPosition.setLength(match.position.getLength()); + if (deletedPosition instanceof CElementPosition && element instanceof IMember) { + CElementPosition jep= (CElementPosition) deletedPosition; + jep.setMember((IMember) element); + } + + deletionIterator.remove(); + if (DEBUG) System.out.println("DefaultCFoldingStructureProvider.update() changed annotation " + deleted); //$NON-NLS-1$ + newChanges.add(deleted); + + if (addToDeletions) { + if (DEBUG) System.out.println("DefaultCFoldingStructureProvider.update() deleted annotation " + match.annotation); //$NON-NLS-1$ + newDeletions.add(match.annotation); + } + } + } + deletions.addAll(newDeletions); changes.addAll(newChanges); } - private Map createAnnotationMap(IAnnotationModel model) { + /** + * Finds a match for tuple in a collection of + * annotations. The positions for the + * CProjectionAnnotation instances in + * annotations can be found in the passed + * positionMap or fCachedModel if + * positionMap is null. + *

+ * A tuple is said to match another if their annotations have the + * same comment flag and their position offsets are equal. + *

+ *

+ * If a match is found, the annotation gets removed from + * annotations. + *

+ * + * @param tuple the tuple for which we want to find a match + * @param annotations collection of + * CProjectionAnnotation + * @param positionMap a Map<Annotation, Position> + * or null + * @return a matching tuple or null for no match + */ + private Tuple findMatch(Tuple tuple, Collection annotations, Map positionMap, FoldingStructureComputationContext ctx) { + Iterator it= annotations.iterator(); + while (it.hasNext()) { + CProjectionAnnotation annotation= (CProjectionAnnotation) it.next(); + if (tuple.annotation.isComment() == annotation.isComment()) { + Position position= positionMap == null ? ctx.getModel().getPosition(annotation) : (Position) positionMap.get(annotation); + if (position == null) + continue; + + if (tuple.position.getOffset() == position.getOffset()) { + it.remove(); + return new Tuple(annotation, position); + } + } + } + + return null; + } + + private Map computeCurrentStructure(FoldingStructureComputationContext ctx) { + boolean includeBranches= fPreprocessorBranchFoldingEnabled && ctx.fAST != null; Map map= new HashMap(); + ProjectionAnnotationModel model= ctx.getModel(); Iterator e= model.getAnnotationIterator(); while (e.hasNext()) { Object annotation= e.next(); if (annotation instanceof CProjectionAnnotation) { - CProjectionAnnotation c= (CProjectionAnnotation) annotation; - List list= (List) map.get(c.getElement()); + CProjectionAnnotation cAnnotation= (CProjectionAnnotation) annotation; + if (!includeBranches && cAnnotation.getCategory() == CProjectionAnnotation.BRANCH) { + continue; + } + Position position= model.getPosition(cAnnotation); + Assert.isNotNull(position); + List list= (List) map.get(cAnnotation.getElement()); if (list == null) { list= new ArrayList(2); - map.put(c.getElement(), list); + map.put(cAnnotation.getElement(), list); } - list.add(c); + list.add(new Tuple(cAnnotation, position)); } } + + Comparator comparator= new Comparator() { + public int compare(Object o1, Object o2) { + return ((Tuple) o1).position.getOffset() - ((Tuple) o2).position.getOffset(); + } + }; + for (Iterator it= map.values().iterator(); it.hasNext();) { + List list= (List) it.next(); + Collections.sort(list, comparator); + } return map; } + + + private void computeFoldingStructure(FoldingStructureComputationContext ctx) { + if (fCommentFoldingEnabled) { + // compute comment positions from partitioning + try { + IDocument doc= ctx.getDocument(); + ITypedRegion[] partitions = TextUtilities.computePartitioning(doc, ICPartitions.C_PARTITIONING, 0, doc.getLength(), false); + computeFoldingStructure(partitions, ctx); + } catch (BadLocationException e) { + // ignore + } + } + IParent parent= (IParent) fInput; + try { + computeFoldingStructure(parent.getChildren(), ctx); + } catch (CModelException x) { + } + if (fPreprocessorBranchFoldingEnabled) { + IASTTranslationUnit ast= ctx.getAST(); + if (ast == null) { + ASTProvider astProvider= CUIPlugin.getDefault().getASTProvider(); + ast= astProvider.getAST(getInputElement(), ASTProvider.WAIT_ACTIVE_ONLY, null); + if (ast != null) { + ctx.fAST= ast; + ctx.fASTPositionConverter= astProvider.getActivePositionConverter(getInputElement()); + fInitialASTReconcile= false; + } + } + computeFoldingStructure(ast, ctx); + } + } + + /** + * Compute folding structure of the preprocessor branches for the given AST. + * + * @param ast + * @param ctx + */ + private void computeFoldingStructure(IASTTranslationUnit ast, FoldingStructureComputationContext ctx) { + if (ast == null) { + return; + } + String fileName = ast.getFilePath(); + if (fileName == null) { + return; + } + IPositionConverter converter= ctx.getASTPositionConverter(); + List branches= new ArrayList(); + Stack branchStack = new Stack(); + + IASTPreprocessorStatement[] preprocStmts = ast.getAllPreprocessorStatements(); + + for (int i = 0; i < preprocStmts.length; i++) { + IASTPreprocessorStatement statement = preprocStmts[i]; + if (!fileName.equals(statement.getContainingFilename())) { + // preprocessor directive is from a different file + continue; + } + IASTNodeLocation[] nodeLocations = statement.getNodeLocations(); + if (nodeLocations.length != 1) { + continue; + } + IASTNodeLocation stmtLocation= nodeLocations[0]; + if (statement instanceof IASTPreprocessorIfStatement) { + IASTPreprocessorIfStatement ifStmt = (IASTPreprocessorIfStatement)statement; + branchStack.push(new Branch(stmtLocation.getNodeOffset(), ifStmt.taken())); + } else if (statement instanceof IASTPreprocessorIfdefStatement) { + IASTPreprocessorIfdefStatement ifdefStmt = (IASTPreprocessorIfdefStatement)statement; + branchStack.push(new Branch(stmtLocation.getNodeOffset(), ifdefStmt.taken())); + } else if (statement instanceof IASTPreprocessorIfndefStatement) { + IASTPreprocessorIfndefStatement ifndefStmt = (IASTPreprocessorIfndefStatement)statement; + branchStack.push(new Branch(stmtLocation.getNodeOffset(), ifndefStmt.taken())); + } else if (statement instanceof IASTPreprocessorElseStatement) { + if (branchStack.isEmpty()) { + // #else without #if + continue; + } + Branch branch= (Branch)branchStack.pop(); + IASTPreprocessorElseStatement elseStmt = (IASTPreprocessorElseStatement)statement; + branchStack.push(new Branch(stmtLocation.getNodeOffset(), elseStmt.taken())); + branch.setEndOffset(stmtLocation.getNodeOffset() + stmtLocation.getNodeLength()); + IRegion converted= converter != null ? converter.historicToActual(branch) : branch; + branches.add(new Branch(converted.getOffset(), converted.getLength(), branch.taken())); + } else if (statement instanceof IASTPreprocessorElifStatement) { + if (branchStack.isEmpty()) { + // #elif without #if + continue; + } + Branch branch= (Branch)branchStack.pop(); + IASTPreprocessorElifStatement elifStmt = (IASTPreprocessorElifStatement) statement; + branchStack.push(new Branch(stmtLocation.getNodeOffset(), elifStmt.taken())); + branch.setEndOffset(stmtLocation.getNodeOffset() + stmtLocation.getNodeLength()); + IRegion converted= converter != null ? converter.historicToActual(branch) : branch; + branches.add(new Branch(converted.getOffset(), converted.getLength(), branch.taken())); + } else if (statement instanceof IASTPreprocessorEndifStatement) { + if (branchStack.isEmpty()) { + // #endif without #if + continue; + } + Branch branch= (Branch)branchStack.pop(); + branch.setEndOffset(stmtLocation.getNodeOffset() + stmtLocation.getNodeLength()); + IRegion converted= converter != null ? converter.historicToActual(branch) : branch; + branches.add(new Branch(converted.getOffset(), converted.getLength(), branch.taken())); + } + } + for (Iterator iter = branches.iterator(); iter.hasNext(); ) { + Branch branch= (Branch) iter.next(); + IRegion aligned = alignRegion(branch, ctx); + if (aligned != null) { + Position alignedPos= new Position(aligned.getOffset(), aligned.getLength()); + ctx.addProjectionRange(new CProjectionAnnotation(!branch.taken() && ctx.collapseInactiveCode(), computeKey(branch, ctx), false), alignedPos); + } + } + } + + /** + * Compute a key for recognizing an annotation based on the given position. + * + * @param pos + * @param ctx + * @return a key to recognise an annotation position + */ + private Object computeKey(Position pos, FoldingStructureComputationContext ctx) { + try { + return ctx.getDocument().get(pos.offset, Math.min(16, pos.length)); + } catch (BadLocationException exc) { + return exc; + } + } + + /** + * Compute folding structure based on partioning information. + * + * @param partitions array of document partitions + * @param ctx the folding structure context + * @throws BadLocationException + */ + private void computeFoldingStructure(ITypedRegion[] partitions, FoldingStructureComputationContext ctx) throws BadLocationException { + boolean collapse = ctx.collapseComments(); + IDocument doc= ctx.getDocument(); + int startLine = -1; + int endLine = -1; + List comments= new ArrayList(); + ModifiableRegion commentRange = new ModifiableRegion(); + for (int i = 0; i < partitions.length; i++) { + ITypedRegion partition = partitions[i]; + // TLETODO [folding] add support for Cppdoc comment? + boolean singleLine = ICPartitions.C_SINGLE_LINE_COMMENT.equals(partition.getType()); + if (singleLine || ICPartitions.C_MULTI_LINE_COMMENT.equals(partition.getType())) { + Position position= singleLine ? null : createCommentPosition(alignRegion(partition, ctx)); + if (position != null) { + comments.add(new Tuple(new CProjectionAnnotation(collapse, doc.get(position.offset, Math.min(16, position.length)), true), position)); + } else { + // if comment starts at column 0 and spans only one line + // and is adjacent to a previous line comment, add it + // to the commentRange + int lineNr = doc.getLineOfOffset(partition.getOffset()); + IRegion lineRegion = doc.getLineInformation(lineNr); + boolean isLineStart = partition.getOffset() == lineRegion.getOffset(); + if (!isLineStart) { + continue; + } + if (!singleLine) { + singleLine = lineRegion.getOffset() + lineRegion.getLength() >= partition.getOffset() + partition.getLength(); + if (!singleLine) { + continue; + } + } + if (startLine < 0 || lineNr - endLine > 1) { + if (startLine >= 0 && endLine - startLine >= fMinCommentLines) { + Position projection = createCommentPosition(alignRegion(commentRange, ctx)); + if (projection != null) { + comments.add(new Tuple(new CProjectionAnnotation(collapse, doc.get(projection.offset, Math.min(16, projection.length)), true), projection)); + } + } + startLine = lineNr; + endLine = lineNr; + commentRange.offset = lineRegion.getOffset(); + commentRange.length = lineRegion.getLength(); + } else { + endLine = lineNr; + int delta = lineRegion.getOffset() + lineRegion.getLength() - commentRange.offset - commentRange.length; + commentRange.length += delta; + } + } + } + } + if (startLine >= 0 && endLine - startLine >= fMinCommentLines) { + Position projection = createCommentPosition(alignRegion(commentRange, ctx)); + if (projection != null) { + comments.add(new Tuple(new CProjectionAnnotation(collapse, doc.get(projection.offset, Math.min(16, projection.length)), true), projection)); + } + } + // first comment is header comment + Iterator iter = comments.iterator(); + Tuple tuple = (Tuple) iter.next(); + if (ctx.collapseHeaderComments()) { + tuple.annotation.markCollapsed(); + } else { + tuple.annotation.markExpanded(); + } + ctx.addProjectionRange(tuple.annotation, tuple.position); + while (iter.hasNext()) { + tuple = (Tuple) iter.next(); + ctx.addProjectionRange(tuple.annotation, tuple.position); + } + } + + private void computeFoldingStructure(ICElement[] elements, FoldingStructureComputationContext ctx) throws CModelException { + for (int i= 0; i < elements.length; i++) { + ICElement element= elements[i]; + + computeFoldingStructure(element, ctx); + + if (element instanceof IParent) { + IParent parent= (IParent) element; + computeFoldingStructure(parent.getChildren(), ctx); + } + } + } + + /** + * Computes the folding structure for a given {@link ICElement C element}. Computed + * projection annotations are + * {@link DefaultCFoldingStructureProvider.FoldingStructureComputationContext#addProjectionRange(DefaultCFoldingStructureProvider.CProjectionAnnotation, Position) added} + * to the computation context. + *

+ * Subclasses may extend or replace. The default implementation creates projection annotations + * for the following elements: + *

+ *

+ * + * @param element the C element to compute the folding structure for + * @param ctx the computation context + */ + protected void computeFoldingStructure(ICElement element, FoldingStructureComputationContext ctx) { + + boolean collapse= false; + switch (element.getElementType()) { + + case ICElement.C_STRUCT: + case ICElement.C_CLASS: + case ICElement.C_UNION: + collapse= ctx.collapseStructures(); + break; + case ICElement.C_MACRO: + collapse= ctx.collapseMacros(); + break; + case ICElement.C_FUNCTION: + collapse= ctx.collapseFunctions(); + break; + case ICElement.C_METHOD: + collapse= ctx.collapseMethods(); + break; + default: + return; + } + + IRegion[] regions= computeProjectionRanges((ISourceReference) element, ctx); + if (regions.length > 0) { + IRegion normalized= alignRegion(regions[regions.length - 1], ctx); + if (normalized != null) { + Position position= element instanceof IMember ? createMemberPosition(normalized, (IMember) element) : createCommentPosition(normalized); + if (position != null) + ctx.addProjectionRange(new CProjectionAnnotation(collapse, element, false), position); + } + } + } + + /** + * Computes the projection ranges for a given ISourceReference. More than one + * range or none at all may be returned. If there are no foldable regions, an empty array is + * returned. + *

+ * The last region in the returned array (if not empty) describes the region for the C + * element that implements the source reference. Any preceding regions describe comments + * of that element. + *

+ * + * @param reference a C element that is a source reference + * @param ctx the folding context + * @return the regions to be folded + */ + protected final IRegion[] computeProjectionRanges(ISourceReference reference, FoldingStructureComputationContext ctx) { + try { + ISourceRange range= reference.getSourceRange(); + return new IRegion[] { + new Region(range.getStartPos(), range.getLength()) + }; + } catch (CModelException e) { + } + + return new IRegion[0]; + } + + /** + * Creates a comment folding position from an + * {@link #alignRegion(IRegion, DefaultCFoldingStructureProvider.FoldingStructureComputationContext) aligned} + * region. + * + * @param aligned an aligned region + * @return a folding position corresponding to aligned + */ + protected final Position createCommentPosition(IRegion aligned) { + if (aligned == null) { + return null; + } + return new CommentPosition(aligned.getOffset(), aligned.getLength()); + } + + /** + * Creates a folding position that remembers its member from an + * {@link #alignRegion(IRegion, DefaultCFoldingStructureProvider.FoldingStructureComputationContext) aligned} + * region. + * + * @param aligned an aligned region + * @param member the member to remember + * @return a folding position corresponding to aligned + */ + protected final Position createMemberPosition(IRegion aligned, IMember member) { + return new CElementPosition(aligned.getOffset(), aligned.getLength(), member); + } + + /** + * Aligns region to start and end at a line offset. The region's start is + * decreased to the next line offset, and the end offset increased to the next line start or the + * end of the document. null is returned if region is + * null itself or does not comprise at least one line delimiter, as a single line + * cannot be folded. + * + * @param region the region to align, may be null + * @param ctx the folding context + * @return a region equal or greater than region that is aligned with line + * offsets, null if the region is too small to be foldable (e.g. covers + * only one line) + */ + protected final IRegion alignRegion(IRegion region, FoldingStructureComputationContext ctx) { + if (region == null) + return null; + + IDocument document= ctx.getDocument(); + + try { + + int start= document.getLineOfOffset(region.getOffset()); + int end= document.getLineOfOffset(region.getOffset() + region.getLength()); + if (start >= end) + return null; + + int offset= document.getLineOffset(start); + int endOffset; + if (document.getNumberOfLines() > end + 1) + endOffset= document.getLineOffset(end + 1); + else + endOffset= document.getLineOffset(end) + document.getLineLength(end); + + return new Region(offset, endOffset - offset); + + } catch (BadLocationException x) { + // concurrent modification + return null; + } + } + + private ProjectionAnnotationModel getModel() { + return (ProjectionAnnotationModel) fEditor.getAdapter(ProjectionAnnotationModel.class); + } + + private IDocument getDocument() { + IDocumentProvider provider= fEditor.getDocumentProvider(); + return provider.getDocument(fEditor.getEditorInput()); + } } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/folding/FoldingMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/folding/FoldingMessages.properties index 8c37eb1079e..bb2b532e042 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/folding/FoldingMessages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/folding/FoldingMessages.properties @@ -7,6 +7,7 @@ # # Contributors: # IBM Corporation - initial API and implementation +# Anton Leherbauer (Wind River Systems) ############################################################################### @@ -15,5 +16,8 @@ DefaultCFoldingPreferenceBlock.macros= &Macros DefaultCFoldingPreferenceBlock.functions= &Functions DefaultCFoldingPreferenceBlock.methods= &Methods DefaultCFoldingPreferenceBlock.structures= &Structures +DefaultCFoldingPreferenceBlock.comments= &Comments +DefaultCFoldingPreferenceBlock.headers= &Header Comments +DefaultCFoldingPreferenceBlock.inactive_code= &Inactive Preprocessor Branches EmptyCFoldingPreferenceBlock.emptyCaption= diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/CUIPlugin.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/CUIPlugin.java index 045dc052fd9..936b6f0edca 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/CUIPlugin.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/CUIPlugin.java @@ -9,6 +9,7 @@ * QNX Software Systems - Initial API and implementation * IBM Corp. - Rational Software * Markus Schorn (Wind River Systems) + * Anton Leherbauer (Wind River Systems) *******************************************************************************/ package org.eclipse.cdt.ui; @@ -435,6 +436,9 @@ public class CUIPlugin extends AbstractUIPlugin { } }; CCorePlugin.getDefault().getDOM().setWorkingCopyProvider(workingCopyProvider); + + // init ast provider + getASTProvider(); } /* (non-Javadoc) diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java index 8aad2f38231..b96fc576d80 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java @@ -8,6 +8,7 @@ * Contributors: * IBM Corporation - initial API and implementation * QNX Software System + * Anton Leherbauer (Wind River Systems) *******************************************************************************/ package org.eclipse.cdt.ui; @@ -399,6 +400,56 @@ public class PreferenceConstants { */ public static final String EDITOR_FOLDING_MACROS= "editor_folding_default_macros"; //$NON-NLS-1$ + /** + * A named preference that stores the value for comment folding for the default folding provider. + *

+ * Value is of type Boolean. + *

+ * + * @since 4.0 + */ + public static final String EDITOR_FOLDING_COMMENTS= "editor_folding_default_comments"; //$NON-NLS-1$ + + /** + * A named preference that stores the value for header comment folding for the default folding provider. + *

+ * Value is of type Boolean. + *

+ * + * @since 4.0 + */ + public static final String EDITOR_FOLDING_HEADERS= "editor_folding_default_headers"; //$NON-NLS-1$ + + /** + * A named preference that stores the value for inactive code folding for the default folding provider. + *

+ * Value is of type Boolean. + *

+ * + * @since 4.0 + */ + public static final String EDITOR_FOLDING_INACTIVE_CODE= "editor_folding_default_inactive"; //$NON-NLS-1$ + + /** + * A named preference that controls whether folding of preprocessor branches is enabled. + *

+ * Value is of type Boolean. + *

+ * + * @since 4.0 + */ + public static final String EDITOR_FOLDING_PREPROCESSOR_BRANCHES_ENABLED= "editor_folding_preprocessor_enabled"; //$NON-NLS-1$ + + /** + * A named preference that controls whether folding of comments is enabled. + *

+ * Value is of type Boolean. + *

+ * + * @since 4.0 + */ + public static final String EDITOR_FOLDING_COMMENTS_ENABLED= "editor_folding_comments_enabled"; //$NON-NLS-1$ + /** * A named preference that controls if templates are formatted when applied. *

@@ -545,6 +596,11 @@ public class PreferenceConstants { store.setDefault(PreferenceConstants.EDITOR_FOLDING_STRUCTURES, true); store.setDefault(PreferenceConstants.EDITOR_FOLDING_METHODS, false); store.setDefault(PreferenceConstants.EDITOR_FOLDING_MACROS, true); + store.setDefault(PreferenceConstants.EDITOR_FOLDING_COMMENTS, false); + store.setDefault(PreferenceConstants.EDITOR_FOLDING_HEADERS, true); + store.setDefault(PreferenceConstants.EDITOR_FOLDING_INACTIVE_CODE, true); + store.setDefault(PreferenceConstants.EDITOR_FOLDING_COMMENTS_ENABLED, false); + store.setDefault(PreferenceConstants.EDITOR_FOLDING_PREPROCESSOR_BRANCHES_ENABLED, false); store.setDefault(PreferenceConstants.EDITOR_CLOSE_STRINGS, true); store.setDefault(PreferenceConstants.EDITOR_CLOSE_BRACKETS, true);