Skip to content

Commit 322b794

Browse files
committed
Added a new code action for fix imports cmd
1 parent b4c38d8 commit 322b794

File tree

5 files changed

+530
-4
lines changed

5 files changed

+530
-4
lines changed

java/java.editor/src/org/netbeans/modules/java/editor/imports/JavaFixAllImports.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ List<TreePathHandle> getImports() {
202202
}
203203
}
204204

205-
private static void performFixImports(WorkingCopy wc, ImportData data, CandidateDescription[] selections, boolean removeUnusedImports) throws IOException {
205+
public static void performFixImports(WorkingCopy wc, ImportData data, CandidateDescription[] selections, boolean removeUnusedImports) throws IOException {
206206
//do imports:
207207
Set<Element> toImport = new HashSet<Element>();
208208
Map<Name, Element> useFQNsFor = new HashMap<Name, Element>();
@@ -263,7 +263,7 @@ private static void performFixImports(WorkingCopy wc, ImportData data, Candidate
263263
}
264264
}
265265

266-
private static ImportData computeImports(CompilationInfo info) {
266+
public static ImportData computeImports(CompilationInfo info) {
267267
ComputeImports imps = new ComputeImports(info);
268268
Pair<Map<String, List<Element>>, Map<String, List<Element>>> candidates = imps.computeCandidates();
269269

@@ -351,7 +351,7 @@ private static ImportData computeImports(CompilationInfo info) {
351351
return data;
352352
}
353353

354-
static final class ImportData {
354+
public static final class ImportData {
355355
public final String[] simpleNames;
356356
public final CandidateDescription[][] variants;
357357
public final CandidateDescription[] defaults;
@@ -440,7 +440,7 @@ public void actionPerformed(ActionEvent e) {
440440
d.dispose();
441441
}
442442

443-
static final class CandidateDescription {
443+
public static final class CandidateDescription {
444444
public final String displayName;
445445
public final Icon icon;
446446
public final ElementHandle<Element> toImport;
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.netbeans.modules.java.lsp.server.protocol;
20+
21+
import com.google.gson.JsonPrimitive;
22+
import java.io.IOException;
23+
import java.util.Collections;
24+
import java.util.List;
25+
import java.util.concurrent.CompletableFuture;
26+
import java.util.concurrent.atomic.AtomicReference;
27+
import java.util.stream.IntStream;
28+
import java.util.stream.Stream;
29+
import net.java.html.json.Function;
30+
import net.java.html.json.Model;
31+
import net.java.html.json.ModelOperation;
32+
import net.java.html.json.Property;
33+
import org.eclipse.lsp4j.CodeAction;
34+
import org.eclipse.lsp4j.CodeActionKind;
35+
import org.eclipse.lsp4j.CodeActionParams;
36+
import org.eclipse.lsp4j.TextEdit;
37+
import org.eclipse.lsp4j.WorkspaceEdit;
38+
import org.netbeans.api.htmlui.HTMLDialog;
39+
import org.netbeans.api.java.source.CompilationController;
40+
import org.netbeans.api.java.source.JavaSource;
41+
import org.netbeans.modules.java.editor.imports.JavaFixAllImports;
42+
import org.netbeans.modules.java.editor.imports.JavaFixAllImports.CandidateDescription;
43+
import org.netbeans.modules.java.editor.imports.JavaFixAllImports.ImportData;
44+
import org.netbeans.modules.java.lsp.server.Utils;
45+
import org.netbeans.modules.parsing.api.ResultIterator;
46+
import org.openide.filesystems.FileObject;
47+
import org.openide.util.Exceptions;
48+
import org.openide.util.NbBundle;
49+
import org.openide.util.lookup.ServiceProvider;
50+
51+
/**
52+
*
53+
* @author shimadan
54+
*/
55+
@ServiceProvider(service = CodeActionsProvider.class, position = 91)
56+
public class FixImportsCodeAction extends CodeActionsProvider {
57+
58+
private static final String FIX_IMPORTS_KIND = "source.fixImports";
59+
@Override
60+
@NbBundle.Messages({
61+
"DN_FixImports=Fix Imports...",})
62+
public List<CodeAction> getCodeActions(NbCodeLanguageClient client, ResultIterator resultIterator, CodeActionParams params) throws Exception {
63+
List<String> only = params.getContext().getOnly();
64+
if (only == null || !only.contains(CodeActionKind.Source)) {
65+
return Collections.emptyList();
66+
}
67+
CompilationController info = resultIterator.getParserResult() != null ? CompilationController.get(resultIterator.getParserResult()) : null;
68+
if (info == null) {
69+
return Collections.emptyList();
70+
}
71+
String uri = Utils.toUri(info.getFileObject());
72+
return Collections.singletonList(createCodeAction(client, Bundle.DN_FixImports(), FIX_IMPORTS_KIND, uri, null));
73+
}
74+
75+
@Override
76+
public CompletableFuture<CodeAction> resolve(NbCodeLanguageClient client, CodeAction codeAction, Object data) {
77+
CompletableFuture<CodeAction> future = new CompletableFuture<>();
78+
try {
79+
String uri = ((JsonPrimitive) data).getAsString();
80+
FileObject file = Utils.fromUri(uri);
81+
JavaSource js = JavaSource.forFileObject(file);
82+
if (js == null) {
83+
throw new IOException("Cannot get JavaSource for: " + uri);
84+
}
85+
final AtomicReference<ImportData> missingImports = new AtomicReference<ImportData>();
86+
js.runUserActionTask(cc -> {
87+
cc.toPhase(JavaSource.Phase.RESOLVED);
88+
missingImports.set(JavaFixAllImports.computeImports(cc));
89+
}, true);
90+
future = showFixImportsDialog(missingImports.get()).thenApply(selections -> {
91+
List<TextEdit> edits;
92+
try {
93+
edits = TextDocumentServiceImpl.modify2TextEdits(js, wc -> {
94+
wc.toPhase(JavaSource.Phase.RESOLVED);
95+
JavaFixAllImports.performFixImports(wc, missingImports.get(), selections, false);
96+
97+
});
98+
if (!edits.isEmpty()) {
99+
codeAction.setEdit(new WorkspaceEdit(Collections.singletonMap(uri, edits)));
100+
}
101+
} catch (IOException ex) {
102+
Exceptions.printStackTrace(ex);
103+
104+
}
105+
106+
return codeAction;
107+
});
108+
} catch (IOException | IllegalArgumentException ex) {
109+
future.completeExceptionally(ex);
110+
}
111+
return future;
112+
}
113+
114+
private CompletableFuture<CandidateDescription[]> showFixImportsDialog(ImportData Impdata) {
115+
CompletableFuture<CandidateDescription[]> selections = new CompletableFuture<>();
116+
Pages.showFixImportsDialog(Impdata, selections);
117+
return selections;
118+
}
119+
120+
@HTMLDialog(url = "ui/FixImports.html", resources = "FixImports.css")
121+
static final HTMLDialog.OnSubmit showFixImportsDialog(ImportData missingImports, CompletableFuture<CandidateDescription[]> selectedCandidates) {
122+
FixImportsUI model = new FixImportsUI();
123+
ImportDataUI[] imports = IntStream.range(0, missingImports.simpleNames.length)
124+
.mapToObj(i -> new ImportDataUI(
125+
missingImports.simpleNames[i],
126+
missingImports.defaults[i].displayName,
127+
Stream.of(missingImports.variants[i]).map((candidate)->candidate.displayName).toArray(String[]::new)
128+
))
129+
.toArray(ImportDataUI[]::new);
130+
model.withImports(imports).assignData(missingImports, selectedCandidates);
131+
model.applyBindings();
132+
return (id) -> {
133+
if ("accept".equals(id)) {
134+
model.completeSelectedCandidates();
135+
}else{
136+
model.cancel();
137+
}
138+
return true;
139+
};
140+
}
141+
142+
@Model(className = "FixImportsUI", targetId = "", instance = true, builder = "with",
143+
properties = {
144+
@Property(name = "imports", type = ImportDataUI.class, array = true)
145+
})
146+
static final class FixImportsControl {
147+
148+
private CompletableFuture<CandidateDescription[]> selectedCandidates;
149+
private ImportData missingImports;
150+
151+
@ModelOperation
152+
void assignData(FixImportsUI ui, ImportData missingImports, CompletableFuture<CandidateDescription[]> selectedCandidates) {
153+
this.selectedCandidates = selectedCandidates;
154+
this.missingImports = missingImports;
155+
}
156+
157+
@ModelOperation
158+
@Function
159+
void completeSelectedCandidates(FixImportsUI ui) {
160+
List<ImportDataUI> imports = ui.getImports();
161+
CandidateDescription[] choosen = IntStream.range(0, imports.size())
162+
.mapToObj(i -> Stream.of(missingImports.variants[i])
163+
.filter((variant) -> variant.displayName.equals(imports.get(i).getSelectedCandidateFQN()))
164+
.findFirst()
165+
.get()
166+
)
167+
.toArray(CandidateDescription[]::new);
168+
selectedCandidates.complete(choosen);
169+
}
170+
@ModelOperation
171+
@Function
172+
void cancel(){
173+
selectedCandidates.cancel(true);
174+
}
175+
}
176+
177+
@Model(className = "ImportDataUI", instance = true, properties = {
178+
@Property(name = "simpleName", type = String.class),
179+
@Property(name = "selectedCandidateFQN", type = String.class),
180+
@Property(name = "candidatesFQN", type = String.class, array = true)
181+
182+
})
183+
static final class ImportDataControl {
184+
185+
}
186+
187+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<!--
2+
3+
Licensed to the Apache Software Foundation (ASF) under one
4+
or more contributor license agreements. See the NOTICE file
5+
distributed with this work for additional information
6+
regarding copyright ownership. The ASF licenses this file
7+
to you under the Apache License, Version 2.0 (the
8+
"License"); you may not use this file except in compliance
9+
with the License. You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing,
14+
software distributed under the License is distributed on an
15+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
KIND, either express or implied. See the License for the
17+
specific language governing permissions and limitations
18+
under the License.
19+
20+
-->
21+
<!DOCTYPE html>
22+
<html lang="en">
23+
24+
<head>
25+
<meta charset="UTF-8">
26+
<meta http-equiv="Content-Security-Policy" content="default-src http: 'unsafe-inline' 'unsafe-eval'">
27+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
28+
<link rel="stylesheet" href="FixImports.css">
29+
<title>Fix Imports...</title>
30+
</head>
31+
32+
<body>
33+
<div class="section">
34+
<label>Missing Imports:</label>
35+
<div class="flex">
36+
<label class="flex-grow params-caption">Name:</label>
37+
<label class="flex-grow params-caption">Candidates:</label>
38+
<span class="filler"></span>
39+
</div>
40+
</div>
41+
<div class="flex-vscrollable section2" data-bind="foreach: imports">
42+
<div class="flex row">
43+
<div class="flex-equal">
44+
<label data-bind="text: simpleName" class="flex-grow"></label>
45+
</div>
46+
<div class="flex-equal hdivider">
47+
<select data-bind="options: candidatesFQN, value: selectedCandidateFQN"></select>
48+
</div>
49+
</div>
50+
</div>
51+
<div>
52+
<button id="accept" hidden class="regular-button vscode-font align-right">Add Imports</button>
53+
<button id="reject" hidden class="regular-button vscode-font">Cancel</button>
54+
</div>
55+
</body>
56+
</html>

0 commit comments

Comments
 (0)