Skip to content

Commit c6938dc

Browse files
authored
preserve contextual information with enhanced folding (#2410)
1 parent 873d801 commit c6938dc

3 files changed

Lines changed: 118 additions & 6 deletions

File tree

org.eclipse.jdt.text.tests/src/org/eclipse/jdt/text/tests/folding/CustomFoldingRegionTest.java

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
import static org.junit.Assume.assumeTrue;
1717
import static org.junit.jupiter.api.Assertions.assertEquals;
1818

19+
import java.util.ArrayList;
20+
import java.util.Iterator;
1921
import java.util.List;
2022

2123
import org.junit.After;
@@ -34,7 +36,12 @@
3436
import org.eclipse.jface.preference.IPreferenceStore;
3537

3638
import org.eclipse.jface.text.IRegion;
39+
import org.eclipse.jface.text.Position;
40+
import org.eclipse.jface.text.source.Annotation;
41+
import org.eclipse.jface.text.source.projection.ProjectionAnnotation;
42+
import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel;
3743

44+
import org.eclipse.jdt.core.ICompilationUnit;
3845
import org.eclipse.jdt.core.IJavaProject;
3946
import org.eclipse.jdt.core.IPackageFragment;
4047
import org.eclipse.jdt.core.IPackageFragmentRoot;
@@ -43,6 +50,8 @@
4350
import org.eclipse.jdt.ui.tests.core.rules.ProjectTestSetup;
4451

4552
import org.eclipse.jdt.internal.ui.JavaPlugin;
53+
import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
54+
import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
4655

4756
@RunWith(Parameterized.class)
4857
public class CustomFoldingRegionTest {
@@ -674,6 +683,58 @@ public String test(boolean b) {
674683
FoldingTestUtils.assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 7, 8);//if
675684
}
676685

686+
@Test
687+
public void testFoldingUpdateWithMultipleCustomRegionsDoesNotSwitchRegions() throws Exception {
688+
String code= """
689+
package org.example.test;
690+
691+
// region outer
692+
693+
public class Test {
694+
// region inner
695+
void someMethod() {
696+
// content
697+
}
698+
// endregion
699+
}
700+
701+
// endregion outer
702+
""";
703+
ICompilationUnit cu= fPackageFragment.createCompilationUnit("Test.java", code, true, null);
704+
JavaEditor editor= (JavaEditor) EditorUtility.openInEditor(cu);
705+
try {
706+
ProjectionAnnotationModel model= editor.getAdapter(ProjectionAnnotationModel.class);
707+
708+
List<IRegion> initialRegions= FoldingTestUtils.extractRegions(model);
709+
710+
assertEquals(3, initialRegions.size());
711+
FoldingTestUtils.assertContainsRegionUsingStartAndEndLine(initialRegions, code, 2, 12);//outer
712+
FoldingTestUtils.assertContainsRegionUsingStartAndEndLine(initialRegions, code, 5, 9);//inner
713+
FoldingTestUtils.assertContainsRegionUsingStartAndEndLine(initialRegions, code, 6, 7);//someMethod
714+
715+
List<Position> positions= new ArrayList<>();
716+
Iterator<Annotation> it= model.getAnnotationIterator();
717+
while (it.hasNext()) {
718+
Annotation a= it.next();
719+
if (a instanceof ProjectionAnnotation) {
720+
Position p= model.getPosition(a);
721+
positions.add(p);
722+
}
723+
}
724+
assertEquals(initialRegions.size(), positions.size());
725+
726+
String additionalText= "more ";
727+
editor.getViewer().getDocument().replace(code.indexOf("content"), 0, additionalText);
728+
729+
for(int i= 0; i < positions.size(); i++) {
730+
assertEquals(initialRegions.get(i).getOffset(), positions.get(i).getOffset());
731+
assertEquals(initialRegions.get(i).getLength() + additionalText.length(), positions.get(i).getLength());
732+
}
733+
} finally {
734+
editor.close(false);
735+
}
736+
}
737+
677738
private List<IRegion> getProjectionRangesOfFile(String str) throws Exception {
678739
return FoldingTestUtils.getProjectionRangesOfFile(fPackageFragment, "Test.java", str);
679740
}

org.eclipse.jdt.text.tests/src/org/eclipse/jdt/text/tests/folding/FoldingTestUtils.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ public static List<IRegion> getProjectionRangesOfFile(IPackageFragment packageFr
4545
JavaEditor editor= (JavaEditor) EditorUtility.openInEditor(cu);
4646
ProjectionAnnotationModel model= editor.getAdapter(ProjectionAnnotationModel.class);
4747

48+
List<IRegion> regions= extractRegions(model);
49+
editor.close(false);
50+
return regions;
51+
}
52+
53+
public static List<IRegion> extractRegions(ProjectionAnnotationModel model) {
4854
List<IRegion> regions= new ArrayList<>();
4955
Iterator<Annotation> it= model.getAnnotationIterator();
5056
while (it.hasNext()) {

org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/text/folding/DefaultJavaFoldingStructureProvider.java

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
import org.eclipse.jdt.core.dom.ASTVisitor;
7878
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
7979
import org.eclipse.jdt.core.dom.Block;
80+
import org.eclipse.jdt.core.dom.BodyDeclaration;
8081
import org.eclipse.jdt.core.dom.BreakStatement;
8182
import org.eclipse.jdt.core.dom.CatchClause;
8283
import org.eclipse.jdt.core.dom.Comment;
@@ -90,6 +91,7 @@
9091
import org.eclipse.jdt.core.dom.Initializer;
9192
import org.eclipse.jdt.core.dom.LambdaExpression;
9293
import org.eclipse.jdt.core.dom.MethodDeclaration;
94+
import org.eclipse.jdt.core.dom.SimpleName;
9395
import org.eclipse.jdt.core.dom.Statement;
9496
import org.eclipse.jdt.core.dom.SwitchCase;
9597
import org.eclipse.jdt.core.dom.SwitchExpression;
@@ -338,7 +340,7 @@ void setIsComment(boolean isComment) {
338340
@Override
339341
public String toString() {
340342
return "JavaProjectionAnnotation:\n" + //$NON-NLS-1$
341-
"\telement: \t"+ fJavaElement.toString() + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
343+
"\telement: \t"+ fJavaElement + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
342344
"\tcollapsed: \t" + isCollapsed() + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
343345
"\tcomment: \t" + isComment() + "\n"; //$NON-NLS-1$ //$NON-NLS-2$
344346
}
@@ -357,9 +359,12 @@ private class FoldingVisitor extends ASTVisitor {
357359

358360
private FoldingStructureComputationContext ctx;
359361
private List<Position> topLevelTypes = new ArrayList<>();
362+
private ICompilationUnit topLevelCompilationUnit;
363+
private Deque<IRegion> currentSurroundingElemenPositions = new ArrayDeque<>();
360364

361-
public FoldingVisitor(FoldingStructureComputationContext ctx) {
365+
public FoldingVisitor(FoldingStructureComputationContext ctx, ICompilationUnit compilationUnit) {
362366
this.ctx= ctx;
367+
this.topLevelCompilationUnit= compilationUnit;
363368
}
364369

365370
@Override
@@ -377,13 +382,15 @@ public boolean visit(CompilationUnit node) {
377382

378383
@Override
379384
public boolean visit(TypeDeclaration node) {
385+
SimpleName name= node.getName();
380386
if (node.isMemberTypeDeclaration() || node.isLocalTypeDeclaration()) {
381-
int start= node.getName().getStartPosition();
387+
int start= name.getStartPosition();
382388
int end= node.getStartPosition() + node.getLength();
383389
createFoldingRegion(start, end - start, ctx.collapseMembers());
384390
} else {
385391
topLevelTypes.add(new Position(node.getStartPosition(), node.getLength()-1));
386392
}
393+
addToCurrentSurroundingPositions(node, name);
387394
return true;
388395
}
389396

@@ -392,9 +399,14 @@ public boolean visit(MethodDeclaration node) {
392399
int start= node.getName().getStartPosition();
393400
int end= node.getStartPosition() + node.getLength();
394401
createFoldingRegion(start, end - start, ctx.collapseMembers());
402+
addToCurrentSurroundingPositions(node, node.getName());
395403
return true;
396404
}
397405

406+
private void addToCurrentSurroundingPositions(BodyDeclaration node, SimpleName name) {
407+
currentSurroundingElemenPositions.add(new Region(name.getStartPosition(), node.getLength() + name.getStartPosition() - node.getStartPosition()));
408+
}
409+
398410
@Override
399411
public boolean visit(IfStatement node) {
400412
int start= node.getStartPosition();
@@ -560,13 +572,46 @@ private void createFoldingRegion(ASTNode node, boolean collapse) {
560572
}
561573

562574
private void createFoldingRegion(int start, int length, boolean collapse) {
575+
createFoldingRegion(start, length, collapse, false);
576+
}
577+
578+
private void createFoldingRegion(int start, int length, boolean collapse, boolean isComment) {
579+
createFoldingRegion(start, length, collapse, resolveJavaElementAt(start, true), isComment);
580+
}
581+
582+
583+
private IJavaElement resolveJavaElementAt(int offset, boolean checkSurrounding) {
584+
IJavaElement[] elements;
585+
try {
586+
elements= topLevelCompilationUnit.codeSelect(offset, 0);
587+
} catch (JavaModelException e) {
588+
JavaPlugin.log(e);
589+
return null;
590+
}
591+
if (elements.length > 0) {
592+
return elements[0];
593+
}
594+
if (checkSurrounding) {
595+
for (Iterator<IRegion> it= currentSurroundingElemenPositions.reversed().iterator(); it.hasNext();) {
596+
IRegion outer= it.next();
597+
if (outer.getOffset() + outer.getLength() < offset) {
598+
it.remove();
599+
} else if(outer.getOffset() <= offset) {
600+
return resolveJavaElementAt(outer.getOffset(), false);
601+
}
602+
}
603+
}
604+
return null;
605+
}
606+
607+
private void createFoldingRegion(int start, int length, boolean collapse, IJavaElement element, boolean isComment) {
563608
if (length > 0) {
564609
IRegion region= new Region(start, length);
565610
IRegion aligned= alignRegion(region, ctx);
566611

567612
if (aligned != null && isMultiline(aligned)) {
568613
Position position= new Position(aligned.getOffset(), aligned.getLength());
569-
JavaProjectionAnnotation annotation= new JavaProjectionAnnotation(collapse, null, false);
614+
JavaProjectionAnnotation annotation= new JavaProjectionAnnotation(collapse, element, isComment);
570615
ctx.addProjectionRange(annotation, position);
571616
}
572617
}
@@ -1399,7 +1444,7 @@ private void processCompilationUnit(ICompilationUnit unit, FoldingStructureCompu
13991444
parser.setCompilerOptions(options);
14001445

14011446
CompilationUnit ast = (CompilationUnit) parser.createAST(null);
1402-
FoldingVisitor visitor= new FoldingVisitor(ctx);
1447+
FoldingVisitor visitor= new FoldingVisitor(ctx, unit);
14031448
ast.accept(visitor);
14041449

14051450
if (fCustomFoldingRegionsEnabled) {
@@ -1568,7 +1613,7 @@ private void checkCustomFolding(Deque<Integer> openCustomRegionStartPositions, c
15681613

15691614
private void checkIncludeLastLineAndCreateCustomFoldingRegion(char[] sourceArray, FoldingVisitor visitor, IRegion customFoldingRegion, boolean excludeEndregionComment) {
15701615
includelastLine = includeLastLineInCustomFoldingRegion(sourceArray, customFoldingRegion.getOffset() + customFoldingRegion.getLength(), excludeEndregionComment);
1571-
visitor.createFoldingRegion(customFoldingRegion.getOffset(), customFoldingRegion.getLength(), fCollapseCustomRegions);
1616+
visitor.createFoldingRegion(customFoldingRegion.getOffset(), customFoldingRegion.getLength(), fCollapseCustomRegions, true);
15721617
}
15731618

15741619
private boolean includeLastLineInCustomFoldingRegion(char[] sourceArray, int regionEnd, boolean excludeEndregionComment) {

0 commit comments

Comments
 (0)