diff --git a/actions/src/main/java/com/itsaky/androidide/actions/internal/DefaultActionsRegistry.kt b/actions/src/main/java/com/itsaky/androidide/actions/internal/DefaultActionsRegistry.kt
index 2075a0d6f..6be06863a 100644
--- a/actions/src/main/java/com/itsaky/androidide/actions/internal/DefaultActionsRegistry.kt
+++ b/actions/src/main/java/com/itsaky/androidide/actions/internal/DefaultActionsRegistry.kt
@@ -164,7 +164,7 @@ class DefaultActionsRegistry : ActionsRegistry() {
}
}
}
-
+
private fun notifyActionExec(action: ActionItem, result: Any) {
for (listener in listeners) {
listener.onExec(action, result)
diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/actions/ActionProvider.java b/lsp/java/src/main/java/com/itsaky/lsp/java/actions/ActionProvider.java
deleted file mode 100644
index 4b6e08c6b..000000000
--- a/lsp/java/src/main/java/com/itsaky/lsp/java/actions/ActionProvider.java
+++ /dev/null
@@ -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 .
- */
-
-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 provideActions(
- @NonNull CompilerProvider compiler,
- @NonNull Path file,
- @NonNull Range range,
- @NonNull List diagnostics);
-}
diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/actions/AddThrowsAction.kt b/lsp/java/src/main/java/com/itsaky/lsp/java/actions/AddThrowsAction.kt
new file mode 100644
index 000000000..9d45be329
--- /dev/null
+++ b/lsp/java/src/main/java/com/itsaky/lsp/java/actions/AddThrowsAction.kt
@@ -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 .
+ */
+
+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))
+ }
+}
diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/actions/CursorCodeActionProvider.java b/lsp/java/src/main/java/com/itsaky/lsp/java/actions/CursorCodeActionProvider.java
index ab1eb5435..a5799f92b 100644
--- a/lsp/java/src/main/java/com/itsaky/lsp/java/actions/CursorCodeActionProvider.java
+++ b/lsp/java/src/main/java/com/itsaky/lsp/java/actions/CursorCodeActionProvider.java
@@ -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 provideActions(
+ public List provideActions (
@NonNull CompilerProvider compiler,
@NonNull Path file,
- @NonNull Range range,
- @NonNull List diagnostics) {
+ @NonNull Range range) {
logStarted(file, range);
Instant started = Instant.now();
diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/actions/DiagnosticsCodeActionProvider.java b/lsp/java/src/main/java/com/itsaky/lsp/java/actions/DiagnosticsCodeActionProvider.java
deleted file mode 100644
index 9ebc1821b..000000000
--- a/lsp/java/src/main/java/com/itsaky/lsp/java/actions/DiagnosticsCodeActionProvider.java
+++ /dev/null
@@ -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 .
- */
-
-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 provideActions(
- @NonNull CompilerProvider compiler,
- @NonNull Path file,
- @NonNull Range range,
- @NonNull List 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> actions =
- synchronizedTask.get(task -> computeActions(compiler, file, diagnostics, task));
-
- final List result = new ArrayList<>();
- for (Pair 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> computeActions(
- CompilerProvider compiler,
- @NonNull Path file,
- @NonNull List diagnostics,
- CompileTask task) {
- List> rewrites = new ArrayList<>();
- for (DiagnosticItem d : diagnostics) {
- List> 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> 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> 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> 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> 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));
- }
-}
diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/actions/GenerateConstructorAction.kt b/lsp/java/src/main/java/com/itsaky/lsp/java/actions/GenerateConstructorAction.kt
new file mode 100644
index 000000000..6eb6ad1a4
--- /dev/null
+++ b/lsp/java/src/main/java/com/itsaky/lsp/java/actions/GenerateConstructorAction.kt
@@ -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 .
+ */
+
+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))
+ }
+}
diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/actions/JavaCodeActionsMenu.kt b/lsp/java/src/main/java/com/itsaky/lsp/java/actions/JavaCodeActionsMenu.kt
index 667f6d352..0fddee2bc 100644
--- a/lsp/java/src/main/java/com/itsaky/lsp/java/actions/JavaCodeActionsMenu.kt
+++ b/lsp/java/src/main/java/com/itsaky/lsp/java/actions/JavaCodeActionsMenu.kt
@@ -32,5 +32,7 @@ class JavaCodeActionsMenu : CodeActionsMenu() {
addAction(RemoveMethodAction())
addAction(RemoveUnusedThrowsAction())
addAction(CreateMissingMethodAction())
+ addAction(SuppressUncheckedWarningAction())
+ addAction(AddThrowsAction())
}
}
diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/actions/SuppressUncheckedWarningAction.kt b/lsp/java/src/main/java/com/itsaky/lsp/java/actions/SuppressUncheckedWarningAction.kt
new file mode 100644
index 000000000..466289bfa
--- /dev/null
+++ b/lsp/java/src/main/java/com/itsaky/lsp/java/actions/SuppressUncheckedWarningAction.kt
@@ -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 .
+ */
+
+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))
+ }
+}
diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/CodeActionProvider.java b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/CodeActionProvider.java
index e5535765b..edacea76a 100644
--- a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/CodeActionProvider.java
+++ b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/CodeActionProvider.java
@@ -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()));
}
}