diff --git a/app/src/main/assets/data/jls.jar b/app/src/main/assets/data/jls.jar index 0b8c395f8..d8f3b59c4 100644 Binary files a/app/src/main/assets/data/jls.jar and b/app/src/main/assets/data/jls.jar differ diff --git a/app/src/main/java/com/itsaky/androidide/EditorActivity.java b/app/src/main/java/com/itsaky/androidide/EditorActivity.java index 927b48249..401775a67 100644 --- a/app/src/main/java/com/itsaky/androidide/EditorActivity.java +++ b/app/src/main/java/com/itsaky/androidide/EditorActivity.java @@ -856,8 +856,8 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F } @Override - public void openFile(File file) { - openFile(file, null); + public EditorFragment openFile(File file) { + return openFile(file, null); } public EditorFragment openFile(File file, org.eclipse.lsp4j.Range selection) { @@ -865,7 +865,11 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F int i = mPagerAdapter.openFile(file, selection, this); if(i >= 0 && !mBinding.tabs.getTabAt(i).isSelected()) mBinding.tabs.getTabAt(i).select(); - mBinding.editorDrawerLayout.closeDrawer(GravityCompat.END); + + if(mBinding.editorDrawerLayout.isDrawerOpen(GravityCompat.END)) { + mBinding.editorDrawerLayout.closeDrawer(GravityCompat.END); + } + invalidateOptionsMenu(); try { return mPagerAdapter.getFrag(i); diff --git a/app/src/main/java/com/itsaky/androidide/fragments/FileTreeFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/FileTreeFragment.java index 8ff018d3b..27212bbe3 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/FileTreeFragment.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/FileTreeFragment.java @@ -214,7 +214,7 @@ public class FileTreeFragment extends BottomSheetDialogFragment implements TreeN } public interface FileActionListener { - public void openFile(File file); + public EditorFragment openFile(File file); public void showFileOptions(File file, TreeNode node); } } diff --git a/app/src/main/java/com/itsaky/androidide/lsp/AbstractLanguageClient.java b/app/src/main/java/com/itsaky/androidide/lsp/AbstractLanguageClient.java index b19206dbc..a2ceefa2d 100644 --- a/app/src/main/java/com/itsaky/androidide/lsp/AbstractLanguageClient.java +++ b/app/src/main/java/com/itsaky/androidide/lsp/AbstractLanguageClient.java @@ -16,16 +16,20 @@ import com.itsaky.androidide.EditorActivity; import com.itsaky.androidide.R; import com.itsaky.androidide.adapters.DiagnosticsAdapter; import com.itsaky.androidide.adapters.SearchListAdapter; +import com.itsaky.androidide.app.StudioApp; import com.itsaky.androidide.databinding.LayoutDiagnosticInfoBinding; import com.itsaky.androidide.fragments.EditorFragment; +import com.itsaky.androidide.fragments.sheets.ProgressSheet; import com.itsaky.androidide.interfaces.EditorActivityProvider; import com.itsaky.androidide.models.DiagnosticGroup; import com.itsaky.androidide.models.SearchResult; +import com.itsaky.androidide.tasks.TaskExecutor; import com.itsaky.androidide.utils.LSPUtils; import com.itsaky.androidide.utils.Logger; import com.itsaky.lsp.SemanticHighlight; import com.itsaky.lsp.services.IDELanguageClient; import com.itsaky.lsp.services.IDELanguageServer; +import com.itsaky.toaster.Toaster; import io.github.rosemoe.editor.text.Content; import io.github.rosemoe.editor.widget.CodeEditor; import java.io.File; @@ -37,7 +41,6 @@ import java.util.Map; import java.util.stream.Collectors; import java9.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Command; import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.LocationLink; @@ -51,8 +54,7 @@ import org.eclipse.lsp4j.ShowDocumentParams; import org.eclipse.lsp4j.ShowDocumentResult; import org.eclipse.lsp4j.SignatureHelp; import org.eclipse.lsp4j.SignatureInformation; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; +import org.eclipse.lsp4j.TextEdit; /** * AndroidIDE specific implementation of the LanguageClient @@ -341,9 +343,43 @@ public abstract class AbstractLanguageClient implements IDELanguageClient { .collect(Collectors.toList()) ); } + + /** + * Perform the given {@link CodeAction} + * + * @param editor The {@link CodeEditor} that invoked the code action request. + * This is required to reduce the time finding the code action from the edits. + * @param action The action to perform + */ + public void performCodeAction(CodeEditor editor, CodeAction action) { + if(activity() == null || editor == null || action == null) { + StudioApp.getInstance().toast(R.string.msg_cannot_perform_fix, Toaster.Type.ERROR); + return; + } + + final ProgressSheet progress = new ProgressSheet(); + progress.setSubMessageEnabled(false); + progress.setWelcomeTextEnabled(false); + progress.setCancelable(false); + progress.setMessage(activity().getString(R.string.msg_performing_fixes)); + progress.show(activity().getSupportFragmentManager(), "quick_fix_progress"); + + new TaskExecutor().executeAsyncProvideError(() -> performCodeActionAsync(editor, action), (a, b) -> { + final Boolean complete = a; + final Throwable error = b; + + progress.dismiss(); + + if(complete == null || error != null || !complete.booleanValue()) { + StudioApp.getInstance().toast(R.string.msg_cannot_perform_fix, Toaster.Type.ERROR); + LOG.error("Quickfix error", error); + return; + } + }); + } /** - * Usually called by {@link io.github.rosemoe.editor.widget.CodeEditor CodeEditor} to show a specific document in EditorActivity and select the specified range + * Usually called {@link CodeEditor} to show a specific document in EditorActivity and select the specified range */ @Override public CompletableFuture showDocument(ShowDocumentParams params) { @@ -425,6 +461,58 @@ public abstract class AbstractLanguageClient implements IDELanguageClient { return groups; } + private Boolean performCodeActionAsync(final CodeEditor editor, final CodeAction action) { + final Map > edits = action.getEdit().getChanges(); + if(edits == null || edits.isEmpty()) { + return Boolean.FALSE; + } + + for(Map.Entry> entry : edits.entrySet()) { + final File file = new File(URI.create(entry.getKey())); + if(!file.exists()) continue; + + for(TextEdit edit : entry.getValue()) { + final String editorFilepath = editor.getFile() == null ? "" : editor.getFile().getAbsolutePath(); + if(file.getAbsolutePath().equals(editorFilepath)) { + // Edit is in the same editor which requested the code action + editInEditor(editor, edit); + } else { + EditorFragment openedFrag = activity().getPagerAdapter().findEditorByFile(file); + + if(openedFrag != null && openedFrag.getEditor() != null) { + // Edit is in another 'opened' file + editInEditor(openedFrag.getEditor(), edit); + } else { + // Edit is in some other file which is not opened + // We should open that file and perform the edit + openedFrag = activity().openFile(file); + if(openedFrag != null && openedFrag.getEditor() != null) { + editInEditor(openedFrag.getEditor(), edit); + } + } + } + } + } + + return Boolean.TRUE; + } + + private void editInEditor (final CodeEditor editor, final TextEdit edit) { + final Range range = edit.getRange(); + final int startLine = range.getStart().getLine(); + final int startCol = range.getStart().getCharacter(); + final int endLine = range.getEnd().getLine(); + final int endCol = range.getEnd().getCharacter(); + + activity().runOnUiThread(() -> { + if(startLine == endLine && startCol == endCol) { + editor.getText().insert(startLine, startCol, edit.getNewText()); + } else { + editor.getText().replace(startLine, startCol, endLine, endCol, edit.getNewText()); + } + }); + } + public DiagnosticsAdapter newDiagnosticsAdapter() { return new DiagnosticsAdapter(mapAsGroup(this.diagnostics), activity()); } diff --git a/app/src/main/java/io/github/rosemoe/editor/util/BinaryHeap.java b/app/src/main/java/io/github/rosemoe/editor/util/BinaryHeap.java index b32dc7d55..edb824382 100644 --- a/app/src/main/java/io/github/rosemoe/editor/util/BinaryHeap.java +++ b/app/src/main/java/io/github/rosemoe/editor/util/BinaryHeap.java @@ -157,7 +157,7 @@ public class BinaryHeap { public void update(int id, int newValue) { int position = idToPosition.get(id, 0); if (position == 0) { - throw new IllegalArgumentException("trying to update with an invalid id"); + return; } int origin = nodes[position].data; nodes[position].data = newValue; @@ -177,7 +177,7 @@ public class BinaryHeap { public void remove(int id) { int position = idToPosition.get(id, 0); if (position == 0) { - throw new IllegalArgumentException("trying to remove with an invalid id"); + return; } idToPosition.delete(id); //Replace removed node with last node diff --git a/app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java b/app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java index ef2630dcb..e252c3254 100644 --- a/app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java +++ b/app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java @@ -876,6 +876,15 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca return mLanguageServer; } + /** + * Get the language client attached to this Editor + * + * @return The language client currently attached + */ + public AbstractLanguageClient getLanguageClient() { + return mLanguageClient; + } + /** * Sets up this editor according to the capabilities of the current language server */ @@ -4835,7 +4844,7 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca * @return A {@link CompletableFuture}. May return {@code null}. */ public CompletableFuture>> requestCodeActions() { - if(mLanguageServer == null && mLanguageClient == null) return null; + if(mLanguageServer == null || mLanguageClient == null) return null; final List diagnostics = findDiagnosticsContainingLine(getCursor().getLeftLine()); return mCodeActionProvider.codeActions(mLanguageServer, getDocumentIdentifier(), getCursorRange(), diagnostics); diff --git a/app/src/main/java/io/github/rosemoe/editor/widget/EditorTextActionModeStarter.java b/app/src/main/java/io/github/rosemoe/editor/widget/EditorTextActionModeStarter.java index 81e6cc214..9871bfb9c 100644 --- a/app/src/main/java/io/github/rosemoe/editor/widget/EditorTextActionModeStarter.java +++ b/app/src/main/java/io/github/rosemoe/editor/widget/EditorTextActionModeStarter.java @@ -22,11 +22,27 @@ import android.graphics.drawable.Drawable; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ListView; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.view.ActionMode; import androidx.appcompat.view.menu.MenuBuilder; import androidx.core.content.ContextCompat; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; +import com.itsaky.androidide.app.StudioApp; +import com.itsaky.toaster.Toaster; import io.github.rosemoe.editor.util.IntPair; +import java.util.List; +import java9.util.concurrent.CompletableFuture; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.Command; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import android.view.View; +import android.widget.TextView; +import com.blankj.utilcode.util.SizeUtils; +import java.util.stream.Collectors; +import android.view.Gravity; /** * Action Mode style text action panel for editor @@ -41,7 +57,7 @@ class EditorTextActionModeStarter implements CodeEditor.EditorTextActionPresente EditorTextActionModeStarter(CodeEditor editor) { mEditor = editor; } - + @SuppressLint("RestrictedApi") @Override public void onBeginTextSelect() { @@ -49,145 +65,253 @@ class EditorTextActionModeStarter implements CodeEditor.EditorTextActionPresente return; } mActionMode = ((AppCompatActivity) mEditor.getContext()).startSupportActionMode(new ActionMode.Callback() { - - @Override - public boolean onCreateActionMode(ActionMode actionMode, Menu menu) { - mEditor.mStartedActionMode = CodeEditor.ACTION_MODE_SELECT_TEXT; - if(menu instanceof MenuBuilder){ - MenuBuilder builder = (MenuBuilder) menu; - builder.setOptionalIconsVisible(true); - } - actionMode.setTitle(android.R.string.selectTextMode); - TypedArray array = mEditor.getContext().getTheme().obtainStyledAttributes(new int[]{ - android.R.attr.actionModeSelectAllDrawable, - android.R.attr.actionModeCutDrawable, - android.R.attr.actionModeCopyDrawable, - android.R.attr.actionModePasteDrawable, - }); - - menu.add(0, 0, 0, mEditor.getContext().getString(android.R.string.selectAll)) - .setShowAsActionFlags(2) - .setIcon(array.getDrawable(0)); - if(mEditor.isEditable()) { - menu.add(0, 1, 0, mEditor.getContext().getString(android.R.string.cut)) + private List> fixes; + + @Override + public boolean onCreateActionMode(ActionMode actionMode, Menu menu) { + mEditor.mStartedActionMode = CodeEditor.ACTION_MODE_SELECT_TEXT; + if (menu instanceof MenuBuilder) { + MenuBuilder builder = (MenuBuilder) menu; + builder.setOptionalIconsVisible(true); + } + actionMode.setTitle(android.R.string.selectTextMode); + TypedArray array = mEditor.getContext().getTheme().obtainStyledAttributes(new int[]{ + android.R.attr.actionModeSelectAllDrawable, + android.R.attr.actionModeCutDrawable, + android.R.attr.actionModeCopyDrawable, + android.R.attr.actionModePasteDrawable, + }); + + final CompletableFuture>> future = mEditor.requestCodeActions(); + + if (future != null) { + + future.whenComplete((a, b) -> { + fixes = a; + }); + + menu.add(0, 8, 0, mEditor.getContext().getString(com.itsaky.androidide.R.string.msg_code_actions)) + .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_NEVER) + .setIcon(createDrawable(com.itsaky.androidide.R.drawable.ic_quickfix)); + } + + menu.add(0, 0, 0, mEditor.getContext().getString(android.R.string.selectAll)) .setShowAsActionFlags(2) - .setIcon(array.getDrawable(1)); - } + .setIcon(array.getDrawable(0)); - menu.add(0, 2, 0, mEditor.getContext().getString(android.R.string.copy)) - .setShowAsActionFlags(2) - .setIcon(array.getDrawable(2)); - - if(mEditor.isEditable()) { - menu.add(0, 3, 0, mEditor.getContext().getString(android.R.string.paste)) + if (mEditor.isEditable()) { + menu.add(0, 1, 0, mEditor.getContext().getString(android.R.string.cut)) + .setShowAsActionFlags(2) + .setIcon(array.getDrawable(1)); + } + + menu.add(0, 2, 0, mEditor.getContext().getString(android.R.string.copy)) .setShowAsActionFlags(2) - .setIcon(array.getDrawable(3)); - } - - if(mEditor.isGotoDefinitionEnabled()) { - menu.add(0, 4, 0, mEditor.getContext().getString(com.itsaky.androidide.R.string.menu_navigate_definition)) + .setIcon(array.getDrawable(2)); + + if (mEditor.isEditable()) { + menu.add(0, 3, 0, mEditor.getContext().getString(android.R.string.paste)) + .setShowAsActionFlags(2) + .setIcon(array.getDrawable(3)); + } + + if (mEditor.isGotoDefinitionEnabled()) { + menu.add(0, 4, 0, mEditor.getContext().getString(com.itsaky.androidide.R.string.menu_navigate_definition)) + .setShowAsActionFlags(1) + .setIcon(createDrawable(com.itsaky.androidide.R.drawable.ic_goto_definition)); + } + + if (mEditor.isFindReferencesEnabled()) { + menu.add(0, 5, 0, mEditor.getContext().getString(com.itsaky.androidide.R.string.menu_navigate_references)) + .setShowAsActionFlags(1) + .setIcon(createDrawable(com.itsaky.androidide.R.drawable.ic_find_references)); + } + + menu.add(0, 6, 0, mEditor.getContext().getString(com.itsaky.androidide.R.string.menu_comment_line)) .setShowAsActionFlags(1) - .setIcon(createDrawable(com.itsaky.androidide.R.drawable.ic_goto_definition)); - } - - if(mEditor.isFindReferencesEnabled()) { - menu.add(0, 5, 0, mEditor.getContext().getString(com.itsaky.androidide.R.string.menu_navigate_references)) + .setIcon(createDrawable(com.itsaky.androidide.R.drawable.ic_comment_line)); + + menu.add(0, 7, 0, mEditor.getContext().getString(com.itsaky.androidide.R.string.menu_uncomment_line)) .setShowAsActionFlags(1) - .setIcon(createDrawable( com.itsaky.androidide.R.drawable.ic_find_references)); + .setIcon(createDrawable(com.itsaky.androidide.R.drawable.ic_uncomment_line)); + + array.recycle(); + return true; } + + private Drawable createDrawable(int icon) { + final Drawable d = ContextCompat.getDrawable(mEditor.getContext(), icon); + d.setColorFilter(ContextCompat.getColor(mEditor.getContext(), com.itsaky.androidide.R.color.secondaryColor), PorterDuff.Mode.SRC_ATOP); + return d; + } + + @Override + public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) { + if (mEditor.getFile() == null) return false; + + final String name = mEditor.getFile().getName(); + final boolean isJava = name.endsWith(".java") || name.endsWith(".gradle"); + final boolean isXml = name.endsWith(".xml"); + final MenuItem def = menu.findItem(4); + final MenuItem ref = menu.findItem(5); + final MenuItem comment = menu.findItem(6); + final MenuItem uncomment = menu.findItem(7); + final MenuItem quickfix = menu.findItem(8); + + comment.setEnabled(isJava || isXml).getIcon().setAlpha(isJava || isXml ? 255 : 76); + uncomment.setEnabled(isJava || isXml).getIcon().setAlpha(isJava || isXml ? 255 : 76); + + /** + * Thesw menu items may, or may not be added + * So we need to check if its null or not + * before we perform any further actions + */ + if (def != null) { + def.getIcon().setAlpha(isJava ? 255 : 76); + def.setVisible(isJava); + } + + if (ref != null) { + ref.getIcon().setAlpha(isJava ? 255 : 76); + ref.setVisible(isJava); + } + + if (quickfix != null) { + quickfix.getIcon().setAlpha(isJava ? 255 : 76); + quickfix.setVisible(isJava && fixes != null && !fixes.isEmpty()); + } + + return true; + } + + @Override + public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) { + switch (menuItem.getItemId()) { + case 0: + mEditor.selectAll(); + break; + case 1: + mEditor.cutText(); + break; + case 2: + mEditor.copyText(); + break; + case 3: + mEditor.pasteText(); + break; + case 4: + mEditor.findDefinition(); + break; + case 5: + mEditor.findReferences(); + break; + case 6: + mEditor.commentLine(); + break; + case 7: + mEditor.uncommentLine(); + break; + case 8 : + if (fixes == null || fixes.isEmpty()) { + StudioApp.getInstance().toast(com.itsaky.androidide.R.string.msg_no_code_actions, Toaster.Type.INFO); + break; + } + showFixes(); + break; + } + + if (menuItem.getItemId() != 0) { + onExit(); + } + return false; + } + + @Override + public void onDestroyActionMode(ActionMode actionMode) { + mEditor.mStartedActionMode = CodeEditor.ACTION_MODE_NONE; + mActionMode = null; + mEditor.setSelection(mEditor.getCursor().getLeftLine(), mEditor.getCursor().getLeftColumn()); + } + + void showFixes() { + final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(mEditor.getContext(), com.itsaky.androidide.R.style.AppTheme_MaterialAlertDialog); + builder.setTitle(com.itsaky.androidide.R.string.msg_code_actions); + builder.setView(createFixesListView()); + builder.show(); + } + + ListView createFixesListView() { + final ListView list = new ListView(mEditor.getContext()); + final QuickFixAdapter adapter = new QuickFixAdapter( + fixes.stream() + .filter(e -> e != null && e.isRight()) + .map(e -> e.getRight()) + .collect(Collectors.toList()) + ); + list.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + list.setAdapter(adapter); + list.setOnItemClickListener((a, b, c, d) -> { + performCodeAction(adapter.getItem(c)); + }); + return list; + } + + class QuickFixAdapter extends BaseAdapter { - menu.add(0, 6, 0, mEditor.getContext().getString(com.itsaky.androidide.R.string.menu_comment_line)) - .setShowAsActionFlags(1) - .setIcon(createDrawable( com.itsaky.androidide.R.drawable.ic_comment_line)); + private final List actions; + private final int dp16 = SizeUtils.dp2px(16); + private final int dp4 = SizeUtils.dp2px(4); + private final int dp48 = SizeUtils.dp2px(48); - menu.add(0, 7, 0, mEditor.getContext().getString(com.itsaky.androidide.R.string.menu_uncomment_line)) - .setShowAsActionFlags(1) - .setIcon(createDrawable( com.itsaky.androidide.R.drawable.ic_uncomment_line)); - - array.recycle(); - return true; - } - - private Drawable createDrawable(int icon) { - final Drawable d = ContextCompat.getDrawable(mEditor.getContext(), icon); - d.setColorFilter(ContextCompat.getColor(mEditor.getContext(), com.itsaky.androidide.R.color.secondaryColor), PorterDuff.Mode.SRC_ATOP); - return d; - } - - @Override - public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) { - if(mEditor.getFile() == null) return false; - - final String name = mEditor.getFile().getName(); - final boolean isJava = name.endsWith(".java") || name.endsWith(".gradle"); - final boolean isXml = name.endsWith(".xml"); - final MenuItem def = menu.findItem(4); - final MenuItem ref = menu.findItem(5); - final MenuItem comment = menu.findItem(6); - final MenuItem uncomment = menu.findItem(7); - - comment.setEnabled(isJava || isXml).getIcon().setAlpha(isJava || isXml ? 255 : 76); - uncomment.setEnabled(isJava || isXml).getIcon().setAlpha(isJava || isXml ? 255 : 76); - - /** - * Thesw menu items may, or may not be added - * So we need to check if its null or not - * before we perform any further actions - */ - if(def != null) { - def.setEnabled(isJava).getIcon().setAlpha(isJava ? 255 : 76); - } - - if(ref != null) { - ref.setEnabled(isJava).getIcon().setAlpha(isJava ? 255 : 76); - } + public QuickFixAdapter(List actions) { + this.actions = actions; + } - return true; - } + @Override + public int getCount() { + return actions.size(); + } + + @Override + public CodeAction getItem(int position) { + return actions.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if(convertView == null) { + convertView = createItemTextView(position, parent); + } + return convertView; + } - @Override - public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) { - switch (menuItem.getItemId()) { - case 0: - mEditor.selectAll(); - break; - case 1: - mEditor.cutText(); - break; - case 2: - mEditor.copyText(); - break; - case 3: - mEditor.pasteText(); - break; - case 4: - mEditor.findDefinition(); - break; - case 5: - mEditor.findReferences(); - break; - case 6: - mEditor.commentLine(); - break; - case 7: - mEditor.uncommentLine(); - break; + private View createItemTextView(int position, ViewGroup parent) { + final CodeAction action = getItem(position); + final TextView text = new TextView(parent.getContext()); + final String title = action.getTitle(); + + text.setText(title); + text.setPaddingRelative(dp16, dp4, dp16, dp4); + text.setMinHeight(dp48); + text.setGravity(Gravity.CENTER_VERTICAL); + + return text; + } } - if(menuItem.getItemId() != 0) { - onExit(); + + void performCodeAction(CodeAction action) { + if(mEditor.getLanguageClient() != null) { + mEditor.getLanguageClient().performCodeAction(mEditor, action); + } } - return false; - } - @Override - public void onDestroyActionMode(ActionMode actionMode) { - mEditor.mStartedActionMode = CodeEditor.ACTION_MODE_NONE; - mActionMode = null; - mEditor.setSelection(mEditor.getCursor().getLeftLine(), mEditor.getCursor().getLeftColumn()); - } - - }); + }); } @Override diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a65a50b7d..10d281839 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,10 +1,12 @@ Confirm changes These files will be modified:\n%s - No code actions found + No quickfix found + Unable to perform quick fix + Performing fixes… Analyzing code actions Services has not been started yet - Code Actions + Quickfixes Since API %d Removed in API %d Deprecated in API %d