-
Notifications
You must be signed in to change notification settings - Fork 95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Folding mechanism for while/for/if/switch-case #1562
base: master
Are you sure you want to change the base?
Folding mechanism for while/for/if/switch-case #1562
Conversation
org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/text/folding/DefaultJavaFoldingStructureProvider.java
Show resolved
Hide resolved
08cd1d9
to
afd4aed
Compare
Bug:
|
c2082f5
to
0390f1c
Compare
c1ac59f
to
80ee66d
Compare
d5cf645
to
a44a7e0
Compare
The test failures seem to be unrelated: https://ci.eclipse.org/jdt/job/eclipse.jdt.ui-github/job/PR-1562/21/testReport/ The only test that fails with age == 1 (i.e. in this PR) is java.lang.AssertionError:
Wrong bundles loaded:
- org.eclipse.jdt.junit
expected:<0> but was:<24>
at org.junit.Assert.fail(Assert.java:89)
at org.junit.Assert.failNotEquals(Assert.java:835)
at org.junit.Assert.assertEquals(Assert.java:647)
at org.eclipse.jdt.text.tests.PluginsNotLoadedTest.pluginsNotLoaded(PluginsNotLoadedTest.java:278)
... Does anyone know the reason for this failure? In the meantime @jakub-suliga, try with another force-push to re-trigger the checks and see if the failure persists. |
a44a7e0
to
b49f6c9
Compare
On a closer look, the test failures are because of the changes introduced in this PR. Run them locally and you will see it. Here's (part of) the console output when running !STACK 0
java.lang.ClassCastException: class org.eclipse.jdt.internal.core.ClassFile cannot be cast to class org.eclipse.jdt.core.ICompilationUnit (org.eclipse.jdt.internal.core.ClassFile and org.eclipse.jdt.core.ICompilationUnit are in unnamed module of loader org.eclipse.osgi.internal.loader.EquinoxClassLoader @21452f5e)
at org.eclipse.jdt.ui.text.folding.DefaultJavaFoldingStructureProvider.computeFoldingStructure(DefaultJavaFoldingStructureProvider.java:981)
at org.eclipse.jdt.ui.text.folding.DefaultJavaFoldingStructureProvider.update(DefaultJavaFoldingStructureProvider.java:907)
at org.eclipse.jdt.ui.text.folding.DefaultJavaFoldingStructureProvider.initialize(DefaultJavaFoldingStructureProvider.java:852)
at org.eclipse.jdt.ui.text.folding.DefaultJavaFoldingStructureProvider.handleProjectionEnabled(DefaultJavaFoldingStructureProvider.java:822)
at org.eclipse.jdt.ui.text.folding.DefaultJavaFoldingStructureProvider$ProjectionListener.projectionEnabled(DefaultJavaFoldingStructureProvider.java:700)
...
at org.eclipse.jdt.ui.tests.quickfix.AnnotateAssistTest1d5.testAnnotateReturn2(AnnotateAssistTest1d5.java:178)
... It seems the It would be interesting to know when exactly is |
d68c88b
to
baf2632
Compare
@iloveeclipse : Jakub and I were looking into the failed tests and we found the culprit: it was a Do you see any possible problem with this change? We couldn't come up with any but maybe you can? |
ee3706a
to
4602a47
Compare
d7b0360
to
bc2e727
Compare
e110a33
to
649e208
Compare
bd7469c
to
8788edb
Compare
Hi @jakub-suliga While there were (unsurprisingly) some merge conflicts, these were all trivial to resolve. My tests noticed the following changes to folding to the current folding when using your code (see danthe1st@d0c8ba8 where I adapted my tests - if your PR is merged after mine, you might have to do something similar): If there is a single import, your implementation adds a folding region for that import. package a;
import java.util.List; If a class contains fields, your implementation adds a folding region for every field of the class. In the following example, package a;
public class JakobTest2 {
int a;
} These are changes in behavior from the current Another change you made that interacted with my test is your removal of You can see the result of me rebasing your changes on top of mine here: https://github.com/danthe1st/eclipse.jdt.ui/tree/jakob-folding-test (I am not recommending to base your changes on mine (on PR can be rebased whenever the other is merged), I just did it as an experiment/to see what happens/how our changes interact with each other.) |
a12d981
to
0248bf7
Compare
Hi @danthe1st, |
For me, this issue also happens even when starting Eclipse without your "new folding" enabled (and without enabling it). Here is the modified test code that should reproduce the issue (put it somewhere in //TODO add your package declaration here
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.eclipse.jdt.testplugin.JavaProjectHelper;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.projection.ProjectionAnnotation;
import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel;
import org.eclipse.ui.PartInitException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.ui.tests.core.rules.ProjectTestSetup;
import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
public class FoldingTest {
@Rule
public ProjectTestSetup projectSetup= new ProjectTestSetup();
private IJavaProject fJProject1;
private IPackageFragmentRoot fSourceFolder;
private IPackageFragment fPackageFragment;
@Before
public void setUp() throws CoreException {
fJProject1= projectSetup.getProject();
fSourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src");
fPackageFragment= fSourceFolder.createPackageFragment("org.example.test", false, null);
}
@After
public void tearDown() throws CoreException {
JavaProjectHelper.delete(fJProject1);
}
@Test
public void testCustomRegionsAroundFieldAndMethod() throws JavaModelException, PartInitException {
String str= """
package org.example.test;
public class Test {
// #region
int a;
void b(){
}
// #endregion
}
""";
List<IRegion> projectionRanges= getProjectionRangesOfFile(str);
assertEquals(1, projectionRanges.size());//needs to be 2 to pass with your code
assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 6, 8);//void b()
//assertContainsRegionUsingStartAndEndLine(projectionRanges, str, 4, 5);//int a;//would unexpectedly pass with your code
}
private void assertContainsRegionUsingStartAndEndLine(List<IRegion> projectionRanges, String input, int startLine, int endLine) {
assertTrue(startLine <= endLine, "start line must be smaller or equal to end line");
int startLineBegin= findLineStartIndex(input, startLine);
int endLineBegin= findLineStartIndex(input, endLine);
int endLineEnd= findNextLineStart(input, endLineBegin);
endLineEnd= getLengthIfNotFound(input, endLineEnd);
for (IRegion region : projectionRanges) {
if (region.getOffset() == startLineBegin + 1 && region.getOffset() + region.getLength() >= endLineBegin && region.getOffset() + region.getLength() <= endLineEnd) {
return;
}
}
fail(
"missing region from line " + startLine + " (index " + (startLineBegin + 1) + ") " +
"to line " + endLine + " (index " + (endLineEnd) + ", length "+(endLineEnd - (startLineBegin + 1)) + ")" +
", actual regions: " + projectionRanges
);
}
private int getLengthIfNotFound(String input, int startLineEnd) {
if (startLineEnd == -1) {
startLineEnd= input.length();
}
return startLineEnd;
}
private int findLineStartIndex(String input, int lineNumber) {
int currentInputIndex= 0;
for (int i= 0; i < lineNumber; i++) {
currentInputIndex= findNextLineStart(input, currentInputIndex);
if (currentInputIndex == -1) {
fail("line number is greater than the total number of lines");
}
}
return currentInputIndex;
}
private int findNextLineStart(String input, int currentInputIndex) {
return input.indexOf('\n', currentInputIndex + 1);
}
private List<IRegion> getProjectionRangesOfFile(String str) throws JavaModelException, PartInitException {
ICompilationUnit compilationUnit= fPackageFragment.createCompilationUnit("Test.java", str, false, null);
JavaEditor editor= (JavaEditor) EditorUtility.openInEditor(compilationUnit);
ProjectionAnnotationModel model= editor.getAdapter(ProjectionAnnotationModel.class);
List<IRegion> regions= new ArrayList<>();
for (Iterator<Annotation> it= model.getAnnotationIterator(); it.hasNext();) {
Annotation annotation= it.next();
if (annotation instanceof ProjectionAnnotation projectionAnnotation) {
Position position= model.getPosition(projectionAnnotation);
regions.add(new Region(position.getOffset(), position.getLength()));
}
}
return regions;
}
} I am developing on Ubuntu 24.10 if that changes anything - but I don't really see a reason why it should. Disclaimer: I am not a committer or otherwise in the position of giving a proper review (and I didn't even try to properly review anything in your PR, I just made a few experiments and shared my results with you).
Is this absolutely necessary? Is there any reason it shouldn't work by just reopening the source file (and can you get around that - that seems to be quite a limitation)?
My changes don't really need it but the tests I implemented rely on it being aligned that way (and your changes modify that). I just wanted to give you a heads-up for what you might need to change in my tests if your changes get merged after mine (if it happens the other way round, I can perform the necessary changes). |
81e1655
to
c367f90
Compare
I added a ChangeListener, now you dont need to restart your IDE. I will have a look on your test. |
Oh I see what you meant. I think that without that listener, it should still apply the changes but only after closing and reopening the source file (at least that's what happened in my experience). What your change with the added listener is doing (I think) is making it work without reopening the editor (i.e. that change causes updates to the preferences take effect in already open editors). |
I didnt test it with closing the file, probably it works too. |
0f80d90
to
9a6d26d
Compare
@danthe1st I looked into your test and found the bug, now it should work. |
9a6d26d
to
9416de9
Compare
I added it because it fixes this #1539. For example you have this: public class Test {
void b(){
} void c(){
}
} and you want to fold the top method, the lower method is folded too (the old version): |
9416de9
to
fceb742
Compare
fceb742
to
06eed8d
Compare
Problem:
Currently, there is no folding mechanism for while/for/if/switch-case statements in Eclipse. VSCode and IntelliJ support a folding mechanism for these statements. Therefore, I have created a method that implements this folding mechanism. This is also an open issue: #1426. In addition, I deleted some commented-out code from 2007.
Before:
After:
How to test:
Here is a small program you can use to test:
Bugs: