Implemented some more actions with the ActionsRegistry

This commit is contained in:
Akash Yadav
2022-04-10 21:06:30 +05:30
parent 24c743b735
commit 7cf47f6cbd
17 changed files with 180 additions and 152 deletions
+1
View File
@@ -4,6 +4,7 @@
<option name="SPACE_BEFORE_OPENING_ANGLE_BRACKET_IN_TYPE_PARAMETER" value="true" />
</JavaCodeStyleSettings>
<JetCodeStyleSettings>
<option name="ALLOW_TRAILING_COMMA" value="true" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="JAVA">
@@ -54,7 +54,9 @@ interface ActionItem {
*
* @param data The data containing various information about the event.
*/
fun postExec(data: ActionData, result: Any) = Unit
fun postExec(data: ActionData, result: Any) = Any()
fun getShowAsActionFlags(data: ActionData): Int = -1
/** Location where an action item will be shown. */
enum class Location(val id: String) {
@@ -125,6 +125,10 @@ class DefaultActionsRegistry : ActionsRegistry() {
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
}
if (action.getShowAsActionFlags(data) != -1) {
item.setShowAsAction(action.getShowAsActionFlags(data))
}
if (action !is ActionMenu) {
item.setOnMenuItemClickListener {
if (action.requiresUIThread) {
@@ -154,8 +158,8 @@ class DefaultActionsRegistry : ActionsRegistry() {
}
ThreadUtils.runOnUiThread {
action.postExec(data, result)
notifyActionExec(action, result)
action.postExec(data, result ?: false)
notifyActionExec(action, result ?: false)
}
}
}
@@ -83,7 +83,6 @@ import com.itsaky.androidide.fragments.sheets.OptionsListFragment;
import com.itsaky.androidide.fragments.sheets.ProgressSheet;
import com.itsaky.androidide.fragments.sheets.TextSheetFragment;
import com.itsaky.androidide.handlers.BuildServiceHandler;
import com.itsaky.androidide.utils.EditorActivityActions;
import com.itsaky.androidide.handlers.FileOptionsHandler;
import com.itsaky.androidide.handlers.IDEHandler;
import com.itsaky.androidide.interfaces.DiagnosticClickListener;
@@ -103,6 +102,7 @@ import com.itsaky.androidide.services.builder.IDEService;
import com.itsaky.androidide.shell.ShellServer;
import com.itsaky.androidide.tasks.TaskExecutor;
import com.itsaky.androidide.utils.DialogUtils;
import com.itsaky.androidide.utils.EditorActivityActions;
import com.itsaky.androidide.utils.EditorBottomSheetBehavior;
import com.itsaky.androidide.utils.Environment;
import com.itsaky.androidide.utils.LSPUtils;
@@ -113,6 +113,7 @@ import com.itsaky.androidide.viewmodel.EditorViewModel;
import com.itsaky.androidide.views.MaterialBanner;
import com.itsaky.androidide.views.SymbolInputView;
import com.itsaky.androidide.views.editor.CodeEditorView;
import com.itsaky.androidide.views.editor.IDEEditor;
import com.itsaky.inflater.values.ValuesTableFactory;
import com.itsaky.lsp.java.models.JavaServerConfiguration;
import com.itsaky.lsp.java.models.JavaServerSettings;
@@ -223,7 +224,6 @@ public class EditorActivity extends StudioActivity
this::onGetUIDesignerResult);
EditorActivityActions.register(this);
ensureToolbarMenu();
}
private void setupDrawerToggle() {
@@ -354,7 +354,6 @@ public class EditorActivity extends StudioActivity
super.onResume();
EditorActivityActions.register(this);
ensureToolbarMenu();
try {
checkForCompilerModule();
@@ -377,91 +376,6 @@ public class EditorActivity extends StudioActivity
super.onDestroy();
}
// @Override
// @SuppressLint("AlwaysShowAction")
// public boolean onPrepareOptionsMenu(Menu menu) {
// for (int id : BUILD_IDS) {
// MenuItem item = menu.findItem(id);
// if (item != null) {
// boolean enabled = getBuildService() != null &&
// !getBuildService().isBuilding();
// item.setEnabled(enabled);
// item.getIcon().setAlpha(enabled ? 255 : 76);
// }
// }
//
// MenuItem run1 = menu.findItem(R.id.menuEditor_quickRun);
// MenuItem run2 = menu.findItem(R.id.menuEditor_run);
// MenuItem undo = menu.findItem(R.id.menuEditor_undo);
// MenuItem redo = menu.findItem(R.id.menuEditor_redo);
// MenuItem save = menu.findItem(R.id.menuEditor_save);
// MenuItem def = menu.findItem(R.id.menuEditor_gotoDefinition);
// MenuItem ref = menu.findItem(R.id.menuEditor_findReferences);
// MenuItem comment = menu.findItem(R.id.menuEditor_commentLine);
// MenuItem uncomment = menu.findItem(R.id.menuEditor_uncommentLine);
// MenuItem findFile = menu.findItem(R.id.menuEditor_findFile);
// MenuItem viewLayout = menu.findItem(R.id.menuEditor_viewLayout);
//
// if (KeyboardUtils.isSoftInputVisible(this)) {
// run1.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
// run2.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
// undo.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
// redo.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
//
// viewLayout.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
// } else {
// run1.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
// run2.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
// undo.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
// redo.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
//
// viewLayout.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
// }
//
// final var editor = getCurrentEditor();
// final var file = editor != null ? editor.getFile() : null;
// final var notNull = editor != null && file != null;
// final var isJava = notNull && file.getName().endsWith(".java");
// final var isXml = notNull && file.getName().endsWith(".xml");
// final var filesModified = notNull && mViewModel != null &&
// mViewModel.areFilesModified();
// final var isLayout =
// isXml
// && file.getParentFile() != null
// && Pattern.compile(FileOptionsHandler.LAYOUT_RES_PATH_REGEX)
// .matcher(file.getParentFile().getAbsolutePath())
// .matches();
// final var nullableAlpha = notNull ? 255 : 76;
// final var javaFileAlpha = isJava ? 255 : 76;
// final var layoutFileAlpha = isLayout ? 255 : 76;
// final var saveAlpha = filesModified ? 255 : 76;
//
// undo.setEnabled(notNull);
// redo.setEnabled(notNull);
// save.setEnabled(filesModified);
// comment.setEnabled(notNull);
// uncomment.setEnabled(notNull);
// findFile.setEnabled(notNull);
// def.setEnabled(isJava);
// ref.setEnabled(isJava);
//
// undo.getIcon().setAlpha(nullableAlpha);
// redo.getIcon().setAlpha(nullableAlpha);
// save.getIcon().setAlpha(saveAlpha);
// comment.getIcon().setAlpha(nullableAlpha);
// uncomment.getIcon().setAlpha(nullableAlpha);
// findFile.getIcon().setAlpha(nullableAlpha);
// def.getIcon().setAlpha(javaFileAlpha);
// ref.getIcon().setAlpha(javaFileAlpha);
//
// viewLayout.setEnabled(isLayout);
// viewLayout.getIcon().setAlpha(layoutFileAlpha);
// viewLayout.setShowAsActionFlags(
// isLayout ? MenuItem.SHOW_AS_ACTION_ALWAYS : MenuItem.SHOW_AS_ACTION_NEVER);
//
// return true;
// }
@SuppressLint("RestrictedApi")
@Override
public boolean onCreateOptionsMenu(Menu menu) {
@@ -473,13 +387,25 @@ public class EditorActivity extends StudioActivity
return true;
}
public void ensureToolbarMenu() {
final var menu = mBinding.editorToolbar.getMenu();
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
ensureToolbarMenu(menu);
return true;
}
public void ensureToolbarMenu(@NonNull Menu menu) {
menu.clear();
final var data = new ActionData();
final var currentEditor = getCurrentEditor();
data.put(Context.class, EditorActivity.this);
data.put(CodeEditorView.class, getCurrentEditor());
data.put(CodeEditorView.class, currentEditor);
if (currentEditor != null) {
data.put(IDEEditor.class, currentEditor.getEditor());
data.put(File.class, currentEditor.getEditor().getFile());
}
ActionsRegistry.getInstance()
.fillMenu(
data,
@@ -668,7 +594,6 @@ public class EditorActivity extends StudioActivity
editorView.onEditorSelected();
mViewModel.setCurrentFile(position, editorView.getFile());
refreshSymbolInput(editorView);
ensureToolbarMenu();
}
@Override
@@ -681,10 +606,6 @@ public class EditorActivity extends StudioActivity
editor.ensureWindowsDismissed();
}
}
if (mViewModel.getOpenedFileCount() <= 0) {
ensureToolbarMenu();
}
}
@Override
@@ -929,7 +850,6 @@ public class EditorActivity extends StudioActivity
mBinding.editorContainer.setDisplayedChild(index);
ensureToolbarMenu();
try {
return getEditorAtIndex(index);
} catch (Throwable th) {
@@ -994,7 +914,6 @@ public class EditorActivity extends StudioActivity
}
mBinding.tabs.requestLayout();
ensureToolbarMenu();
}
public void closeAll() {
@@ -1031,8 +950,6 @@ public class EditorActivity extends StudioActivity
mBinding.tabs.requestLayout();
mBinding.editorContainer.removeAllViews();
ensureToolbarMenu();
if (onSaved != null) {
onSaved.run();
}
@@ -1457,7 +1374,7 @@ public class EditorActivity extends StudioActivity
}
private void onSoftInputChanged() {
ensureToolbarMenu();
invalidateOptionsMenu();
if (KeyboardUtils.isSoftInputVisible(this)) {
TransitionManager.beginDelayedTransition(mBinding.getRoot(), new Slide(Gravity.TOP));
symbolInput.setVisibility(View.VISIBLE);
@@ -45,7 +45,7 @@ abstract class BaseBuildAction : EditorActivityAction() {
override fun postExec(data: ActionData, result: Any) {
val context = getActivity(data) ?: return
context.ensureToolbarMenu()
context.invalidateOptionsMenu()
}
fun shouldPrepare() = visible && enabled
@@ -18,17 +18,29 @@
package com.itsaky.androidide.actions
import com.itsaky.androidide.views.editor.CodeEditorView
import java.io.File
import com.itsaky.androidide.views.editor.IDEEditor
/** @author Akash Yadav */
abstract class EditorRelatedAction : EditorActivityAction() {
override fun prepare(data: ActionData) {
val editor = getEditor(data)
val file = data.get(File::class.java)
visible = editor != null && file != null
override var requiresUIThread: Boolean = true
override fun prepare(data: ActionData) {
val editor =
getEditor(data)
?: run {
visible = false
enabled = false
return
}
val file = editor.file
visible = file != null
enabled = visible
}
fun getEditor(data: ActionData): CodeEditorView? = data.get(CodeEditorView::class.java)
fun getEditor(data: ActionData): IDEEditor? = data.get(IDEEditor::class.java)
fun getEditorView(data: ActionData): CodeEditorView? = data[CodeEditorView::class.java]
}
@@ -35,7 +35,7 @@ class FindInFileAction() : EditorRelatedAction() {
}
override fun execAction(data: ActionData): Boolean {
val editor = getEditor(data) ?: return false
val editor = getEditorView(data) ?: return false
editor.beginSearch()
return true
}
@@ -26,6 +26,8 @@ import com.itsaky.androidide.actions.EditorActivityAction
/** @author Akash Yadav */
class FindInProjectAction() : EditorActivityAction() {
override var requiresUIThread: Boolean = true
constructor(context: Context) : this() {
label = context.getString(R.string.menu_find_project)
icon = ContextCompat.getDrawable(context, R.drawable.ic_search_project)
@@ -17,12 +17,14 @@
package com.itsaky.androidide.actions.text
import android.app.Activity
import android.content.Context
import android.view.MenuItem
import androidx.core.content.ContextCompat
import com.blankj.utilcode.util.KeyboardUtils
import com.itsaky.androidide.R
import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.actions.EditorRelatedAction
import com.itsaky.androidide.views.editor.CodeEditorView
/** @author Akash Yadav */
class RedoAction() : EditorRelatedAction() {
@@ -34,14 +36,28 @@ class RedoAction() : EditorRelatedAction() {
override val id: String = "editor_redo"
override fun execAction(data: ActionData): Boolean {
val editor = data.get(CodeEditorView::class.java)
override fun prepare(data: ActionData) {
super.prepare(data)
return if (editor != null) {
editor.redo()
true
if (!visible) {
return
}
val editor = getEditor(data)!!
enabled = editor.canRedo()
}
override fun execAction(data: ActionData): Boolean {
val editor = getEditor(data)!!
editor.redo()
return true
}
override fun getShowAsActionFlags(data: ActionData): Int {
return if (KeyboardUtils.isSoftInputVisible(data.get(Context::class.java) as Activity)) {
MenuItem.SHOW_AS_ACTION_IF_ROOM
} else {
false
MenuItem.SHOW_AS_ACTION_NEVER
}
}
}
@@ -22,7 +22,6 @@ import androidx.core.content.ContextCompat
import com.itsaky.androidide.R
import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.actions.EditorRelatedAction
import com.itsaky.androidide.views.editor.CodeEditorView
/** @author Akash Yadav */
class UndoAction() : EditorRelatedAction() {
@@ -34,8 +33,19 @@ class UndoAction() : EditorRelatedAction() {
icon = ContextCompat.getDrawable(context, R.drawable.ic_undo)
}
override fun execAction(data: ActionData): Boolean {
val editor = data.get(CodeEditorView::class.java)
override fun prepare(data: ActionData) {
super.prepare(data)
if (!visible) {
return
}
val editor = getEditor(data)!!
enabled = editor.canUndo()
}
override fun execAction(data: ActionData): Any {
val editor = getEditor(data)
return if (editor != null) {
editor.undo()
true
@@ -154,7 +154,6 @@ public class BuildServiceHandler extends IDEHandler implements BuildListener {
.getApp()
.getPrefManager()
.putBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, false);
activity().ensureToolbarMenu();
activity().getBinding().buildProgressIndicator.setVisibility(View.GONE);
}
@@ -173,7 +172,6 @@ public class BuildServiceHandler extends IDEHandler implements BuildListener {
.getApp()
.getPrefManager()
.putBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, false);
activity().ensureToolbarMenu();
activity().getBinding().buildProgressIndicator.setVisibility(View.GONE);
}
@@ -34,6 +34,7 @@
package com.itsaky.androidide.views.editor;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
@@ -291,6 +292,10 @@ public class CodeEditorView extends FrameLayout {
// File must be set only after setting the language server
// This will make sure that textDocument/didOpen is sent
binding.editor.setFile(getFile());
if (getContext() instanceof Activity) {
((Activity) getContext()).invalidateOptionsMenu();
}
}
private void configureEditorIfNeeded() {
@@ -16,6 +16,7 @@
*/
package com.itsaky.androidide.views.editor;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
@@ -869,6 +870,10 @@ public class IDEEditor extends CodeEditor {
* @param event The content change event.
*/
private void handleContentChange(ContentChangeEvent event, Unsubscribe unsubscribe) {
if (getContext() instanceof Activity) {
((Activity) getContext()).invalidateOptionsMenu();
}
if (getFile() == null || mLanguageServer == null) {
return;
}
@@ -24,7 +24,6 @@ import static com.itsaky.lsp.java.utils.CodeActionUtils.extractRange;
import static com.itsaky.lsp.java.utils.CodeActionUtils.findClassNeedingConstructor;
import static com.itsaky.lsp.java.utils.CodeActionUtils.findMethod;
import static com.itsaky.lsp.java.utils.CodeActionUtils.findPosition;
import static com.itsaky.lsp.java.utils.CodeActionUtils.unwrapJCDiagnostic;
import android.text.TextUtils;
import android.util.Pair;
@@ -36,13 +35,11 @@ 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.AddImport;
import com.itsaky.lsp.java.rewrite.AddSuppressWarningAnnotation;
import com.itsaky.lsp.java.rewrite.ConvertFieldToBlock;
import com.itsaky.lsp.java.rewrite.ConvertVariableToStatement;
import com.itsaky.lsp.java.rewrite.CreateMissingMethod;
import com.itsaky.lsp.java.rewrite.GenerateRecordConstructor;
import com.itsaky.lsp.java.rewrite.ImplementAbstractMethods;
import com.itsaky.lsp.java.rewrite.RemoveClass;
import com.itsaky.lsp.java.rewrite.RemoveException;
import com.itsaky.lsp.java.rewrite.RemoveMethod;
@@ -51,7 +48,6 @@ 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 com.sun.tools.javac.util.JCDiagnostic;
import java.nio.file.Path;
import java.time.Instant;
@@ -61,9 +57,6 @@ import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
/**
* Provides code actions for diagnostics.
*
@@ -149,8 +142,6 @@ public class DiagnosticsCodeActionProvider implements ActionProvider {
return handleUnreportedException(task, d);
case "compiler.err.var.not.initialized.in.default.constructor":
return handleVarNotInitialized(task, d);
case "compiler.err.does.not.override.abstract":
return handleDoesNotOverrideAbstract(d);
case "compiler.err.cant.resolve.location.args":
return handleMissingMethod(task, file, d);
default:
@@ -168,23 +159,6 @@ public class DiagnosticsCodeActionProvider implements ActionProvider {
return Collections.singletonList(Pair.create(title, rewrite));
}
@NonNull
private List<Pair<String, Rewrite>> handleDoesNotOverrideAbstract(@NonNull DiagnosticItem d) {
Rewrite rewrite;
String title;
//noinspection unchecked
final Diagnostic<? extends JavaFileObject> diagnostic =
(Diagnostic<? extends JavaFileObject>) d.getExtra();
JCDiagnostic jcDiagnostic = unwrapJCDiagnostic(diagnostic);
if (jcDiagnostic == null) {
return Collections.singletonList(Pair.create("", null));
}
rewrite = new ImplementAbstractMethods(jcDiagnostic);
title = "Implement abstract methods";
return Collections.singletonList(Pair.create(title, rewrite));
}
@NonNull
private List<Pair<String, Rewrite>> handleVarNotInitialized(
CompileTask task, @NonNull DiagnosticItem d) {
@@ -199,7 +173,7 @@ public class DiagnosticsCodeActionProvider implements ActionProvider {
title = "Generate constructor";
return Collections.singletonList(Pair.create(title, rewrite));
}
@NonNull
private List<Pair<String, Rewrite>> handleUnreportedException(
CompileTask task, @NonNull DiagnosticItem d) {
@@ -31,8 +31,8 @@ import javax.tools.JavaFileObject
class ImplementAbstractMethodsAction : BaseCodeAction() {
override val id: String = "lsp_java_implementAbstractMethods"
override var label: String = "Implement abstract method(s)"
private val log = Logger.newInstance(javaClass.simpleName)
private var diagnosticCode = "compiler.err.does.not.override.abstract"
private val log = Logger.newInstance(javaClass.simpleName)
@Suppress("UNCHECKED_CAST")
override fun prepare(data: ActionData) {
@@ -26,5 +26,6 @@ class JavaCodeActionsMenu : CodeActionsMenu() {
addAction(UncommentAction())
addAction(AddImportAction())
addAction(ImplementAbstractMethodsAction())
addAction(VariableToStatementAction())
}
}
@@ -0,0 +1,81 @@
/*
* 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.ConvertVariableToStatement
import com.itsaky.lsp.java.utils.CodeActionUtils.findPosition
import com.itsaky.lsp.models.DiagnosticItem
import java.io.File
/** @author Akash Yadav */
class VariableToStatementAction : BaseCodeAction() {
override val id: String = "lsp_java_variableToStatement"
override var label: String = "Convert to statement"
private val diagnosticCode = "unused_local"
private val log = Logger.newInstance(javaClass.simpleName)
@Suppress("UNCHECKED_CAST")
override fun prepare(data: ActionData) {
super.prepare(data)
if (!visible) {
return
}
if (!hasRequiredData(
data, JavaLanguageServer::class.java, DiagnosticItem::class.java, File::class.java)) {
markInvisible()
return
}
val diagnostic = data.get(DiagnosticItem::class.java)!!
if (diagnosticCode != diagnostic.code) {
markInvisible()
return
}
visible = true
enabled = true
}
override fun execAction(data: ActionData): Any {
val diagnostic = data[DiagnosticItem::class.java]!!
val server = data[JavaLanguageServer::class.java]!!
val compiler = server.compiler!!
val path = requirePath(data)
return compiler.compile(path).get {
ConvertVariableToStatement(path, findPosition(it, diagnostic.range.start))
}
}
override fun postExec(data: ActionData, result: Any) {
if (result !is ConvertVariableToStatement) {
log.warn("Unable to convert variable to statement")
return
}
val file = data[File::class.java]
val server = data[JavaLanguageServer::class.java]!!
val client = server.client!!
client.performCodeAction(file, result.asCodeActions(server.compiler, label))
}
}