mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
Added some more code actions
This commit is contained in:
+1
-1
@@ -164,7 +164,7 @@ class DefaultActionsRegistry : ActionsRegistry() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun notifyActionExec(action: ActionItem, result: Any) {
|
||||
for (listener in listeners) {
|
||||
listener.onExec(action, result)
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.lsp.java.actions;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.lsp.java.compiler.CompilerProvider;
|
||||
import com.itsaky.lsp.models.CodeActionItem;
|
||||
import com.itsaky.lsp.models.DiagnosticItem;
|
||||
import com.itsaky.lsp.models.Range;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public interface ActionProvider {
|
||||
|
||||
/**
|
||||
* Provide code actions.
|
||||
*
|
||||
* @param compiler The compiler.
|
||||
* @param file The file.
|
||||
* @param range The cursor range.
|
||||
* @param diagnostics The diagnostics within the cursor range.
|
||||
* @return The list of code actions.
|
||||
*/
|
||||
@NonNull
|
||||
List<CodeActionItem> provideActions(
|
||||
@NonNull CompilerProvider compiler,
|
||||
@NonNull Path file,
|
||||
@NonNull Range range,
|
||||
@NonNull List<DiagnosticItem> diagnostics);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.lsp.java.actions
|
||||
|
||||
import com.itsaky.androidide.actions.ActionData
|
||||
import com.itsaky.androidide.utils.Logger
|
||||
import com.itsaky.lsp.java.JavaLanguageServer
|
||||
import com.itsaky.lsp.java.rewrite.AddException
|
||||
import com.itsaky.lsp.java.utils.CodeActionUtils
|
||||
import com.itsaky.lsp.models.DiagnosticItem
|
||||
|
||||
/** @author Akash Yadav */
|
||||
class AddThrowsAction : BaseCodeAction() {
|
||||
override val id = "lsp_java_addThrows"
|
||||
override var label: String = "Add 'throws'"
|
||||
private val diagnosticCode = "compiler.err.unreported.exception.need.to.catch.or.throw"
|
||||
private val log = Logger.newInstance(javaClass.simpleName)
|
||||
|
||||
override fun prepare(data: ActionData) {
|
||||
super.prepare(data)
|
||||
|
||||
if (!visible || !hasRequiredData(data, DiagnosticItem::class.java)) {
|
||||
markInvisible()
|
||||
return
|
||||
}
|
||||
|
||||
val diagnostic = data[DiagnosticItem::class.java]!!
|
||||
if (diagnosticCode != diagnostic.code) {
|
||||
markInvisible()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
override fun execAction(data: ActionData): Any {
|
||||
val diagnostic = data[DiagnosticItem::class.java]!!
|
||||
val server = data[JavaLanguageServer::class.java]!!
|
||||
val file = requirePath(data)
|
||||
return server.compiler.compile(file).get { task ->
|
||||
val needsThrow = CodeActionUtils.findMethod(task, diagnostic.range)
|
||||
val exceptionName = CodeActionUtils.extractExceptionName(diagnostic.message)
|
||||
return@get AddException(
|
||||
needsThrow.className,
|
||||
needsThrow.methodName,
|
||||
needsThrow.erasedParameterTypes,
|
||||
exceptionName)
|
||||
}
|
||||
}
|
||||
|
||||
override fun postExec(data: ActionData, result: Any) {
|
||||
if (result !is AddException) {
|
||||
log.warn("Unable to add 'throws' expression")
|
||||
return
|
||||
}
|
||||
|
||||
val server = data[JavaLanguageServer::class.java]!!
|
||||
val client = server.client!!
|
||||
val file = requireFile(data)
|
||||
|
||||
client.performCodeAction(file, result.asCodeActions(server.compiler, label))
|
||||
}
|
||||
}
|
||||
@@ -83,17 +83,15 @@ import javax.lang.model.util.Elements;
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class CursorCodeActionProvider implements ActionProvider {
|
||||
public class CursorCodeActionProvider {
|
||||
|
||||
private static final Logger LOG = newInstance("JavaCursorCodeActionProvider");
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public List<CodeActionItem> provideActions(
|
||||
public List<CodeActionItem> provideActions (
|
||||
@NonNull CompilerProvider compiler,
|
||||
@NonNull Path file,
|
||||
@NonNull Range range,
|
||||
@NonNull List<DiagnosticItem> diagnostics) {
|
||||
@NonNull Range range) {
|
||||
logStarted(file, range);
|
||||
|
||||
Instant started = Instant.now();
|
||||
|
||||
-177
@@ -1,177 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.lsp.java.actions;
|
||||
|
||||
import static com.itsaky.androidide.utils.Logger.newInstance;
|
||||
import static com.itsaky.lsp.java.utils.CodeActionUtils.extractExceptionName;
|
||||
import static com.itsaky.lsp.java.utils.CodeActionUtils.findClassNeedingConstructor;
|
||||
import static com.itsaky.lsp.java.utils.CodeActionUtils.findMethod;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import com.itsaky.lsp.java.compiler.CompileTask;
|
||||
import com.itsaky.lsp.java.compiler.CompilerProvider;
|
||||
import com.itsaky.lsp.java.compiler.SynchronizedTask;
|
||||
import com.itsaky.lsp.java.rewrite.AddException;
|
||||
import com.itsaky.lsp.java.rewrite.AddSuppressWarningAnnotation;
|
||||
import com.itsaky.lsp.java.rewrite.GenerateRecordConstructor;
|
||||
import com.itsaky.lsp.java.rewrite.Rewrite;
|
||||
import com.itsaky.lsp.java.utils.MethodPtr;
|
||||
import com.itsaky.lsp.models.CodeActionItem;
|
||||
import com.itsaky.lsp.models.DiagnosticItem;
|
||||
import com.itsaky.lsp.models.Range;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Provides code actions for diagnostics.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class DiagnosticsCodeActionProvider implements ActionProvider {
|
||||
|
||||
private static final Logger LOG = newInstance("JavaDiagnosticsCodeActionProvider");
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public List<CodeActionItem> provideActions(
|
||||
@NonNull CompilerProvider compiler,
|
||||
@NonNull Path file,
|
||||
@NonNull Range range,
|
||||
@NonNull List<DiagnosticItem> diagnostics) {
|
||||
LOG.info(
|
||||
String.format(
|
||||
Locale.getDefault(),
|
||||
"Check %d diagnostics for quick fixes...",
|
||||
diagnostics.size()));
|
||||
Instant started = Instant.now();
|
||||
final SynchronizedTask synchronizedTask = compiler.compile(file);
|
||||
final List<Pair<String, Rewrite>> actions =
|
||||
synchronizedTask.get(task -> computeActions(compiler, file, diagnostics, task));
|
||||
|
||||
final List<CodeActionItem> result = new ArrayList<>();
|
||||
for (Pair<String, Rewrite> pair : actions) {
|
||||
try {
|
||||
final CodeActionItem action = pair.second.asCodeActions(compiler, pair.first);
|
||||
if (action != null) {
|
||||
result.add(action);
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
LOG.error("Unable to create rewrite for quickfix", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
LOG.debug(
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"Created %d code actions in %dms",
|
||||
result.size(),
|
||||
Instant.now().toEpochMilli() - started.toEpochMilli()));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<Pair<String, Rewrite>> computeActions(
|
||||
CompilerProvider compiler,
|
||||
@NonNull Path file,
|
||||
@NonNull List<DiagnosticItem> diagnostics,
|
||||
CompileTask task) {
|
||||
List<Pair<String, Rewrite>> rewrites = new ArrayList<>();
|
||||
for (DiagnosticItem d : diagnostics) {
|
||||
List<Pair<String, Rewrite>> pairs =
|
||||
codeActionForDiagnostic(compiler, task, file, d).stream()
|
||||
.filter(pair -> !TextUtils.isEmpty(pair.first) && pair.second != null)
|
||||
.collect(Collectors.toList());
|
||||
rewrites.addAll(pairs);
|
||||
}
|
||||
return rewrites;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<Pair<String, Rewrite>> codeActionForDiagnostic(
|
||||
CompilerProvider compiler, CompileTask task, Path file, @NonNull DiagnosticItem d) {
|
||||
switch (d.getCode()) {
|
||||
case "compiler.warn.unchecked.call.mbr.of.raw.type":
|
||||
return handleUnchecked(task, d);
|
||||
case "compiler.err.unreported.exception.need.to.catch.or.throw":
|
||||
return handleUnreportedException(task, d);
|
||||
case "compiler.err.var.not.initialized.in.default.constructor":
|
||||
return handleVarNotInitialized(task, d);
|
||||
default:
|
||||
return Collections.singletonList(Pair.create("", null));
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<Pair<String, Rewrite>> handleVarNotInitialized(
|
||||
CompileTask task, @NonNull DiagnosticItem d) {
|
||||
Rewrite rewrite;
|
||||
String title;
|
||||
final String needsConstructor = findClassNeedingConstructor(task, d.getRange());
|
||||
if (needsConstructor == null) {
|
||||
return Collections.singletonList(Pair.create("", null));
|
||||
}
|
||||
|
||||
rewrite = new GenerateRecordConstructor(needsConstructor);
|
||||
title = "Generate constructor";
|
||||
return Collections.singletonList(Pair.create(title, rewrite));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<Pair<String, Rewrite>> handleUnreportedException(
|
||||
CompileTask task, @NonNull DiagnosticItem d) {
|
||||
Rewrite rewrite;
|
||||
String title;
|
||||
final MethodPtr needsThrow = findMethod(task, d.getRange());
|
||||
final String exceptionName = extractExceptionName(d.getMessage());
|
||||
rewrite =
|
||||
new AddException(
|
||||
needsThrow.className,
|
||||
needsThrow.methodName,
|
||||
needsThrow.erasedParameterTypes,
|
||||
exceptionName);
|
||||
title = "Add 'throws'";
|
||||
return Collections.singletonList(Pair.create(title, rewrite));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<Pair<String, Rewrite>> handleUnchecked(
|
||||
CompileTask task, @NonNull DiagnosticItem d) {
|
||||
Rewrite rewrite;
|
||||
String title;
|
||||
final MethodPtr warnedMethod = findMethod(task, d.getRange());
|
||||
rewrite =
|
||||
new AddSuppressWarningAnnotation(
|
||||
warnedMethod.className,
|
||||
warnedMethod.methodName,
|
||||
warnedMethod.erasedParameterTypes);
|
||||
title = "Suppress 'unchecked' warning";
|
||||
return Collections.singletonList(Pair.create(title, rewrite));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.lsp.java.actions
|
||||
|
||||
import com.itsaky.androidide.actions.ActionData
|
||||
import com.itsaky.androidide.utils.Logger
|
||||
import com.itsaky.lsp.java.JavaLanguageServer
|
||||
import com.itsaky.lsp.java.rewrite.GenerateRecordConstructor
|
||||
import com.itsaky.lsp.java.utils.CodeActionUtils
|
||||
import com.itsaky.lsp.models.DiagnosticItem
|
||||
|
||||
/** @author Akash Yadav */
|
||||
class GenerateConstructorAction : BaseCodeAction() {
|
||||
override val id = "lsp_java_generateConstructor"
|
||||
override var label: String = "Generate constructor"
|
||||
private val diagnosticCode = "compiler.err.var.not.initialized.in.default.constructor"
|
||||
private val log = Logger.newInstance(javaClass.simpleName)
|
||||
|
||||
override fun prepare(data: ActionData) {
|
||||
super.prepare(data)
|
||||
|
||||
if (!visible || !hasRequiredData(data, DiagnosticItem::class.java)) {
|
||||
markInvisible()
|
||||
return
|
||||
}
|
||||
|
||||
val diagnostic = data[DiagnosticItem::class.java]!!
|
||||
if (diagnosticCode != diagnostic.code) {
|
||||
markInvisible()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
override fun execAction(data: ActionData): Any {
|
||||
val diagnostic = data[DiagnosticItem::class.java]!!
|
||||
val server = data[JavaLanguageServer::class.java]!!
|
||||
val file = requirePath(data)
|
||||
return server.compiler.compile(file).get { task ->
|
||||
val needsConstructor =
|
||||
CodeActionUtils.findClassNeedingConstructor(task, diagnostic.range)
|
||||
?: return@get false
|
||||
return@get GenerateRecordConstructor(needsConstructor)
|
||||
}
|
||||
}
|
||||
|
||||
override fun postExec(data: ActionData, result: Any) {
|
||||
if (result !is GenerateRecordConstructor) {
|
||||
log.warn("Unable to generate constructor")
|
||||
return
|
||||
}
|
||||
|
||||
val server = data[JavaLanguageServer::class.java]!!
|
||||
val client = server.client!!
|
||||
val file = requireFile(data)
|
||||
|
||||
client.performCodeAction(file, result.asCodeActions(server.compiler, label))
|
||||
}
|
||||
}
|
||||
@@ -32,5 +32,7 @@ class JavaCodeActionsMenu : CodeActionsMenu() {
|
||||
addAction(RemoveMethodAction())
|
||||
addAction(RemoveUnusedThrowsAction())
|
||||
addAction(CreateMissingMethodAction())
|
||||
addAction(SuppressUncheckedWarningAction())
|
||||
addAction(AddThrowsAction())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.lsp.java.actions
|
||||
|
||||
import com.itsaky.androidide.actions.ActionData
|
||||
import com.itsaky.androidide.utils.Logger
|
||||
import com.itsaky.lsp.java.JavaLanguageServer
|
||||
import com.itsaky.lsp.java.rewrite.AddSuppressWarningAnnotation
|
||||
import com.itsaky.lsp.java.utils.CodeActionUtils
|
||||
import com.itsaky.lsp.models.DiagnosticItem
|
||||
|
||||
/** @author Akash Yadav */
|
||||
class SuppressUncheckedWarningAction : BaseCodeAction() {
|
||||
override val id = "lsp_java_suppressUncheckedWarning"
|
||||
override var label: String = "Suppress 'unchecked' warning"
|
||||
private val diagnosticCode = "compiler.warn.unchecked.call.mbr.of.raw.type"
|
||||
private val log = Logger.newInstance(javaClass.simpleName)
|
||||
|
||||
override fun prepare(data: ActionData) {
|
||||
super.prepare(data)
|
||||
|
||||
if (!visible || !hasRequiredData(data, DiagnosticItem::class.java)) {
|
||||
markInvisible()
|
||||
return
|
||||
}
|
||||
|
||||
val diagnostic = data[DiagnosticItem::class.java]!!
|
||||
if (diagnosticCode != diagnostic.code) {
|
||||
markInvisible()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
override fun execAction(data: ActionData): Any {
|
||||
val diagnostic = data[DiagnosticItem::class.java]!!
|
||||
val server = data[JavaLanguageServer::class.java]!!
|
||||
val file = requirePath(data)
|
||||
return server.compiler.compile(file).get { task ->
|
||||
val warnedMethod = CodeActionUtils.findMethod(task, diagnostic.range)
|
||||
return@get AddSuppressWarningAnnotation(
|
||||
warnedMethod.className, warnedMethod.methodName, warnedMethod.erasedParameterTypes)
|
||||
}
|
||||
}
|
||||
|
||||
override fun postExec(data: ActionData, result: Any) {
|
||||
if (result !is AddSuppressWarningAnnotation) {
|
||||
log.warn("Unable to suppress 'unchecked' warning")
|
||||
return
|
||||
}
|
||||
|
||||
val server = data[JavaLanguageServer::class.java]!!
|
||||
val client = server.client!!
|
||||
val file = requireFile(data)
|
||||
|
||||
client.performCodeAction(file, result.asCodeActions(server.compiler, label))
|
||||
}
|
||||
}
|
||||
@@ -20,9 +20,7 @@ package com.itsaky.lsp.java.providers;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import com.itsaky.lsp.java.actions.ActionProvider;
|
||||
import com.itsaky.lsp.java.actions.CursorCodeActionProvider;
|
||||
import com.itsaky.lsp.java.actions.DiagnosticsCodeActionProvider;
|
||||
import com.itsaky.lsp.java.compiler.CompilerProvider;
|
||||
import com.itsaky.lsp.models.CodeActionParams;
|
||||
import com.itsaky.lsp.models.CodeActionResult;
|
||||
@@ -38,15 +36,8 @@ public class CodeActionProvider {
|
||||
|
||||
@NonNull
|
||||
public CodeActionResult codeActions(@NonNull CodeActionParams params) {
|
||||
final ActionProvider actionProvider;
|
||||
if (params.getDiagnostics().isEmpty()) {
|
||||
actionProvider = new CursorCodeActionProvider();
|
||||
} else {
|
||||
actionProvider = new DiagnosticsCodeActionProvider();
|
||||
}
|
||||
|
||||
return new CodeActionResult(
|
||||
actionProvider.provideActions(
|
||||
compiler, params.getFile(), params.getRange(), params.getDiagnostics()));
|
||||
new CursorCodeActionProvider()
|
||||
.provideActions(compiler, params.getFile(), params.getRange()));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user