Skip to content

Commit 2cc9c2d

Browse files
committed
enable folding with "Only show selected Java element" #2264
1 parent 0c60a3c commit 2cc9c2d

File tree

5 files changed

+236
-10
lines changed

5 files changed

+236
-10
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
@Suite
2020
@SelectClasses({
2121
FoldingTest.class,
22-
CustomFoldingRegionTest.class
22+
CustomFoldingRegionTest.class,
23+
FoldingWithShowSelectedElementTests.class
2324
})
2425
public class FoldingTestSuite {
2526
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public static List<IRegion> getProjectionRangesOfFile(IPackageFragment packageFr
5454
regions.add(new Region(p.getOffset(), p.getLength()));
5555
}
5656
}
57+
editor.close(false);
5758
return regions;
5859
}
5960

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Daniel Schmid and others.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* Daniel Schmid - initial API and implementation
13+
*******************************************************************************/
14+
package org.eclipse.jdt.text.tests.folding;
15+
16+
import static org.junit.jupiter.api.Assertions.assertEquals;
17+
18+
import java.util.ArrayList;
19+
import java.util.Iterator;
20+
import java.util.List;
21+
22+
import org.junit.After;
23+
import org.junit.Before;
24+
import org.junit.Rule;
25+
import org.junit.Test;
26+
27+
import org.eclipse.jdt.testplugin.JavaProjectHelper;
28+
29+
import org.eclipse.core.runtime.CoreException;
30+
31+
import org.eclipse.jface.preference.IPreferenceStore;
32+
33+
import org.eclipse.jface.text.IDocument;
34+
import org.eclipse.jface.text.IRegion;
35+
import org.eclipse.jface.text.Position;
36+
import org.eclipse.jface.text.Region;
37+
import org.eclipse.jface.text.source.Annotation;
38+
import org.eclipse.jface.text.source.projection.ProjectionAnnotation;
39+
import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel;
40+
41+
import org.eclipse.jdt.core.ICompilationUnit;
42+
import org.eclipse.jdt.core.IJavaProject;
43+
import org.eclipse.jdt.core.IPackageFragment;
44+
import org.eclipse.jdt.core.IPackageFragmentRoot;
45+
46+
import org.eclipse.jdt.ui.PreferenceConstants;
47+
import org.eclipse.jdt.ui.tests.core.rules.ProjectTestSetup;
48+
49+
import org.eclipse.jdt.internal.ui.JavaPlugin;
50+
import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
51+
import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
52+
53+
public class FoldingWithShowSelectedElementTests {
54+
@Rule
55+
public ProjectTestSetup projectSetup= new ProjectTestSetup();
56+
57+
private IJavaProject jProject;
58+
59+
private IPackageFragmentRoot sourceFolder;
60+
61+
private IPackageFragment packageFragment;
62+
63+
@Before
64+
public void setUp() throws CoreException {
65+
jProject= projectSetup.getProject();
66+
sourceFolder= jProject.findPackageFragmentRoot(jProject.getResource().getFullPath().append("src"));
67+
if (sourceFolder == null) {
68+
sourceFolder= JavaProjectHelper.addSourceContainer(jProject, "src");
69+
}
70+
packageFragment= sourceFolder.createPackageFragment("org.example.test", false, null);
71+
IPreferenceStore store= JavaPlugin.getDefault().getPreferenceStore();
72+
store.setValue(PreferenceConstants.EDITOR_SHOW_SEGMENTS, true);
73+
}
74+
75+
@After
76+
public void tearDown() throws CoreException {
77+
JavaProjectHelper.delete(jProject);
78+
JavaPlugin.getDefault().getPreferenceStore().setToDefault(PreferenceConstants.EDITOR_SHOW_SEGMENTS);
79+
JavaPlugin.getDefault().getPreferenceStore().setToDefault(PreferenceConstants.EDITOR_FOLDING_CUSTOM_REGIONS_ENABLED);
80+
}
81+
82+
@Test
83+
public void testFoldingActive() throws Exception {
84+
String str= """
85+
package org.example.test;
86+
public class A {
87+
void someMethod() {
88+
// this method should be folded
89+
}
90+
}
91+
""";
92+
List<IRegion> regions= FoldingTestUtils.getProjectionRangesOfFile(packageFragment, "B.java", str);
93+
assertEquals(1, regions.size());
94+
FoldingTestUtils.assertContainsRegionUsingStartAndEndLine(regions, str, 2, 3);
95+
}
96+
97+
@Test
98+
public void testInsertText() throws Exception {
99+
JavaPlugin.getDefault().getPreferenceStore().setValue(PreferenceConstants.EDITOR_FOLDING_CUSTOM_REGIONS_ENABLED, true);
100+
String str= """
101+
package org.example.test;
102+
public class A {
103+
// region
104+
void someMethod() {
105+
// content here
106+
}
107+
// endregion
108+
}
109+
""";
110+
111+
ICompilationUnit cu= packageFragment.createCompilationUnit("A.java", str, true, null);
112+
JavaEditor editor= (JavaEditor) EditorUtility.openInEditor(cu);
113+
114+
try {
115+
editor.setSelection(cu.getElementAt(str.indexOf("someMethod")));
116+
117+
ProjectionAnnotationModel model= editor.getAdapter(ProjectionAnnotationModel.class);
118+
119+
assertEquals("""
120+
// region
121+
void someMethod() {
122+
// content here
123+
}
124+
""", editor.getViewer().getTextWidget().getText());
125+
126+
List<IRegion> regions= extractRegions(model);
127+
128+
assertEquals(2, regions.size());
129+
130+
IDocument document= editor.getViewer().getDocument();
131+
FoldingTestUtils.assertContainsRegionUsingStartAndEndLine(regions, str, 2, 6);
132+
FoldingTestUtils.assertContainsRegionUsingStartAndEndLine(regions, str, 3, 4);
133+
134+
document.replace(str.indexOf("content"), 0, "method ");
135+
136+
regions = extractRegions(model);
137+
str = document.get();
138+
139+
assertEquals(2, regions.size());
140+
141+
FoldingTestUtils.assertContainsRegionUsingStartAndEndLine(regions, str, 2, 6);
142+
FoldingTestUtils.assertContainsRegionUsingStartAndEndLine(regions, str, 3, 4);
143+
} finally {
144+
editor.close(false);
145+
}
146+
}
147+
148+
@Test
149+
public void testWithTrailingWhitespaceAfterClosingBrace() throws Exception {
150+
JavaPlugin.getDefault().getPreferenceStore().setValue(PreferenceConstants.EDITOR_FOLDING_CUSTOM_REGIONS_ENABLED, true);
151+
String str= """
152+
package org.example.test;
153+
public class A {
154+
void someMethod() {
155+
// content here
156+
}\t\t\t\t
157+
}
158+
""";
159+
160+
ICompilationUnit cu= packageFragment.createCompilationUnit("A.java", str, true, null);
161+
JavaEditor editor= (JavaEditor) EditorUtility.openInEditor(cu);
162+
163+
try {
164+
editor.setSelection(cu.getElementAt(str.indexOf("someMethod")));
165+
166+
assertEquals("""
167+
void someMethod() {
168+
// content here
169+
}\t\t\t\t
170+
""", editor.getViewer().getTextWidget().getText());
171+
172+
} finally {
173+
editor.close(false);
174+
}
175+
}
176+
177+
private List<IRegion> extractRegions(ProjectionAnnotationModel model) {
178+
List<IRegion> regions= new ArrayList<>();
179+
Iterator<Annotation> it= model.getAnnotationIterator();
180+
while (it.hasNext()) {
181+
Annotation a= it.next();
182+
if (a instanceof ProjectionAnnotation) {
183+
Position p= model.getPosition(a);
184+
regions.add(new Region(p.getOffset(), p.getLength()));
185+
}
186+
}
187+
return regions;
188+
}
189+
190+
}

