mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
v1.0.3-alpha
- Implemented
• Find all references
• Comment/Uncomment line
• Find in file
• Find in project
This commit is contained in:
Binary file not shown.
@@ -4,7 +4,6 @@ import abhishekti7.unicorn.filepicker.UnicornFilePicker;
|
||||
import android.content.Intent;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
@@ -25,9 +24,6 @@ import com.itsaky.toaster.Toaster;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
public class DownloadActivity extends StudioActivity {
|
||||
|
||||
@@ -88,7 +84,7 @@ public class DownloadActivity extends StudioActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private final String DONE = "DONE";
|
||||
private final String DONE = "'Installed successfully.'";
|
||||
private void installAll() {
|
||||
showProgress();
|
||||
getApp().getPrefManager().putBoolean(PreferenceManager.KEY_FRAMEWORK_DOWNLOADED, true);
|
||||
@@ -106,7 +102,8 @@ public class DownloadActivity extends StudioActivity {
|
||||
}
|
||||
}
|
||||
sb.append("rm -rf '" + getApp().getToolsDownloadDirectory().getAbsolutePath() + "' && ");
|
||||
sb.append("echo " + DONE);
|
||||
sb.append("echo 'Cleaning unsupported flags in binaries...' && $BUSYBOX find $JAVA_HOME/bin -type f -exec androidide-cleaner {} \\; && ");
|
||||
sb.append("echo " + DONE);
|
||||
server.bgAppend(sb.toString());
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ import com.itsaky.androidide.databinding.LayoutDialogTextInputBinding;
|
||||
import com.itsaky.androidide.fragments.EditorFragment;
|
||||
import com.itsaky.androidide.fragments.FileTreeFragment;
|
||||
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.interfaces.DiagnosticClickListener;
|
||||
import com.itsaky.androidide.interfaces.JLSRequestor;
|
||||
@@ -59,6 +60,7 @@ import com.itsaky.androidide.language.logs.LogLanguageImpl;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
import com.itsaky.androidide.models.DiagnosticGroup;
|
||||
import com.itsaky.androidide.models.LogLine;
|
||||
import com.itsaky.androidide.models.SearchResult;
|
||||
import com.itsaky.androidide.models.SheetOption;
|
||||
import com.itsaky.androidide.receivers.LogReceiver;
|
||||
import com.itsaky.androidide.services.IDEService;
|
||||
@@ -102,10 +104,10 @@ import com.itsaky.lsp.TextDocumentPositionParams;
|
||||
import com.itsaky.toaster.Toaster;
|
||||
import com.unnamed.b.atv.model.TreeNode;
|
||||
import io.github.rosemoe.editor.langs.EmptyLanguage;
|
||||
import io.github.rosemoe.editor.text.Content;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -113,6 +115,11 @@ import java.util.Stack;
|
||||
import java.util.regex.Pattern;
|
||||
import me.piruin.quickaction.ActionItem;
|
||||
import me.piruin.quickaction.QuickAction;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import com.itsaky.androidide.databinding.LayoutSearchProjectBinding;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.LinearLayout;
|
||||
import com.itsaky.androidide.utils.RecursiveFileSearcher;
|
||||
|
||||
public class EditorActivity extends StudioActivity implements FileTreeFragment.FileActionListener,
|
||||
IDEService.BuildListener,
|
||||
@@ -141,6 +148,8 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
private QuickAction mTabCloseAction;
|
||||
private TextSheetFragment mDaemonStatusFragment;
|
||||
private OptionsListFragment mFileOptionsFragment;
|
||||
private ProgressSheet mSearchingProgress;
|
||||
private AlertDialog mFindInProjectDialog;
|
||||
|
||||
private boolean recreateCompletionServices = false;
|
||||
|
||||
@@ -256,21 +265,10 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
}
|
||||
|
||||
private void setupContainers() {
|
||||
if(getBuildView().getParent() != null && getBuildView().getParent() instanceof ViewGroup) {
|
||||
((ViewGroup) getBuildView().getParent()).removeView(getBuildView());
|
||||
}
|
||||
|
||||
if(getLogView().getParent() != null && getLogView().getParent() instanceof ViewGroup) {
|
||||
((ViewGroup) getLogView().getParent()).removeView(getLogView());
|
||||
}
|
||||
|
||||
if(getDiagnosticsList().getParent() != null && getDiagnosticsList().getParent() instanceof ViewGroup) {
|
||||
((ViewGroup) getDiagnosticsList().getParent()).removeView(getDiagnosticsList());
|
||||
}
|
||||
|
||||
if(getSearchResultList().getParent() != null && getSearchResultList().getParent() instanceof ViewGroup) {
|
||||
((ViewGroup) getSearchResultList().getParent()).removeView(getSearchResultList());
|
||||
}
|
||||
removeFromParent(getBuildView());
|
||||
removeFromParent(getLogView());
|
||||
removeFromParent(getDiagnosticsList());
|
||||
removeFromParent(getSearchResultList());
|
||||
|
||||
mBinding.buildOutcontainer.addView(getBuildView(), new ViewGroup.LayoutParams(-1, -1));
|
||||
mBinding.buildContainer.setVisibility(View.GONE);
|
||||
@@ -303,6 +301,12 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
mBinding.viewFiles.setOnClickListener(v -> showFiles());
|
||||
mBinding.viewDaemonStatus.setOnClickListener(v -> showDaemonStatus());
|
||||
}
|
||||
|
||||
private void removeFromParent(View v) {
|
||||
if(v.getParent() != null && v.getParent() instanceof ViewGroup) {
|
||||
((ViewGroup) v.getParent()).removeView(v);
|
||||
}
|
||||
}
|
||||
|
||||
private void onSoftInputChanged() {
|
||||
invalidateOptionsMenu();
|
||||
@@ -388,14 +392,21 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
if (item != null) {
|
||||
boolean enabled = getBuildService() != null && !getBuildService().isBuilding();
|
||||
item.setEnabled(enabled);
|
||||
item.getIcon().setAlpha(enabled ? 255 : 130);
|
||||
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);
|
||||
|
||||
if(KeyboardUtils.isSoftInputVisible(this)) {
|
||||
run1.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
|
||||
run2.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
|
||||
@@ -408,25 +419,28 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
redo.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
|
||||
}
|
||||
|
||||
if(mPagerAdapter != null && mPagerAdapter.getCount() <= 0) {
|
||||
undo.setEnabled(false);
|
||||
redo.setEnabled(false);
|
||||
save.setEnabled(false);
|
||||
|
||||
final int alpha = 76;
|
||||
undo.getIcon().setAlpha(alpha);
|
||||
redo.getIcon().setAlpha(alpha);
|
||||
save.getIcon().setAlpha(alpha);
|
||||
} else {
|
||||
undo.setEnabled(true);
|
||||
redo.setEnabled(true);
|
||||
save.setEnabled(true);
|
||||
|
||||
final int alpha = 255;
|
||||
undo.getIcon().setAlpha(alpha);
|
||||
redo.getIcon().setAlpha(alpha);
|
||||
save.getIcon().setAlpha(alpha);
|
||||
}
|
||||
final boolean enabled1 = mCurrentFile != null;
|
||||
final boolean enabled2 = enabled1 && mCurrentFile.getName().endsWith(".java");
|
||||
final int alpha1 = enabled1 ? 255 : 76;
|
||||
final int alpha2 = enabled2 ? 255 : 76;
|
||||
|
||||
undo.setEnabled(enabled1);
|
||||
redo.setEnabled(enabled1);
|
||||
save.setEnabled(enabled1);
|
||||
comment.setEnabled(enabled1);
|
||||
uncomment.setEnabled(enabled1);
|
||||
findFile.setEnabled(enabled1);
|
||||
def.setEnabled(enabled2);
|
||||
ref.setEnabled(enabled2);
|
||||
|
||||
undo.getIcon().setAlpha(alpha1);
|
||||
redo.getIcon().setAlpha(alpha1);
|
||||
save.getIcon().setAlpha(alpha1);
|
||||
comment.getIcon().setAlpha(alpha1);
|
||||
uncomment.getIcon().setAlpha(alpha1);
|
||||
findFile.getIcon().setAlpha(alpha1);
|
||||
def.getIcon().setAlpha(alpha2);
|
||||
ref.getIcon().setAlpha(alpha2);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -467,10 +481,113 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
this.mCurrentFragment.undo();
|
||||
} else if (id == R.id.menuEditor_redo && this.mCurrentFragment != null && this.mCurrentFragment.isVisible()) {
|
||||
this.mCurrentFragment.redo();
|
||||
}
|
||||
} else if(id == R.id.menuEditor_gotoDefinition && mCurrentFragment != null && this.mCurrentFragment.isVisible()) {
|
||||
this.mCurrentFragment.findDefinition();
|
||||
} else if(id == R.id.menuEditor_findReferences && mCurrentFragment != null && this.mCurrentFragment.isVisible()) {
|
||||
this.mCurrentFragment.findReferences();
|
||||
} else if(id == R.id.menuEditor_commentLine && mCurrentFragment != null && this.mCurrentFragment.isVisible()) {
|
||||
this.mCurrentFragment.commentLine();
|
||||
} else if(id == R.id.menuEditor_uncommentLine && mCurrentFragment != null && this.mCurrentFragment.isVisible()) {
|
||||
this.mCurrentFragment.uncommentLine();
|
||||
} else if(id == R.id.menuEditor_findFile && mCurrentFragment != null && this.mCurrentFragment.isVisible()) {
|
||||
this.mCurrentFragment.beginSearch();
|
||||
} else if(id == R.id.menuEditor_findProject) {
|
||||
AlertDialog d = getFindInProjectDialog();
|
||||
if(d != null) d.show();
|
||||
}
|
||||
invalidateOptionsMenu();
|
||||
return true;
|
||||
}
|
||||
|
||||
private AlertDialog getFindInProjectDialog() {
|
||||
return mFindInProjectDialog == null ? createFindInProjectDialog() : mFindInProjectDialog;
|
||||
}
|
||||
|
||||
private AlertDialog createFindInProjectDialog() {
|
||||
if(mProject == null
|
||||
|| mProject.getModulePaths() == null
|
||||
|| mProject.getModulePaths().size() <= 0) {
|
||||
getApp().toast(R.string.msg_no_modules, Toaster.Type.ERROR);
|
||||
return null;
|
||||
}
|
||||
final List<String> modules = mProject.getModulePaths();
|
||||
final List<File> srcDirs = new ArrayList<>();
|
||||
final LayoutSearchProjectBinding binding = LayoutSearchProjectBinding.inflate(getLayoutInflater());
|
||||
binding.modulesContainer.removeAllViews();
|
||||
for(int i=0;i<modules.size();i++) {
|
||||
final File file = new File(modules.get(i));
|
||||
final File src = new File(file, "src");
|
||||
if(!file.exists() || !file.isDirectory() || !src.exists() || !src.isDirectory()) continue;
|
||||
|
||||
CheckBox check = new CheckBox(this);
|
||||
check.setText(file.getName());
|
||||
check.setChecked(true);
|
||||
|
||||
LinearLayout.MarginLayoutParams params = new LinearLayout.MarginLayoutParams(-2, -2);
|
||||
params.bottomMargin = SizeUtils.dp2px(4);
|
||||
binding.modulesContainer.addView(check, params);
|
||||
|
||||
srcDirs.add(src);
|
||||
}
|
||||
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this, R.style.AppTheme_MaterialAlertDialog);
|
||||
builder.setTitle(R.string.menu_find_project);
|
||||
builder.setView(binding.getRoot());
|
||||
builder.setPositiveButton(R.string.menu_find, (dialog, which) -> {
|
||||
final String text = binding.input.getEditText().getText().toString().trim();
|
||||
if(text == null || text.isEmpty()) {
|
||||
getApp().toast(R.string.msg_empty_search_query, Toaster.Type.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
final List<File> searchDirs = new ArrayList<>();
|
||||
for(int i=0;i<binding.modulesContainer.getChildCount();i++) {
|
||||
CheckBox check = (CheckBox) binding.modulesContainer.getChildAt(i);
|
||||
if(check.isChecked())
|
||||
searchDirs.add(srcDirs.get(i));
|
||||
}
|
||||
|
||||
final String extensions = binding.filter.getEditText().getText().toString().trim();
|
||||
final List<String> exts = new ArrayList<>();
|
||||
if(extensions != null && !extensions.isEmpty()) {
|
||||
if(extensions.contains("|")) {
|
||||
for(String str : extensions.split(Pattern.quote("|"))) {
|
||||
if(str == null || str.trim().isEmpty())
|
||||
continue;
|
||||
|
||||
exts.add(str);
|
||||
}
|
||||
} else
|
||||
exts.add(extensions);
|
||||
}
|
||||
|
||||
if(searchDirs == null || searchDirs.isEmpty()) {
|
||||
getApp().toast(R.string.msg_select_search_modules, Toaster.Type.ERROR);
|
||||
} else {
|
||||
dialog.dismiss();
|
||||
getProgressSheet(R.string.msg_searching_project).show(getSupportFragmentManager(), "search_in_project_progress");
|
||||
RecursiveFileSearcher.searchRecursiveAsync(text, exts, searchDirs, result -> handleSearchResults(result));
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(android.R.string.cancel, (__, ___) -> __.dismiss());
|
||||
mFindInProjectDialog = builder.create();
|
||||
return mFindInProjectDialog;
|
||||
}
|
||||
|
||||
private void handleSearchResults(Map<File, List<SearchResult>> results) {
|
||||
getSearchResultList().setAdapter(new SearchListAdapter(results, file -> {
|
||||
openFile(file);
|
||||
hideSearchResults();
|
||||
}, match -> {
|
||||
openFileAndSelect(match.file, match);
|
||||
hideSearchResults();
|
||||
}));
|
||||
mBinding.transformScrim.setVisibility(View.VISIBLE);
|
||||
mBinding.fabView.setVisibility(View.GONE);
|
||||
showSearchResults();
|
||||
|
||||
if(mSearchingProgress != null && mSearchingProgress.isShowing())
|
||||
mSearchingProgress.dismiss();
|
||||
}
|
||||
|
||||
/************************************
|
||||
* Build Related *
|
||||
@@ -681,7 +798,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
|
||||
private void showSearchResults() {
|
||||
TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.viewSearchResults, mBinding.searchResultsContainer));
|
||||
mBinding.diagContainer.setVisibility(View.VISIBLE);
|
||||
mBinding.searchResultsContainer.setVisibility(View.VISIBLE);
|
||||
mBinding.viewOptionsCard.setVisibility(View.GONE);
|
||||
hideToolbarAndTabs();
|
||||
}
|
||||
@@ -689,7 +806,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
private void hideSearchResults() {
|
||||
showToolbarAndTabs();
|
||||
TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.searchResultsContainer, mBinding.viewSearchResults));
|
||||
mBinding.diagContainer.setVisibility(View.GONE);
|
||||
mBinding.searchResultsContainer.setVisibility(View.GONE);
|
||||
mBinding.viewOptionsCard.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@@ -772,7 +889,9 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDiagnosticClick(Diagnostic diagnostic) {
|
||||
public void onDiagnosticClick(File file, Diagnostic diagnostic) {
|
||||
openFileAndSelect(file, diagnostic.range);
|
||||
hideDiagnostics();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -954,10 +1073,13 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
final JavaLanguageServer server = getApp().getJavaLanguageServer();
|
||||
if(server == null) return;
|
||||
server.findDefinition(params);
|
||||
getProgressSheet(R.string.msg_finding_definition).show(getSupportFragmentManager(), "definition_progress");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gotoDefinition(List<Location> locations) {
|
||||
if(mSearchingProgress != null && mSearchingProgress.isShowing())
|
||||
mSearchingProgress.dismiss();
|
||||
if(locations == null || locations.size() <= 0) {
|
||||
getApp().toast(R.string.msg_no_definition, Toaster.Type.ERROR);
|
||||
return;
|
||||
@@ -977,42 +1099,89 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
&& frag.getEditor() != null
|
||||
&& frag.getFile().getAbsolutePath().equals(file.getAbsolutePath())) {
|
||||
if(range.start.equals(range.end)) {
|
||||
frag.getEditor().setSelection(range.start.line, range.start.character);
|
||||
frag.getEditor().setSelection(range.start.line, range.start.column);
|
||||
} else {
|
||||
frag.getEditor().setSelectionRegion(range.start.line, range.start.character, range.end.line, range.end.character);
|
||||
frag.getEditor().setSelectionRegion(range.start.line, range.start.column, range.end.line, range.end.column);
|
||||
}
|
||||
} else {
|
||||
openFile(file, range);
|
||||
EditorFragment opened = mPagerAdapter.findEditorByFile(file);
|
||||
if(opened != null && opened.getEditor() != null) {
|
||||
CodeEditor editor = opened.getEditor();
|
||||
editor.post(() -> {
|
||||
if(range.start.equals(range.end)) {
|
||||
editor.setSelection(range.start.line, range.start.character);
|
||||
} else {
|
||||
editor.setSelectionRegion(range.start.line, range.start.character, range.end.line, range.end.character);
|
||||
}
|
||||
});
|
||||
}
|
||||
openFileAndSelect(file, range);
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Logger.instance().e(ThrowableUtils.getFullStackTrace(th));
|
||||
}
|
||||
}
|
||||
|
||||
private void openFileAndSelect(File file, Range range) {
|
||||
openFile(file, range);
|
||||
EditorFragment opened = mPagerAdapter.findEditorByFile(file);
|
||||
if (opened != null && opened.getEditor() != null) {
|
||||
CodeEditor editor = opened.getEditor();
|
||||
editor.post(() -> {
|
||||
if (range.start.equals(range.end)) {
|
||||
editor.setSelection(range.start.line, range.start.column);
|
||||
} else {
|
||||
editor.setSelectionRegion(range.start.line, range.start.column, range.end.line, range.end.column);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findReferences(ReferenceParams params) {
|
||||
final JavaLanguageServer server = getApp().getJavaLanguageServer();
|
||||
if(server == null) return;
|
||||
server.findReferences(params);
|
||||
getProgressSheet(R.string.msg_finding_references).show(getSupportFragmentManager(), "references_progress");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void references(List<Location> references) {
|
||||
if(references == null || references.size() <= 0) {
|
||||
if(mSearchingProgress != null && mSearchingProgress.isShowing())
|
||||
mSearchingProgress.dismiss();
|
||||
if(references == null) {
|
||||
getApp().toast(R.string.msg_no_references, Toaster.Type.INFO);
|
||||
return;
|
||||
}
|
||||
|
||||
if(references.size() <= 0) {
|
||||
getApp().toast(R.string.msg_no_references, Toaster.Type.INFO);
|
||||
getSearchResultList().setAdapter(new SearchListAdapter(null, null, null));
|
||||
return;
|
||||
}
|
||||
|
||||
final Map<File, List<SearchResult>> results = new HashMap<>();
|
||||
for(int i=0;i<references.size();i++) {
|
||||
try {
|
||||
final Location loc = references.get(i);
|
||||
if(loc == null || loc.uri == null || loc.range == null) continue;
|
||||
final File file = new File(loc.uri);
|
||||
if(!file.exists() || !file.isFile()) continue;
|
||||
EditorFragment frag = mPagerAdapter.findEditorByFile(file);
|
||||
Content content;
|
||||
if(frag != null && frag.getEditor() != null)
|
||||
content = frag.getEditor().getText();
|
||||
else content = new Content(null, FileIOUtils.readFile2String(file));
|
||||
final List<SearchResult> matches = results.containsKey(file) ? results.get(file) : new ArrayList<>();
|
||||
matches.add(
|
||||
new SearchResult(
|
||||
loc.range,
|
||||
file,
|
||||
content.getLineString(loc.range.start.line),
|
||||
content.subContent(
|
||||
loc.range.start.line,
|
||||
loc.range.start.column,
|
||||
loc.range.end.line,
|
||||
loc.range.end.column
|
||||
).toString()
|
||||
)
|
||||
);
|
||||
results.put(file, matches);
|
||||
} catch (Throwable th) {
|
||||
Logger.instance().e(ThrowableUtils.getFullStackTrace(th));
|
||||
}
|
||||
}
|
||||
|
||||
handleSearchResults(results);
|
||||
}
|
||||
|
||||
private void addPendingMessage(Message msg) {
|
||||
@@ -1052,9 +1221,11 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
EditorFragment current = mPagerAdapter.getFrag(p1.getPosition());
|
||||
if(current != null && current.getFile() != null) {
|
||||
this.mCurrentFragment = current;
|
||||
mCurrentFile = current.getFile();
|
||||
this.mCurrentFile = current.getFile();
|
||||
refreshSymbolInput(current);
|
||||
}
|
||||
|
||||
invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1102,9 +1273,10 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
// ---- Fragment Related
|
||||
// -----------------------------
|
||||
// -----------------------------
|
||||
// ---- Fragment Related
|
||||
// -------------------------------
|
||||
|
||||
|
||||
public void loadFragment(Fragment fragment) {
|
||||
super.loadFragment(fragment, mBinding.editorFrameLayout.getId());
|
||||
@@ -1122,6 +1294,18 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
mFileOptionsFragment.setOnOptionsClickListener(o -> handleOptionClick(o));
|
||||
return mFileOptionsFragment;
|
||||
}
|
||||
|
||||
private ProgressSheet getProgressSheet(int msg) {
|
||||
if(mSearchingProgress != null && mSearchingProgress.isShowing()) {
|
||||
mSearchingProgress.dismiss();
|
||||
}
|
||||
mSearchingProgress = new ProgressSheet();
|
||||
mSearchingProgress.setCancelable(false);
|
||||
mSearchingProgress.setWelcomeTextEnabled(false);
|
||||
mSearchingProgress.setMessage(getString(msg));
|
||||
mSearchingProgress.setSubMessageEnabled(false);
|
||||
return mSearchingProgress;
|
||||
}
|
||||
|
||||
private MaterialBanner getSyncBanner() {
|
||||
return mBinding.syncBanner.setContentTextColor(ContextCompat.getColor(this, R.color.primaryTextColor))
|
||||
@@ -1209,7 +1393,6 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
private RecyclerView createSearchResultList() {
|
||||
searchResultList = new RecyclerView(this);
|
||||
searchResultList.setLayoutManager(new LinearLayoutManager(this));
|
||||
searchResultList.setAdapter(new SearchListAdapter());
|
||||
return searchResultList;
|
||||
}
|
||||
|
||||
@@ -1602,12 +1785,14 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
}
|
||||
|
||||
private void closeOthers() {
|
||||
final Fragment frag = mPagerAdapter.getFragments().get(mBinding.tabs.getSelectedTabPosition());
|
||||
final EditorFragment frag = mPagerAdapter.getFrag(mBinding.tabs.getSelectedTabPosition());
|
||||
final File file = mPagerAdapter.getOpenedFiles().get(mBinding.tabs.getSelectedTabPosition());
|
||||
mPagerAdapter = new EditorPagerAdapter(getSupportFragmentManager(), mProject, Arrays.asList(frag), Arrays.asList(file));
|
||||
try { frag.save(); } catch (Throwable th) { }
|
||||
mPagerAdapter = new EditorPagerAdapter(getSupportFragmentManager(), mProject);
|
||||
mBinding.editorViewPager.setAdapter(mPagerAdapter);
|
||||
mBinding.tabs.setupWithViewPager(mBinding.editorViewPager);
|
||||
mBinding.tabs.addOnTabSelectedListener(this);
|
||||
openFile(file);
|
||||
}
|
||||
|
||||
private void closeFile(int index) {
|
||||
|
||||
@@ -11,14 +11,17 @@ import com.itsaky.androidide.interfaces.DiagnosticClickListener;
|
||||
import com.itsaky.lsp.Diagnostic;
|
||||
import com.itsaky.lsp.DiagnosticSeverity;
|
||||
import java.util.List;
|
||||
import java.io.File;
|
||||
|
||||
public class DiagnosticItemAdpater extends RecyclerView.Adapter<DiagnosticItemAdpater.VH> {
|
||||
|
||||
private List<Diagnostic> diags;
|
||||
private final File file;
|
||||
private DiagnosticClickListener listener;
|
||||
|
||||
public DiagnosticItemAdpater(List<Diagnostic> diags, DiagnosticClickListener listener) {
|
||||
public DiagnosticItemAdpater(List<Diagnostic> diags, File file, DiagnosticClickListener listener) {
|
||||
this.diags = diags;
|
||||
this.file = file;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@@ -38,7 +41,7 @@ public class DiagnosticItemAdpater extends RecyclerView.Adapter<DiagnosticItemAd
|
||||
|
||||
binding.getRoot().setOnClickListener(v -> {
|
||||
if(listener != null) {
|
||||
listener.onDiagnosticClick(diagnostic);
|
||||
listener.onDiagnosticClick(file, diagnostic);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public class DiagnosticsAdapter extends RecyclerView.Adapter<DiagnosticsAdapter.
|
||||
binding.info.title.setText(group.text);
|
||||
binding.info.title.setTextColor(color);
|
||||
binding.diagnostics.setLayoutManager(new LinearLayoutManager(binding.diagnostics.getContext()));
|
||||
binding.diagnostics.setAdapter(new DiagnosticItemAdpater(group.diagnostics, listener));
|
||||
binding.diagnostics.setAdapter(new DiagnosticItemAdpater(group.diagnostics, group.file, listener));
|
||||
|
||||
binding.info.getRoot().setOnClickListener(v -> {
|
||||
if(listener != null) {
|
||||
|
||||
@@ -1,7 +1,133 @@
|
||||
package com.itsaky.androidide.adapters;
|
||||
|
||||
public class SearchListAdapter {
|
||||
import android.graphics.PorterDuff;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.blankj.utilcode.util.ThreadUtils;
|
||||
import com.blankj.utilcode.util.ThrowableUtils;
|
||||
import com.itsaky.androidide.databinding.LayoutSearchResultGroupBinding;
|
||||
import com.itsaky.androidide.databinding.LayoutSearchResultItemBinding;
|
||||
import com.itsaky.androidide.models.SearchResult;
|
||||
import com.itsaky.androidide.syntax.lexer.highlighters.JavaHighlighter;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class SearchListAdapter extends RecyclerView.Adapter<SearchListAdapter.VH> {
|
||||
|
||||
private final Map<File, List<SearchResult>> results;
|
||||
private final List<File> keys;
|
||||
|
||||
private final OnFileClickListener fileListener;
|
||||
private final OnMatchClickListener matchListener;
|
||||
|
||||
public SearchListAdapter(Map<File, List<SearchResult>> results, OnFileClickListener fileListener, OnMatchClickListener matchListener) {
|
||||
if (results == null)
|
||||
results = new HashMap<>();
|
||||
this.results = results;
|
||||
this.keys = new ArrayList<File>(results.keySet());
|
||||
this.fileListener = fileListener;
|
||||
this.matchListener = matchListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchListAdapter.VH onCreateViewHolder(ViewGroup p1, int p2) {
|
||||
return new VH(LayoutSearchResultGroupBinding.inflate(LayoutInflater.from(p1.getContext())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(SearchListAdapter.VH p1, int p2) {
|
||||
final LayoutSearchResultGroupBinding binding = p1.binding;
|
||||
final File file = keys.get(p2);
|
||||
final List<SearchResult> matches = results.get(file);
|
||||
final int color = ContextCompat.getColor(binding.icon.getContext(), com.itsaky.androidide.R.color.secondaryColor);
|
||||
binding.title.setText(file.getName());
|
||||
binding.icon.setImageResource(com.itsaky.androidide.R.drawable.ic_language_java);
|
||||
binding.icon.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
|
||||
binding.items.setLayoutManager(new LinearLayoutManager(binding.items.getContext()));
|
||||
binding.items.setAdapter(new ChildAdapter(matches));
|
||||
|
||||
binding.getRoot().setOnClickListener(v -> {
|
||||
if(fileListener != null)
|
||||
fileListener.onClick(file);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return results.size();
|
||||
}
|
||||
|
||||
public class ChildAdapter extends RecyclerView.Adapter<ChildVH> {
|
||||
|
||||
private final List<SearchResult> matches;
|
||||
|
||||
public ChildAdapter(List<SearchResult> matches) {
|
||||
if (matches == null)
|
||||
matches = new ArrayList<>();
|
||||
this.matches = matches;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchListAdapter.ChildVH onCreateViewHolder(ViewGroup p1, int p2) {
|
||||
return new ChildVH(LayoutSearchResultItemBinding.inflate(LayoutInflater.from(p1.getContext())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(SearchListAdapter.ChildVH p1, int p2) {
|
||||
final SearchResult match = matches.get(p2);
|
||||
final LayoutSearchResultItemBinding binding = p1.binding;
|
||||
new Thread(() -> {
|
||||
try {
|
||||
final SpannableStringBuilder sb = new JavaHighlighter().highlight(match.line, match.match);
|
||||
ThreadUtils.runOnUiThread(() -> binding.text.setText(sb));
|
||||
} catch (Exception e) {
|
||||
Logger.instance().e(ThrowableUtils.getFullStackTrace(e));
|
||||
ThreadUtils.runOnUiThread(() -> binding.text.setText(match.match));
|
||||
}
|
||||
}).start();
|
||||
binding.getRoot().setOnClickListener(v -> {
|
||||
if(matchListener != null) {
|
||||
matchListener.onClick(match);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return matches.size();
|
||||
}
|
||||
}
|
||||
|
||||
public class VH extends RecyclerView.ViewHolder {
|
||||
private LayoutSearchResultGroupBinding binding;
|
||||
public VH(LayoutSearchResultGroupBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
public class ChildVH extends RecyclerView.ViewHolder {
|
||||
private LayoutSearchResultItemBinding binding;
|
||||
public ChildVH(LayoutSearchResultItemBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static interface OnFileClickListener {
|
||||
void onClick(File file);
|
||||
}
|
||||
|
||||
public static interface OnMatchClickListener {
|
||||
void onClick(SearchResult match);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,10 +85,12 @@ public class StudioApp extends MultiDexApplication
|
||||
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||
JavaCharacter.initMap();
|
||||
|
||||
newShell(line -> handleLog(line)).bgAppend("logcat -v threadtime");
|
||||
setupLibsIfNeeded();
|
||||
extractJlsIfNeeded();
|
||||
extractLogsenderIfNeeded();
|
||||
extractCleanerIfNeeded();
|
||||
|
||||
newShell(line -> handleLog(line)).bgAppend("androidide-cleaner && logcat -v threadtime");
|
||||
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
createNotificationChannels();
|
||||
@@ -97,6 +99,20 @@ public class StudioApp extends MultiDexApplication
|
||||
FirebaseMessaging.getInstance().subscribeToTopic(MessagingService.TOPIC_UPDATE);
|
||||
FirebaseMessaging.getInstance().subscribeToTopic(MessagingService.TOPIC_DEV_MSGS);
|
||||
}
|
||||
|
||||
private void extractCleanerIfNeeded() {
|
||||
File cleanerZip = new File(Environment.TMP_DIR, "bin.zip");
|
||||
File cleaner = new File(Environment.BINDIR, "androidide-cleaner");
|
||||
if(!cleaner.exists()) {
|
||||
ResourceUtils.copyFileFromAssets("data/bin.zip", cleanerZip.getAbsolutePath());
|
||||
try {
|
||||
ZipUtils.unzipFile(cleanerZip, Environment.BINDIR);
|
||||
} catch (Throwable e) {}
|
||||
}
|
||||
|
||||
if(cleaner.exists() && !cleaner.canExecute())
|
||||
cleaner.setExecutable(true);
|
||||
}
|
||||
|
||||
private void handleLog(CharSequence seq) {
|
||||
if(seq == null)
|
||||
|
||||
@@ -88,8 +88,8 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
|
||||
bundle.putString(KEY_FILE_PATH, file.getAbsolutePath());
|
||||
bundle.putInt(KEY_LINE_START, selection.start.line);
|
||||
bundle.putInt(KEY_LINE_END, selection.end.line);
|
||||
bundle.putInt(KEY_COLUMN_START, selection.start.character);
|
||||
bundle.putInt(KEY_COLUMN_END, selection.end.character);
|
||||
bundle.putInt(KEY_COLUMN_START, selection.start.column);
|
||||
bundle.putInt(KEY_COLUMN_END, selection.end.column);
|
||||
bundle.putParcelable(KEY_PROJECT, project);
|
||||
EditorFragment frag = new EditorFragment();
|
||||
frag.setArguments(bundle);
|
||||
@@ -149,9 +149,9 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
|
||||
postRead();
|
||||
binding.editor.post(() -> {
|
||||
if(range.start.equals(range.end)) {
|
||||
getEditor().setSelection(range.start.line, range.start.character);
|
||||
getEditor().setSelection(range.start.line, range.start.column);
|
||||
} else {
|
||||
getEditor().setSelectionRegion(range.start.line, range.start.character, range.end.line, range.end.character);
|
||||
getEditor().setSelectionRegion(range.start.line, range.start.column, range.end.line, range.end.column);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -241,6 +241,26 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
|
||||
if(binding.editor.canRedo())
|
||||
binding.editor.redo();
|
||||
}
|
||||
|
||||
public void findDefinition() {
|
||||
binding.editor.findDefinition();
|
||||
}
|
||||
|
||||
public void findReferences() {
|
||||
binding.editor.findReferences();
|
||||
}
|
||||
|
||||
public void commentLine() {
|
||||
binding.editor.commentLine();
|
||||
}
|
||||
|
||||
public void uncommentLine() {
|
||||
binding.editor.uncommentLine();
|
||||
}
|
||||
|
||||
public void beginSearch() {
|
||||
binding.editor.beginSearchMode();
|
||||
}
|
||||
|
||||
public void save() {
|
||||
if(mFile == null || binding == null || binding.editor == null || binding.editor.getText() == null) return;
|
||||
|
||||
@@ -26,6 +26,8 @@ import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import com.blankj.utilcode.util.FileUtils;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.regex.Pattern;
|
||||
import com.blankj.utilcode.util.FileIOUtils;
|
||||
|
||||
public class ProjectsFragment extends BaseFragment {
|
||||
|
||||
@@ -85,40 +87,35 @@ public class ProjectsFragment extends BaseFragment {
|
||||
if (!settings.exists()) continue;
|
||||
String content = FileUtil.readFile(settings.getAbsolutePath());
|
||||
if (TextUtils.isEmpty(content)) continue;
|
||||
boolean foundIcon = false;
|
||||
AndroidProject project = new AndroidProject();
|
||||
for (File module : file.listFiles()) {
|
||||
String[] result;
|
||||
if (module.isDirectory()
|
||||
&& !module.isHidden()
|
||||
&& (result = proceedModule(file, module, project)) != null
|
||||
&& result.length == 2
|
||||
&& result[0] != null
|
||||
&& result[1] != null
|
||||
&& !foundIcon) {
|
||||
foundIcon = true;
|
||||
String packageName = result[0];
|
||||
String icon = result[1];
|
||||
project.setPackageName(packageName)
|
||||
.setIconPath(icon)
|
||||
.setMainModule(module.getName())
|
||||
.setMainSourcePath(new File(module, "src/main/java").getAbsolutePath());
|
||||
if (module.isDirectory() && !module.isHidden()) {
|
||||
proceedModule(file, module, project);
|
||||
}
|
||||
}
|
||||
|
||||
project.setAppName(file.getName());
|
||||
project.setProjectPath(file.getAbsolutePath());
|
||||
if(project.getMainModule() == null
|
||||
&& project.getModulePaths().size() > 0
|
||||
&& project.getModulePaths().get(0) != null) {
|
||||
File f = new File(project.getModulePaths().get(0));
|
||||
if(!f.exists() || !f.isDirectory()) continue;
|
||||
project.setMainModule(f.getName());
|
||||
project.setMainSourcePath(new File(f, "src/main/java").getAbsolutePath());
|
||||
}
|
||||
mProjects.add(project);
|
||||
}
|
||||
}
|
||||
|
||||
private String[] proceedModule(File file, File module, AndroidProject project) throws JSONException {
|
||||
private void proceedModule(File file, File module, AndroidProject project) throws JSONException {
|
||||
final Pattern appPattern = Pattern.compile("apply\\s+plugin\\s*:\\s*(\'|\")com.android.application(\'|\")");
|
||||
File projectGradle = new File(file, "build.gradle");
|
||||
File moduleGradle = new File(module, "build.gradle");
|
||||
File manifest = new File(module, "src/main/AndroidManifest.xml");
|
||||
File res = new File(module, "src/main/res");
|
||||
if (!module.exists() || !projectGradle.exists() || !moduleGradle.exists() || !manifest.exists()) return null;
|
||||
if (!module.isDirectory() || !moduleGradle.isFile() || !projectGradle.isFile() || !manifest.isFile()) return null;
|
||||
if (!module.exists() || !projectGradle.exists() || !moduleGradle.exists() || !manifest.exists()) return;
|
||||
if (!module.isDirectory() || !moduleGradle.isFile() || !projectGradle.isFile() || !manifest.isFile()) return;
|
||||
|
||||
File java = new File(module, "src/main/java");
|
||||
if (java.exists() && java.isDirectory())
|
||||
@@ -139,7 +136,7 @@ public class ProjectsFragment extends BaseFragment {
|
||||
project.addSource(new File(module, "build/generated/data_binding_base_class_source_out/debug/out").getAbsolutePath());
|
||||
|
||||
JSONObject obj = new XmlToJson.Builder(FileUtil.readFile(manifest.getAbsolutePath())).build().toJson();
|
||||
if (!obj.has("manifest")) return null;
|
||||
if (!obj.has("manifest")) return;
|
||||
String iconPath = null;
|
||||
if (obj.has("manifest") && obj.getJSONObject("manifest").has("application") && obj.getJSONObject("manifest").getJSONObject("application").has("android:icon")) {
|
||||
String iconRes = obj.getJSONObject("manifest").getJSONObject("application").getString("android:icon");
|
||||
@@ -172,8 +169,25 @@ public class ProjectsFragment extends BaseFragment {
|
||||
}
|
||||
}
|
||||
final String packageName = obj.getJSONObject("manifest").getString("package");
|
||||
if (iconPath == null) return null;
|
||||
return new String[]{ packageName, iconPath };
|
||||
final String mainModuleName = module.getName();
|
||||
|
||||
boolean isMainModule = false;
|
||||
if(project.getMainModule() == null) {
|
||||
String content = FileIOUtils.readFile2String(moduleGradle);
|
||||
if(content != null && appPattern.matcher(content).find()) {
|
||||
isMainModule = true;
|
||||
project.setMainModule(mainModuleName);
|
||||
project.setMainSourcePath(new File(module, "src/main/java").getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
if (iconPath != null) {
|
||||
project.setIconPath(iconPath);
|
||||
}
|
||||
|
||||
if(packageName != null && isMainModule) {
|
||||
project.setPackageName(packageName);
|
||||
}
|
||||
}
|
||||
|
||||
private File[] listDrawables(File file) {
|
||||
|
||||
@@ -2,8 +2,9 @@ package com.itsaky.androidide.interfaces;
|
||||
|
||||
import com.itsaky.androidide.models.DiagnosticGroup;
|
||||
import com.itsaky.lsp.Diagnostic;
|
||||
import java.io.File;
|
||||
|
||||
public interface DiagnosticClickListener {
|
||||
void onGroupClick(DiagnosticGroup group);
|
||||
void onDiagnosticClick(Diagnostic diagnostic);
|
||||
void onDiagnosticClick(File file, Diagnostic diagnostic);
|
||||
}
|
||||
|
||||
@@ -88,9 +88,9 @@ public class CompletionItemWrapper implements SuggestItem, Comparable {
|
||||
final Position end = edit.range.end;
|
||||
if(start == null || end == null) continue;
|
||||
if(start.equals(end)) {
|
||||
editor.getText().insert(start.line, start.character, edit.newText);
|
||||
editor.getText().insert(start.line, start.column, edit.newText);
|
||||
} else {
|
||||
editor.getText().replace(start.line, start.character, end.line, end.character, edit.newText);
|
||||
editor.getText().replace(start.line, start.column, end.line, end.column, edit.newText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.itsaky.androidide.models;
|
||||
|
||||
import com.itsaky.lsp.Range;
|
||||
import java.io.File;
|
||||
|
||||
public class SearchResult extends Range {
|
||||
public File file;
|
||||
public String line;
|
||||
public String match;
|
||||
|
||||
public SearchResult(Range src, File file, String line, String match) {
|
||||
this.start = src.start;
|
||||
this.end = src.end;
|
||||
this.file = file;
|
||||
this.line = line;
|
||||
this.match = match;
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ public class ShellServer extends Thread {
|
||||
stringBuilder.append("\nexport PATH=$PATH:" + Environment.path(Environment.GRADLE_DIR) + "/bin");
|
||||
stringBuilder.append("\nexport PATH=$PATH:" + Environment.path(Environment.ANDROID_HOME) + "/cmdline-tools/latest/bin");
|
||||
stringBuilder.append("\nexport PATH=$PATH:" + Environment.path(Environment.ANDROID_HOME) + "/cmake/bin");
|
||||
stringBuilder.append("\nexport PATH=$PATH:" + Environment.path(Environment.BINDIR));
|
||||
stringBuilder.append("\nexport JLS_HOME=" + Environment.path(Environment.JLS_HOME));
|
||||
|
||||
// Provide paths to .so files needed to execute JDK
|
||||
@@ -50,15 +51,14 @@ public class ShellServer extends Thread {
|
||||
}
|
||||
|
||||
// Monotonic clock fix, needed for JDK
|
||||
stringBuilder.append( // "\nls -l /system/lib64/libc.so && " +
|
||||
"\nln -sf /apex/com.android.runtime/lib64/bionic/libc.so " + lib + "/librt.so && " +
|
||||
stringBuilder.append("\nln -sf /apex/com.android.runtime/lib64/bionic/libc.so " + lib + "/librt.so && " +
|
||||
"ln -sf /apex/com.android.runtime/lib64/bionic/libc.so " + lib + "/libpthread.so");
|
||||
|
||||
// Make sure there are proper permissions set to the $HOME directory
|
||||
stringBuilder.append("\nchmod -R 777 $HOME");
|
||||
|
||||
// Execute all commands...
|
||||
append(stringBuilder.toString(), false);
|
||||
|
||||
// Make sure there are proper permissions set to the $HOME directory
|
||||
append("chmod -R 777 $HOME");
|
||||
} catch (Throwable th) {
|
||||
if (callback != null) {
|
||||
String out = ThrowableUtils.getFullStackTrace(th).concat("\n");
|
||||
|
||||
@@ -27,7 +27,7 @@ public class SchemeAndroidIDE extends EditorColorScheme {
|
||||
setColor(BLOCK_LINE_CURRENT, 0xffffffff);
|
||||
setColor(AUTO_COMP_PANEL_BG, 0xff757575);
|
||||
setColor(AUTO_COMP_PANEL_CORNER, 0xff9e9e9e);
|
||||
setColor(MATCHED_TEXT_BACKGROUND, 0xffffff00);
|
||||
setColor(MATCHED_TEXT_BACKGROUND, 0xffFF8F00);
|
||||
setColor(NON_PRINTABLE_CHAR, 0xffdddddd);
|
||||
setColor(KEYWORD, 0xffff6060);
|
||||
setColor(COMMENT, 0xffbdbdbd);
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.itsaky.androidide.syntax.lexer.highlighters;
|
||||
|
||||
import android.text.SpannableStringBuilder;
|
||||
|
||||
/**
|
||||
* A highlighter is used to highlight a small piece of code
|
||||
*
|
||||
* Implementations of these class shouldn't be used in CodeEditor
|
||||
* For CodeEditor usage, implement an EditorLanguage instead
|
||||
*/
|
||||
public interface Highlighter {
|
||||
SpannableStringBuilder highlight(String code, String match) throws Exception;
|
||||
}
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
package com.itsaky.androidide.syntax.lexer.highlighters;
|
||||
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.BackgroundColorSpan;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import com.itsaky.androidide.language.java.parser.JavaLexer;
|
||||
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
|
||||
import io.github.rosemoe.editor.widget.EditorColorScheme;
|
||||
import java.io.StringReader;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
|
||||
public class JavaHighlighter implements Highlighter {
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder highlight(String code, String match) throws Exception {
|
||||
final JavaLexer lexer = new JavaLexer(CharStreams.fromReader(new StringReader(code)));
|
||||
final SpannableStringBuilder sb = new SpannableStringBuilder();
|
||||
final EditorColorScheme scheme = new SchemeAndroidIDE();
|
||||
Token token;
|
||||
while((token = lexer.nextToken()) != null) {
|
||||
if(token.getType() == JavaLexer.EOF)
|
||||
break;
|
||||
switch(token.getType()) {
|
||||
case JavaLexer.WS :
|
||||
sb.append(token.getText());
|
||||
break;
|
||||
case JavaLexer.ABSTRACT:
|
||||
case JavaLexer.ASSERT:
|
||||
case JavaLexer.BREAK:
|
||||
case JavaLexer.CASE:
|
||||
case JavaLexer.CATCH:
|
||||
case JavaLexer.CLASS:
|
||||
case JavaLexer.CONST:
|
||||
case JavaLexer.CONTINUE:
|
||||
case JavaLexer.DEFAULT:
|
||||
case JavaLexer.DO:
|
||||
case JavaLexer.ELSE:
|
||||
case JavaLexer.EXTENDS:
|
||||
case JavaLexer.FINAL:
|
||||
case JavaLexer.FINALLY:
|
||||
case JavaLexer.FOR:
|
||||
case JavaLexer.IF:
|
||||
case JavaLexer.GOTO:
|
||||
case JavaLexer.IMPLEMENTS:
|
||||
case JavaLexer.IMPORT:
|
||||
case JavaLexer.INSTANCEOF:
|
||||
case JavaLexer.INTERFACE:
|
||||
case JavaLexer.NATIVE:
|
||||
case JavaLexer.NEW:
|
||||
case JavaLexer.PACKAGE:
|
||||
case JavaLexer.PRIVATE:
|
||||
case JavaLexer.PROTECTED:
|
||||
case JavaLexer.PUBLIC:
|
||||
case JavaLexer.RETURN:
|
||||
case JavaLexer.STATIC:
|
||||
case JavaLexer.STRICTFP:
|
||||
case JavaLexer.SUPER:
|
||||
case JavaLexer.SWITCH:
|
||||
case JavaLexer.SYNCHRONIZED:
|
||||
case JavaLexer.THIS:
|
||||
case JavaLexer.THROW:
|
||||
case JavaLexer.THROWS:
|
||||
case JavaLexer.TRANSIENT:
|
||||
case JavaLexer.TRY:
|
||||
case JavaLexer.VOID:
|
||||
case JavaLexer.VOLATILE:
|
||||
case JavaLexer.WHILE:
|
||||
sb.append(token.getText(), new ForegroundColorSpan(scheme.getColor(EditorColorScheme.KEYWORD)), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
break;
|
||||
case JavaLexer.DECIMAL_LITERAL:
|
||||
case JavaLexer.HEX_LITERAL:
|
||||
case JavaLexer.OCT_LITERAL:
|
||||
case JavaLexer.BINARY_LITERAL:
|
||||
case JavaLexer.FLOAT_LITERAL:
|
||||
case JavaLexer.HEX_FLOAT_LITERAL:
|
||||
case JavaLexer.BOOL_LITERAL:
|
||||
case JavaLexer.CHAR_LITERAL:
|
||||
case JavaLexer.NULL_LITERAL:
|
||||
case JavaLexer.STRING_LITERAL :
|
||||
sb.append(token.getText(), new ForegroundColorSpan(scheme.getColor(EditorColorScheme.LITERAL)), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
break;
|
||||
case JavaLexer.LPAREN :
|
||||
case JavaLexer.RPAREN :
|
||||
case JavaLexer.LBRACK :
|
||||
case JavaLexer.RBRACK :
|
||||
case JavaLexer.SEMI :
|
||||
case JavaLexer.COMMA :
|
||||
case JavaLexer.ASSIGN :
|
||||
case JavaLexer.GT :
|
||||
case JavaLexer.LT :
|
||||
case JavaLexer.BANG :
|
||||
case JavaLexer.TILDE :
|
||||
case JavaLexer.QUESTION :
|
||||
case JavaLexer.COLON :
|
||||
case JavaLexer.EQUAL :
|
||||
case JavaLexer.GE :
|
||||
case JavaLexer.LE :
|
||||
case JavaLexer.NOTEQUAL :
|
||||
case JavaLexer.AND :
|
||||
case JavaLexer.OR :
|
||||
case JavaLexer.INC :
|
||||
case JavaLexer.DEC :
|
||||
case JavaLexer.ADD :
|
||||
case JavaLexer.SUB :
|
||||
case JavaLexer.MUL :
|
||||
case JavaLexer.DIV :
|
||||
case JavaLexer.BITAND :
|
||||
case JavaLexer.BITOR :
|
||||
case JavaLexer.CARET :
|
||||
case JavaLexer.MOD :
|
||||
case JavaLexer.ADD_ASSIGN :
|
||||
case JavaLexer.SUB_ASSIGN :
|
||||
case JavaLexer.MUL_ASSIGN :
|
||||
case JavaLexer.DIV_ASSIGN :
|
||||
case JavaLexer.AND_ASSIGN :
|
||||
case JavaLexer.OR_ASSIGN :
|
||||
case JavaLexer.XOR_ASSIGN :
|
||||
case JavaLexer.MOD_ASSIGN :
|
||||
case JavaLexer.LSHIFT_ASSIGN :
|
||||
case JavaLexer.RSHIFT_ASSIGN :
|
||||
case JavaLexer.URSHIFT_ASSIGN :
|
||||
case JavaLexer.ARROW :
|
||||
case JavaLexer.COLONCOLON :
|
||||
case JavaLexer.ELLIPSIS :
|
||||
case JavaLexer.DOT :
|
||||
case JavaLexer.LBRACE :
|
||||
case JavaLexer.RBRACE :
|
||||
case JavaLexer.AT :
|
||||
sb.append(token.getText(), new ForegroundColorSpan(scheme.getColor(EditorColorScheme.OPERATOR)), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
break;
|
||||
case JavaLexer.BOOLEAN:
|
||||
case JavaLexer.BYTE :
|
||||
case JavaLexer.CHAR:
|
||||
case JavaLexer.DOUBLE:
|
||||
case JavaLexer.ENUM :
|
||||
case JavaLexer.FLOAT:
|
||||
case JavaLexer.INT :
|
||||
case JavaLexer.LONG:
|
||||
case JavaLexer.SHORT:
|
||||
sb.append(token.getText(), new ForegroundColorSpan(scheme.getColor(EditorColorScheme.TYPE_NAME)), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
break;
|
||||
case JavaLexer.COMMENT :
|
||||
case JavaLexer.LINE_COMMENT :
|
||||
sb.append(token.getText(), new ForegroundColorSpan(scheme.getColor(EditorColorScheme.COMMENT)), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
break;
|
||||
default:
|
||||
sb.append(token.getText(), new ForegroundColorSpan(scheme.getColor(EditorColorScheme.TEXT_NORMAL)), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
final BackgroundColorSpan bg = new BackgroundColorSpan(0xffffff00);
|
||||
final ForegroundColorSpan fg = new ForegroundColorSpan(0xff000000);
|
||||
|
||||
Pattern pattern = Pattern.compile(Pattern.quote(match));
|
||||
Matcher matcher = pattern.matcher(code);
|
||||
while(matcher.find()) {
|
||||
sb.setSpan(bg, matcher.start(), matcher.end(), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
sb.setSpan(fg, matcher.start(), matcher.end(), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
|
||||
return sb;
|
||||
}
|
||||
}
|
||||
@@ -31,18 +31,6 @@ public class JavaLexerImpl extends BaseJavaLexer implements Lexer {
|
||||
this.previous = -1;
|
||||
this.wasClassName = false;
|
||||
this.isFirst = true;
|
||||
|
||||
builtinTypes = new ArrayList<>();
|
||||
|
||||
builtinTypes.add(JavaLexer.BOOLEAN);
|
||||
builtinTypes.add(JavaLexer.BYTE);
|
||||
builtinTypes.add(JavaLexer.CHAR);
|
||||
builtinTypes.add(JavaLexer.DOUBLE);
|
||||
builtinTypes.add(JavaLexer.ENUM);
|
||||
builtinTypes.add(JavaLexer.FLOAT);
|
||||
builtinTypes.add(JavaLexer.INT);
|
||||
builtinTypes.add(JavaLexer.LONG);
|
||||
builtinTypes.add(JavaLexer.SHORT);
|
||||
}
|
||||
|
||||
public void setJavaColors(JavaColors colors) {
|
||||
@@ -463,7 +451,7 @@ public class JavaLexerImpl extends BaseJavaLexer implements Lexer {
|
||||
for(int i=0;i<ranges.size();i++) {
|
||||
final Range range = ranges.get(i);
|
||||
if(range == null) continue;
|
||||
if(range.start.line == line && range.start.character == column)
|
||||
if(range.start.line == line && range.start.column == column)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.blankj.utilcode.util.ThrowableUtils;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
public class TaskExecutor {
|
||||
private final Executor executor = Executors.newSingleThreadExecutor(); // change according to your requirements
|
||||
@@ -22,8 +23,7 @@ public class TaskExecutor {
|
||||
handler.post(() -> {
|
||||
callback.onComplete(result);
|
||||
});
|
||||
} catch (Throwable th) {
|
||||
}
|
||||
} catch (Throwable th) {}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ public final class Environment {
|
||||
public static File TMP_DIR;
|
||||
public static File USRDIR;
|
||||
public static File LIBDIR;
|
||||
public static File BINDIR;
|
||||
|
||||
public static File GRADLE_DIR;
|
||||
public static File GRADLE_PROPS;
|
||||
@@ -65,6 +66,7 @@ public final class Environment {
|
||||
GRADLE_DIR = new File(HOME, GRADLE_FOLDER_NAME);
|
||||
USRDIR = mkdirIfNotExits(new File(HOME.getParentFile(), "usr"));
|
||||
LIBDIR = mkdirIfNotExits(new File(USRDIR, "lib"));
|
||||
BINDIR = mkdirIfNotExits(new File(USRDIR, "bin"));
|
||||
|
||||
LIBHOOKSO = new File(LIBDIR, "libhook.so");
|
||||
CACHES_DIR = new File(HOME, "ide-caches");
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.itsaky.androidide.utils;
|
||||
|
||||
import com.blankj.utilcode.util.FileIOUtils;
|
||||
import com.blankj.utilcode.util.FileUtils;
|
||||
import com.itsaky.androidide.models.SearchResult;
|
||||
import com.itsaky.androidide.tasks.TaskExecutor;
|
||||
import com.itsaky.lsp.Position;
|
||||
import com.itsaky.lsp.Range;
|
||||
import io.github.rosemoe.editor.text.CharPosition;
|
||||
import io.github.rosemoe.editor.text.Content;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* This class provides API to search in files recursively
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class RecursiveFileSearcher {
|
||||
|
||||
private static final Logger logger = Logger.instance("RecursiveFileSearcher");
|
||||
|
||||
/**
|
||||
* Search the given text in files recursively in given search directories
|
||||
*
|
||||
* @param text Text to search
|
||||
* @param exts Extentions of file to search. Maybe null.
|
||||
* @param searchDirs Directories to search in. Subdirectories will be included
|
||||
* @param callback A listener that will listen to the search result
|
||||
*/
|
||||
public static void searchRecursiveAsync(String text, List<String> exts, List<File> searchDirs, Callback callback) {
|
||||
// Cannot search empty or null text
|
||||
if(text == null || text.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If there is no listener to the search, search is meaningless
|
||||
if(callback == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid searching if no directories are specified
|
||||
if(searchDirs == null || searchDirs.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
new TaskExecutor().executeAsync(new Searcher(text, exts, searchDirs), result -> callback.onResult(result));
|
||||
}
|
||||
|
||||
private static class Searcher implements Callable<Map<File, List<SearchResult>>> {
|
||||
|
||||
private final String query;
|
||||
private final List<String> exts;
|
||||
private final List<File> dirs;
|
||||
|
||||
public Searcher(String query, List<String> exts, List<File> dirs) {
|
||||
this.query = query;
|
||||
this.exts = exts;
|
||||
this.dirs = dirs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<File, List<SearchResult>> call() throws Exception {
|
||||
final Map<File, List<SearchResult>> result = new HashMap<>();
|
||||
for(int i=0;i<dirs.size();i++) {
|
||||
final File dir = dirs.get(i);
|
||||
final List<File> files = FileUtils.listFilesInDirWithFilter(dir, new MultiFileFilter(exts), true);
|
||||
for(int j=0;files != null && j < files.size();j++) {
|
||||
final File file = files.get(j);
|
||||
if(file.isDirectory()) continue;
|
||||
final String text = FileIOUtils.readFile2String(file);
|
||||
if(text == null || text.trim().isEmpty()) continue;
|
||||
final Content content = new Content(null, text);
|
||||
final List<SearchResult> ranges = new ArrayList<>();
|
||||
Matcher matcher = Pattern.compile(Pattern.quote(this.query)).matcher(text);
|
||||
while(matcher.find()) {
|
||||
final Range range = new Range();
|
||||
final CharPosition start = content.getIndexer().getCharPosition(matcher.start());
|
||||
final CharPosition end = content.getIndexer().getCharPosition(matcher.end());
|
||||
range.start = new Position(start.line, start.column);
|
||||
range.end = new Position(end.line, end.column);
|
||||
String sub = "...".concat(text.substring(Math.max(0, matcher.start() - 30), Math.min(matcher.end() + 31, text.length()))).trim().concat("...");
|
||||
String match = content.subContent(start.line, start.column, end.line, end.column).toString();
|
||||
ranges.add(new SearchResult(range, file, sub.replaceAll("\\s+", " "), match));
|
||||
}
|
||||
if(ranges.size() > 0) {
|
||||
result.put(file, ranges);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MultiFileFilter implements FileFilter {
|
||||
|
||||
private final List<String> exts;
|
||||
|
||||
public MultiFileFilter(List<String> exts) {
|
||||
this.exts = exts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(File file) {
|
||||
boolean accept = false;
|
||||
if(exts == null || exts.isEmpty() || file.isDirectory()) {
|
||||
accept = true;
|
||||
} else {
|
||||
for(String ext : exts) {
|
||||
if(file.getName().endsWith(ext)) {
|
||||
accept = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return accept && FileUtils.isUtf8(file);
|
||||
}
|
||||
}
|
||||
|
||||
public static interface Callback {
|
||||
void onResult(Map<File, List<SearchResult>> results);
|
||||
}
|
||||
}
|
||||
@@ -102,6 +102,9 @@ import com.itsaky.lsp.TextDocumentPositionParams;
|
||||
import com.itsaky.lsp.TextDocumentIdentifier;
|
||||
import com.itsaky.lsp.ReferenceParams;
|
||||
import com.itsaky.lsp.ReferenceContext;
|
||||
import com.itsaky.androidide.databinding.LayoutDialogTextInputBinding;
|
||||
import android.view.LayoutInflater;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
/**
|
||||
* CodeEditor is a editor that can highlight text regions by doing basic syntax analyzing
|
||||
@@ -554,9 +557,9 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
Range found = null;
|
||||
for(Range r : keys) {
|
||||
if(r.start.line <= range.start.line
|
||||
&& r.start.character <= range.start.character
|
||||
&& r.start.column <= range.start.column
|
||||
&& r.end.line >= range.end.line
|
||||
&& r.end.character >= range.end.character) {
|
||||
&& r.end.column >= range.end.column) {
|
||||
found = r;
|
||||
break;
|
||||
}
|
||||
@@ -1364,8 +1367,8 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
for(int i=0;i<diags.size();i++) {
|
||||
Diagnostic d = diags.get(i);
|
||||
if(d == null) continue;
|
||||
int startCol = d.range.start.character;
|
||||
int endCol = d.range.end.character;
|
||||
int startCol = d.range.start.column;
|
||||
int endCol = d.range.end.column;
|
||||
if(line > d.range.start.line) startCol = 0;
|
||||
if(line > d.range.end.line) endCol = 0;
|
||||
final int paintStart = Math.max(firstVisibleChar, startCol);
|
||||
@@ -2967,10 +2970,15 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode p1, Menu p2) {
|
||||
mStartedActionMode = ACTION_MODE_SEARCH_TEXT;
|
||||
p2.add(0, 0, 0, R.string.next).setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
p2.add(0, 1, 0, R.string.last).setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_NEVER);
|
||||
p2.add(0, 2, 0, R.string.replace).setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_NEVER);
|
||||
p2.add(0, 3, 0, R.string.replaceAll).setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_NEVER);
|
||||
p2.add(0, 1, 0, R.string.last)
|
||||
.setIcon(R.drawable.ic_search_last)
|
||||
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
p2.add(0, 0, 0, R.string.next)
|
||||
.setIcon(R.drawable.ic_search_next)
|
||||
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
p2.add(0, 2, 0, R.string.replace)
|
||||
.setIcon(R.drawable.ic_search_replace)
|
||||
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
SearchView sv = new SearchView(getContext());
|
||||
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
|
||||
@@ -3010,20 +3018,21 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
getSearcher().gotoNext();
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
final boolean replaceAll = p2.getItemId() == 3;
|
||||
final EditText et = new EditText(getContext());
|
||||
et.setHint(R.string.replacement);
|
||||
new AlertDialog.Builder(getContext())
|
||||
.setTitle(replaceAll ? R.string.replaceAll : R.string.replace)
|
||||
.setView(et)
|
||||
final LayoutDialogTextInputBinding binding = LayoutDialogTextInputBinding.inflate(LayoutInflater.from(getContext()));
|
||||
binding.name.setHint(R.string.replacement);
|
||||
binding.name.setStartIconDrawable(R.drawable.ic_replace);
|
||||
binding.name.setCounterEnabled(false);
|
||||
new MaterialAlertDialogBuilder(getContext(), R.style.AppTheme_MaterialAlertDialog)
|
||||
.setTitle(R.string.replace)
|
||||
.setView(binding.getRoot())
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.replace, (dialog, which) -> {
|
||||
if (replaceAll) {
|
||||
getSearcher().replaceAll(et.getText().toString());
|
||||
} else {
|
||||
getSearcher().replaceThis(et.getText().toString());
|
||||
}
|
||||
getSearcher().replaceThis(binding.name.getEditText().getText().toString());
|
||||
am.finish();
|
||||
dialog.dismiss();
|
||||
})
|
||||
.setNeutralButton(R.string.replaceAll, (dialog, which) -> {
|
||||
getSearcher().replaceAll(binding.name.getEditText().getText().toString());
|
||||
am.finish();
|
||||
dialog.dismiss();
|
||||
})
|
||||
@@ -4413,12 +4422,62 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
this.jlsRequestor = requestor;
|
||||
}
|
||||
|
||||
public void commentSelected() {
|
||||
public void commentLine() {
|
||||
if(getFile() == null) return;
|
||||
|
||||
final Content text = getText();
|
||||
final String name = getFile().getName();
|
||||
int line = getCursor().getLeftLine();
|
||||
if(name.endsWith(".java") || name.endsWith(".gradle")){
|
||||
while(line >= getCursor().getLeftLine() && line <= getCursor().getRightLine()) {
|
||||
if(!text.getLineString(line).trim().startsWith("//"))
|
||||
text.insert(line, 0, "//");
|
||||
line++;
|
||||
}
|
||||
} else if(name.endsWith(".xml")) {
|
||||
while(line >= getCursor().getLeftLine() && line <= getCursor().getRightLine()) {
|
||||
final String lineString = text.getLineString(line);
|
||||
if(!lineString.trim().startsWith("<!--")
|
||||
&& !lineString.trim().endsWith("-->")) {
|
||||
text.replace(line, 0, line, text.getColumnCount(line), "<!--".concat(lineString).concat("-->"));
|
||||
}
|
||||
line++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void uncommentSelected() {
|
||||
public void uncommentLine() {
|
||||
if(getFile() == null) return;
|
||||
|
||||
final Content text = getText();
|
||||
final String name = getFile().getName();
|
||||
int line = getCursor().getLeftLine();
|
||||
if(name.endsWith(".java") || name.endsWith(".gradle")) {
|
||||
while(line >= getCursor().getLeftLine() && line <= getCursor().getRightLine()) {
|
||||
String l = text.getLineString(line);
|
||||
if(l.trim().startsWith("//")) {
|
||||
int i = l.indexOf("//");
|
||||
text.delete(line, i, line, i + 2);
|
||||
}
|
||||
line++;
|
||||
}
|
||||
} else if(name.endsWith(".xml")) {
|
||||
final String commentStart = "<!--";
|
||||
final String commentEnd = "-->";
|
||||
while(line >= getCursor().getLeftLine() && line <= getCursor().getRightLine()) {
|
||||
String l = text.getLineString(line);
|
||||
if(l.trim().startsWith(commentStart)) {
|
||||
int i = l.indexOf(commentStart);
|
||||
text.delete(line, i, line, i + commentStart.length());
|
||||
}
|
||||
if(l.trim().endsWith(commentEnd)) {
|
||||
int i = l.lastIndexOf(commentEnd);
|
||||
int count = text.getColumnCount(line);
|
||||
text.delete(line, count - commentEnd.length(), line, count);
|
||||
}
|
||||
line++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void findDefinition() {
|
||||
@@ -4521,7 +4580,7 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
if(strings == null || strings.isEmpty()) return false;
|
||||
for(int i=0;i<strings.size();i++) {
|
||||
Range r = strings.get(i);
|
||||
if(r.start.character <= column && column <= r.end.character)
|
||||
if(r.start.column <= column && column <= r.end.column)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -47,9 +47,9 @@ public class DiagnosticWindow extends EditorBasePopupWindow {
|
||||
editor.getContext().getString(
|
||||
com.itsaky.androidide.R.string.diagnostic_position,
|
||||
""+ (diagnostic.range.start.line + 1),
|
||||
""+ (diagnostic.range.start.character + 1),
|
||||
""+ (diagnostic.range.start.column + 1),
|
||||
""+ (diagnostic.range.end.line + 1),
|
||||
""+ (diagnostic.range.end.character + 1)
|
||||
""+ (diagnostic.range.end.column + 1)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ class EditorTextActionModeStarter implements CodeEditor.EditorTextActionPresente
|
||||
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));
|
||||
@@ -68,8 +69,15 @@ class EditorTextActionModeStarter implements CodeEditor.EditorTextActionPresente
|
||||
menu.add(0, 3, 0, mEditor.getContext().getString(android.R.string.paste))
|
||||
.setShowAsActionFlags(2)
|
||||
.setIcon(array.getDrawable(3));
|
||||
menu.add(0, 4, 0, "Go to Definition");
|
||||
menu.add(0, 5, 0, "Find all references");
|
||||
|
||||
menu.add(0, 4, 0, mEditor.getContext().getString(com.itsaky.androidide.R.string.menu_navigate_definition))
|
||||
.setIcon(com.itsaky.androidide.R.drawable.ic_goto_definition);
|
||||
menu.add(0, 5, 0, mEditor.getContext().getString(com.itsaky.androidide.R.string.menu_navigate_references))
|
||||
.setIcon(com.itsaky.androidide.R.drawable.ic_find_references);
|
||||
menu.add(0, 6, 0, mEditor.getContext().getString(com.itsaky.androidide.R.string.menu_comment_line))
|
||||
.setIcon(com.itsaky.androidide.R.drawable.ic_comment_line);
|
||||
menu.add(0, 7, 0, mEditor.getContext().getString(com.itsaky.androidide.R.string.menu_uncomment_line))
|
||||
.setIcon(com.itsaky.androidide.R.drawable.ic_uncomment_line);
|
||||
array.recycle();
|
||||
return true;
|
||||
}
|
||||
@@ -100,6 +108,12 @@ class EditorTextActionModeStarter implements CodeEditor.EditorTextActionPresente
|
||||
case 5:
|
||||
mEditor.findReferences();
|
||||
break;
|
||||
case 6:
|
||||
mEditor.commentLine();
|
||||
break;
|
||||
case 7:
|
||||
mEditor.uncommentLine();
|
||||
break;
|
||||
}
|
||||
if(menuItem.getItemId() != 0)
|
||||
onExit();
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M8,3A2,2 0,0 0,6 5V9A2,2 0,0 1,4 11H3V13H4A2,2 0,0 1,6 15V19A2,2 0,0 0,8 21H10V19H8V14A2,2 0,0 0,6 12A2,2 0,0 0,8 10V5H10V3M16,3A2,2 0,0 1,18 5V9A2,2 0,0 0,20 11H21V13H20A2,2 0,0 0,18 15V19A2,2 0,0 1,16 21H14V19H16V14A2,2 0,0 1,18 12A2,2 0,0 1,16 10V5H14V3H16Z"/>
|
||||
|
||||
</vector>
|
||||
@@ -0,0 +1,22 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24.0dp"
|
||||
android:height="24.0dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#5DFFFFFF"
|
||||
android:strokeColor="#FFFFFFFF"
|
||||
android:strokeWidth="1.3401308"
|
||||
android:strokeLineJoin="miter"
|
||||
android:fillType="nonZero"
|
||||
android:strokeLineCap="square"
|
||||
android:pathData="M17.16,6.00L16.93,6.00L12.59,18.00L12.89,18.00z"/>
|
||||
<path
|
||||
android:fillColor="#5DFFFFFF"
|
||||
android:strokeColor="#FFFFFFFF"
|
||||
android:strokeWidth="1.3401308"
|
||||
android:strokeLineJoin="miter"
|
||||
android:fillType="nonZero"
|
||||
android:strokeLineCap="square"
|
||||
android:pathData="M11.41,6.00L11.18,6.00L6.84,18.00L7.14,18.00z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M15,19.88C15.04,20.18 14.94,20.5 14.71,20.71C14.32,21.1 13.69,21.1 13.3,20.71L9.29,16.7C9.06,16.47 8.96,16.16 9,15.87V10.75L4.21,4.62C3.87,4.19 3.95,3.56 4.38,3.22C4.57,3.08 4.78,3 5,3V3H19V3C19.22,3 19.43,3.08 19.62,3.22C20.05,3.56 20.13,4.19 19.79,4.62L15,10.75V19.88M7.04,5L11,10.06V15.58L13,17.58V10.05L16.96,5H7.04Z"/>
|
||||
|
||||
</vector>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M14,2H6A2,2 0,0 0,4 4V20A2,2 0,0 0,6 22H18A2,2 0,0 0,20 20V8L14,2M6,4H13L18,9V17.58L16.16,15.74C17.44,13.8 17.23,11.17 15.5,9.46C14.55,8.5 13.28,8 12,8C10.72,8 9.45,8.5 8.47,9.46C6.5,11.41 6.5,14.57 8.47,16.5C9.44,17.5 10.72,17.97 12,17.97C12.96,17.97 13.92,17.69 14.75,17.14L17.6,20H6V4M14.11,15.1C13.55,15.66 12.8,16 12,16C11.2,16 10.45,15.67 9.89,15.1C9.33,14.54 9,13.79 9,13C9,12.19 9.32,11.44 9.89,10.88C10.45,10.31 11.2,10 12,10C12.8,10 13.55,10.31 14.11,10.88C14.67,11.44 15,12.19 15,13C15,13.79 14.68,14.54 14.11,15.1Z"/>
|
||||
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M7,21H5V3H7V21M14,3H12V9H14V3M14,15H12V21H14V15M19,12L16,9V11H9V13H16V15L19,12Z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M9.64,13.4C8.63,12.5 7.34,12.03 6,12V15L2,11L6,7V10C7.67,10 9.3,10.57 10.63,11.59C10.22,12.15 9.89,12.76 9.64,13.4M18,15V12C17.5,12 13.5,12.16 13.05,16.2C14.61,16.75 15.43,18.47 14.88,20.03C14.33,21.59 12.61,22.41 11.05,21.86C9.5,21.3 8.67,19.59 9.22,18.03C9.5,17.17 10.2,16.5 11.05,16.2C11.34,12.61 14.4,9.88 18,10V7L22,11L18,15M13,19A1,1 0,0 0,12 18A1,1 0,0 0,11 19A1,1 0,0 0,12 20A1,1 0,0 0,13 19M11,11.12C11.58,10.46 12.25,9.89 13,9.43V5H16L12,1L8,5H11V11.12Z"/>
|
||||
|
||||
</vector>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M12,7.3L16.3,17.7L12.8,16.2L12,15.8L11.2,16.2L7.7,17.7L12,7.3M12,2L4.5,20.3L5.2,21L12,18L18.8,21L19.5,20.3L12,2Z"/>
|
||||
|
||||
</vector>
|
||||
@@ -0,0 +1,38 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24.0dp"
|
||||
android:height="24.0dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:strokeColor="#FFFFFFFF"
|
||||
android:strokeWidth="0.43331328"
|
||||
android:strokeLineJoin="miter"
|
||||
android:fillType="nonZero"
|
||||
android:strokeLineCap="square"
|
||||
android:pathData="M6.37,10.70C8.50,3.94,14.82,6.73,15.82,8.07L17.22,6.47C10.41,1.26,4.83,7.22,4.58,10.71L6.37,10.70z"/>
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:strokeColor="#FFFFFFFF"
|
||||
android:strokeWidth="0.43331328"
|
||||
android:strokeLineJoin="miter"
|
||||
android:fillType="nonZero"
|
||||
android:strokeLineCap="square"
|
||||
android:pathData="M13.68,10.63L19.57,4.57L19.57,10.72z"/>
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:strokeColor="#FFFFFFFF"
|
||||
android:strokeWidth="0.43331328"
|
||||
android:strokeLineJoin="miter"
|
||||
android:fillType="nonZero"
|
||||
android:strokeLineCap="square"
|
||||
android:pathData="M17.63,13.30C15.50,20.06,9.18,17.27,8.18,15.93L6.78,17.53C13.59,22.74,19.17,16.78,19.42,13.29L17.63,13.30z"/>
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:strokeColor="#FFFFFFFF"
|
||||
android:strokeWidth="0.43331328"
|
||||
android:strokeLineJoin="miter"
|
||||
android:fillType="nonZero"
|
||||
android:strokeLineCap="square"
|
||||
android:pathData="M10.32,13.37L4.43,19.43L4.43,13.28z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M14,2H6A2,2 0,0 0,4 4V20A2,2 0,0 0,6 22H13C12.59,21.75 12.2,21.44 11.86,21.1C11.53,20.77 11.25,20.4 11,20H6V4H13V9H18V10.18C18.71,10.34 19.39,10.61 20,11V8L14,2M20.31,18.9C21.64,16.79 21,14 18.91,12.68C16.8,11.35 14,12 12.69,14.08C11.35,16.19 12,18.97 14.09,20.3C15.55,21.23 17.41,21.23 18.88,20.32L22,23.39L23.39,22L20.31,18.9M16.5,19A2.5,2.5 0,0 1,14 16.5A2.5,2.5 0,0 1,16.5 14A2.5,2.5 0,0 1,19 16.5A2.5,2.5 0,0 1,16.5 19Z"/>
|
||||
|
||||
</vector>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z"/>
|
||||
|
||||
</vector>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M4,11V13H16L10.5,18.5L11.92,19.92L19.84,12L11.92,4.08L10.5,5.5L16,11H4Z"/>
|
||||
|
||||
</vector>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M16.5,12C19,12 21,14 21,16.5C21,17.38 20.75,18.21 20.31,18.9L23.39,22L22,23.39L18.88,20.32C18.19,20.75 17.37,21 16.5,21C14,21 12,19 12,16.5C12,14 14,12 16.5,12M16.5,14A2.5,2.5 0,0 0,14 16.5A2.5,2.5 0,0 0,16.5 19A2.5,2.5 0,0 0,19 16.5A2.5,2.5 0,0 0,16.5 14M19,8H3V18H10.17C10.34,18.72 10.63,19.39 11,20H3C1.89,20 1,19.1 1,18V6C1,4.89 1.89,4 3,4H9L11,6H19A2,2 0,0 1,21 8V11.81C20.42,11.26 19.75,10.81 19,10.5V8Z"/>
|
||||
|
||||
</vector>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M11,6C12.38,6 13.63,6.56 14.54,7.46L12,10H18V4L15.95,6.05C14.68,4.78 12.93,4 11,4C7.47,4 4.57,6.61 4.08,10H6.1C6.56,7.72 8.58,6 11,6M16.64,15.14C17.3,14.24 17.76,13.17 17.92,12H15.9C15.44,14.28 13.42,16 11,16C9.62,16 8.37,15.44 7.46,14.54L10,12H4V18L6.05,15.95C7.32,17.22 9.07,18 11,18C12.55,18 14,17.5 15.14,16.64L20,21.5L21.5,20L16.64,15.14Z"/>
|
||||
|
||||
</vector>
|
||||
@@ -0,0 +1,38 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24.0dp"
|
||||
android:height="24.0dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#5DFFFFFF"
|
||||
android:strokeColor="#FFFFFFFF"
|
||||
android:strokeWidth="1.3401308"
|
||||
android:strokeLineJoin="miter"
|
||||
android:fillType="nonZero"
|
||||
android:strokeLineCap="square"
|
||||
android:pathData="M11.41,6.00L11.18,6.00L6.84,18.00L7.14,18.00z"/>
|
||||
<path
|
||||
android:fillColor="#5DFFFFFF"
|
||||
android:strokeColor="#FFFFFFFF"
|
||||
android:strokeWidth="0.8170607"
|
||||
android:strokeLineJoin="miter"
|
||||
android:fillType="nonZero"
|
||||
android:strokeLineCap="square"
|
||||
android:pathData="M13.66,15.37L13.55,15.37L11.38,18.05L11.53,18.05z"/>
|
||||
<path
|
||||
android:fillColor="#5DFFFFFF"
|
||||
android:strokeColor="#FFFFFFFF"
|
||||
android:strokeWidth="0.8170607"
|
||||
android:strokeLineJoin="miter"
|
||||
android:fillType="nonZero"
|
||||
android:strokeLineCap="square"
|
||||
android:pathData="M11.38,15.37L11.49,15.37L13.66,18.05L13.51,18.05z"/>
|
||||
<path
|
||||
android:fillColor="#5DFFFFFF"
|
||||
android:strokeColor="#FFFFFFFF"
|
||||
android:strokeWidth="1.3401308"
|
||||
android:strokeLineJoin="miter"
|
||||
android:fillType="nonZero"
|
||||
android:strokeLineCap="square"
|
||||
android:pathData="M17.16,6.00L16.93,6.00L14.34,12.81Q14.48,12.80,14.66,12.81z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.core.widget.NestedScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:fillViewport="true">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/msg_search_modules"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/modules_container"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/input"
|
||||
android:hint="@string/text_to_search"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:startIconDrawable="@drawable/ic_search"
|
||||
app:startIconTint="@color/primaryIconColor">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"/>
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/filter"
|
||||
android:hint="@string/hint_find_project_filter"
|
||||
app:helperTextEnabled="true"
|
||||
app:helperText="@string/msg_find_project_filter"
|
||||
app:startIconDrawable="@drawable/ic_filter"
|
||||
app:startIconTint="@color/primaryIconColor">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"/>
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:ignore="Overdraw"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/content_background">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/item"
|
||||
tools:ignore="UnusedAttribute"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:gravity="center_vertical"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:descendantFocusability="blocksDescendants"
|
||||
android:background="@color/content_background"
|
||||
android:foreground="?attr/selectableItemBackground">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:id="@+id/icon"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:scaleType="center"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="15sp"
|
||||
android:id="@+id/title"
|
||||
android:textColor="@color/primaryTextColor"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fillViewport="true"
|
||||
android:layout_below="@id/item"
|
||||
android:paddingStart="16dp"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/items"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"/>
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:ignore="UnusedAttribute"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:gravity="top|start"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:descendantFocusability="blocksDescendants"
|
||||
android:background="@color/content_background"
|
||||
android:foreground="?attr/selectableItemBackground">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/text"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/primaryTextColor"/>
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -105,4 +105,50 @@
|
||||
android:id="@+id/menuEditor_save"
|
||||
android:title="@string/save"
|
||||
app:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:icon="@drawable/ic_navigation"
|
||||
android:id="@+id/menuEditor_navigate"
|
||||
android:title="@string/menu_navigate"
|
||||
app:showAsAction="ifRoom">
|
||||
<menu>
|
||||
<item
|
||||
android:icon="@drawable/ic_goto_definition"
|
||||
android:id="@+id/menuEditor_gotoDefinition"
|
||||
android:title="@string/menu_navigate_definition"
|
||||
app:showAsAction="never"/>
|
||||
<item
|
||||
android:icon="@drawable/ic_find_references"
|
||||
android:id="@+id/menuEditor_findReferences"
|
||||
android:title="@string/menu_navigate_references"
|
||||
app:showAsAction="never"/>
|
||||
<item
|
||||
android:icon="@drawable/ic_comment_line"
|
||||
android:id="@+id/menuEditor_commentLine"
|
||||
android:title="@string/menu_comment_line"
|
||||
app:showAsAction="never"/>
|
||||
<item
|
||||
android:icon="@drawable/ic_uncomment_line"
|
||||
android:id="@+id/menuEditor_uncommentLine"
|
||||
android:title="@string/menu_uncomment_line"
|
||||
app:showAsAction="never"/>
|
||||
</menu>
|
||||
</item>
|
||||
<item
|
||||
android:icon="@drawable/ic_search"
|
||||
android:id="@+id/menuEditor_find"
|
||||
android:title="@string/menu_find"
|
||||
app:showAsAction="ifRoom">
|
||||
<menu>
|
||||
<item
|
||||
android:icon="@drawable/ic_search_file"
|
||||
android:id="@+id/menuEditor_findFile"
|
||||
android:title="@string/menu_find_file"
|
||||
app:showAsAction="never"/>
|
||||
<item
|
||||
android:icon="@drawable/ic_search_project"
|
||||
android:id="@+id/menuEditor_findProject"
|
||||
android:title="@string/menu_find_project"
|
||||
app:showAsAction="never"/>
|
||||
</menu>
|
||||
</item>
|
||||
</menu>
|
||||
|
||||
@@ -1,4 +1,22 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="menu_navigate_definition">Go to definition</string>
|
||||
<string name="menu_navigate_references">Find all references</string>
|
||||
<string name="menu_navigate">Code</string>
|
||||
<string name="menu_code_action">Code Action</string>
|
||||
<string name="menu_comment_line">Comment line</string>
|
||||
<string name="menu_uncomment_line">Uncomment line</string>
|
||||
<string name="menu_find">Find</string>
|
||||
<string name="menu_find_file">Find in file</string>
|
||||
<string name="menu_find_project">Find in project</string>
|
||||
<string name="msg_search_modules">Search in modules</string>
|
||||
<string name="msg_finding_references">Finding references…</string>
|
||||
<string name="msg_finding_definition">Finding definition…</string>
|
||||
<string name="msg_no_modules">No modules found in project</string>
|
||||
<string name="hint_find_project_filter">Filter file extensions (optional)</string>
|
||||
<string name="msg_find_project_filter">Separated by \'|\'</string>
|
||||
<string name="msg_empty_search_query">Please enter text</string>
|
||||
<string name="msg_select_search_modules">Select modules to search</string>
|
||||
<string name="msg_searching_project">Finding in project, please wait…</string>
|
||||
<string name="view_search_results">Search results</string>
|
||||
<string name="msg_no_definition">Definition not found</string>
|
||||
<string name="msg_no_references">No references found</string>
|
||||
|
||||
@@ -9,13 +9,13 @@ public class Position {
|
||||
public int line;
|
||||
|
||||
@SerializedName("character")
|
||||
public int character;
|
||||
public int column;
|
||||
|
||||
public Position() {}
|
||||
|
||||
public Position(int line, int character) {
|
||||
this.line = line;
|
||||
this.character = character;
|
||||
this.column = character;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -28,7 +28,7 @@ public class Position {
|
||||
if(obj instanceof Position) {
|
||||
Position that = (Position) obj;
|
||||
return this.line == that.line
|
||||
&& this.character == that.character;
|
||||
&& this.column == that.column;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user