mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
Implemented custom searcher for IDEEditor (related: #158)
This commit is contained in:
Generated
+4
@@ -39,6 +39,8 @@
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/drawable/ic_run.xml" value="0.151" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/drawable/ic_run_debug.xml" value="0.151" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/drawable/ic_save.xml" value="0.26205128205128203" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/drawable/ic_search_next.xml" value="0.147" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/drawable/ic_search_previous.xml" value="0.147" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/drawable/ic_signature_help.xml" value="0.19907407407407407" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/drawable/ic_submenu_arrow.xml" value="0.1305" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/drawable/ic_text_actions.xml" value="0.147" />
|
||||
@@ -66,11 +68,13 @@
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/layout_crash_report.xml" value="0.25" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/layout_diagnostic_info.xml" value="0.20512820512820512" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/layout_diagnostic_window.xml" value="0.25" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/layout_dialog_text_input.xml" value="0.23854166666666668" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/layout_dimension_attr_editor.xml" value="0.25" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/layout_divider.xml" value="0.25" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/layout_editor_actions.xml" value="0.25" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/layout_editor_bottom_sheet.xml" value="0.19572649572649573" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/layout_editor_code_actions.xml" value="0.25" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/layout_editor_find_replace.xml" value="0.23854166666666668" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/layout_editor_text_actions.xml" value="0.25" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/layout_filetree_item.xml" value="0.25" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/layout_popup_menu_item.xml" value="0.264" />
|
||||
|
||||
@@ -62,16 +62,19 @@ interface ActionItem {
|
||||
enum class Location(val id: String) {
|
||||
|
||||
/** Location marker for action items shown in editor activity's toolbar. */
|
||||
EDITOR_TOOLBAR("editor_toolbar"),
|
||||
EDITOR_TOOLBAR("editor.activity.toolbar"),
|
||||
|
||||
/** Location marker for action items shown in editor's text action menu. */
|
||||
EDITOR_TEXT_ACTIONS("editor_text_actions"),
|
||||
EDITOR_TEXT_ACTIONS("editor.textActions"),
|
||||
|
||||
/**
|
||||
* Location marker for action items shown in 'Code actions' submenu in editor's text action
|
||||
* menu.
|
||||
*/
|
||||
EDITOR_CODE_ACTIONS("editor_code_actions");
|
||||
EDITOR_CODE_ACTIONS("editor.codeActions"),
|
||||
|
||||
/** Location marker for action items show in 'Find & Replace' action mode menu in editor. */
|
||||
EDITOR_SEARCH_ACTION_MODE("editor.searchActionMode");
|
||||
|
||||
override fun toString(): String {
|
||||
return id
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.androidide.actions.editor.find
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.itsaky.androidide.R
|
||||
import com.itsaky.androidide.actions.ActionData
|
||||
|
||||
/**
|
||||
* 'Go to next' action in editor's search action mode.
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class GoToNextAction() : SearchActionModeAction() {
|
||||
override val id: String = "editor.find.next"
|
||||
|
||||
constructor(context: Context) : this() {
|
||||
label = context.getString(R.string.next)
|
||||
icon = ContextCompat.getDrawable(context, R.drawable.ic_search_next)
|
||||
}
|
||||
|
||||
override fun execAction(data: ActionData): Any {
|
||||
val editor = getEditor(data)!!
|
||||
if (!editor.searcher.hasQuery()) {
|
||||
return false
|
||||
}
|
||||
|
||||
return editor.searcher.gotoNext()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.androidide.actions.editor.find
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.itsaky.androidide.R
|
||||
import com.itsaky.androidide.R.string
|
||||
import com.itsaky.androidide.actions.ActionData
|
||||
|
||||
/**
|
||||
* 'Go to previous' action in editor's search action mode.
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class GoToPreviousAction() : SearchActionModeAction() {
|
||||
override val id: String = "editor.find.previous"
|
||||
|
||||
constructor(context: Context) : this() {
|
||||
label = context.getString(string.previous)
|
||||
icon = ContextCompat.getDrawable(context, R.drawable.ic_search_previous)
|
||||
}
|
||||
|
||||
override fun execAction(data: ActionData): Any {
|
||||
val editor = getEditor(data)!!
|
||||
if (!editor.searcher.hasQuery()) {
|
||||
return false
|
||||
}
|
||||
|
||||
return editor.searcher.gotoPrevious()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.androidide.actions.editor.find
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.itsaky.androidide.R
|
||||
import com.itsaky.androidide.actions.ActionData
|
||||
import com.itsaky.androidide.databinding.LayoutEditorFindReplaceBinding
|
||||
import com.itsaky.androidide.utils.DialogUtils
|
||||
import com.itsaky.androidide.utils.Logger
|
||||
|
||||
/**
|
||||
* Shows dialog to replace one or all the matched text in editor searcher.
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class ReplaceAction() : SearchActionModeAction() {
|
||||
|
||||
override val id: String = "editor.search.replace"
|
||||
private val log = Logger.newInstance(javaClass.simpleName)
|
||||
|
||||
constructor(context: Context) : this() {
|
||||
label = context.getString(R.string.replace)
|
||||
icon = ContextCompat.getDrawable(context, R.drawable.ic_search_replace)
|
||||
}
|
||||
|
||||
override fun execAction(data: ActionData): Any {
|
||||
val context = data[Context::class.java]!!
|
||||
val editor = getEditor(data)!!
|
||||
|
||||
if (!editor.searcher.hasQuery()) {
|
||||
return false
|
||||
}
|
||||
|
||||
val binding = LayoutEditorFindReplaceBinding.inflate(LayoutInflater.from(context))
|
||||
val builder = DialogUtils.newMaterialDialogBuilder(context)
|
||||
builder.setTitle(R.string.replace)
|
||||
builder.setView(binding.root)
|
||||
builder.setNegativeButton(android.R.string.cancel, null)
|
||||
builder.setPositiveButton(R.string.replace) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
val input = binding.replacementInput.editText
|
||||
if (input == null) {
|
||||
log.error("Unable to perform replace action. Input field is null")
|
||||
return@setPositiveButton
|
||||
}
|
||||
|
||||
editor.searcher.replaceThis(input.text.toString())
|
||||
}
|
||||
builder.setNeutralButton(R.string.replaceAll) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
val input = binding.replacementInput.editText
|
||||
if (input == null) {
|
||||
log.error("Unable to perform replace action. Input field is null")
|
||||
return@setNeutralButton
|
||||
}
|
||||
|
||||
editor.searcher.replaceAllAsync(input.text.toString()).whenComplete { _, error ->
|
||||
if (error != null) {
|
||||
log.error("Unable to replace all matched text", error)
|
||||
} else {
|
||||
editor.post { data[ActionMode::class.java]?.finish() }
|
||||
}
|
||||
}
|
||||
}
|
||||
builder.show()
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.androidide.actions.editor.find
|
||||
|
||||
import com.itsaky.androidide.actions.ActionItem
|
||||
import com.itsaky.androidide.actions.ActionItem.Location
|
||||
import com.itsaky.androidide.actions.EditorRelatedAction
|
||||
|
||||
/** @author Akash Yadav */
|
||||
abstract class SearchActionModeAction : EditorRelatedAction() {
|
||||
override var location: Location = ActionItem.Location.EDITOR_SEARCH_ACTION_MODE
|
||||
}
|
||||
@@ -45,6 +45,9 @@ import com.itsaky.androidide.actions.editor.CutAction
|
||||
import com.itsaky.androidide.actions.editor.ExpandSelectionAction
|
||||
import com.itsaky.androidide.actions.editor.PasteAction
|
||||
import com.itsaky.androidide.actions.editor.SelectAllAction
|
||||
import com.itsaky.androidide.actions.editor.find.GoToNextAction
|
||||
import com.itsaky.androidide.actions.editor.find.GoToPreviousAction
|
||||
import com.itsaky.androidide.actions.editor.find.ReplaceAction
|
||||
import com.itsaky.androidide.actions.etc.DaemonStatusAction
|
||||
import com.itsaky.androidide.actions.etc.FileTreeAction
|
||||
import com.itsaky.androidide.actions.etc.FindActionMenu
|
||||
@@ -84,6 +87,11 @@ class EditorActivityActions {
|
||||
registry.registerAction(CopyAction(context))
|
||||
registry.registerAction(PasteAction(context))
|
||||
registry.registerAction(FormatCodeAction(context))
|
||||
|
||||
// editor search actions
|
||||
registry.registerAction(GoToNextAction(context))
|
||||
registry.registerAction(GoToPreviousAction(context))
|
||||
registry.registerAction(ReplaceAction(context))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.view.inputmethod.EditorInfo;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.blankj.utilcode.util.ThreadUtils;
|
||||
import com.itsaky.androidide.R;
|
||||
@@ -34,10 +35,8 @@ import com.itsaky.androidide.language.IDELanguage;
|
||||
import com.itsaky.androidide.lsp.IDELanguageClientImpl;
|
||||
import com.itsaky.androidide.managers.PreferenceManager;
|
||||
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
|
||||
import com.itsaky.androidide.utils.DialogUtils;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import com.itsaky.lsp.api.ILanguageServer;
|
||||
import com.itsaky.lsp.models.CodeActionItem;
|
||||
import com.itsaky.lsp.models.Command;
|
||||
import com.itsaky.lsp.models.DefinitionResult;
|
||||
import com.itsaky.lsp.models.DiagnosticItem;
|
||||
@@ -65,6 +64,7 @@ import io.github.rosemoe.sora.event.ContentChangeEvent;
|
||||
import io.github.rosemoe.sora.event.SelectionChangeEvent;
|
||||
import io.github.rosemoe.sora.event.Unsubscribe;
|
||||
import io.github.rosemoe.sora.widget.CodeEditor;
|
||||
import io.github.rosemoe.sora.widget.IDEEditorSearcher;
|
||||
import io.github.rosemoe.sora.widget.component.EditorAutoCompletion;
|
||||
import io.github.rosemoe.sora.widget.component.EditorTextActionWindow;
|
||||
|
||||
@@ -73,6 +73,7 @@ public class IDEEditor extends CodeEditor {
|
||||
public static final String KEY_FILE = "editor_file";
|
||||
private static final Logger LOG = Logger.newInstance("IDEEditor");
|
||||
private final EditorActionsMenu mActionsPopup;
|
||||
private IDEEditorSearcher mSearcher;
|
||||
private int mFileVersion;
|
||||
private File file;
|
||||
private ILanguageServer mLanguageServer;
|
||||
@@ -99,6 +100,7 @@ public class IDEEditor extends CodeEditor {
|
||||
mActionsPopup.init();
|
||||
|
||||
setColorScheme(new SchemeAndroidIDE());
|
||||
setSearcher(new IDEEditorSearcher(this));
|
||||
getComponent(EditorTextActionWindow.class).setEnabled(false);
|
||||
subscribeEvent(SelectionChangeEvent.class, this::handleSelectionChange);
|
||||
subscribeEvent(ContentChangeEvent.class, this::handleContentChange);
|
||||
@@ -857,15 +859,23 @@ public class IDEEditor extends CodeEditor {
|
||||
|
||||
return new Range(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the language client to perform the given code action item.
|
||||
*
|
||||
* @param action The action to perform.
|
||||
*/
|
||||
public void performCodeAction(CodeActionItem action) {
|
||||
if (mLanguageClient != null) {
|
||||
mLanguageClient.performCodeAction(this, action);
|
||||
|
||||
@Override
|
||||
public IDEEditorSearcher getSearcher() {
|
||||
return mSearcher;
|
||||
}
|
||||
|
||||
protected void setSearcher(@NonNull IDEEditorSearcher searcher) {
|
||||
mSearcher = searcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginSearchMode() {
|
||||
final var callback = new SearchActionMode(this);
|
||||
if (getContext() instanceof AppCompatActivity) {
|
||||
startActionMode (callback);
|
||||
} else {
|
||||
LOG.error("Unable start search action mode. Activity must inherit AppCompatActivity.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.androidide.views.editor
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.text.TextUtils
|
||||
import android.view.ActionMode
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.view.menu.MenuBuilder
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import com.itsaky.androidide.actions.ActionData
|
||||
import com.itsaky.androidide.actions.ActionItem
|
||||
import com.itsaky.androidide.actions.ActionsRegistry
|
||||
import io.github.rosemoe.sora.R
|
||||
import io.github.rosemoe.sora.widget.CodeEditor
|
||||
import io.github.rosemoe.sora.widget.EditorSearcher.SearchOptions
|
||||
|
||||
/**
|
||||
* Action mode for find and replace functions in editor.
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class SearchActionMode(val editor: IDEEditor) : ActionMode.Callback {
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
||||
|
||||
if (mode == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (menu is MenuBuilder) {
|
||||
menu.setOptionalIconsVisible(true)
|
||||
}
|
||||
|
||||
val search = SearchView(editor.context)
|
||||
search.setOnQueryTextListener(OnQueryListener(editor))
|
||||
search.isIconified = false
|
||||
search.queryHint = editor.context.getString(R.string.text_to_search)
|
||||
search.performClick()
|
||||
search.setIconifiedByDefault(false)
|
||||
mode.customView = search
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
||||
if (mode == null || menu == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
menu.clear()
|
||||
val registry = ActionsRegistry.getInstance()
|
||||
registry.fillMenu(createData(mode), ActionItem.Location.EDITOR_SEARCH_ACTION_MODE, menu)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDestroyActionMode(mode: ActionMode?) {
|
||||
editor.searcher.stopSearch()
|
||||
}
|
||||
|
||||
private fun createData(mode: ActionMode): ActionData {
|
||||
return ActionData().apply {
|
||||
put(Context::class.java, editor.context)
|
||||
put(IDEEditor::class.java, editor)
|
||||
put(CodeEditor::class.java, editor)
|
||||
put(ActionMode::class.java, mode)
|
||||
}
|
||||
}
|
||||
|
||||
private class OnQueryListener(val editor: IDEEditor) : SearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||
editor.searcher.gotoNext()
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String?): Boolean {
|
||||
if (TextUtils.isEmpty(newText)) {
|
||||
editor.searcher.stopSearch()
|
||||
return false
|
||||
}
|
||||
|
||||
editor.searcher.search(newText!!, SearchOptions(false, false))
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package io.github.rosemoe.sora.widget
|
||||
|
||||
import android.app.ProgressDialog
|
||||
import android.widget.Toast
|
||||
import com.itsaky.androidide.R.string
|
||||
import com.itsaky.androidide.utils.Logger
|
||||
import com.itsaky.androidide.views.editor.IDEEditor
|
||||
import io.github.rosemoe.sora.text.TextUtils
|
||||
import io.github.rosemoe.sora.util.IntPair
|
||||
import java.util.concurrent.*
|
||||
|
||||
/**
|
||||
* Search text in editor. As the constructor of [EditorSearcher] is package private, we cannot
|
||||
* extend it in another package. So we put this class in the same package.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
open class IDEEditorSearcher(editor: IDEEditor) : EditorSearcher(editor) {
|
||||
|
||||
private val log = Logger.newInstance(javaClass.simpleName)
|
||||
|
||||
protected fun getEditor(): CodeEditor {
|
||||
try {
|
||||
val field = EditorSearcher::class.java.getDeclaredField("mEditor")
|
||||
field.isAccessible = true
|
||||
return field.get(this) as CodeEditor
|
||||
} catch (error: Throwable) {
|
||||
throw RuntimeException("Unable get instance of editor", error)
|
||||
}
|
||||
}
|
||||
|
||||
fun replaceAllAsync(replacement: String): CompletableFuture<Void> {
|
||||
val dialog =
|
||||
ProgressDialog.show(
|
||||
getEditor().context,
|
||||
getEditor().context.getString(string.replaceAll),
|
||||
getEditor().context.getString(string.msg_replacing),
|
||||
true,
|
||||
false)
|
||||
return CompletableFuture.runAsync { replaceAll(replacement) }.thenAccept {
|
||||
getEditor().post { dialog.dismiss() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun replaceAll(replacement: String) {
|
||||
checkState()
|
||||
if (!isResultValid) {
|
||||
Toast.makeText(getEditor().context, "Editor is still preparing", Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
return
|
||||
}
|
||||
|
||||
val res = mLastResults
|
||||
try {
|
||||
val sb = getEditor().text.toStringBuilder()
|
||||
val newLength = replacement.length
|
||||
if (mOptions.useRegex) {
|
||||
var delta = 0
|
||||
for (i in 0 until res.size()) {
|
||||
val region = res[i]
|
||||
val start = IntPair.getFirst(region)
|
||||
val end = IntPair.getSecond(region)
|
||||
val oldLength = end - start
|
||||
sb.replace(start + delta, end + delta, replacement)
|
||||
delta += newLength - oldLength
|
||||
}
|
||||
} else {
|
||||
var fromIndex = 0
|
||||
var foundIndex: Int
|
||||
while (TextUtils.indexOf(sb, mPattern, mOptions.ignoreCase, fromIndex).also {
|
||||
foundIndex = it
|
||||
} != -1) {
|
||||
sb.replace(foundIndex, foundIndex + mPattern.length, replacement)
|
||||
fromIndex = foundIndex + newLength
|
||||
}
|
||||
}
|
||||
getEditor().post {
|
||||
val pos = getEditor().cursor.left()
|
||||
// stopSearch();
|
||||
getEditor()
|
||||
.text
|
||||
.replace(
|
||||
0,
|
||||
0,
|
||||
getEditor().lineCount - 1,
|
||||
getEditor().text.getColumnCount(getEditor().lineCount - 1),
|
||||
sb)
|
||||
getEditor().setSelectionAround(pos.line, pos.column)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
log.error("Failed to replace", e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkState() {
|
||||
check(hasQuery()) { "pattern not set" }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M20,12l-1.41,-1.41L13,16.17V4h-2v12.17l-5.58,-5.59L4,12l8,8 8,-8z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M4,12l1.41,1.41L11,7.83V20h2V7.83l5.58,5.59L20,12l-8,-8 -8,8z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M11,6c1.38,0 2.63,0.56 3.54,1.46L12,10h6L18,4l-2.05,2.05C14.68,4.78 12.93,4 11,4c-3.53,0 -6.43,2.61 -6.92,6L6.1,10c0.46,-2.28 2.48,-4 4.9,-4zM16.64,15.14c0.66,-0.9 1.12,-1.97 1.28,-3.14L15.9,12c-0.46,2.28 -2.48,4 -4.9,4 -1.38,0 -2.63,-0.56 -3.54,-1.46L10,12L4,12v6l2.05,-2.05C7.32,17.22 9.07,18 11,18c1.55,0 2.98,-0.51 4.14,-1.36L20,21.49 21.49,20l-4.85,-4.86z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ 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/>.
|
||||
-->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/replacement_input"
|
||||
style="@style/AppTheme.TextInputLayout.Outlined"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/replacement"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:startIconDrawable="@drawable/ic_search_replace"
|
||||
app:startIconTint="@color/primaryIconColor">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -349,6 +349,10 @@
|
||||
<string name="idepref_visiblePassword_title">Visible password flag</string>
|
||||
<string name="idepref_visiblePassword_summary">Use for visible password input type flag in editor. This makes sure that no suggestions are proposed by soft keyboard.</string>
|
||||
<string name="msg_no_actions">No code actions found</string>
|
||||
<string name="previous">Previous</string>
|
||||
<string name="msg_editor_find_case_sensitive">Case sensitive</string>
|
||||
<string name="msg_editor_find_regex">Regex</string>
|
||||
<string name="msg_replacing">Replacing...</string>
|
||||
</resources>
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user