org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/javaeditor/JavaEditor.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1875,8 +1875,8 @@ protected final ISourceViewer createSourceViewer(Composite parent, IVerticalRule
18751875
* This is a performance optimization to reduce the computation of
18761876
* the text presentation triggered by {@link #setVisibleDocument(IDocument)}
18771877
*/
1878-
if (sourceViewer instanceof JavaSourceViewer && isFoldingEnabled() && (store == null || !store.getBoolean(PreferenceConstants.EDITOR_SHOW_SEGMENTS)))
1879-
((JavaSourceViewer)sourceViewer).prepareDelayedProjection();
1878+
if (sourceViewer instanceof JavaSourceViewer javaSourceViewer && isFoldingEnabled())
1879+
javaSourceViewer.prepareDelayedProjection();
18801880

18811881
if (sourceViewer instanceof ProjectionViewer) {
18821882
fProjectionSupport= new ProjectionSupport((ProjectionViewer)sourceViewer, getAnnotationAccess(), getSharedColors());
@@ -2560,8 +2560,7 @@ private void internalDoSetInput(IEditorInput input) throws CoreException {
25602560
if (sourceViewer instanceof JavaSourceViewer)
25612561
javaSourceViewer= (JavaSourceViewer)sourceViewer;
25622562

2563-
IPreferenceStore store= getPreferenceStore();
2564-
if (javaSourceViewer != null && isFoldingEnabled() &&(store == null || !store.getBoolean(PreferenceConstants.EDITOR_SHOW_SEGMENTS)))
2563+
if (javaSourceViewer != null && isFoldingEnabled())
25652564
javaSourceViewer.prepareDelayedProjection();
25662565

25672566
super.doSetInput(input);

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

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ void setIsComment(boolean isComment) {
338338
@Override
339339
public String toString() {
340340
return "JavaProjectionAnnotation:\n" + //$NON-NLS-1$
341-
"\telement: \t"+ fJavaElement.toString() + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
341+
"\telement: \t"+ fJavaElement + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
342342
"\tcollapsed: \t" + isCollapsed() + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
343343
"\tcomment: \t" + isComment() + "\n"; //$NON-NLS-1$ //$NON-NLS-2$
344344
}
@@ -357,9 +357,12 @@ private class FoldingVisitor extends ASTVisitor {
357357

358358
private FoldingStructureComputationContext ctx;
359359
private List<Position> topLevelTypes = new ArrayList<>();
360+
private ICompilationUnit topLevelCompilationUnit;
361+
private Deque<IRegion> currentSurroundingElemenPositions = new ArrayDeque<>();
360362

361-
public FoldingVisitor(FoldingStructureComputationContext ctx) {
363+
public FoldingVisitor(FoldingStructureComputationContext ctx, ICompilationUnit compilationUnit) {
362364
this.ctx= ctx;
365+
this.topLevelCompilationUnit= compilationUnit;
363366
}
364367

365368
@Override
@@ -560,14 +563,46 @@ private void createFoldingRegion(ASTNode node, boolean collapse) {
560563
}
561564

562565
private void createFoldingRegion(int start, int length, boolean collapse) {
566+
createFoldingRegion(start, length, collapse, resolveJavaElementAt(start, true));
567+
}
568+
569+
570+
private IJavaElement resolveJavaElementAt(int offset, boolean checkSurrounding) {
571+
IJavaElement[] elements;
572+
try {
573+
elements= topLevelCompilationUnit.codeSelect(offset, 0);
574+
} catch (JavaModelException e) {
575+
e.printStackTrace();
576+
return null;
577+
}
578+
if (elements.length > 0) {
579+
return elements[0];
580+
}
581+
if (checkSurrounding) {
582+
for (Iterator<IRegion> it= currentSurroundingElemenPositions.reversed().iterator(); it.hasNext();) {
583+
IRegion outer= it.next();
584+
if (outer.getOffset() + outer.getLength() < offset) {
585+
it.remove();
586+
} else {
587+
return resolveJavaElementAt(outer.getOffset(), false);
588+
}
589+
}
590+
}
591+
return null;
592+
}
593+
594+
private void createFoldingRegion(int start, int length, boolean collapse, IJavaElement element) {
563595
if (length > 0) {
564596
IRegion region= new Region(start, length);
565597
IRegion aligned= alignRegion(region, ctx);
566598

567599
if (aligned != null && isMultiline(aligned)) {
568600
Position position= new Position(aligned.getOffset(), aligned.getLength());
569-
JavaProjectionAnnotation annotation= new JavaProjectionAnnotation(collapse, null, false);
601+
JavaProjectionAnnotation annotation= new JavaProjectionAnnotation(collapse, element, false);
570602
ctx.addProjectionRange(annotation, position);
603+
if (element != null) {
604+
currentSurroundingElemenPositions.add(region);
605+
}
571606
}
572607
}
573608
}
@@ -1399,7 +1434,7 @@ private void processCompilationUnit(ICompilationUnit unit, FoldingStructureCompu
13991434
parser.setCompilerOptions(options);
14001435

14011436
CompilationUnit ast = (CompilationUnit) parser.createAST(null);
1402-
FoldingVisitor visitor= new FoldingVisitor(ctx);
1437+
FoldingVisitor visitor= new FoldingVisitor(ctx, unit);
14031438
ast.accept(visitor);
14041439

14051440
if (fCustomFoldingRegionsEnabled) {
@@ -1558,7 +1593,7 @@ private void checkCustomFolding(Deque<Integer> openCustomRegionStartPositions, c
15581593

15591594
private void checkIncludeLastLineAndCreateCustomFoldingRegion(char[] sourceArray, FoldingVisitor visitor, IRegion customFoldingRegion, boolean excludeEndregionComment) {
15601595
includelastLine = includeLastLineInCustomFoldingRegion(sourceArray, customFoldingRegion.getOffset() + customFoldingRegion.getLength(), excludeEndregionComment);
1561-
visitor.createFoldingRegion(customFoldingRegion.getOffset(), customFoldingRegion.getLength(), fCollapseCustomRegions);
1596+
visitor.createFoldingRegion(customFoldingRegion.getOffset(), customFoldingRegion.getLength(), fCollapseCustomRegions, null);
15621597
}
15631598

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

0 commit comments

Comments
 (0)