Skip to content

Commit 90be5e5

Browse files
committed
Organize Imports should resolve static imports as well
Signed-off-by: Snjezana Peco <snjezana.peco@redhat.com>
1 parent a5d7452 commit 90be5e5

File tree

3 files changed

+160
-6
lines changed

3 files changed

+160
-6
lines changed

org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/BaseDocumentLifeCycleHandler.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@
3030
import org.eclipse.core.runtime.IProgressMonitor;
3131
import org.eclipse.core.runtime.IStatus;
3232
import org.eclipse.core.runtime.NullProgressMonitor;
33+
import org.eclipse.core.runtime.OperationCanceledException;
3334
import org.eclipse.core.runtime.Status;
3435
import org.eclipse.core.runtime.SubMonitor;
36+
import org.eclipse.core.runtime.jobs.Job;
3537
import org.eclipse.jdt.core.IBuffer;
3638
import org.eclipse.jdt.core.ICompilationUnit;
3739
import org.eclipse.jdt.core.IJavaElement;
@@ -272,6 +274,11 @@ public void run(IProgressMonitor monitor) throws CoreException {
272274

273275
public void didSave(DidSaveTextDocumentParams params) {
274276
try {
277+
try {
278+
Job.getJobManager().join(DOCUMENT_LIFE_CYCLE_JOBS, new NullProgressMonitor());
279+
} catch (OperationCanceledException | InterruptedException e) {
280+
JavaLanguageServerPlugin.logException(e.getMessage(), e);
281+
}
275282
ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() {
276283
@Override
277284
public void run(IProgressMonitor monitor) throws CoreException {
@@ -361,7 +368,6 @@ public ICompilationUnit handleChanged(DidChangeTextDocumentParams params) {
361368
} else {
362369
edit = new ReplaceEdit(startOffset, length, text);
363370
}
364-
365371
IDocument document = JsonRpcHelpers.toDocument(unit.getBuffer());
366372
edit.apply(document, TextEdit.NONE);
367373

org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/OrganizeImportsHandler.java

+103-5
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,41 @@
2222
import java.util.stream.Stream;
2323

2424
import org.eclipse.core.runtime.CoreException;
25+
import org.eclipse.core.runtime.NullProgressMonitor;
2526
import org.eclipse.core.runtime.OperationCanceledException;
27+
import org.eclipse.core.runtime.jobs.Job;
2628
import org.eclipse.jdt.core.ICompilationUnit;
29+
import org.eclipse.jdt.core.IJavaProject;
2730
import org.eclipse.jdt.core.ISourceRange;
2831
import org.eclipse.jdt.core.JavaModelException;
32+
import org.eclipse.jdt.core.Signature;
33+
import org.eclipse.jdt.core.dom.AST;
2934
import org.eclipse.jdt.core.dom.CompilationUnit;
35+
import org.eclipse.jdt.core.dom.Name;
36+
import org.eclipse.jdt.core.dom.SimpleName;
37+
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
38+
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
39+
import org.eclipse.jdt.core.manipulation.CodeStyleConfiguration;
40+
import org.eclipse.jdt.core.manipulation.ImportReferencesCollector;
3041
import org.eclipse.jdt.core.manipulation.OrganizeImportsOperation;
3142
import org.eclipse.jdt.core.search.TypeNameMatch;
43+
import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
3244
import org.eclipse.jdt.internal.corext.dom.IASTSharedValues;
3345
import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
46+
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
3447
import org.eclipse.jdt.ls.core.internal.JDTUtils;
3548
import org.eclipse.jdt.ls.core.internal.JSONUtility;
3649
import org.eclipse.jdt.ls.core.internal.JavaClientConnection;
3750
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
51+
import org.eclipse.jdt.ls.core.internal.corrections.SimilarElementsRequestor;
52+
import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager;
3853
import org.eclipse.jdt.ls.core.internal.text.correction.SourceAssistProcessor;
3954
import org.eclipse.lsp4j.CodeActionParams;
4055
import org.eclipse.lsp4j.Range;
4156
import org.eclipse.lsp4j.WorkspaceEdit;
57+
import org.eclipse.text.edits.DeleteEdit;
58+
import org.eclipse.text.edits.InsertEdit;
59+
import org.eclipse.text.edits.MalformedTreeException;
4260
import org.eclipse.text.edits.TextEdit;
4361

4462
import com.google.gson.Gson;
@@ -50,7 +68,6 @@ public static TextEdit organizeImports(ICompilationUnit unit, Function<ImportSel
5068
if (unit == null) {
5169
return null;
5270
}
53-
5471
RefactoringASTParser astParser = new RefactoringASTParser(IASTSharedValues.SHARED_AST_LEVEL);
5572
CompilationUnit astRoot = astParser.parse(unit, true);
5673
OrganizeImportsOperation op = new OrganizeImportsOperation(unit, astRoot, true, false, true, (TypeNameMatch[][] openChoices, ISourceRange[] ranges) -> {
@@ -78,16 +95,97 @@ public static TextEdit organizeImports(ICompilationUnit unit, Function<ImportSel
7895
});
7996
return Stream.of(chosens).filter(chosen -> chosen != null && typeMaps.containsKey(chosen.id)).map(chosen -> typeMaps.get(chosen.id)).toArray(TypeNameMatch[]::new);
8097
});
81-
8298
try {
83-
return op.createTextEdit(null);
84-
} catch (OperationCanceledException | CoreException e) {
99+
Job.getJobManager().join(BaseDocumentLifeCycleHandler.DOCUMENT_LIFE_CYCLE_JOBS, new NullProgressMonitor());
100+
TextEdit edit = op.createTextEdit(null);
101+
// See https://bugs.eclipse.org/bugs/show_bug.cgi?id=283287
102+
TextEdit staticEdit = wrapStaticImports(edit, astRoot, unit);
103+
if (staticEdit.getChildrenSize() == 0) {
104+
return null;
105+
}
106+
return staticEdit;
107+
} catch (OperationCanceledException | CoreException | InterruptedException e) {
85108
JavaLanguageServerPlugin.logException("Failed to resolve organize imports source action", e);
86109
}
87-
88110
return null;
89111
}
90112

113+
private static TextEdit wrapStaticImports(TextEdit edit, CompilationUnit root, ICompilationUnit unit) throws MalformedTreeException, CoreException {
114+
String[] favourites = PreferenceManager.getPrefs(unit.getResource()).getJavaCompletionFavoriteMembers();
115+
if (favourites.length == 0) {
116+
return edit;
117+
}
118+
IJavaProject project = unit.getJavaProject();
119+
if (JavaModelUtil.is50OrHigher(project)) {
120+
List<SimpleName> typeReferences = new ArrayList<>();
121+
List<SimpleName> staticReferences = new ArrayList<>();
122+
ImportReferencesCollector.collect(root, project, null, typeReferences, staticReferences);
123+
if (staticReferences.isEmpty()) {
124+
return edit;
125+
}
126+
ImportRewrite importRewrite = CodeStyleConfiguration.createImportRewrite(root, true);
127+
AST ast = root.getAST();
128+
ASTRewrite astRewrite = ASTRewrite.create(ast);
129+
for (SimpleName node : staticReferences) {
130+
addImports(root, unit, favourites, importRewrite, ast, astRewrite, node, true);
131+
addImports(root, unit, favourites, importRewrite, ast, astRewrite, node, false);
132+
}
133+
TextEdit staticEdit = importRewrite.rewriteImports(null);
134+
if (staticEdit != null && staticEdit.getChildrenSize() > 0) {
135+
TextEdit lastStatic = staticEdit.getChildren()[staticEdit.getChildrenSize() - 1];
136+
if (lastStatic instanceof DeleteEdit) {
137+
if (edit.getChildrenSize() > 0) {
138+
TextEdit last = edit.getChildren()[edit.getChildrenSize() - 1];
139+
if (last instanceof DeleteEdit && lastStatic.getOffset() == last.getOffset() && lastStatic.getLength() == last.getLength()) {
140+
edit.removeChild(last);
141+
}
142+
}
143+
}
144+
TextEdit firstStatic = staticEdit.getChildren()[0];
145+
if (firstStatic instanceof InsertEdit) {
146+
if (edit.getChildrenSize() > 0) {
147+
TextEdit firstEdit = edit.getChildren()[0];
148+
if (firstEdit instanceof InsertEdit) {
149+
if (isEquals((InsertEdit) firstEdit, (InsertEdit) firstStatic)) {
150+
edit.removeChild(firstEdit);
151+
}
152+
}
153+
}
154+
}
155+
try {
156+
staticEdit.addChild(edit);
157+
return staticEdit;
158+
} catch (MalformedTreeException e) {
159+
JavaLanguageServerPlugin.logException("Failed to resolve static organize imports source action", e);
160+
}
161+
}
162+
}
163+
return edit;
164+
}
165+
166+
private static boolean isEquals(InsertEdit edit1, InsertEdit edit2) {
167+
if (edit1 != null && edit2 != null) {
168+
return edit1.getOffset() == edit2.getOffset() && edit1.getLength() == edit2.getLength() && edit1.getText().equals(edit2.getText());
169+
}
170+
return false;
171+
}
172+
173+
private static void addImports(CompilationUnit root, ICompilationUnit unit, String[] favourites, ImportRewrite importRewrite, AST ast, ASTRewrite astRewrite, SimpleName node, boolean isMethod) throws JavaModelException {
174+
String name = node.getIdentifier();
175+
String[] imports = SimilarElementsRequestor.getStaticImportFavorites(unit, name, isMethod, favourites);
176+
for (int i = 0; i < imports.length; i++) {
177+
String curr = imports[i];
178+
String qualifiedTypeName = Signature.getQualifier(curr);
179+
String res = importRewrite.addStaticImport(qualifiedTypeName, name, isMethod, new ContextSensitiveImportRewriteContext(root, node.getStartPosition(), importRewrite));
180+
int dot = res.lastIndexOf('.');
181+
if (dot != -1) {
182+
String usedTypeName = importRewrite.addImport(qualifiedTypeName);
183+
Name newName = ast.newQualifiedName(ast.newName(usedTypeName), ast.newSimpleName(name));
184+
astRewrite.replace(node, newName, null);
185+
}
186+
}
187+
}
188+
91189
public static WorkspaceEdit organizeImports(JavaClientConnection connection, CodeActionParams params) {
92190
String uri = params.getTextDocument().getUri();
93191
final ICompilationUnit unit = JDTUtils.resolveCompilationUnit(params.getTextDocument().getUri());

org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/AdvancedOrganizeImportsHandlerTest.java

+50
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,16 @@
1717
import static org.junit.Assert.assertNotNull;
1818

1919
import java.io.IOException;
20+
import java.util.ArrayList;
21+
import java.util.Arrays;
22+
import java.util.List;
2023

2124
import org.eclipse.core.runtime.CoreException;
2225
import org.eclipse.jdt.core.ICompilationUnit;
2326
import org.eclipse.jdt.core.IPackageFragment;
2427
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
2528
import org.eclipse.jdt.internal.corext.util.ValidateEditException;
29+
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
2630
import org.eclipse.jdt.ls.core.internal.codemanipulation.AbstractSourceTestCase;
2731
import org.eclipse.jdt.ls.core.internal.handlers.OrganizeImportsHandler.ImportCandidate;
2832
import org.eclipse.jdt.ls.core.internal.handlers.OrganizeImportsHandler.ImportSelection;
@@ -80,4 +84,50 @@ public void testChooseImport() throws ValidateEditException, CoreException, IOEx
8084
//@formatter:on
8185
compareSource(expected, unit.getSource());
8286
}
87+
88+
@Test
89+
public void testStaticImports() throws ValidateEditException, CoreException, IOException {
90+
String[] favourites = JavaLanguageServerPlugin.getPreferencesManager().getPreferences().getJavaCompletionFavoriteMembers();
91+
try {
92+
List<String> list = new ArrayList<>();
93+
list.add("java.lang.Math.*");
94+
list.add("java.util.stream.Collectors.*");
95+
JavaLanguageServerPlugin.getPreferencesManager().getPreferences().setJavaCompletionFavoriteMembers(list);
96+
//@formatter:off
97+
IPackageFragment pack = fRoot.createPackageFragment("p1", true, null);
98+
ICompilationUnit unit = pack.createCompilationUnit("C.java",
99+
"package p1;\n" +
100+
"\n" +
101+
"public class C {\n" +
102+
" List list = List.of(1).stream().collect(toList());\n" +
103+
" double i = abs(-1);\n" +
104+
" double pi = PI;\n" +
105+
"}\n"
106+
, true, null);
107+
//@formatter:on
108+
TextEdit edit = OrganizeImportsHandler.organizeImports(unit, (selections) -> {
109+
return new ImportCandidate[0];
110+
});
111+
assertNotNull(edit);
112+
JavaModelUtil.applyEdit(unit, edit, true, null);
113+
/* @formatter:off */
114+
String expected = "package p1;\n" +
115+
"\n" +
116+
"import static java.lang.Math.PI;\n" +
117+
"import static java.lang.Math.abs;\n" +
118+
"import static java.util.stream.Collectors.toList;\n" +
119+
"\n" +
120+
"import java.util.List;\n" +
121+
"\n" +
122+
"public class C {\n" +
123+
" List list = List.of(1).stream().collect(toList());\n" +
124+
" double i = abs(-1);\n" +
125+
" double pi = PI;\n" +
126+
"}\n";
127+
//@formatter:on
128+
compareSource(expected, unit.getSource());
129+
} finally {
130+
JavaLanguageServerPlugin.getPreferencesManager().getPreferences().setJavaCompletionFavoriteMembers(Arrays.asList(favourites));
131+
}
132+
}
83133
}

0 commit comments

Comments
 (0)