diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/text/folding/DefaultJavaFoldingStructureProvider.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/text/folding/DefaultJavaFoldingStructureProvider.java index c907bf2ab6f..066ab511925 100755 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/text/folding/DefaultJavaFoldingStructureProvider.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/text/folding/DefaultJavaFoldingStructureProvider.java @@ -79,7 +79,6 @@ import org.eclipse.jdt.core.dom.DoStatement; import org.eclipse.jdt.core.dom.EnhancedForStatement; import org.eclipse.jdt.core.dom.EnumDeclaration; -import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.ForStatement; import org.eclipse.jdt.core.dom.IfStatement; import org.eclipse.jdt.core.dom.Initializer; @@ -87,10 +86,10 @@ import org.eclipse.jdt.core.dom.LambdaExpression; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.Statement; +import org.eclipse.jdt.core.dom.SwitchCase; import org.eclipse.jdt.core.dom.SwitchStatement; import org.eclipse.jdt.core.dom.SynchronizedStatement; import org.eclipse.jdt.core.dom.TryStatement; -import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.WhileStatement; import org.eclipse.jdt.ui.PreferenceConstants; @@ -99,7 +98,6 @@ import org.eclipse.jdt.internal.ui.actions.SelectionConverter; import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility; import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor; -import org.eclipse.jdt.internal.ui.text.DocumentCharacterIterator; /** * Updates the projection model of a class file or compilation unit. @@ -331,6 +329,148 @@ private static final class Tuple { } } + private class FoldingVisitor extends ASTVisitor { + + private FoldingStructureComputationContext ctx; + + public FoldingVisitor(FoldingStructureComputationContext ctx) { + this.ctx = ctx; + } + + @Override + public boolean visit(MethodDeclaration node) { + processMethodDeclaration(node, ctx); + return true; + } + + @Override + public boolean visit(IfStatement node) { + processIfStatement(node, ctx); + return false; + } + + @Override + public boolean visit(WhileStatement node) { + createFoldingRegion(node, ctx, ctx.collapseMembers()); + return true; + } + + @Override + public boolean visit(ForStatement node) { + createFoldingRegion(node, ctx, ctx.collapseMembers()); + return true; + } + + @Override + public boolean visit(EnhancedForStatement node) { + createFoldingRegion(node, ctx, ctx.collapseMembers()); + return true; + } + + @Override + public boolean visit(DoStatement node) { + createFoldingRegion(node, ctx, ctx.collapseMembers()); + return true; + } + + @Override + public boolean visit(TryStatement node) { + createFoldingRegion(node, ctx, ctx.collapseMembers()); + processStatement(node.getBody(), ctx); + for (Object catchClauseObj : node.catchClauses()) { + CatchClause catchClause = (CatchClause) catchClauseObj; + createFoldingRegion(catchClause, ctx, ctx.collapseMembers()); + processStatement(catchClause.getBody(), ctx); + } + if (node.getFinally() != null) { + createFoldingRegion(node.getFinally(), ctx, ctx.collapseMembers()); + processStatement(node.getFinally(), ctx); + } + + return true; + } + + @Override + public boolean visit(SwitchStatement node) { + createFoldingRegion(node, ctx, ctx.collapseMembers()); + + List statements = node.statements(); + int statementsSize = statements.size(); + + for (int i = 0; i < statementsSize; i++) { + Statement stmt = (Statement) statements.get(i); + + if (stmt instanceof SwitchCase) { + SwitchCase switchCase = (SwitchCase) stmt; + int start = switchCase.getStartPosition(); + int end; + int j = i + 1; + while (j < statementsSize && !(statements.get(j) instanceof SwitchCase)) { + j++; + } + + if (j < statementsSize) { + end = ((Statement) statements.get(j)).getStartPosition(); + } else { + end = node.getStartPosition() + node.getLength(); + } + + int length = end - start; + if (length > 0) { + IRegion region = new Region(start, length); + IRegion aligned = alignRegion(region, ctx); + + if (aligned != null) { + Position position = new Position(aligned.getOffset(), aligned.getLength()); + JavaProjectionAnnotation annotation = new JavaProjectionAnnotation(ctx.collapseMembers(), null, false); + ctx.addProjectionRange(annotation, position); + } + } + } + } + + return true; + } + + @Override + public boolean visit(SynchronizedStatement node) { + createFoldingRegion(node, ctx, ctx.collapseMembers()); + return true; + } + + @Override + public boolean visit(LambdaExpression node) { + if (node.getBody() instanceof Block) { + createFoldingRegion(node.getBody(), ctx, ctx.collapseMembers()); + } + return true; + } + + @Override + public boolean visit(AnonymousClassDeclaration node) { + createFoldingRegion(node, ctx, ctx.collapseMembers()); + return true; + } + + @Override + public boolean visit(EnumDeclaration node) { + createFoldingRegion(node, ctx, ctx.collapseMembers()); + return true; + } + + @Override + public boolean visit(Initializer node) { + createFoldingRegion(node, ctx, ctx.collapseMembers()); + return true; + } + + @Override + public boolean visit(Javadoc node) { + processComment(node, ctx); + return false; + } + } + /** * Filter for annotations. */ @@ -492,87 +632,6 @@ private IJavaElementDelta findElement(IJavaElement target, IJavaElementDelta del } } - /** - * 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) - */ - @Override - 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) { - 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); - int postLength= offset + length - postOffset; - if (postLength > 0) { - IRegion postRegion= new Region(postOffset, postLength); - 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 - * @param prefixEnd the end of the prefix - * @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; - } - - /* - * @see org.eclipse.jface.text.source.projection.IProjectionPosition#computeCaptionOffset(org.eclipse.jface.text.IDocument) - */ - @Override - public int computeCaptionOffset(IDocument document) throws BadLocationException { - 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 java element, one @@ -1006,104 +1065,7 @@ private void processCompilationUnit(ICompilationUnit unit, FoldingStructureCompu parser.setResolveBindings(true); CompilationUnit ast = (CompilationUnit) parser.createAST(null); - ast.accept(new ASTVisitor() { - @Override - public boolean visit(MethodDeclaration node) { - processMethodDeclaration(node, ctx); - return super.visit(node); - } - - @Override - public boolean visit(FieldDeclaration node) { - processFieldDeclaration(node, ctx); - return super.visit(node); - } - - @Override - public boolean visit(IfStatement node) { - processIfStatement(node, ctx); - return false; // Prevents visiting children since we handle them explicitly - } - - @Override - public boolean visit(WhileStatement node) { - processStatement(node, ctx); - return super.visit(node); - } - - @Override - public boolean visit(ForStatement node) { - processStatement(node, ctx); - return super.visit(node); - } - - @Override - public boolean visit(EnhancedForStatement node) { - processStatement(node, ctx); - return super.visit(node); - } - - @Override - public boolean visit(DoStatement node) { - processStatement(node, ctx); - return super.visit(node); - } - - @Override - public boolean visit(SwitchStatement node) { - processStatement(node, ctx); - return super.visit(node); - } - - @Override - public boolean visit(TryStatement node) { - processTryStatement(node, ctx); - return false; // We handle the catch and finally blocks explicitly - } - - @Override - public boolean visit(SynchronizedStatement node) { - processStatement(node, ctx); - return super.visit(node); - } - - @Override - public boolean visit(LambdaExpression node) { - processLambdaExpression(node, ctx); - return false; // Prevents visiting children since we handle them explicitly - } - - @Override - public boolean visit(TypeDeclaration node) { - processTypeDeclaration(node, ctx); - return super.visit(node); - } - - @Override - public boolean visit(AnonymousClassDeclaration node) { - processAnonymousClassDeclaration(node, ctx); - return super.visit(node); - } - - @Override - public boolean visit(EnumDeclaration node) { - processEnumDeclaration(node, ctx); - return super.visit(node); - } - - @Override - public boolean visit(Initializer node) { - processInitializer(node, ctx); - return super.visit(node); - } - - @Override - public boolean visit(Javadoc node) { - processComment(node, ctx); - return false; // No need to visit children of Javadoc - } - - }); + ast.accept(new FoldingVisitor(ctx)); } catch (JavaModelException e) { e.printStackTrace(); } @@ -1136,35 +1098,6 @@ private void processMethodDeclaration(MethodDeclaration node, FoldingStructureCo } } - private void processFieldDeclaration(FieldDeclaration node, FoldingStructureComputationContext ctx) { - createFoldingRegion(node, ctx, ctx.collapseMembers()); - if (node.getJavadoc() != null) { - processComment(node.getJavadoc(), ctx); - } - } - - private void processTypeDeclaration(TypeDeclaration node, FoldingStructureComputationContext ctx) { - createFoldingRegion(node, ctx, ctx.collapseMembers()); - if (node.getJavadoc() != null) { - processComment(node.getJavadoc(), ctx); - } - } - - private void processAnonymousClassDeclaration(AnonymousClassDeclaration node, FoldingStructureComputationContext ctx) { - createFoldingRegion(node, ctx, ctx.collapseMembers()); - } - - private void processEnumDeclaration(EnumDeclaration node, FoldingStructureComputationContext ctx) { - createFoldingRegion(node, ctx, ctx.collapseMembers()); - if (node.getJavadoc() != null) { - processComment(node.getJavadoc(), ctx); - } - } - - private void processInitializer(Initializer node, FoldingStructureComputationContext ctx) { - createFoldingRegion(node, ctx, ctx.collapseMembers()); - } - private void processIfStatement(IfStatement node, FoldingStructureComputationContext ctx) { createFoldingRegion(node, ctx, ctx.collapseMembers()); processStatement(node.getThenStatement(), ctx); @@ -1183,31 +1116,20 @@ private void processElseStatement(Statement elseStmt, FoldingStructureComputatio } } - - private void processTryStatement(TryStatement node, FoldingStructureComputationContext ctx) { - processStatement(node.getBody(), ctx); - for (Object catchClauseObj : node.catchClauses()) { - CatchClause catchClause = (CatchClause) catchClauseObj; - processStatement(catchClause.getBody(), ctx); - } - if (node.getFinally() != null) { - processStatement(node.getFinally(), ctx); - } - } - - private void processLambdaExpression(LambdaExpression node, FoldingStructureComputationContext ctx) { - if (node.getBody() instanceof Block) { - createFoldingRegion(node.getBody(), ctx, ctx.collapseMembers()); - } - } - private void processStatement(Statement node, FoldingStructureComputationContext ctx) { if (node == null) { return; } - createFoldingRegion(node, ctx, ctx.collapseMembers()); + if (node instanceof Block || node instanceof IfStatement || node instanceof WhileStatement || + node instanceof ForStatement || node instanceof EnhancedForStatement || + node instanceof DoStatement || node instanceof SwitchStatement || + node instanceof TryStatement || node instanceof SynchronizedStatement) { + createFoldingRegion(node, ctx, ctx.collapseMembers()); + } + node.accept(new FoldingVisitor(ctx)); } + private void createFoldingRegion(ASTNode node, FoldingStructureComputationContext ctx, boolean collapse) { int start = node.getStartPosition(); int end = calculateEndPosition(node, ctx); @@ -1215,6 +1137,12 @@ private void createFoldingRegion(ASTNode node, FoldingStructureComputationContex if (end <= start) { return; } + if (node instanceof MethodDeclaration) { + Block body = ((MethodDeclaration) node).getBody(); + if (body != null) { + start = body.getStartPosition(); + } + } int length = end - start; IRegion region = new Region(start, length); @@ -1227,6 +1155,7 @@ private void createFoldingRegion(ASTNode node, FoldingStructureComputationContex } } + private int calculateEndPosition(ASTNode node, FoldingStructureComputationContext ctx) { int end = node.getStartPosition() + node.getLength(); @@ -1248,6 +1177,19 @@ private int calculateEndPosition(ASTNode node, FoldingStructureComputationContex return end; } + /** + * 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 IRegion alignRegion(IRegion region, FoldingStructureComputationContext ctx) { if (region == null) { return null; @@ -1289,6 +1231,14 @@ private void processComment(Javadoc node, FoldingStructureComputationContext ctx } } + /** + * Creates a comment folding position from an + * {@link #alignRegion(IRegion, DefaultJavaFoldingStructureProvider.FoldingStructureComputationContext) aligned} + * region. + * + * @param aligned an aligned region + * @return a folding position corresponding to aligned + */ protected Position createCommentPosition(IRegion aligned) { return new Position(aligned.getOffset(), aligned.getLength()); }