mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
Refactor code
This commit is contained in:
+3
-4
@@ -114,11 +114,8 @@ dependencies {
|
||||
implementation 'com.github.piruin:quickaction:2.4.3'
|
||||
implementation 'com.github.Rosemoe:BlockLinkedList:1.0.0'
|
||||
implementation 'com.github.smart-fun:XmlToJson:1.5.1'
|
||||
|
||||
// Dependencies for java completion
|
||||
implementation 'com.google.code.gson:gson:2.8.8'
|
||||
implementation 'com.google.guava:guava:30.1.1-android'
|
||||
implementation 'org.antlr:antlr4-runtime:4.9.2'
|
||||
|
||||
implementation 'org.jsoup:jsoup:1.14.2'
|
||||
|
||||
@@ -143,12 +140,14 @@ dependencies {
|
||||
implementation 'com.google.firebase:firebase-crashlytics'
|
||||
implementation 'com.google.firebase:firebase-messaging'
|
||||
|
||||
// Local projects
|
||||
// Local projects here
|
||||
implementation project(':treeview')
|
||||
implementation project(':emulatorview')
|
||||
implementation project(':javapoet')
|
||||
implementation project(':lsp')
|
||||
implementation project(':layout-inflater')
|
||||
implementation project(':lexers')
|
||||
implementation project(':shell')
|
||||
|
||||
// LSP4J
|
||||
implementation 'org.eclipse.lsp4j:org.eclipse.lsp4j:0.12.0'
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
</intent-filter>
|
||||
</service>
|
||||
<receiver
|
||||
android:name="com.itsaky.androidide.receivers.LogReceiver"
|
||||
android:name="com.itsaky.androidide.services.LogReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -9,22 +9,28 @@ import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.transition.TransitionManager;
|
||||
import com.blankj.utilcode.util.FileIOUtils;
|
||||
import com.blankj.utilcode.util.SizeUtils;
|
||||
import com.blankj.utilcode.util.ThreadUtils;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.itsaky.androidide.app.StudioActivity;
|
||||
import com.itsaky.androidide.databinding.ActivityDownloadBinding;
|
||||
import com.itsaky.androidide.fragments.sheets.ProgressSheet;
|
||||
import com.itsaky.androidide.managers.PreferenceManager;
|
||||
import com.itsaky.androidide.shell.ShellServer;
|
||||
import com.itsaky.androidide.shell.IProcessExecutor;
|
||||
import com.itsaky.androidide.shell.ProcessExecutorFactory;
|
||||
import com.itsaky.androidide.shell.ProcessStreamsHolder;
|
||||
import com.itsaky.androidide.tasks.TaskExecutor;
|
||||
import com.itsaky.androidide.tasks.callables.ListDirectoryCallable;
|
||||
import com.itsaky.androidide.utils.Environment;
|
||||
import com.itsaky.androidide.utils.FileUtil;
|
||||
import com.itsaky.androidide.utils.InputStreamLineReader;
|
||||
import com.itsaky.toaster.Toaster;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import com.itsaky.androidide.utils.Environment;
|
||||
|
||||
public class DownloadActivity extends StudioActivity {
|
||||
|
||||
@@ -68,7 +74,7 @@ public class DownloadActivity extends StudioActivity {
|
||||
.build()
|
||||
.forResult(abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE);
|
||||
} catch (Throwable th){
|
||||
getApp().writeException(th);
|
||||
LOG.error("Unable to start file picker", th);
|
||||
}
|
||||
} else if(id == binding.install.getId()) {
|
||||
if(choosenDir == null) {
|
||||
@@ -86,47 +92,119 @@ public class DownloadActivity extends StudioActivity {
|
||||
}
|
||||
|
||||
private final String DONE = "DONE";
|
||||
|
||||
private void installAll() {
|
||||
showProgress();
|
||||
getApp().getPrefManager().putBoolean(PreferenceManager.KEY_FRAMEWORK_DOWNLOADED, true);
|
||||
final ShellServer server = getApp().newShell(__ -> checkInstalled(__));
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("cd $HOME && ");
|
||||
sb.append("echo 'Installing...' && ");
|
||||
File[] files = choosenDir.listFiles(ARCHIVE_FILTER);
|
||||
if(files != null) {
|
||||
for(File f : files) {
|
||||
if(f.getName().endsWith(".tar.xz")) {
|
||||
if(f.getName().startsWith("androidide-sysroot")) {
|
||||
sb.append("cd $SYSROOT/.. && ");
|
||||
}
|
||||
sb.append("$BUSYBOX tar xvJf '" + f.getAbsolutePath() + "' && ");
|
||||
sb.append("cd $HOME && ");
|
||||
} else if(f.getName().endsWith(".zip")) {
|
||||
sb.append("$BUSYBOX unzip '" + f.getAbsolutePath() + "' && ");
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
final File script = createExtractScript();
|
||||
final ProcessStreamsHolder holder = new ProcessStreamsHolder ();
|
||||
final IProcessExecutor executor = ProcessExecutorFactory.commonExecutor();
|
||||
|
||||
executor.execAsync(holder,
|
||||
code -> onInstallProcessExit (code),
|
||||
true,
|
||||
Environment.BUSYBOX.getAbsolutePath(), "sh",// We use busybox's sh, because Environment.SHELL is not installed yet...
|
||||
script.getAbsolutePath()
|
||||
);
|
||||
|
||||
final InputStreamLineReader reader = new InputStreamLineReader (holder.in, line -> onInstallationOutput (line));
|
||||
new Thread (reader).start();
|
||||
|
||||
} catch (DownloadActivity.InstallationException e) {
|
||||
LOG.error("Installation error", e);
|
||||
onInstallationFailed(e.exitCode);
|
||||
} catch (IOException e) {
|
||||
LOG.error("Installation error", e);
|
||||
onInstallationFailed(5); // Exit code 5 : I/O Error
|
||||
}
|
||||
sb.append("echo 'Cleaning unsupported flags in binaries...' && $BUSYBOX find $JAVA_HOME -type f -exec androidide-cleaner {} \\; && ");
|
||||
sb.append("echo " + DONE);
|
||||
|
||||
server.bgAppend(sb.toString());
|
||||
}
|
||||
|
||||
private void checkInstalled(CharSequence out) {
|
||||
if(out != null) {
|
||||
final String line = out.toString().trim();
|
||||
runOnUiThread(() -> {
|
||||
getProgressSheet().setSubMessage(line);
|
||||
if(line.contains(DONE)) {
|
||||
getApp().getPrefManager().putBoolean(PreferenceManager.KEY_FRAMEWORK_INSTALLED, true);
|
||||
showRestartNeeded();
|
||||
if(getProgressSheet().isShowing())
|
||||
getProgressSheet().dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
private void onInstallationOutput (final String line) {
|
||||
ThreadUtils.runOnUiThread(() -> {
|
||||
getProgressSheet().setSubMessage(line);
|
||||
});
|
||||
}
|
||||
|
||||
private void onInstallProcessExit (final int code) {
|
||||
ThreadUtils.runOnUiThread(() -> {
|
||||
if (code == 0) { // 0 = normal execution
|
||||
getApp().getPrefManager().putBoolean(PreferenceManager.KEY_FRAMEWORK_INSTALLED, true);
|
||||
showRestartNeeded();
|
||||
if(getProgressSheet().isShowing()) {
|
||||
getProgressSheet().dismiss();
|
||||
}
|
||||
} else {
|
||||
onInstallationFailed (code);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onInstallationFailed(int code) {
|
||||
// TODO Can this be improved by adding some animations?
|
||||
|
||||
if(getProgressSheet().isShowing()) {
|
||||
getProgressSheet().dismiss();
|
||||
}
|
||||
|
||||
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder (this, R.style.AppTheme_MaterialAlertDialog);
|
||||
builder.setTitle(R.string.title_installation_failed);
|
||||
builder.setMessage(getString(R.string.msg_installation_failed, code));
|
||||
builder.setPositiveButton(android.R.string.ok, (d, w) -> {
|
||||
d.dismiss();
|
||||
finishAffinity();
|
||||
});
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private File createExtractScript() throws DownloadActivity.InstallationException {
|
||||
final StringBuilder sb = new StringBuilder ();
|
||||
sb.append("cd");
|
||||
joiner(sb);
|
||||
sb.append("echo 'Installing...'");
|
||||
joiner(sb);
|
||||
|
||||
File[] files = choosenDir.listFiles(ARCHIVE_FILTER);
|
||||
|
||||
if (files == null || files.length <= 0) {
|
||||
throw new InstallationException (2);
|
||||
}
|
||||
|
||||
for (File f : files) {
|
||||
if(f.getName().endsWith(".tar.xz")) {
|
||||
if(f.getName().startsWith("androidide-sysroot")) {
|
||||
sb.append("cd $SYSROOT/..");
|
||||
joiner(sb);
|
||||
}
|
||||
sb.append("$BUSYBOX tar xvJf '" + f.getAbsolutePath() + "'");
|
||||
joiner(sb);
|
||||
sb.append("cd $HOME");
|
||||
joiner(sb);
|
||||
} else if(f.getName().endsWith(".zip")) {
|
||||
sb.append("$BUSYBOX unzip '" + f.getAbsolutePath() + "'");
|
||||
joiner(sb);
|
||||
}
|
||||
}
|
||||
|
||||
sb.append("echo 'Cleaning unsupported flags in binaries...'");
|
||||
joiner(sb);
|
||||
sb.append("$BUSYBOX find $JAVA_HOME -type f -exec androidide-cleaner {} \\;");
|
||||
joiner(sb);
|
||||
sb.append("echo " + DONE);
|
||||
|
||||
final File script = new File (Environment.TMP_DIR, "extract_tools.sh");
|
||||
if (!FileIOUtils.writeFileFromString(script, sb.toString())) {
|
||||
throw new InstallationException (2);
|
||||
}
|
||||
|
||||
return script;
|
||||
}
|
||||
|
||||
private void joiner (StringBuilder sb) {
|
||||
sb.append(" && ");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
@@ -200,4 +278,11 @@ public class DownloadActivity extends StudioActivity {
|
||||
return p1.isFile() && (p1.getName().endsWith(".tar.xz") || p1.getName().endsWith(".zip"));
|
||||
}
|
||||
};
|
||||
|
||||
private class InstallationException extends Exception {
|
||||
private final int exitCode;
|
||||
public InstallationException(int exitCode) {
|
||||
this.exitCode = exitCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,15 +56,15 @@ import com.itsaky.androidide.language.logs.LogLanguageImpl;
|
||||
import com.itsaky.androidide.lsp.LSP;
|
||||
import com.itsaky.androidide.lsp.LSPProvider;
|
||||
import com.itsaky.androidide.managers.PreferenceManager;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
import com.itsaky.androidide.project.AndroidProject;
|
||||
import com.itsaky.androidide.models.DiagnosticGroup;
|
||||
import com.itsaky.androidide.models.LogLine;
|
||||
import com.itsaky.androidide.models.SaveResult;
|
||||
import com.itsaky.androidide.models.SearchResult;
|
||||
import com.itsaky.androidide.models.SheetOption;
|
||||
import com.itsaky.androidide.models.project.IDEProject;
|
||||
import com.itsaky.androidide.project.IDEProject;
|
||||
import com.itsaky.androidide.project.ProjectResourceFinder;
|
||||
import com.itsaky.androidide.receivers.LogReceiver;
|
||||
import com.itsaky.androidide.services.LogReceiver;
|
||||
import com.itsaky.androidide.services.IDEService;
|
||||
import com.itsaky.androidide.shell.ShellServer;
|
||||
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
|
||||
@@ -98,6 +98,7 @@ import org.eclipse.lsp4j.Diagnostic;
|
||||
import org.eclipse.lsp4j.DidChangeConfigurationParams;
|
||||
import org.eclipse.lsp4j.InitializeResult;
|
||||
import org.eclipse.lsp4j.Position;
|
||||
import com.itsaky.androidide.lsp.IDELanguageClientImpl;
|
||||
|
||||
public class EditorActivity extends StudioActivity implements FileTreeFragment.FileActionListener,
|
||||
TabLayout.OnTabSelectedListener,
|
||||
@@ -1199,9 +1200,14 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
}
|
||||
|
||||
private void startServices() {
|
||||
|
||||
if (!IDELanguageClientImpl.isInitialized()) {
|
||||
IDELanguageClientImpl.initialize(this);
|
||||
}
|
||||
|
||||
// Actually, we don't need to start FileOptionsHandler
|
||||
// Because it would work anyway
|
||||
// But it's a good practice to call the start() method
|
||||
// But still we do...
|
||||
mFileOptionsHandler.start();
|
||||
|
||||
mBuildServiceHandler.start();
|
||||
|
||||
@@ -14,10 +14,10 @@ import com.itsaky.androidide.databinding.LayoutCreateProjectContentBinding;
|
||||
import com.itsaky.androidide.fragments.sheets.ProgressSheet;
|
||||
import com.itsaky.androidide.interfaces.ProjectWriterCallback;
|
||||
import com.itsaky.androidide.managers.PreferenceManager;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
import com.itsaky.androidide.project.AndroidProject;
|
||||
import com.itsaky.androidide.models.NewProjectDetails;
|
||||
import com.itsaky.androidide.models.ProjectTemplate;
|
||||
import com.itsaky.androidide.project.utils.ProjectFinder;
|
||||
import com.itsaky.androidide.utils.ProjectFinder;
|
||||
import com.itsaky.androidide.tasks.TaskExecutor;
|
||||
import com.itsaky.androidide.tasks.callables.ProjectCreatorCallable;
|
||||
import com.itsaky.androidide.utils.TransformUtils;
|
||||
|
||||
@@ -4,7 +4,7 @@ import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||
import com.itsaky.androidide.fragments.EditorFragment;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
import com.itsaky.androidide.project.AndroidProject;
|
||||
import com.itsaky.androidide.models.SaveResult;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import java.io.File;
|
||||
|
||||
@@ -7,7 +7,7 @@ import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.itsaky.androidide.databinding.LayoutModuleInfoItemBinding;
|
||||
import com.itsaky.androidide.models.project.IDEModule;
|
||||
import com.itsaky.androidide.project.IDEModule;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -8,7 +8,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.databinding.LayoutProjectsItemBinding;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
import com.itsaky.androidide.project.AndroidProject;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
@@ -213,7 +213,7 @@ public class StudioApp extends MultiDexApplication {
|
||||
}
|
||||
|
||||
public ShellServer newShell(ShellServer.Callback callback, boolean redirectErrors) {
|
||||
ShellServer shellServer = new ShellServer(callback, "sh", Environment.mkdirIfNotExits(getRootDir()).getAbsolutePath(), redirectErrors);
|
||||
ShellServer shellServer = new ShellServer(callback, "sh", Environment.mkdirIfNotExits(getRootDir()).getAbsolutePath(), Environment.getEnvironment(true), redirectErrors);
|
||||
shellServer.start();
|
||||
return shellServer;
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ import com.itsaky.androidide.fragments.preferences.EditorPreferences;
|
||||
import com.itsaky.androidide.language.groovy.GroovyLanguage;
|
||||
import com.itsaky.androidide.language.java.JavaLanguage;
|
||||
import com.itsaky.androidide.language.xml.XMLLanguage;
|
||||
import com.itsaky.androidide.language.xml.lexer.XMLLexer;
|
||||
import com.itsaky.androidide.lexers.xml.XMLLexer;
|
||||
import com.itsaky.androidide.managers.PreferenceManager;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
import com.itsaky.androidide.project.AndroidProject;
|
||||
import com.itsaky.androidide.models.ConstantsBridge;
|
||||
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
|
||||
import com.itsaky.androidide.tasks.TaskExecutor;
|
||||
|
||||
@@ -16,7 +16,7 @@ import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.adapters.viewholders.FileTreeViewHolder;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.databinding.LayoutEditorFileTreeBinding;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
import com.itsaky.androidide.project.AndroidProject;
|
||||
import com.itsaky.androidide.tasks.TaskExecutor;
|
||||
import com.itsaky.androidide.tasks.callables.FileTreeCallable;
|
||||
import com.itsaky.androidide.utils.Environment;
|
||||
|
||||
@@ -14,7 +14,7 @@ import com.itsaky.androidide.EditorActivity;
|
||||
import com.itsaky.androidide.adapters.ProjectsListAdapter;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.databinding.LayoutProjectsBinding;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
import com.itsaky.androidide.project.AndroidProject;
|
||||
import com.itsaky.androidide.tasks.TaskExecutor;
|
||||
import com.itsaky.androidide.utils.FileUtil;
|
||||
import fr.arnaudguyon.xmltojsonlib.XmlToJson;
|
||||
@@ -26,7 +26,7 @@ import java.util.concurrent.Callable;
|
||||
import java.util.regex.Pattern;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import com.itsaky.androidide.project.utils.ProjectFinder;
|
||||
import com.itsaky.androidide.utils.ProjectFinder;
|
||||
|
||||
public class ProjectsFragment extends BaseFragment {
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.itsaky.androidide.adapters.ModuleInfoAdapter;
|
||||
import com.itsaky.androidide.databinding.LayoutProjectDetailsBinding;
|
||||
import com.itsaky.androidide.models.project.IDEProject;
|
||||
import com.itsaky.androidide.project.IDEProject;
|
||||
import java.io.File;
|
||||
import java.util.Locale;
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ import android.view.View;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.managers.PreferenceManager;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
import com.itsaky.androidide.models.project.IDEModule;
|
||||
import com.itsaky.androidide.models.project.IDEProject;
|
||||
import com.itsaky.androidide.project.AndroidProject;
|
||||
import com.itsaky.androidide.project.IDEModule;
|
||||
import com.itsaky.androidide.project.IDEProject;
|
||||
import com.itsaky.androidide.services.IDEService;
|
||||
import com.itsaky.androidide.tasks.GradleTask;
|
||||
import com.itsaky.androidide.tasks.gradle.build.ApkGeneratingTask;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.itsaky.androidide.handlers;
|
||||
|
||||
import com.itsaky.androidide.EditorActivity;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
import com.itsaky.androidide.models.project.IDEProject;
|
||||
import com.itsaky.androidide.project.AndroidProject;
|
||||
import com.itsaky.androidide.project.IDEProject;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,18 +14,18 @@ import org.eclipse.lsp4j.Diagnostic;
|
||||
public class GroovyAnalyzer extends io.github.rosemoe.editor.langs.AbstractCodeAnalyzer {
|
||||
|
||||
private final EditorLanguage language;
|
||||
|
||||
public GroovyAnalyzer (EditorLanguage language) {
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void analyze(IDELanguageServer server, File file, CharSequence content, TextAnalyzeResult colors, TextAnalyzer.AnalyzeThread.Delegate delegate) {
|
||||
try {
|
||||
GroovyLexerImpl lexer = new GroovyLexerImpl(this.language, (Content) content, colors);
|
||||
lexer.init();
|
||||
|
||||
// Adding spans, hex colors, block lines and all is handled by JavaLexerImpl
|
||||
// Adding spans, hex colors, block lines and all is handled by GroovyLexerImpl
|
||||
while (delegate.shouldAnalyze()) {
|
||||
// null = EOF
|
||||
if(lexer.nextToken() == null)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.itsaky.androidide.language.groovy;
|
||||
|
||||
import com.itsaky.androidide.language.BaseLanguage;
|
||||
import com.itsaky.androidide.language.groovy.lexer.GroovyLexer;
|
||||
import com.itsaky.androidide.language.java.manager.JavaCharacter;
|
||||
import com.itsaky.androidide.lexers.groovy.GroovyLexer;
|
||||
import com.itsaky.androidide.lsp.LSPProvider;
|
||||
import com.itsaky.lsp.services.IDELanguageServer;
|
||||
import io.github.rosemoe.editor.interfaces.AutoCompleteProvider;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.itsaky.androidide.language.java;
|
||||
|
||||
import com.itsaky.androidide.antlr4.java.JavaLexer;
|
||||
import com.itsaky.androidide.antlr4.java.JavaParser;
|
||||
import com.itsaky.androidide.lexers.java.JavaLexer;
|
||||
import com.itsaky.androidide.lexers.java.JavaParser;
|
||||
import com.itsaky.androidide.language.BaseLanguage;
|
||||
import com.itsaky.androidide.language.java.manager.JavaCharacter;
|
||||
import com.itsaky.androidide.lsp.LSPProvider;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.itsaky.androidide.language.xml;
|
||||
|
||||
import android.graphics.Color;
|
||||
import com.itsaky.androidide.language.xml.lexer.XMLLexer;
|
||||
import com.itsaky.androidide.lexers.xml.XMLLexer;
|
||||
import com.itsaky.lsp.SemanticHighlight;
|
||||
import com.itsaky.lsp.services.IDELanguageServer;
|
||||
import io.github.rosemoe.editor.interfaces.CodeAnalyzer;
|
||||
import io.github.rosemoe.editor.struct.HexColor;
|
||||
import io.github.rosemoe.editor.text.CharPosition;
|
||||
import io.github.rosemoe.editor.text.TextAnalyzeResult;
|
||||
@@ -12,13 +12,12 @@ import io.github.rosemoe.editor.widget.EditorColorScheme;
|
||||
import java.io.File;
|
||||
import java.io.StringReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CodePointCharStream;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import com.itsaky.lsp.SemanticHighlight;
|
||||
import java.util.Map;
|
||||
import org.eclipse.lsp4j.Diagnostic;
|
||||
|
||||
public class XMLAnalyzer extends io.github.rosemoe.editor.langs.AbstractCodeAnalyzer {
|
||||
@@ -87,15 +86,15 @@ public class XMLAnalyzer extends io.github.rosemoe.editor.langs.AbstractCodeAnal
|
||||
} catch (Throwable th) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setSemanticHighlights(SemanticHighlight highlights) {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void updateDiagnostics(Map<Integer, Map<Integer, Diagnostic>> diagnostics) {
|
||||
}
|
||||
|
||||
|
||||
private void addHexColorIfPresent(Token token, HashMap<HexColor, Integer> lineColors, int line, int column) {
|
||||
Matcher m = HEX.matcher(token.getText());
|
||||
if (m.find()) {
|
||||
@@ -104,17 +103,11 @@ public class XMLAnalyzer extends io.github.rosemoe.editor.langs.AbstractCodeAnal
|
||||
final CharPosition end = new CharPosition(line, start.column + (m.end() - m.start()));
|
||||
final HexColor key = new HexColor(start, end);
|
||||
final Integer value = Color.parseColor(token.getText().substring(m.start(), m.end()));
|
||||
|
||||
// Null checks
|
||||
start.getClass();
|
||||
end.getClass();
|
||||
key.getClass();
|
||||
value.getClass();
|
||||
|
||||
|
||||
lineColors.put(key, value);
|
||||
} catch (Throwable th) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final Pattern HEX = Pattern.compile("#[a-fA-F0-9]{3,8}");
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package com.itsaky.androidide.language.xml;
|
||||
|
||||
import com.itsaky.androidide.language.BaseLanguage;
|
||||
import com.itsaky.androidide.language.java.manager.JavaCharacter;
|
||||
import com.itsaky.androidide.language.xml.lexer.XMLLexer;
|
||||
import com.itsaky.androidide.lexers.xml.XMLLexer;
|
||||
import com.itsaky.androidide.lsp.LSPProvider;
|
||||
import com.itsaky.lsp.services.IDELanguageServer;
|
||||
import io.github.rosemoe.editor.interfaces.AutoCompleteProvider;
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ package com.itsaky.androidide.language.xml.completion;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.language.xml.lexer.XMLLexer;
|
||||
import com.itsaky.androidide.lexers.xml.XMLLexer;
|
||||
import com.itsaky.attrinfo.AttrInfo;
|
||||
import com.itsaky.attrinfo.models.Attr;
|
||||
import com.itsaky.widgets.WidgetInfo;
|
||||
|
||||
@@ -30,8 +30,6 @@ public class AsynchronousSocketLauncher extends LSPClientLauncher {
|
||||
serverSocket = AsynchronousServerSocketChannel.open();
|
||||
serverSocket.bind(new InetSocketAddress(this.port));
|
||||
|
||||
languageClient.startServerViaCommand();
|
||||
|
||||
final AsynchronousSocketChannel server = serverSocket.accept().get();
|
||||
final OutputStream outWriter = new OutputStreamWrapper(Channels.newOutputStream(server));
|
||||
final InputStream inReader = Channels.newInputStream(server);
|
||||
@@ -40,8 +38,6 @@ public class AsynchronousSocketLauncher extends LSPClientLauncher {
|
||||
this.listeningFuture = launcher.startListening();
|
||||
this.server = launcher.getRemoteProxy();
|
||||
|
||||
this.languageClient.onServerConnected(this.server);
|
||||
|
||||
try {
|
||||
listeningFuture.get();
|
||||
} catch (Throwable th) {}
|
||||
@@ -62,7 +58,5 @@ public class AsynchronousSocketLauncher extends LSPClientLauncher {
|
||||
}
|
||||
|
||||
CloseUtils.closeIOQuietly(serverSocket);
|
||||
|
||||
this.languageClient.onServerDisconnected();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,28 +61,57 @@ import org.eclipse.lsp4j.SignatureHelp;
|
||||
import org.eclipse.lsp4j.SignatureInformation;
|
||||
import org.eclipse.lsp4j.TextEdit;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.eclipse.lsp4j.SetTraceParams;
|
||||
import org.eclipse.lsp4j.ApplyWorkspaceEditResponse;
|
||||
import org.eclipse.lsp4j.WorkDoneProgressCreateParams;
|
||||
import org.eclipse.lsp4j.ProgressParams;
|
||||
import org.eclipse.lsp4j.ConfigurationParams;
|
||||
import org.eclipse.lsp4j.RegistrationParams;
|
||||
import org.eclipse.lsp4j.ApplyWorkspaceEditParams;
|
||||
import org.eclipse.lsp4j.MessageActionItem;
|
||||
import org.eclipse.lsp4j.WorkspaceFolder;
|
||||
import org.eclipse.lsp4j.ShowMessageRequestParams;
|
||||
import org.eclipse.lsp4j.UnregistrationParams;
|
||||
|
||||
/**
|
||||
* AndroidIDE specific implementation of the LanguageClient
|
||||
*/
|
||||
public abstract class IDELanguageClientImpl implements IDELanguageClient {
|
||||
|
||||
public class IDELanguageClientImpl implements IDELanguageClient {
|
||||
|
||||
protected static final Gson gson = new Gson();
|
||||
protected static final Logger LOG = Logger.instance("AbstractLanguageClient");
|
||||
|
||||
protected EditorActivityProvider activityProvider;
|
||||
public static final int DIAGNOSTIC_TRANSITION_DURATION = 80;
|
||||
|
||||
private final Map<File, List<Diagnostic>> diagnostics = new HashMap<>();
|
||||
private final StarterListener starterListener;
|
||||
private final OnConnectedListener onConnectedListener;
|
||||
|
||||
private static IDELanguageClientImpl mInstance;
|
||||
|
||||
protected EditorActivityProvider activityProvider;
|
||||
private boolean isConnected;
|
||||
|
||||
public static final int DIAGNOSTIC_TRANSITION_DURATION = 80;
|
||||
|
||||
public IDELanguageClientImpl(StarterListener starterListener, OnConnectedListener onConnectedListener) {
|
||||
this.starterListener = starterListener;
|
||||
this.onConnectedListener = onConnectedListener;
|
||||
private IDELanguageClientImpl (EditorActivityProvider provider) {
|
||||
setActivityProvider(provider);
|
||||
}
|
||||
|
||||
public static IDELanguageClientImpl getInstance () {
|
||||
if (mInstance == null) {
|
||||
throw new IllegalStateException ("Client not initialized");
|
||||
}
|
||||
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
public static IDELanguageClientImpl initialize (EditorActivityProvider provider) {
|
||||
if (mInstance != null) {
|
||||
throw new IllegalStateException ("Client is already initialized");
|
||||
}
|
||||
|
||||
mInstance = new IDELanguageClientImpl (provider);
|
||||
|
||||
return getInstance();
|
||||
}
|
||||
|
||||
public static boolean isInitialized () {
|
||||
return mInstance != null;
|
||||
}
|
||||
|
||||
public void setActivityProvider(EditorActivityProvider provider) {
|
||||
@@ -95,33 +124,7 @@ public abstract class IDELanguageClientImpl implements IDELanguageClient {
|
||||
public boolean isConnected() {
|
||||
return isConnected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the LanguageServer is connected successfully
|
||||
*/
|
||||
protected void onServerConnected(IDELanguageServer server) {
|
||||
this.isConnected = true;
|
||||
if (onConnectedListener != null)
|
||||
onConnectedListener.onConnected(server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the connection to the LanguageServer was cancelled.
|
||||
*/
|
||||
protected void onServerDisconnected() {
|
||||
this.isConnected = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the ServerSocket is started. At this point, the {@link LSPClient} waits for the server to connect. <br>
|
||||
* This is (probably) the proper time to start your language server as ServerSocket is waiting for a client to connect.
|
||||
*/
|
||||
protected void startServerViaCommand() {
|
||||
if (this.starterListener != null) {
|
||||
starterListener.startServer();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected EditorActivity activity() {
|
||||
if (activityProvider == null) return null;
|
||||
return activityProvider.provide();
|
||||
@@ -602,25 +605,65 @@ public abstract class IDELanguageClientImpl implements IDELanguageClient {
|
||||
return new DiagnosticsAdapter(mapAsGroup(this.diagnostics), activity());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports connection progress
|
||||
*/
|
||||
protected void connectionReport(String message) {
|
||||
|
||||
|
||||
/**************************************************
|
||||
UNUSED METHODS
|
||||
**************************************************/
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ApplyWorkspaceEditResponse> applyEdit(ApplyWorkspaceEditParams p1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when there was an error connecting to server.
|
||||
*/
|
||||
protected void connectionError(Throwable th) {
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<Object>> configuration(ConfigurationParams p1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static interface StarterListener {
|
||||
void startServer();
|
||||
@Override
|
||||
public CompletableFuture<Void> createProgress(WorkDoneProgressCreateParams p1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static interface OnConnectedListener {
|
||||
void onConnected(IDELanguageServer server);
|
||||
@Override
|
||||
public void notifyProgress(ProgressParams p1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> refreshCodeLenses() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> refreshSemanticTokens() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> registerCapability(RegistrationParams p1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTrace(SetTraceParams p1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showMessage(MessageParams p1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<MessageActionItem> showMessageRequest(ShowMessageRequestParams p1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> unregisterCapability(UnregistrationParams p1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<WorkspaceFolder>> workspaceFolders() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ public abstract class LSPClientLauncher extends Thread {
|
||||
try {
|
||||
launch();
|
||||
} catch (Throwable th) {
|
||||
languageClient.connectionError(th);
|
||||
// TODO Log this
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.itsaky.androidide.lsp;
|
||||
|
||||
import com.itsaky.lsp.services.IDELanguageClient;
|
||||
import com.itsaky.lsp.services.IDELanguageServer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -54,10 +55,6 @@ public class LSPProvider {
|
||||
capabilitiesMap.put(languageCode, capabilities);
|
||||
}
|
||||
|
||||
public static void setClientForLanguage(String languageCode, IDELanguageClientImpl client) {
|
||||
clientMap.put(languageCode, client);
|
||||
}
|
||||
|
||||
public static Map<String, IDELanguageServer> getAvailableServers() {
|
||||
return serverMap;
|
||||
}
|
||||
@@ -66,10 +63,6 @@ public class LSPProvider {
|
||||
return capabilitiesMap;
|
||||
}
|
||||
|
||||
public static Map<String, IDELanguageClientImpl> getAllServerClients() {
|
||||
return clientMap;
|
||||
}
|
||||
|
||||
public static IDELanguageServer getServerForLanguage(String languageCode) {
|
||||
return languageCode == null ? null : serverMap.get(languageCode);
|
||||
}
|
||||
@@ -78,7 +71,7 @@ public class LSPProvider {
|
||||
return language == null ? null : capabilitiesMap.get(language);
|
||||
}
|
||||
|
||||
public static IDELanguageClientImpl getClientForLanguage(String language) {
|
||||
return language == null ? null : clientMap.get(language);
|
||||
public static IDELanguageClientImpl getClient() {
|
||||
return IDELanguageClientImpl.isInitialized() ? IDELanguageClientImpl.getInstance() : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,13 +13,15 @@ public class StandardStreamsLauncher extends LSPClientLauncher {
|
||||
|
||||
private final InputStream in;
|
||||
private final OutputStream out;
|
||||
private final String langCode;
|
||||
|
||||
private Future<Void> listening;
|
||||
|
||||
public StandardStreamsLauncher(IDELanguageClientImpl client, InputStream in, OutputStream out) {
|
||||
public StandardStreamsLauncher(IDELanguageClientImpl client, InputStream in, OutputStream out, String langCode) {
|
||||
super(client);
|
||||
this.in = new BufferedInputStream( in );
|
||||
this.out = new BufferedOutputStream( out );
|
||||
this.langCode = langCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -29,7 +31,7 @@ public class StandardStreamsLauncher extends LSPClientLauncher {
|
||||
this.server = server.getRemoteProxy();
|
||||
this.listening = server.startListening();
|
||||
|
||||
languageClient.onServerConnected(this.server);
|
||||
LSPProvider.setLanguageServer(this.langCode, this.server);
|
||||
|
||||
try {
|
||||
listening.get();
|
||||
@@ -52,7 +54,5 @@ public class StandardStreamsLauncher extends LSPClientLauncher {
|
||||
listening.cancel(true);
|
||||
}
|
||||
CloseUtils.closeIO(in, out);
|
||||
|
||||
languageClient.onServerDisconnected();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
package com.itsaky.androidide.lsp.client.java;
|
||||
|
||||
import com.itsaky.androidide.lsp.IDELanguageClientImpl;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.eclipse.lsp4j.ApplyWorkspaceEditParams;
|
||||
import org.eclipse.lsp4j.ApplyWorkspaceEditResponse;
|
||||
import org.eclipse.lsp4j.ConfigurationParams;
|
||||
import org.eclipse.lsp4j.LogTraceParams;
|
||||
import org.eclipse.lsp4j.MessageActionItem;
|
||||
import org.eclipse.lsp4j.MessageParams;
|
||||
import org.eclipse.lsp4j.ProgressParams;
|
||||
import org.eclipse.lsp4j.PublishDiagnosticsParams;
|
||||
import org.eclipse.lsp4j.RegistrationParams;
|
||||
import org.eclipse.lsp4j.SetTraceParams;
|
||||
import org.eclipse.lsp4j.ShowDocumentParams;
|
||||
import org.eclipse.lsp4j.ShowDocumentResult;
|
||||
import org.eclipse.lsp4j.ShowMessageRequestParams;
|
||||
import org.eclipse.lsp4j.UnregistrationParams;
|
||||
import org.eclipse.lsp4j.WorkDoneProgressCreateParams;
|
||||
import org.eclipse.lsp4j.WorkspaceFolder;
|
||||
|
||||
public class JavaLanguageClient extends IDELanguageClientImpl {
|
||||
|
||||
public JavaLanguageClient(StarterListener starterListener, OnConnectedListener onConnectedListener) {
|
||||
super(starterListener, onConnectedListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ApplyWorkspaceEditResponse> applyEdit(ApplyWorkspaceEditParams p1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<Object>> configuration(ConfigurationParams p1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> createProgress(WorkDoneProgressCreateParams p1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyProgress(ProgressParams p1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> refreshCodeLenses() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> refreshSemanticTokens() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> registerCapability(RegistrationParams p1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setTrace(SetTraceParams p1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showMessage(MessageParams p1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<MessageActionItem> showMessageRequest(ShowMessageRequestParams p1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> unregisterCapability(UnregistrationParams p1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<WorkspaceFolder>> workspaceFolders() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,23 @@
|
||||
package com.itsaky.androidide.lsp.handlers;
|
||||
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.lsp.IDELanguageClientImpl;
|
||||
import com.itsaky.androidide.lsp.LSP;
|
||||
import com.itsaky.androidide.lsp.LSPClientLauncher;
|
||||
import com.itsaky.androidide.lsp.LSPProvider;
|
||||
import com.itsaky.androidide.lsp.StandardStreamsLauncher;
|
||||
import com.itsaky.androidide.lsp.client.java.JavaLanguageClient;
|
||||
import com.itsaky.androidide.shell.IProcessExecutor;
|
||||
import com.itsaky.androidide.shell.IProcessExitListener;
|
||||
import com.itsaky.androidide.shell.ProcessExecutorFactory;
|
||||
import com.itsaky.androidide.shell.ProcessStreamsHolder;
|
||||
import com.itsaky.androidide.utils.Environment;
|
||||
import com.itsaky.androidide.utils.FileUtil;
|
||||
import com.itsaky.androidide.utils.InputStreamWriter;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import com.itsaky.lsp.services.IDELanguageServer;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -21,18 +29,6 @@ import org.eclipse.lsp4j.WorkspaceFolder;
|
||||
|
||||
public class JLSHandler implements LSPHandler {
|
||||
|
||||
/**
|
||||
* Why not use {@link com.itsaky.androidide.shell.ShellServer ShellServer} instead?
|
||||
* Because it will try to read from the process's input stream
|
||||
* and at the same time, LSP4J will try to do the same. But,
|
||||
* as {@code ShellServer} will be started first, LSP4J will not he able to read at all.
|
||||
* <p>
|
||||
* That's why we use a ProcessBuilder without redirecting {@code System.err} of the process
|
||||
*/
|
||||
private static ProcessBuilder mShellBuilder;
|
||||
private static Process mShell;
|
||||
|
||||
private static JavaLanguageClient javaClient;
|
||||
private static LSPClientLauncher mLauncher;
|
||||
|
||||
private static final Logger LOG = Logger.instance("JLSHandler");
|
||||
@@ -40,26 +36,32 @@ public class JLSHandler implements LSPHandler {
|
||||
|
||||
@Override
|
||||
public void start(Runnable onStarted) {
|
||||
if (mShell != null && mLauncher != null && mLauncher.isConnected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
mShellBuilder = new ProcessBuilder("/system/bin/sh");
|
||||
mShellBuilder.directory(Environment.HOME);
|
||||
mShellBuilder.redirectErrorStream(false);
|
||||
mShellBuilder.environment().putAll(Environment.getEnvironment(false));
|
||||
final IProcessExecutor executor = ProcessExecutorFactory.commonExecutor();
|
||||
final ProcessStreamsHolder holder = new ProcessStreamsHolder();
|
||||
final IProcessExitListener listener = new IProcessExitListener() {
|
||||
@Override
|
||||
public void onExit(int code) {
|
||||
LOG.info("Java Language server terminated with exit code " + code);
|
||||
shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
mShell = mShellBuilder.start();
|
||||
executor.execAsync(holder, listener, false, Environment.JAVA.getAbsolutePath(), "-agentlib:hook2", "-jar", Environment.JLS_JAR.getAbsolutePath(), QUIET ? "--quiet" : "");
|
||||
startReader (holder.err); // holder.in is used by LSP4J
|
||||
|
||||
mShell.getOutputStream().write(("java -agentlib:hook2 -jar " + Environment.JLS_JAR.getAbsolutePath() + (QUIET ? " --quiet" : "") + "\n").getBytes());
|
||||
mShell.getOutputStream().flush();
|
||||
if (!IDELanguageClientImpl.isInitialized()) {
|
||||
IDELanguageClientImpl.initialize(LSP.PROVIDER);
|
||||
}
|
||||
|
||||
javaClient = new JavaLanguageClient(null, server -> storeServerInfoAndNotify(server, onStarted));
|
||||
javaClient.setActivityProvider(LSP.PROVIDER);
|
||||
mLauncher = new StandardStreamsLauncher(javaClient, mShell.getInputStream(), mShell.getOutputStream());
|
||||
IDELanguageClientImpl client = IDELanguageClientImpl.getInstance();
|
||||
|
||||
mLauncher = new StandardStreamsLauncher(client, holder.in, holder.out, LSPProvider.LANGUAGE_JAVA);
|
||||
mLauncher.start();
|
||||
} catch (Throwable th) {}
|
||||
|
||||
} catch (Throwable th) {
|
||||
LOG.error("Unable to start Java Language server", th);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -89,6 +91,7 @@ public class JLSHandler implements LSPHandler {
|
||||
params.setCapabilities(LSP.getCapabilities());
|
||||
|
||||
CompletableFuture<InitializeResult> initResult = server.initialize(params);
|
||||
|
||||
try {
|
||||
InitializeResult result = initResult.get();
|
||||
if(result.getCapabilities() != null) {
|
||||
@@ -106,8 +109,6 @@ public class JLSHandler implements LSPHandler {
|
||||
final IDELanguageServer server = LSPProvider.getServerForLanguage(LSPProvider.LANGUAGE_JAVA);
|
||||
if (server == null)
|
||||
return;
|
||||
|
||||
// Initialized params do not have any parameters
|
||||
server.initialized(new InitializedParams());
|
||||
|
||||
}
|
||||
@@ -117,19 +118,16 @@ public class JLSHandler implements LSPHandler {
|
||||
if(mLauncher != null) {
|
||||
mLauncher.shutdown();
|
||||
}
|
||||
if(mShell != null) {
|
||||
mShell.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
private void startReader(InputStream in) throws FileNotFoundException {
|
||||
final InputStreamWriter writer = new InputStreamWriter (in, getOutputFile());
|
||||
final Thread readerThread = new Thread (writer, "JavaLanguageServerOutputReader");
|
||||
readerThread.setDaemon(true);
|
||||
readerThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the instance of Java Language Server in LSPProvider
|
||||
*/
|
||||
private void storeServerInfoAndNotify(IDELanguageServer server, Runnable startedListener) {
|
||||
LSPProvider.setLanguageServer(LSPProvider.LANGUAGE_JAVA, server);
|
||||
LSPProvider.setClientForLanguage(LSPProvider.LANGUAGE_JAVA, javaClient);
|
||||
|
||||
if(startedListener != null)
|
||||
startedListener.run();
|
||||
private File getOutputFile () {
|
||||
return new File (FileUtil.getExternalStorageDir(), "ide_xlog/jls_output.txt");
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -1,8 +1,7 @@
|
||||
package com.itsaky.androidide.models;
|
||||
package com.itsaky.androidide.project;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import androidx.core.util.Pair;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.itsaky.androidide.models.project;
|
||||
package com.itsaky.androidide.project;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
package com.itsaky.androidide.models.project;
|
||||
package com.itsaky.androidide.project;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.itsaky.androidide.models.project;
|
||||
package com.itsaky.androidide.project;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
@@ -12,8 +12,8 @@ import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.lsp.LSP;
|
||||
import com.itsaky.androidide.managers.PreferenceManager;
|
||||
import com.itsaky.androidide.models.project.IDEModule;
|
||||
import com.itsaky.androidide.models.project.IDEProject;
|
||||
import com.itsaky.androidide.project.IDEModule;
|
||||
import com.itsaky.androidide.project.IDEProject;
|
||||
import com.itsaky.androidide.shell.ShellServer;
|
||||
import com.itsaky.androidide.tasks.GradleTask;
|
||||
import com.itsaky.androidide.tasks.gradle.BaseGradleTasks;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.itsaky.androidide.receivers;
|
||||
package com.itsaky.androidide.services;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
+1
-1
@@ -3,7 +3,7 @@ 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.antlr4.java.JavaLexer;
|
||||
import com.itsaky.androidide.lexers.java.JavaLexer;
|
||||
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
|
||||
import io.github.rosemoe.editor.widget.EditorColorScheme;
|
||||
import java.io.StringReader;
|
||||
|
||||
+3
-3
@@ -1,11 +1,12 @@
|
||||
package com.itsaky.androidide.syntax.lexer.impls.groovy;
|
||||
|
||||
import com.itsaky.androidide.language.groovy.lexer.GroovyLexer;
|
||||
import com.itsaky.androidide.lexers.groovy.GroovyLexer;
|
||||
import com.itsaky.androidide.syntax.lexer.Lexer;
|
||||
import com.itsaky.androidide.syntax.lexer.impls.BaseJavaLexer;
|
||||
import com.itsaky.androidide.syntax.lexer.tokens.Token;
|
||||
import com.itsaky.androidide.syntax.lexer.tokens.TokenType;
|
||||
import com.itsaky.androidide.utils.LSPUtils;
|
||||
import io.github.rosemoe.editor.interfaces.EditorLanguage;
|
||||
import io.github.rosemoe.editor.interfaces.NewlineHandler;
|
||||
import io.github.rosemoe.editor.struct.BlockLine;
|
||||
import io.github.rosemoe.editor.text.CharPosition;
|
||||
@@ -21,7 +22,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import io.github.rosemoe.editor.interfaces.EditorLanguage;
|
||||
|
||||
public class GroovyLexerImpl extends BaseJavaLexer implements Lexer {
|
||||
|
||||
@@ -54,7 +54,7 @@ public class GroovyLexerImpl extends BaseJavaLexer implements Lexer {
|
||||
|
||||
@Override
|
||||
public void init() throws IOException {
|
||||
lexer = new GroovyLexer(CharStreams.fromReader(new StringReader(content.toString())));
|
||||
lexer = new GroovyLexer(content.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.itsaky.androidide.syntax.lexer.impls.java;
|
||||
|
||||
import androidx.core.util.Pair;
|
||||
import com.itsaky.androidide.antlr4.java.JavaLexer;
|
||||
import com.itsaky.androidide.lexers.java.JavaLexer;
|
||||
import com.itsaky.androidide.models.ConstantsBridge;
|
||||
import com.itsaky.androidide.syntax.lexer.Lexer;
|
||||
import com.itsaky.androidide.syntax.lexer.impls.BaseJavaLexer;
|
||||
|
||||
@@ -40,6 +40,7 @@ public final class Environment {
|
||||
public static final File JAVA;
|
||||
public static final File BUSYBOX;
|
||||
public static final File SHELL;
|
||||
public static final File BUSYBOX_SH;
|
||||
|
||||
public static File BOOTCLASSPATH;
|
||||
|
||||
@@ -83,6 +84,7 @@ public final class Environment {
|
||||
JAVA = new File(JAVA_HOME, "bin/java");
|
||||
BUSYBOX = new File(BINDIR, "busybox");
|
||||
SHELL = new File(BINDIR, "sh.sh");
|
||||
BUSYBOX_SH = new File (BINDIR, "sh");
|
||||
|
||||
if(!JAVA.canExecute()) {
|
||||
JAVA.setExecutable(true);
|
||||
@@ -96,6 +98,7 @@ public final class Environment {
|
||||
SHELL.setExecutable(true);
|
||||
}
|
||||
|
||||
System.setProperty("user.home", HOME.getAbsolutePath());
|
||||
System.setProperty("java.home", JAVA_HOME.getAbsolutePath());
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.itsaky.androidide.utils;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* Reads an input stream line by line
|
||||
*/
|
||||
public class InputStreamLineReader implements Runnable {
|
||||
|
||||
private final InputStream in;
|
||||
private final OnReadListener listener;
|
||||
|
||||
public InputStreamLineReader(InputStream in, OnReadListener listener) {
|
||||
this.in = in;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
final BufferedReader reader = new BufferedReader (new InputStreamReader (in));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null && listener != null) {
|
||||
listener.onRead(line);
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
public static interface OnReadListener {
|
||||
void onRead (String line);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.itsaky.androidide.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* Writes an InputStream to a file
|
||||
*/
|
||||
public class InputStreamWriter implements Runnable {
|
||||
|
||||
private final InputStream in;
|
||||
private final File out;
|
||||
|
||||
private final BufferedOutputStream os;
|
||||
|
||||
public InputStreamWriter(InputStream in, File out) throws FileNotFoundException {
|
||||
this.in = in;
|
||||
this.out = out;
|
||||
|
||||
this.os = new BufferedOutputStream (new FileOutputStream (out, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
final BufferedReader reader = new BufferedReader (new InputStreamReader (in));
|
||||
while (true) {
|
||||
final int data = reader.read();
|
||||
if (data == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
os.write(data);
|
||||
os.flush();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
package com.itsaky.androidide.project.utils;
|
||||
package com.itsaky.androidide.utils;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import com.blankj.utilcode.util.FileIOUtils;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
import com.itsaky.androidide.project.AndroidProject;
|
||||
import com.itsaky.androidide.utils.FileUtil;
|
||||
import fr.arnaudguyon.xmltojsonlib.XmlToJson;
|
||||
import java.io.File;
|
||||
@@ -761,7 +761,7 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
}
|
||||
this.mLanguage = lang;
|
||||
this.mLanguageServer = mLanguage.getLanguageServer();
|
||||
this.mLanguageClient = LSPProvider.getClientForLanguage(mLanguage.getLanguageCode());
|
||||
this.mLanguageClient = LSPProvider.getClient();
|
||||
|
||||
// Update spanner
|
||||
if (mSpanner != null) {
|
||||
|
||||
@@ -244,6 +244,7 @@ public class EditorAutoCompleteWindow extends EditorBasePopupWindow {
|
||||
|
||||
// Trigger signature help request if included in CompletionItem
|
||||
// TODO Don't rely on CompletionItem for requesting signature help
|
||||
|
||||
// If the insert text contains '(', automatically trigger this action
|
||||
mEditor.signatureHelp("(");
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="title_installation_failed">Installation failed</string>
|
||||
<string name="msg_installation_failed">Installation process terminated with exit code %1$d. Make sure you downloaded the build tools from original sources.\n\nYou could try to clear data of AndroidIDE and install again.</string>
|
||||
<string name="msg_sdk_info_load_failed">Failed to load SDK information</string>
|
||||
<string name="msg_cannot_preview_layout">Unable to preview layout!</string>
|
||||
<string name="title_preview_layout">Preview Layout</string>
|
||||
|
||||
+2
-2
@@ -4,8 +4,8 @@ buildscript {
|
||||
buildTools = "31.0.0"
|
||||
minSdk = 24
|
||||
targetSdk = 28
|
||||
versionCode = 1031211101
|
||||
versionName = "1.0.3.1-alpha"
|
||||
versionCode = 2001211120
|
||||
versionName = "2.0-beta"
|
||||
packageName = "com.itsaky.androidide"
|
||||
|
||||
javaSourceVersion = JavaVersion.VERSION_1_8
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
# Built application files
|
||||
*.apk
|
||||
*.aar
|
||||
*.ap_
|
||||
*.aab
|
||||
|
||||
# Files for the ART/Dalvik VM
|
||||
*.dex
|
||||
|
||||
# Java class files
|
||||
*.class
|
||||
|
||||
# Generated files
|
||||
bin/
|
||||
gen/
|
||||
out/
|
||||
# Uncomment the following line in case you need and you don't have the release build type files in your app
|
||||
# release/
|
||||
|
||||
# Gradle files
|
||||
.gradle/
|
||||
build/
|
||||
|
||||
# Local configuration file (sdk path, etc)
|
||||
local.properties
|
||||
|
||||
# Proguard folder generated by Eclipse
|
||||
proguard/
|
||||
|
||||
# Log Files
|
||||
*.log
|
||||
|
||||
# Android Studio Navigation editor temp files
|
||||
.navigation/
|
||||
|
||||
# Android Studio captures folder
|
||||
captures/
|
||||
|
||||
# IntelliJ
|
||||
*.iml
|
||||
.idea/workspace.xml
|
||||
.idea/tasks.xml
|
||||
.idea/gradle.xml
|
||||
.idea/assetWizardSettings.xml
|
||||
.idea/dictionaries
|
||||
.idea/libraries
|
||||
# Android Studio 3 in .gitignore file.
|
||||
.idea/caches
|
||||
.idea/modules.xml
|
||||
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
|
||||
.idea/navEditor.xml
|
||||
|
||||
# Keystore files
|
||||
# Uncomment the following lines if you do not want to check your keystore files in.
|
||||
#*.jks
|
||||
#*.keystore
|
||||
|
||||
# External native build folder generated in Android Studio 2.2 and later
|
||||
.externalNativeBuild
|
||||
.cxx/
|
||||
|
||||
# Google Services (e.g. APIs or Firebase)
|
||||
# google-services.json
|
||||
|
||||
# Freeline
|
||||
freeline.py
|
||||
freeline/
|
||||
freeline_project_description.json
|
||||
|
||||
# fastlane
|
||||
fastlane/report.xml
|
||||
fastlane/Preview.html
|
||||
fastlane/screenshots
|
||||
fastlane/test_output
|
||||
fastlane/readme.md
|
||||
|
||||
# Version control
|
||||
vcs.xml
|
||||
|
||||
# lint
|
||||
lint/intermediates/
|
||||
lint/generated/
|
||||
lint/outputs/
|
||||
lint/tmp/
|
||||
# lint/reports/
|
||||
@@ -0,0 +1,30 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdk
|
||||
buildToolsVersion rootProject.ext.buildTools
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.ext.minSdk
|
||||
targetSdkVersion rootProject.ext.targetSdk
|
||||
versionCode rootProject.ext.versionCode
|
||||
versionName rootProject.ext.versionName
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility rootProject.ext.javaSourceVersion
|
||||
targetCompatibility rootProject.ext.javaTargetVersion
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
api 'org.antlr:antlr4-runtime:4.9.2'
|
||||
}
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /Users/bogdan/Library/Android/sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
@@ -0,0 +1 @@
|
||||
<manifest package="com.itsaky.androidide.lexers"/>
|
||||
+17
-9
@@ -1,14 +1,19 @@
|
||||
// Generated from GroovyLexer.g4 by ANTLR 4.9.2
|
||||
package com.itsaky.androidide.language.groovy.lexer;
|
||||
package com.itsaky.androidide.lexers.groovy;
|
||||
|
||||
import org.antlr.v4.runtime.Lexer;
|
||||
import java.io.StringReader;
|
||||
import org.antlr.v4.runtime.CharStream;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.TokenStream;
|
||||
import org.antlr.v4.runtime.*;
|
||||
import org.antlr.v4.runtime.atn.*;
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.Lexer;
|
||||
import org.antlr.v4.runtime.RuntimeMetaData;
|
||||
import org.antlr.v4.runtime.Vocabulary;
|
||||
import org.antlr.v4.runtime.VocabularyImpl;
|
||||
import org.antlr.v4.runtime.atn.ATN;
|
||||
import org.antlr.v4.runtime.atn.ATNDeserializer;
|
||||
import org.antlr.v4.runtime.atn.LexerATNSimulator;
|
||||
import org.antlr.v4.runtime.atn.PredictionContextCache;
|
||||
import org.antlr.v4.runtime.dfa.DFA;
|
||||
import org.antlr.v4.runtime.misc.*;
|
||||
import java.io.IOException;
|
||||
|
||||
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
|
||||
public class GroovyLexer extends Lexer {
|
||||
@@ -141,8 +146,11 @@ public class GroovyLexer extends Lexer {
|
||||
public Vocabulary getVocabulary() {
|
||||
return VOCABULARY;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public GroovyLexer (String input) throws IOException {
|
||||
this (CharStreams.fromReader(new StringReader(input)));
|
||||
}
|
||||
|
||||
public GroovyLexer(CharStream input) {
|
||||
super(input);
|
||||
_interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache);
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
// Generated from JavaLexer.g4 by ANTLR 4.9.2
|
||||
package com.itsaky.androidide.antlr4.java;
|
||||
package com.itsaky.androidide.lexers.java;
|
||||
import org.antlr.v4.runtime.Lexer;
|
||||
import org.antlr.v4.runtime.CharStream;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
@@ -506,4 +506,4 @@ public class JavaLexer extends Lexer {
|
||||
_decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
// Generated from JavaParser.g4 by ANTLR 4.9.2
|
||||
package com.itsaky.androidide.antlr4.java;
|
||||
package com.itsaky.androidide.lexers.java;
|
||||
import org.antlr.v4.runtime.atn.*;
|
||||
import org.antlr.v4.runtime.dfa.DFA;
|
||||
import org.antlr.v4.runtime.*;
|
||||
@@ -10215,4 +10215,4 @@ public class JavaParser extends Parser {
|
||||
_decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
// Generated from JavaParser.g4 by ANTLR 4.9.2
|
||||
package com.itsaky.androidide.antlr4.java;
|
||||
package com.itsaky.androidide.lexers.java;
|
||||
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.tree.ErrorNode;
|
||||
@@ -1296,4 +1296,4 @@ public class JavaParserBaseListener implements JavaParserListener {
|
||||
* <p>The default implementation does nothing.</p>
|
||||
*/
|
||||
@Override public void visitErrorNode(ErrorNode node) { }
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
// Generated from JavaParser.g4 by ANTLR 4.9.2
|
||||
package com.itsaky.androidide.antlr4.java;
|
||||
package com.itsaky.androidide.lexers.java;
|
||||
import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor;
|
||||
|
||||
/**
|
||||
@@ -746,4 +746,4 @@ public class JavaParserBaseVisitor<T> extends AbstractParseTreeVisitor<T> implem
|
||||
* {@link #visitChildren} on {@code ctx}.</p>
|
||||
*/
|
||||
@Override public T visitArguments(JavaParser.ArgumentsContext ctx) { return visitChildren(ctx); }
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
// Generated from JavaParser.g4 by ANTLR 4.9.2
|
||||
package com.itsaky.androidide.antlr4.java;
|
||||
package com.itsaky.androidide.lexers.java;
|
||||
import org.antlr.v4.runtime.tree.ParseTreeListener;
|
||||
|
||||
/**
|
||||
@@ -1057,4 +1057,4 @@ public interface JavaParserListener extends ParseTreeListener {
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
void exitArguments(JavaParser.ArgumentsContext ctx);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
// Generated from JavaParser.g4 by ANTLR 4.9.2
|
||||
package com.itsaky.androidide.antlr4.java;
|
||||
package com.itsaky.androidide.lexers.java;
|
||||
import org.antlr.v4.runtime.tree.ParseTreeVisitor;
|
||||
|
||||
/**
|
||||
@@ -640,4 +640,4 @@ public interface JavaParserVisitor<T> extends ParseTreeVisitor<T> {
|
||||
* @return the visitor result
|
||||
*/
|
||||
T visitArguments(JavaParser.ArgumentsContext ctx);
|
||||
}
|
||||
}
|
||||
+17
-9
@@ -1,14 +1,19 @@
|
||||
package com.itsaky.androidide.language.xml.lexer;
|
||||
package com.itsaky.androidide.lexers.xml;
|
||||
|
||||
// Generated from XMLLexer.g4 by ANTLR 4.9.2
|
||||
import org.antlr.v4.runtime.Lexer;
|
||||
import java.io.StringReader;
|
||||
import org.antlr.v4.runtime.CharStream;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.TokenStream;
|
||||
import org.antlr.v4.runtime.*;
|
||||
import org.antlr.v4.runtime.atn.*;
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.Lexer;
|
||||
import org.antlr.v4.runtime.RuntimeMetaData;
|
||||
import org.antlr.v4.runtime.Vocabulary;
|
||||
import org.antlr.v4.runtime.VocabularyImpl;
|
||||
import org.antlr.v4.runtime.atn.ATN;
|
||||
import org.antlr.v4.runtime.atn.ATNDeserializer;
|
||||
import org.antlr.v4.runtime.atn.LexerATNSimulator;
|
||||
import org.antlr.v4.runtime.atn.PredictionContextCache;
|
||||
import org.antlr.v4.runtime.dfa.DFA;
|
||||
import org.antlr.v4.runtime.misc.*;
|
||||
import java.io.IOException;
|
||||
|
||||
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
|
||||
public class XMLLexer extends Lexer {
|
||||
@@ -88,8 +93,11 @@ public class XMLLexer extends Lexer {
|
||||
public Vocabulary getVocabulary() {
|
||||
return VOCABULARY;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public XMLLexer (String input) throws IOException {
|
||||
this (CharStreams.fromReader(new StringReader(input)));
|
||||
}
|
||||
|
||||
public XMLLexer(CharStream input) {
|
||||
super(input);
|
||||
_interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache);
|
||||
@@ -5,5 +5,7 @@ include ':javapoet'
|
||||
include ':lsp'
|
||||
include ':sdk-info'
|
||||
include ':layout-inflater'
|
||||
include ':lexers'
|
||||
include ':shell'
|
||||
|
||||
rootProject.name = "AndroidIDE"
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
# Built application files
|
||||
*.apk
|
||||
*.aar
|
||||
*.ap_
|
||||
*.aab
|
||||
|
||||
# Files for the ART/Dalvik VM
|
||||
*.dex
|
||||
|
||||
# Java class files
|
||||
*.class
|
||||
|
||||
# Generated files
|
||||
bin/
|
||||
gen/
|
||||
out/
|
||||
# Uncomment the following line in case you need and you don't have the release build type files in your app
|
||||
# release/
|
||||
|
||||
# Gradle files
|
||||
.gradle/
|
||||
build/
|
||||
|
||||
# Local configuration file (sdk path, etc)
|
||||
local.properties
|
||||
|
||||
# Proguard folder generated by Eclipse
|
||||
proguard/
|
||||
|
||||
# Log Files
|
||||
*.log
|
||||
|
||||
# Android Studio Navigation editor temp files
|
||||
.navigation/
|
||||
|
||||
# Android Studio captures folder
|
||||
captures/
|
||||
|
||||
# IntelliJ
|
||||
*.iml
|
||||
.idea/workspace.xml
|
||||
.idea/tasks.xml
|
||||
.idea/gradle.xml
|
||||
.idea/assetWizardSettings.xml
|
||||
.idea/dictionaries
|
||||
.idea/libraries
|
||||
# Android Studio 3 in .gitignore file.
|
||||
.idea/caches
|
||||
.idea/modules.xml
|
||||
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
|
||||
.idea/navEditor.xml
|
||||
|
||||
# Keystore files
|
||||
# Uncomment the following lines if you do not want to check your keystore files in.
|
||||
#*.jks
|
||||
#*.keystore
|
||||
|
||||
# External native build folder generated in Android Studio 2.2 and later
|
||||
.externalNativeBuild
|
||||
.cxx/
|
||||
|
||||
# Google Services (e.g. APIs or Firebase)
|
||||
# google-services.json
|
||||
|
||||
# Freeline
|
||||
freeline.py
|
||||
freeline/
|
||||
freeline_project_description.json
|
||||
|
||||
# fastlane
|
||||
fastlane/report.xml
|
||||
fastlane/Preview.html
|
||||
fastlane/screenshots
|
||||
fastlane/test_output
|
||||
fastlane/readme.md
|
||||
|
||||
# Version control
|
||||
vcs.xml
|
||||
|
||||
# lint
|
||||
lint/intermediates/
|
||||
lint/generated/
|
||||
lint/outputs/
|
||||
lint/tmp/
|
||||
# lint/reports/
|
||||
@@ -0,0 +1,28 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdk
|
||||
buildToolsVersion rootProject.ext.buildTools
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.ext.minSdk
|
||||
targetSdkVersion rootProject.ext.targetSdk
|
||||
versionCode rootProject.ext.versionCode
|
||||
versionName rootProject.ext.versionName
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility rootProject.ext.javaSourceVersion
|
||||
targetCompatibility rootProject.ext.javaTargetVersion
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
}
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /Users/bogdan/Library/Android/sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
@@ -0,0 +1 @@
|
||||
<manifest package="com.itsaky.androidide.shell"/>
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.itsaky.androidide.shell;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Creates subprocesses using a {@link ProcessBuilder}
|
||||
*/
|
||||
public class CommonProcessExecutor implements IProcessExecutor {
|
||||
|
||||
final File HOME = getHome();
|
||||
|
||||
@Override
|
||||
public int exec(ProcessStreamsHolder holder, String... args) throws IOException, InterruptedException {
|
||||
return exec(holder, true, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int exec(ProcessStreamsHolder holder, boolean redirectErr, String... args) throws IOException, InterruptedException {
|
||||
final Process proc = newProcess(args, redirectErr, holder);
|
||||
return proc.waitFor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execAsync(ProcessStreamsHolder holder, IProcessExitListener onExit, boolean redirectErr, String[] args) throws IOException {
|
||||
final Process proc = newProcess(args, redirectErr, holder);
|
||||
final Thread exitListener = new Thread(new ExitListenerRunnable (proc, onExit), "ProcessExitListener");
|
||||
exitListener.start();
|
||||
}
|
||||
|
||||
private Process newProcess(String[] args, boolean redirectErr, ProcessStreamsHolder holder) throws IOException {
|
||||
final ProcessBuilder builder = new ProcessBuilder(args);
|
||||
builder.directory(HOME);
|
||||
builder.redirectErrorStream(redirectErr);
|
||||
builder.environment().putAll(getEnv());
|
||||
|
||||
final Process proc = builder.start();
|
||||
holder.in = proc.getInputStream();
|
||||
holder.err = proc.getErrorStream();
|
||||
holder.out = proc.getOutputStream();
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
private static File getHome() {
|
||||
try {
|
||||
final Class<?> env = CommonProcessExecutor.class.getClassLoader().loadClass("com.itsaky.androidide.utils.Environment");
|
||||
final Field home = env.getDeclaredField("HOME");
|
||||
home.setAccessible(true);
|
||||
return (File) home.get(null);
|
||||
} catch (Throwable th) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
// Return this if we cannot get it from Environment class
|
||||
return new File("/data/data/com.itsaky.androidide/files/framework");
|
||||
}
|
||||
|
||||
private static Map<String, String> getEnv() {
|
||||
try {
|
||||
final Class<?> env = CommonProcessExecutor.class.getClassLoader().loadClass("com.itsaky.androidide.utils.Environment");
|
||||
final Method method = env.getDeclaredMethod("getEnvironment", boolean.class);
|
||||
method.setAccessible(true);
|
||||
return (Map<String, String>) method.invoke(null, true);
|
||||
} catch (Throwable th) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
// Return this if we cannot get it from Environment class
|
||||
return new HashMap<String, String>();
|
||||
}
|
||||
|
||||
private class ExitListenerRunnable implements Runnable {
|
||||
|
||||
private final Process proc;
|
||||
private final IProcessExitListener listener;
|
||||
|
||||
public ExitListenerRunnable(Process proc, IProcessExitListener listener) {
|
||||
this.proc = proc;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
proc.waitFor();
|
||||
} catch (InterruptedException e) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
listener.onExit(proc.exitValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.itsaky.androidide.shell;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* An executor to execute a command.
|
||||
* <p>
|
||||
* Subclasses of this interface can decide how to execute the command.
|
||||
* For example, you could use {@link ProcessBuilder} or you could
|
||||
* use {@link com.itsaky.terminal.JNI JNI} to create subprocesses.
|
||||
* </p>
|
||||
*/
|
||||
public interface IProcessExecutor {
|
||||
|
||||
/**
|
||||
* @see exec(ProcessStreamsHolder, boolean, String[])
|
||||
*/
|
||||
int exec (ProcessStreamsHolder holder, String... args) throws IOException, InterruptedException;
|
||||
|
||||
/**
|
||||
* Executes the given command with its attributes.
|
||||
* This method WILL block the current thread.
|
||||
*
|
||||
* @param holder A holder which will be provided the process's input, error and output stream
|
||||
* @param redirectErr Whether to redirect error stream to output stream
|
||||
* @param args Arguments for {@code cmd}
|
||||
* @return The exit code of the command
|
||||
*/
|
||||
int exec (ProcessStreamsHolder holder, boolean redirectErr, String... args) throws IOException, InterruptedException;
|
||||
|
||||
/**
|
||||
* Executes the given command with its attributes.
|
||||
* This method will not block the current thread.
|
||||
*
|
||||
* @param holder A holder which will be provided the process's input, error and output stream
|
||||
* @param redirectErr Whether to redirect error stream to output stream
|
||||
* @param onExit A listener that will be called once the process exits
|
||||
* @param args Arguments for {@code cmd}
|
||||
*/
|
||||
void execAsync (ProcessStreamsHolder holder, IProcessExitListener onExit, boolean redirectErr, String... args) throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.itsaky.androidide.shell;
|
||||
|
||||
public interface IProcessExitListener {
|
||||
void onExit (int code);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.itsaky.androidide.shell;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Provides static access to the process executor
|
||||
*/
|
||||
public class ProcessExecutorFactory {
|
||||
|
||||
private static final CommonProcessExecutor executor = new CommonProcessExecutor ();
|
||||
|
||||
public static IProcessExecutor commonExecutor () {
|
||||
return executor;
|
||||
}
|
||||
|
||||
public static int exec (ProcessStreamsHolder holder, String ... args) throws InterruptedException, IOException {
|
||||
return executor.exec(holder, args);
|
||||
}
|
||||
|
||||
public static int exec (ProcessStreamsHolder holder, boolean redirectErr, String ... args) throws InterruptedException, IOException {
|
||||
return executor.exec(holder, redirectErr, args);
|
||||
}
|
||||
|
||||
public static void execAsync (ProcessStreamsHolder holder, IProcessExitListener exitListener, boolean redirectErr, String ... args) throws IOException {
|
||||
executor.execAsync(holder, exitListener, redirectErr, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.itsaky.androidide.shell;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Holder input, error and output streams for a {@link java.lang.Process}
|
||||
*/
|
||||
public class ProcessStreamsHolder {
|
||||
public InputStream in;
|
||||
public InputStream err;
|
||||
public OutputStream out;
|
||||
}
|
||||
+29
-56
@@ -1,9 +1,5 @@
|
||||
package com.itsaky.androidide.shell;
|
||||
|
||||
import com.blankj.utilcode.util.CloseUtils;
|
||||
import com.blankj.utilcode.util.ThrowableUtils;
|
||||
import com.itsaky.androidide.utils.Environment;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
@@ -11,62 +7,42 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import com.blankj.utilcode.util.ThreadUtils;
|
||||
|
||||
public class ShellServer extends Thread {
|
||||
|
||||
private final List<Callback> callbacks = Collections.synchronizedList(new ArrayList<Callback>());
|
||||
private final Callback callback;
|
||||
private BufferedReader output;
|
||||
private Process process;
|
||||
|
||||
private static final Logger LOG = Logger.instance("ShellServer");
|
||||
|
||||
public ShellServer(Callback callback, String command, String dirPath) {
|
||||
this(callback, command, dirPath, true);
|
||||
}
|
||||
|
||||
public ShellServer(Callback callback, String command, String dirPath, boolean redirectErrors) {
|
||||
addCallback(callback);
|
||||
public ShellServer(Callback callback, String command, String dirPath, Map<String, String> env, boolean redirectErrors) {
|
||||
this.callback = callback;
|
||||
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(new String[]{command});
|
||||
processBuilder.directory(new File(dirPath));
|
||||
processBuilder.redirectErrorStream(redirectErrors);
|
||||
processBuilder.environment().putAll(Environment.getEnvironment(false));
|
||||
processBuilder.environment().putAll(env);
|
||||
|
||||
try {
|
||||
this.process = processBuilder.start();
|
||||
this.output = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
} catch (Throwable th) {
|
||||
if (callback != null) {
|
||||
String out = ThrowableUtils.getFullStackTrace(th).concat("\n");
|
||||
String out = getFullStackTrace(th).concat("\n");
|
||||
callback.output(out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getPid() {
|
||||
int pid = -1;
|
||||
try {
|
||||
Field f = process.getClass().getDeclaredField("pid");
|
||||
f.setAccessible(true);
|
||||
pid = f.getInt(process);
|
||||
f.setAccessible(false);
|
||||
} catch (Throwable ignored) {
|
||||
try {
|
||||
Matcher m = Pattern.compile("pid=(\\d+)").matcher(process.toString());
|
||||
pid = m.find() ? Integer.parseInt(m.group(1)) : -1;
|
||||
} catch (Throwable ignored2) {
|
||||
pid = -1;
|
||||
}
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
public InputStream getProcessInputStream() {
|
||||
return process.getInputStream();
|
||||
}
|
||||
@@ -75,13 +51,6 @@ public class ShellServer extends Thread {
|
||||
return process.getOutputStream();
|
||||
}
|
||||
|
||||
public ShellServer addCallback(Callback callback) {
|
||||
if (callback != null) {
|
||||
this.callbacks.add(callback);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void append(String str) {
|
||||
append(str, true);
|
||||
}
|
||||
@@ -129,23 +98,11 @@ public class ShellServer extends Thread {
|
||||
}
|
||||
|
||||
public void output(String str) {
|
||||
int i = 0;
|
||||
if (this.callbacks != null && this.callbacks.size() != 0) {
|
||||
Callback[] callbackArr = this.callbacks.toArray(new Callback[0]);
|
||||
while (i < callbackArr.length) {
|
||||
callbackArr[i].output(str);
|
||||
i++;
|
||||
}
|
||||
if (this.callback != null) {
|
||||
this.callback.output(str);
|
||||
}
|
||||
}
|
||||
|
||||
public ShellServer removeCallback(Callback callback) {
|
||||
if (callback != null) {
|
||||
this.callbacks.remove(callback);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
@@ -159,9 +116,25 @@ public class ShellServer extends Thread {
|
||||
output(th.toString().concat("\n"));
|
||||
}
|
||||
}
|
||||
CloseUtils.closeIOQuietly(new Closeable[]{this.output, this.process.getInputStream(), this.process.getErrorStream(), this.process.getOutputStream()});
|
||||
closeIOQuietly(new Closeable[]{this.output, this.process.getInputStream(), this.process.getErrorStream(), this.process.getOutputStream()});
|
||||
this.process.destroy();
|
||||
}
|
||||
|
||||
private String getFullStackTrace (Throwable th) {
|
||||
StringWriter sw = new StringWriter ();
|
||||
th.printStackTrace( new PrintWriter (sw));
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
private void closeIOQuietly (Closeable ... toClose) {
|
||||
for (Closeable c : toClose) {
|
||||
try {
|
||||
c.close();
|
||||
} catch (IOException e) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface Callback {
|
||||
public void output(CharSequence charSequence);
|
||||
Reference in New Issue
Block a user