mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
- Implmented Diagnostics
This commit is contained in:
+4
-3
@@ -59,7 +59,7 @@ android {
|
||||
stringfog.enable false
|
||||
}
|
||||
release {
|
||||
minifyEnabled true
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
signingConfig signingConfigs.release
|
||||
stringfog.enable true
|
||||
@@ -75,8 +75,9 @@ android {
|
||||
}
|
||||
|
||||
stringfog {
|
||||
def isRelease = getGradle().getStartParameter().getTaskRequests().toString().contains("release")
|
||||
key 'WVV0aGMwaEFZVzVFVW05SlJFbGtaUT09'
|
||||
enable true
|
||||
enable false
|
||||
implementation 'com.github.megatronking.stringfog.xor.StringFogImpl'
|
||||
}
|
||||
|
||||
@@ -90,6 +91,7 @@ dependencies {
|
||||
implementation 'com.github.bumptech.glide:glide:4.12.0'
|
||||
implementation 'com.github.megatronking.stringfog:xor:1.1.0'
|
||||
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
|
||||
@@ -119,7 +121,6 @@ dependencies {
|
||||
|
||||
// Local projects
|
||||
implementation project(':treeview')
|
||||
implementation project(':editor')
|
||||
implementation project(':spinkit')
|
||||
implementation project(':slidr')
|
||||
implementation project(':javadocparser')
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
o0OoO0
|
||||
0oO00Oo0
|
||||
oO0o0O0O
|
||||
0O0o00
|
||||
Binary file not shown.
Vendored
+9
-9
@@ -1,5 +1,8 @@
|
||||
-keepattributes SourceFile, LineNumberTable
|
||||
-renamesourcefileattribute FuckYou
|
||||
-obfuscationdictionary dictionary.txt
|
||||
-packageobfuscationdictionary dictionary.txt
|
||||
-classobfuscationdictionary dictionary.txt
|
||||
|
||||
-ignorewarnings
|
||||
-dontwarn
|
||||
@@ -14,17 +17,14 @@
|
||||
-dontwarn androidx.lifecycle.**
|
||||
-keep class androidx.arch.** { *; }
|
||||
-keep class androidx.lifecycle.** { *; }
|
||||
-keepclasseswithmembernames,includedescriptorclasses class * {
|
||||
native <methods>;
|
||||
}
|
||||
-keep class org.eclipse.jdt.** { <init>(...); }
|
||||
-keep class org.eclipse.jdt.** { static *; }
|
||||
|
||||
-keepclasseswithmembernames,includedescriptorclasses class * { native <methods>; }
|
||||
-keep public class * implements com.bumptech.glide.module.GlideModule
|
||||
-keep class * extends com.bumptech.glide.module.AppGlideModule {
|
||||
<init>(...);
|
||||
}
|
||||
-keep class * extends com.bumptech.glide.module.AppGlideModule { <init>(...); }
|
||||
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
|
||||
**[] $VALUES;
|
||||
public *;
|
||||
}
|
||||
-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder {
|
||||
*** rewind();
|
||||
}
|
||||
-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder { *** rewind(); }
|
||||
|
||||
@@ -17,6 +17,8 @@ import androidx.appcompat.app.ActionBarDrawerToggle;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.view.GravityCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.transition.Slide;
|
||||
import androidx.transition.TransitionManager;
|
||||
import com.blankj.utilcode.util.ClipboardUtils;
|
||||
@@ -29,6 +31,7 @@ import com.google.android.material.navigation.NavigationView;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import com.google.android.material.transition.MaterialArcMotion;
|
||||
import com.google.android.material.transition.MaterialContainerTransform;
|
||||
import com.itsaky.androidide.adapters.DiagnosticsAdapter;
|
||||
import com.itsaky.androidide.adapters.EditorPagerAdapter;
|
||||
import com.itsaky.androidide.adapters.viewholders.FileTreeViewHolder;
|
||||
import com.itsaky.androidide.app.StudioActivity;
|
||||
@@ -41,9 +44,11 @@ import com.itsaky.androidide.fragments.sheets.OptionsListFragment;
|
||||
import com.itsaky.androidide.fragments.sheets.TextSheetFragment;
|
||||
import com.itsaky.androidide.interfaces.CompileListener;
|
||||
import com.itsaky.androidide.interfaces.DexResultListener;
|
||||
import com.itsaky.androidide.interfaces.DiagnosticClickListener;
|
||||
import com.itsaky.androidide.language.logs.LogLanguageImpl;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
import com.itsaky.androidide.models.ConstantsBridge;
|
||||
import com.itsaky.androidide.models.DiagnosticGroup;
|
||||
import com.itsaky.androidide.models.LogLine;
|
||||
import com.itsaky.androidide.models.SheetOption;
|
||||
import com.itsaky.androidide.receivers.LogReceiver;
|
||||
@@ -64,6 +69,7 @@ import com.unnamed.b.atv.model.TreeNode;
|
||||
import io.github.rosemoe.editor.langs.EmptyLanguage;
|
||||
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;
|
||||
@@ -71,14 +77,14 @@ import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
import me.piruin.quickaction.ActionItem;
|
||||
import me.piruin.quickaction.QuickAction;
|
||||
import com.elvishew.xlog.XLog;
|
||||
|
||||
public class EditorActivity extends StudioActivity implements FileTreeFragment.FileActionListener,
|
||||
IDEService.BuildListener,
|
||||
TabLayout.OnTabSelectedListener,
|
||||
NavigationView.OnNavigationItemSelectedListener,
|
||||
DexResultListener,
|
||||
CompileListener {
|
||||
CompileListener,
|
||||
DiagnosticClickListener {
|
||||
|
||||
private ActivityEditorBinding mBinding;
|
||||
private EditorPagerAdapter mPagerAdapter;
|
||||
@@ -89,6 +95,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
private TreeNode mLastHolded;
|
||||
private CodeEditor buildView;
|
||||
private CodeEditor logView;
|
||||
private RecyclerView diagnosticList;
|
||||
|
||||
private LogLanguageImpl mLogLanguageImpl;
|
||||
|
||||
@@ -97,6 +104,8 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
private OptionsListFragment mFileOptionsFragment;
|
||||
|
||||
private boolean recreateCompletionServices = false;
|
||||
|
||||
private final Map<File, List<CompilerDiagnostic>> diagnostics = new HashMap<>();
|
||||
|
||||
private final String RES_PATH_REGEX = "/.*/src/.*/res";
|
||||
private final String LAYOUTRES_PATH_REGEX = "/.*/src/.*/res/layout";
|
||||
@@ -111,9 +120,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
private static final int ACTIONID_CLOSE = 100;
|
||||
private static final int ACTIONID_OTHERS = 101;
|
||||
private static final int ACTIONID_ALL = 102;
|
||||
|
||||
private static final HashMap<File, List<CompilerDiagnostic>> mDiagnosticsMap = new HashMap<>();
|
||||
|
||||
|
||||
private LogReceiver mLogReceiver = new LogReceiver().setLogListener(line -> appendApkLog(line));
|
||||
private BroadcastReceiver mServiceStartReceiver = new BroadcastReceiver(){
|
||||
|
||||
@@ -186,7 +193,6 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
|
||||
KeyboardUtils.registerSoftInputChangedListener(this, __ -> onSoftInputChanged());
|
||||
registerLogReceiver();
|
||||
|
||||
setupContainers();
|
||||
}
|
||||
|
||||
@@ -198,6 +204,10 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
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());
|
||||
}
|
||||
|
||||
mBinding.buildOutcontainer.addView(getBuildView(), new ViewGroup.LayoutParams(-1, -1));
|
||||
mBinding.buildContainer.setVisibility(View.GONE);
|
||||
@@ -208,12 +218,18 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
mBinding.logContainer.setVisibility(View.GONE);
|
||||
mBinding.logOutToolbar.setNavigationOnClickListener(v -> hideLogResult());
|
||||
mBinding.logOutToolbar.setOnClickListener(v -> hideLogResult());
|
||||
|
||||
mBinding.diagListContainer.addView(getDiagnosticsList(), new ViewGroup.LayoutParams(-1, -1));
|
||||
mBinding.diagContainer.setVisibility(View.GONE);
|
||||
mBinding.diagToolbar.setNavigationOnClickListener(v -> hideDiagnostics());
|
||||
mBinding.diagToolbar.setOnClickListener(v -> hideDiagnostics());
|
||||
|
||||
mBinding.viewOptionsCard.setVisibility(View.GONE);
|
||||
mBinding.transformScrim.setVisibility(View.GONE);
|
||||
|
||||
mBinding.transformScrim.setOnClickListener(v -> hideViewOptions());
|
||||
mBinding.viewBuildOut.setOnClickListener(v -> showBuildResult());
|
||||
mBinding.viewDiags.setOnClickListener(v -> showDiagnostics());
|
||||
mBinding.viewLogs.setOnClickListener(v -> showLogResult());
|
||||
mBinding.viewFiles.setOnClickListener(v -> showFiles());
|
||||
mBinding.viewDaemonStatus.setOnClickListener(v -> showDaemonStatus());
|
||||
@@ -250,18 +266,20 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if(mBinding.buildContainer.getVisibility() == View.VISIBLE) {
|
||||
hideBuildResult();
|
||||
} else if(mBinding.logContainer.getVisibility() == View.VISIBLE) {
|
||||
hideLogResult();
|
||||
} else if(mBinding.viewOptionsCard.getVisibility() == View.VISIBLE) {
|
||||
hideViewOptions();
|
||||
} else if(mBinding.getRoot().isDrawerOpen(GravityCompat.END)) {
|
||||
if(mBinding.getRoot().isDrawerOpen(GravityCompat.END)) {
|
||||
mBinding.getRoot().closeDrawer(GravityCompat.END);
|
||||
} else if(mBinding.getRoot().isDrawerOpen(GravityCompat.START)) {
|
||||
mBinding.getRoot().closeDrawer(GravityCompat.START);
|
||||
} else if(getDaemonStatusFragment().isShowing()) {
|
||||
getDaemonStatusFragment().dismiss();
|
||||
} else if(mBinding.buildContainer.getVisibility() == View.VISIBLE) {
|
||||
hideBuildResult();
|
||||
} else if(mBinding.logContainer.getVisibility() == View.VISIBLE) {
|
||||
hideLogResult();
|
||||
} else if(mBinding.viewOptionsCard.getVisibility() == View.VISIBLE) {
|
||||
hideViewOptions();
|
||||
} else if(mBinding.diagContainer.getVisibility() == View.VISIBLE) {
|
||||
hideDiagnostics();
|
||||
} else if(mFileOptionsFragment != null && mFileOptionsFragment.isShowing()) {
|
||||
mFileOptionsFragment.dismiss();
|
||||
} else {
|
||||
@@ -411,6 +429,8 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
if(recreateCompletionServices) {
|
||||
getApp().createCompletionService(mProject);
|
||||
} else if(task.getType() == GradleTask.Type.BUILD) {
|
||||
getApp().createJavaCompiler(mProject);
|
||||
getApp().getJavaCompiler().compileAllAsync(this);
|
||||
if(task.getTaskID() == IDEService.TASK_ASSEMBLE_DEBUG) {
|
||||
if(task.buildsApk()) {
|
||||
install(task.getApk(new File(mProject.getMainModulePath(), "build").getAbsolutePath(), mProject.getMainModule()));
|
||||
@@ -429,6 +449,11 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
if(task.canOutput()) {
|
||||
setStatus(msg);
|
||||
}
|
||||
|
||||
if(task.getType() == GradleTask.Type.BUILD) {
|
||||
getApp().createJavaCompiler(mProject);
|
||||
getApp().getJavaCompiler().compileAllAsync(this);
|
||||
}
|
||||
|
||||
showBuildResult();
|
||||
|
||||
@@ -441,9 +466,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
public void onGetDependencies(List<String> dependencies) {
|
||||
setStatus(getString(R.string.msg_starting_completion));
|
||||
mProject.setClassPaths(dependencies);
|
||||
getApp().createJavaCompiler(mProject, this);
|
||||
getApp().getJavaCompiler().compileAll();
|
||||
new Jar2DexService(getApp().getIndexedDir(), dependencies, this).startDexing();
|
||||
createCompletionServices();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -464,19 +487,31 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
public void saveFiles() {
|
||||
saveAll();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated This is replaced by the ClassFileReader. There is no need to dex the JAR files in order to load them.
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public void onDex(String name) {
|
||||
setStatus(getString(R.string.msg_indexing, name));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated This is replaced by the ClassFileReader. There is no need to dex the JAR files in order to load them.
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public void onDexSuccess(List<String> dexes) {
|
||||
ConstantsBridge.PROJECT_DEXES = dexes;
|
||||
setStatus(getString(R.string.msg_indexing_success));
|
||||
createCompletionServices();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated This is replaced by the ClassFileReader. There is no need to dex the JAR files in order to load them.
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public void onDexFailed(String message) {
|
||||
appendBuildOut(message);
|
||||
@@ -484,14 +519,20 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
showBuildResult();
|
||||
}
|
||||
|
||||
private void appendBuildOut(String str) {
|
||||
str = str.endsWith("\n") ? str : str.concat("\n");
|
||||
getBuildView(true).getText().append(str);
|
||||
// Can be called from a different different, better to run on UI thread
|
||||
private void appendBuildOut(final String str) {
|
||||
runOnUiThread(() -> {
|
||||
String strFinal = str.endsWith("\n") ? str : str.concat("\n");
|
||||
getBuildView(true).getText().append(strFinal);
|
||||
});
|
||||
}
|
||||
|
||||
private void appendLogOut(String str) {
|
||||
str = str.endsWith("\n") ? str : str.concat("\n");
|
||||
getLogView(true).getText().append(str);
|
||||
// Can be called from a different different, better to run on UI thread
|
||||
private void appendLogOut(final String str) {
|
||||
runOnUiThread(() -> {
|
||||
String strFinal = str.endsWith("\n") ? str : str.concat("\n");
|
||||
getLogView(true).getText().append(strFinal);
|
||||
});
|
||||
}
|
||||
|
||||
private void showFiles() {
|
||||
@@ -563,6 +604,20 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
mBinding.logContainer.setVisibility(View.GONE);
|
||||
mBinding.viewOptionsCard.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void showDiagnostics() {
|
||||
TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.viewDiags, mBinding.diagContainer));
|
||||
mBinding.diagContainer.setVisibility(View.VISIBLE);
|
||||
mBinding.viewOptionsCard.setVisibility(View.GONE);
|
||||
hideToolbarAndTabs();
|
||||
}
|
||||
|
||||
private void hideDiagnostics() {
|
||||
showToolbarAndTabs();
|
||||
TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.diagContainer, mBinding.viewDiags));
|
||||
mBinding.diagContainer.setVisibility(View.GONE);
|
||||
mBinding.viewOptionsCard.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private MaterialContainerTransform createContainerTransformFor(View start, View end) {
|
||||
return createContainerTransformFor(start, end, mBinding.realContainer);
|
||||
@@ -632,7 +687,12 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompilationResult(boolean success, Map<File, List<CompilerDiagnostic>> diagnostics) {
|
||||
public void onCompilationResult(boolean singleFile, Map<File, List<CompilerDiagnostic>> diagnostics) {
|
||||
if(getDiagnosticsList().getParent() == null || !(getDiagnosticsList().getParent() instanceof ViewGroup))
|
||||
setupContainers();
|
||||
getDiagnosticsList().setAdapter(new DiagnosticsAdapter(mapAsGroup(diagnostics), this));
|
||||
this.diagnostics.clear();
|
||||
this.diagnostics.putAll(diagnostics);
|
||||
provideDiagnosticsToEditors(diagnostics);
|
||||
}
|
||||
|
||||
@@ -650,6 +710,21 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGroupClick(DiagnosticGroup group) {
|
||||
if(group != null
|
||||
&& group.file != null
|
||||
&& group.file.exists()
|
||||
&& FileUtils.isUtf8(group.file))
|
||||
{
|
||||
openFile(group.file);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDiagnosticClick(CompilerDiagnostic diagnostic) {
|
||||
}
|
||||
|
||||
/*****************************
|
||||
**** Tab Related ************
|
||||
@@ -690,8 +765,8 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
final int id = p1.getItemId();
|
||||
if(id == R.id.editornav_discuss) {
|
||||
getApp().openTelegramGroup();
|
||||
// } else if(id == R.id.editornav_suggest) {
|
||||
// getApp().openIssueTracker();
|
||||
} else if(id == R.id.editornav_suggest) {
|
||||
getApp().openIssueTracker();
|
||||
} else if(id == R.id.editornav_needHelp) {
|
||||
showNeedHelpDialog();
|
||||
} else if(id == R.id.editornav_settings) {
|
||||
@@ -796,7 +871,32 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
logView.setTextSize(12);
|
||||
return logView;
|
||||
}
|
||||
|
||||
|
||||
private RecyclerView getDiagnosticsList() {
|
||||
return diagnosticList == null ? createDiagnosticsList() : diagnosticList;
|
||||
}
|
||||
|
||||
private RecyclerView createDiagnosticsList() {
|
||||
diagnosticList = new RecyclerView(this);
|
||||
diagnosticList.setLayoutManager(new LinearLayoutManager(this));
|
||||
diagnosticList.setAdapter(new DiagnosticsAdapter(mapAsGroup(this.diagnostics), this));
|
||||
return diagnosticList;
|
||||
}
|
||||
|
||||
private List<DiagnosticGroup> mapAsGroup(Map<File, List<CompilerDiagnostic>> diags) {
|
||||
List<DiagnosticGroup> groups = new ArrayList<>();
|
||||
if(diags == null || diags.size() <= 0)
|
||||
return groups;
|
||||
for(File file : diags.keySet()) {
|
||||
List<CompilerDiagnostic> fileDiags = diags.get(file);
|
||||
if(fileDiags == null || fileDiags.size() <= 0)
|
||||
continue;
|
||||
DiagnosticGroup group = new DiagnosticGroup(R.drawable.ic_language_java, file, fileDiags);
|
||||
groups.add(group);
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
private LogLanguageImpl getLogLanguage() {
|
||||
return mLogLanguageImpl == null ? mLogLanguageImpl = new LogLanguageImpl() : mLogLanguageImpl;
|
||||
}
|
||||
@@ -823,6 +923,8 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
boolean isGradleSaved = mPagerAdapter.saveAll();
|
||||
getApp().toast(R.string.all_saved, Toaster.Type.SUCCESS);
|
||||
if(isGradleSaved) notifySyncNeeded();
|
||||
if(getApp().getJavaCompiler() != null)
|
||||
getApp().getJavaCompiler().compileAllAsync(this);
|
||||
return isGradleSaved;
|
||||
}
|
||||
|
||||
@@ -1148,7 +1250,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
mBinding.tabs.setupWithViewPager(mBinding.editorViewPager);
|
||||
mBinding.tabs.addOnTabSelectedListener(this);
|
||||
}
|
||||
|
||||
|
||||
private void closeFile(int index) {
|
||||
closeFile(index, true);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.itsaky.androidide.adapters;
|
||||
|
||||
import android.graphics.PorterDuff;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.databinding.LayoutDiagnosticItemBinding;
|
||||
import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
|
||||
import java.util.List;
|
||||
import javax.tools.Diagnostic;
|
||||
import com.itsaky.androidide.interfaces.DiagnosticClickListener;
|
||||
|
||||
public class DiagnosticItemAdpater extends RecyclerView.Adapter<DiagnosticItemAdpater.VH> {
|
||||
|
||||
private List<CompilerDiagnostic> diags;
|
||||
private DiagnosticClickListener listener;
|
||||
|
||||
public DiagnosticItemAdpater(List<CompilerDiagnostic> diags, DiagnosticClickListener listener) {
|
||||
this.diags = diags;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiagnosticItemAdpater.VH onCreateViewHolder(ViewGroup p1, int p2) {
|
||||
return new VH(LayoutDiagnosticItemBinding.inflate(LayoutInflater.from(p1.getContext()), p1, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(DiagnosticItemAdpater.VH p1, int p2) {
|
||||
final CompilerDiagnostic diagnostic = diags.get(p2);
|
||||
final LayoutDiagnosticItemBinding binding = p1.binding;
|
||||
|
||||
binding.icon.setImageResource(getDiagnosticIconId(diagnostic));
|
||||
binding.icon.setColorFilter(ContextCompat.getColor(binding.icon.getContext(), diagnostic.kind() == Diagnostic.Kind.ERROR ? R.color.diagnostic_error : R.color.diagnostic_warning), PorterDuff.Mode.SRC_ATOP);
|
||||
binding.title.setText(diagnostic.message());
|
||||
|
||||
binding.getRoot().setOnClickListener(v -> {
|
||||
if(listener != null) {
|
||||
listener.onDiagnosticClick(diagnostic);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return diags.size();
|
||||
}
|
||||
|
||||
private int getDiagnosticIconId(CompilerDiagnostic diagnostic) {
|
||||
if(diagnostic.kind() == Diagnostic.Kind.ERROR)
|
||||
return R.drawable.ic_compilation_error;
|
||||
return R.drawable.ic_info;
|
||||
}
|
||||
|
||||
public class VH extends RecyclerView.ViewHolder {
|
||||
private LayoutDiagnosticItemBinding binding;
|
||||
public VH(LayoutDiagnosticItemBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.itsaky.androidide.adapters;
|
||||
|
||||
import android.graphics.PorterDuff;
|
||||
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.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.databinding.LayoutDiagnosticGroupBinding;
|
||||
import com.itsaky.androidide.interfaces.DiagnosticClickListener;
|
||||
import com.itsaky.androidide.models.DiagnosticGroup;
|
||||
import java.util.List;
|
||||
|
||||
public class DiagnosticsAdapter extends RecyclerView.Adapter<DiagnosticsAdapter.VH> {
|
||||
|
||||
private final List<DiagnosticGroup> diags;
|
||||
private final DiagnosticClickListener listener;
|
||||
|
||||
public DiagnosticsAdapter(List<DiagnosticGroup> diags, DiagnosticClickListener listener) {
|
||||
this.diags = diags;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiagnosticsAdapter.VH onCreateViewHolder(ViewGroup p1, int p2) {
|
||||
return new VH(LayoutDiagnosticGroupBinding.inflate(LayoutInflater.from(p1.getContext()), p1, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(DiagnosticsAdapter.VH p1, int p2) {
|
||||
final DiagnosticGroup group = diags.get(p2);
|
||||
final LayoutDiagnosticGroupBinding binding = p1.binding;
|
||||
|
||||
final int color = ContextCompat.getColor(binding.info.icon.getContext(), R.color.secondaryColor);
|
||||
binding.info.icon.setImageResource(group.icon);
|
||||
binding.info.icon.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
|
||||
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.info.getRoot().setOnClickListener(v -> {
|
||||
if(listener != null) {
|
||||
listener.onGroupClick(group);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return diags.size();
|
||||
}
|
||||
|
||||
public class VH extends RecyclerView.ViewHolder {
|
||||
private LayoutDiagnosticGroupBinding binding;
|
||||
public VH(LayoutDiagnosticGroupBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,15 +11,10 @@ import android.text.TextUtils;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.multidex.MultiDexApplication;
|
||||
import com.blankj.utilcode.util.EncodeUtils;
|
||||
import com.blankj.utilcode.util.FileUtils;
|
||||
import com.blankj.utilcode.util.ResourceUtils;
|
||||
import com.blankj.utilcode.util.SPStaticUtils;
|
||||
import com.blankj.utilcode.util.ThrowableUtils;
|
||||
import com.elvishew.xlog.LogConfiguration;
|
||||
import com.elvishew.xlog.LogLevel;
|
||||
import com.elvishew.xlog.XLog;
|
||||
import com.elvishew.xlog.printer.Printer;
|
||||
import com.elvishew.xlog.printer.file.FilePrinter;
|
||||
import com.elvishew.xlog.printer.file.naming.DateFileNameGenerator;
|
||||
import com.google.firebase.messaging.FirebaseMessaging;
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
@@ -28,8 +23,10 @@ import com.itsaky.androidide.language.java.manager.JavaCharacter;
|
||||
import com.itsaky.androidide.language.java.provider.JavaCompletionProvider;
|
||||
import com.itsaky.androidide.language.xml.completion.XMLCompletionService;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
import com.itsaky.androidide.models.ConstantsBridge;
|
||||
import com.itsaky.androidide.services.IDEService;
|
||||
import com.itsaky.androidide.services.MessagingService;
|
||||
import com.itsaky.androidide.services.compiler.JavaCompilerService;
|
||||
import com.itsaky.androidide.shell.ShellServer;
|
||||
import com.itsaky.androidide.utils.Environment;
|
||||
import com.itsaky.androidide.utils.FileUtil;
|
||||
@@ -37,16 +34,11 @@ import com.itsaky.androidide.utils.PreferenceManager;
|
||||
import com.itsaky.androidide.utils.StudioUtils;
|
||||
import com.itsaky.toaster.Toaster;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import com.itsaky.androidide.models.ConstantsBridge;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import com.itsaky.androidide.services.compiler.JavaCompilerService;
|
||||
import com.itsaky.androidide.interfaces.CompileListener;
|
||||
import com.blankj.utilcode.util.FileUtils;
|
||||
|
||||
public class StudioApp extends MultiDexApplication
|
||||
{
|
||||
@@ -93,10 +85,6 @@ public class StudioApp extends MultiDexApplication
|
||||
newShell(line -> handleLog(line)).bgAppend("logcat -v threadtime");
|
||||
setupLibsIfNeeded();
|
||||
|
||||
if(isFrameworkInstalled()) {
|
||||
newShell(t -> XLog.i(t)).bgAppend("printenv > /sdcard/androidideenv.txt && java -version");
|
||||
}
|
||||
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
createNotificationChannels();
|
||||
}
|
||||
@@ -153,41 +141,28 @@ public class StudioApp extends MultiDexApplication
|
||||
}
|
||||
|
||||
private void initLogger() {
|
||||
LogConfiguration config = new LogConfiguration.Builder()
|
||||
.logLevel(DEBUG ? LogLevel.ALL : LogLevel.NONE)
|
||||
.tag("Android IDE")
|
||||
.enableThreadInfo()
|
||||
.enableStackTrace(2)
|
||||
.enableBorder()
|
||||
.build();
|
||||
Printer filePrinter = new FilePrinter
|
||||
.Builder(FileUtil.getExternalStorageDir() + "/ide_xlog")
|
||||
.fileNameGenerator(new DateFileNameGenerator())
|
||||
.build();
|
||||
XLog.init(
|
||||
config,
|
||||
filePrinter);
|
||||
|
||||
}
|
||||
|
||||
private boolean setupLibsIfNeeded() {
|
||||
File lib = Environment.LIBDIR;
|
||||
return extractLibs(lib);
|
||||
// if(lib != null && lib.exists() && lib.isDirectory()) {
|
||||
// boolean libhook, librt, libgcc, libgcc_real, libpthread, libz;
|
||||
// libhook = librt = libgcc = libgcc_real = libpthread = libz = false;
|
||||
// File[] list = lib.listFiles();
|
||||
// if(list != null) {
|
||||
// for(File f : list) {
|
||||
// libhook = libhook == false && f.getName().equals("libhook.so");
|
||||
// librt = librt == false && f.getName().equals("librt.so");
|
||||
// libpthread = libpthread == false && f.getName().equals("libpthread.so");
|
||||
// libgcc = libgcc == false && f.getName().equals("libgcc.so");
|
||||
// libgcc_real = libgcc_real = false & f.getName().equals("libgcc_real.so");
|
||||
// libz = libz == false && f.getName().equals("libz.so.1");
|
||||
// }
|
||||
// } else return extractLibs(lib);
|
||||
// return !(libhook && librt && libgcc && libgcc_real && libpthread && libz) ? extractLibs(lib) : true;
|
||||
// } else return extractLibs(lib);
|
||||
if(lib != null && lib.exists() && lib.isDirectory()) {
|
||||
boolean libhook, librt, libgcc, libgcc_real, libpthread, libz, libcpp_shared;
|
||||
libhook = librt = libgcc = libgcc_real = libpthread = libz = libcpp_shared = false;
|
||||
File[] list = lib.listFiles();
|
||||
if(list != null) {
|
||||
for(File f : list) {
|
||||
libhook = libhook == false ? f.getName().equals("libhook.so") : libhook;
|
||||
librt = librt == false ? f.getName().equals("librt.so") : librt;
|
||||
libpthread = libpthread == false ? f.getName().equals("libpthread.so") : libpthread;
|
||||
libgcc = libgcc == false ? f.getName().equals("libgcc.so") : libgcc;
|
||||
libgcc_real = libgcc_real = false ? f.getName().equals("libgcc_real.so") : libgcc_real;
|
||||
libz = libz == false ? f.getName().equals("libz.so.1") : libz;
|
||||
libcpp_shared = libcpp_shared == false ? f.getName().equals("libc++_shared.so") : libcpp_shared;
|
||||
}
|
||||
} else return extractLibs(lib);
|
||||
return !(libhook && librt && libgcc && libgcc_real && libpthread && libz && libcpp_shared) ? extractLibs(lib) : true;
|
||||
} else return extractLibs(lib);
|
||||
}
|
||||
|
||||
private boolean extractLibs(File lib) {
|
||||
@@ -248,10 +223,9 @@ public class StudioApp extends MultiDexApplication
|
||||
this.mCompletionProvider.loadProject(project);
|
||||
}
|
||||
|
||||
public void createJavaCompiler(AndroidProject project, CompileListener listener) {
|
||||
public void createJavaCompiler(AndroidProject project) {
|
||||
this.mJavaCompilerService =
|
||||
new JavaCompilerService(project)
|
||||
.setListener(listener);
|
||||
new JavaCompilerService(project);
|
||||
}
|
||||
|
||||
public JavaCompilerService getJavaCompiler() {
|
||||
|
||||
@@ -5,21 +5,26 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import com.itsaky.androidide.adapters.CompletionListAdapter;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.databinding.FragmentEditorBinding;
|
||||
import com.itsaky.androidide.fragments.preferences.EditorPreferences;
|
||||
import com.itsaky.androidide.interfaces.CompileListener;
|
||||
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.models.AndroidProject;
|
||||
import com.itsaky.androidide.models.ConstantsBridge;
|
||||
import com.itsaky.androidide.services.compiler.JavaCompilerService;
|
||||
import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
|
||||
import com.itsaky.androidide.syntax.colorschemes.SchemeWombat;
|
||||
import com.itsaky.androidide.tasks.TaskExecutor;
|
||||
import com.itsaky.androidide.tasks.callables.ReadFileTask;
|
||||
import com.itsaky.androidide.tools.SourceJavaFileObject;
|
||||
import com.itsaky.androidide.utils.FileUtil;
|
||||
import com.itsaky.androidide.utils.PreferenceManager;
|
||||
import com.itsaky.androidide.utils.TypefaceUtils;
|
||||
import com.sun.tools.javac.resources.compiler;
|
||||
import io.github.rosemoe.editor.interfaces.EditorEventListener;
|
||||
import io.github.rosemoe.editor.langs.EmptyLanguage;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
@@ -28,13 +33,11 @@ import java.io.File;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import com.elvishew.xlog.XLog;
|
||||
import android.text.TextUtils;
|
||||
import com.itsaky.androidide.services.compiler.JavaCompilerService;
|
||||
|
||||
public class EditorFragment extends BaseFragment implements EditorEventListener {
|
||||
public class EditorFragment extends BaseFragment implements EditorEventListener, CompileListener {
|
||||
|
||||
public FragmentEditorBinding binding;
|
||||
private File mFile;
|
||||
@@ -43,9 +46,7 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
|
||||
private boolean isFirstCreate = false;
|
||||
|
||||
private static AndroidProject project;
|
||||
|
||||
private List<CompilerDiagnostic> diags;
|
||||
|
||||
|
||||
public static final String KEY_FILE_PATH = "file_path";
|
||||
public static final String KEY_PROJECT = "project";
|
||||
public static final String EXT_JAVA = ".java";
|
||||
@@ -55,7 +56,7 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
|
||||
public static final String EXT_GROOVY = ".groovy";
|
||||
public static final String EXT_KOTLIN = ".kt";
|
||||
public static final String EXT_JSON = ".json";
|
||||
|
||||
|
||||
public static EditorFragment newInstance(File file, AndroidProject project) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(KEY_FILE_PATH, file.getAbsolutePath());
|
||||
@@ -100,11 +101,30 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
|
||||
postRead();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompilationResult(boolean singleFile, Map<File, List<CompilerDiagnostic>> diagnostics) {
|
||||
if(diagnostics != null
|
||||
&& getFile() != null
|
||||
&& diagnostics.containsKey(getFile()))
|
||||
{
|
||||
binding.editorCodeEditor.setDiagnostics(diagnostics.get(getFile()));
|
||||
}
|
||||
}
|
||||
|
||||
private void requestCompilation() {
|
||||
final long s = System.currentTimeMillis();
|
||||
final SourceJavaFileObject source = new SourceJavaFileObject(getFile(), binding.editorCodeEditor.getText().toString());
|
||||
JavaCompilerService compiler = StudioApp.getInstance().getJavaCompiler();
|
||||
if(compiler != null) {
|
||||
compiler.compileSingleAsync(source, this);
|
||||
}
|
||||
}
|
||||
|
||||
public void setDiagnostics(List<CompilerDiagnostic> diags) {
|
||||
this.diags = diags;
|
||||
XLog.i("[EditorFragment] Diagnostics for file: " + getFile().getAbsolutePath()
|
||||
+ "\ndiagnostics: " + TextUtils.join("\n", diags));
|
||||
if(binding.editorCodeEditor != null) {
|
||||
binding.editorCodeEditor.setDiagnostics(diags);
|
||||
}
|
||||
}
|
||||
|
||||
private void configureEditorIfNeeded() {
|
||||
@@ -176,10 +196,10 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
|
||||
|
||||
private void postRead() {
|
||||
if (mFile.isFile() && mFile.getName().endsWith(EXT_JAVA)) {
|
||||
binding.editorCodeEditor.setEditorLanguage(new JavaLanguage(binding.editorCodeEditor, project));
|
||||
binding.editorCodeEditor.setEditorLanguage(new JavaLanguage(project));
|
||||
binding.editorCodeEditor.setColorScheme(new SchemeVS2019());
|
||||
} else if (mFile.isFile() && mFile.getName().endsWith(EXT_XML)) {
|
||||
binding.editorCodeEditor.setEditorLanguage(new XMLLanguage(binding.editorCodeEditor));
|
||||
binding.editorCodeEditor.setEditorLanguage(new XMLLanguage());
|
||||
binding.editorCodeEditor.setColorScheme(new SchemeWombat());
|
||||
} else if (mFile.isFile() && mFile.getName().endsWith(EXT_GRADLE)) {
|
||||
binding.editorCodeEditor.setEditorLanguage(new GroovyLanguage());
|
||||
@@ -251,6 +271,8 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
|
||||
@Override
|
||||
public void afterDelete(CodeEditor editor, CharSequence content, int startLine, int startColumn, int endLine, int endColumn, CharSequence deletedContent) {
|
||||
isModified = true;
|
||||
|
||||
requestCompilation();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -263,11 +285,8 @@ public class EditorFragment extends BaseFragment implements EditorEventListener
|
||||
if(isOpen && insertedContent.toString().equals("/")) {
|
||||
closeCurrentTag(editor.getText().toString(), endLine, endColumn);
|
||||
}
|
||||
|
||||
JavaCompilerService compiler = getStudioActivity().getApp().getJavaCompiler();
|
||||
if(compiler != null) {
|
||||
compiler.compileAll();
|
||||
}
|
||||
|
||||
requestCompilation();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.unnamed.b.atv.view.AndroidTreeView;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import com.blankj.utilcode.util.FileUtils;
|
||||
|
||||
public class FileTreeFragment extends BottomSheetDialogFragment implements TreeNode.TreeNodeClickListener, TreeNode.TreeNodeLongClickListener {
|
||||
|
||||
@@ -86,13 +87,8 @@ public class FileTreeFragment extends BottomSheetDialogFragment implements TreeN
|
||||
public void onClick(TreeNode node, Object p2) {
|
||||
final File f = (File) p2;
|
||||
if(f.isFile()) {
|
||||
if (mFileActionListener != null && f.isFile()) {
|
||||
for(String ext : compatibleExtensions) {
|
||||
if(f.getName().endsWith(ext)) {
|
||||
mFileActionListener.openFile(f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mFileActionListener != null && FileUtils.isUtf8(f)) {
|
||||
mFileActionListener.openFile(f);
|
||||
}
|
||||
} else if(f.isDirectory() && f.exists()) {
|
||||
if(node.isExpanded()) {
|
||||
@@ -181,7 +177,6 @@ public class FileTreeFragment extends BottomSheetDialogFragment implements TreeN
|
||||
final File gradleHome = Environment.GRADLE_PROPS_DIR;
|
||||
final File root = StudioApp.getInstance().getRootDir().getParentFile();
|
||||
File projectDir = new File(mProject.getProjectPath());
|
||||
projectDir = root;
|
||||
mRoot = TreeNode.root(projectDir);
|
||||
if(gradleHome.exists() && gradleHome.isDirectory()) {
|
||||
if(!gradleProps.exists())
|
||||
|
||||
@@ -84,7 +84,7 @@ public class IDEPreferences extends BasePreferenceFragment {
|
||||
|
||||
screen.addPreference(editor);
|
||||
screen.addPreference(build);
|
||||
// screen.addPreference(issueTracker);
|
||||
screen.addPreference(issueTracker);
|
||||
screen.addPreference(telegram);
|
||||
screen.addPreference(changelog);
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import android.widget.LinearLayout;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
import com.blankj.utilcode.util.ThrowableUtils;
|
||||
import com.elvishew.xlog.XLog;
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.models.LogLine;
|
||||
|
||||
|
||||
@@ -6,5 +6,5 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface CompileListener {
|
||||
void onCompilationResult(boolean success, Map<File, List<CompilerDiagnostic>> diagnostics);
|
||||
void onCompilationResult(boolean singleFile, Map<File, List<CompilerDiagnostic>> diagnostics);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,13 @@ package com.itsaky.androidide.interfaces;
|
||||
import androidx.core.util.Pair;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A callback to listen for updates while dexing JAR files
|
||||
*
|
||||
* @deprecated This is replaced by the ClassFileReader. There is no need to dex the JAR files in order to load them.
|
||||
*/
|
||||
|
||||
@Deprecated
|
||||
public interface DexResultListener {
|
||||
public void onDex(String name);
|
||||
public void onDexSuccess(List<String> dexes);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.itsaky.androidide.interfaces;
|
||||
|
||||
import com.itsaky.androidide.models.DiagnosticGroup;
|
||||
import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
|
||||
|
||||
public interface DiagnosticClickListener {
|
||||
void onGroupClick(DiagnosticGroup group);
|
||||
void onDiagnosticClick(CompilerDiagnostic diagnostic);
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import com.itsaky.androidide.utils.Either;
|
||||
import io.github.rosemoe.editor.interfaces.AutoCompleteProvider;
|
||||
import io.github.rosemoe.editor.struct.CompletionItem;
|
||||
import io.github.rosemoe.editor.text.TextAnalyzeResult;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -17,7 +18,7 @@ public class GroovyAutoComplete implements AutoCompleteProvider {
|
||||
private static final List<String> OTHERS = createOtherCompletions();
|
||||
|
||||
@Override
|
||||
public List<Either<SuggestItem, CompletionItem>> getAutoCompleteItems(String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
|
||||
public List<Either<SuggestItem, CompletionItem>> getAutoCompleteItems(CodeEditor editor, String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
|
||||
List<Either<SuggestItem, CompletionItem>> result = new ArrayList<>();
|
||||
|
||||
for(String artifact : ANDROIDX_ARTIFACTS) {
|
||||
|
||||
@@ -15,22 +15,23 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.tools.Diagnostic;
|
||||
import javax.tools.JavaFileObject;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
import com.itsaky.androidide.services.compiler.model.CompilerDiagnosticImpl;
|
||||
|
||||
public class JavaAutoComplete implements AutoCompleteProvider {
|
||||
|
||||
private CodeEditor editor;
|
||||
private JavaLanguageAnalyzer analyzer;
|
||||
|
||||
public JavaAutoComplete(CodeEditor editor, JavaLanguageAnalyzer analyzer) {
|
||||
this.editor = editor;
|
||||
public JavaAutoComplete(JavaLanguageAnalyzer analyzer) {
|
||||
this.analyzer = analyzer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Either<SuggestItem, CompletionItem>> getAutoCompleteItems(String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
|
||||
public List<Either<SuggestItem, CompletionItem>> getAutoCompleteItems(CodeEditor editor, String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
|
||||
Pair<ArrayList<SuggestItem>, List<Diagnostic<? extends JavaFileObject>>> pair = StudioApp.getInstance().getCompletionProvider().getSuggestions(editor);
|
||||
final ArrayList<SuggestItem> suggestions = pair.first;
|
||||
final List<Diagnostic<? extends JavaFileObject>> diags = pair.second;
|
||||
// final List<Diagnostic<? extends JavaFileObject>> diags = pair.second;
|
||||
// editor.setDiagnostics(diags.stream().map(CompilerDiagnosticImpl::fromDiagnostic).collect(Collectors.toList()));
|
||||
if(suggestions != null) {
|
||||
List<Either<SuggestItem, CompletionItem>> result = new ArrayList<>();
|
||||
for(SuggestItem item : suggestions) {
|
||||
|
||||
@@ -21,14 +21,12 @@ public class JavaLanguage implements EditorLanguage
|
||||
private JavaLanguageAnalyzer analyzer;
|
||||
private JavaAutoComplete complete;
|
||||
private AndroidProject project;
|
||||
private CodeEditor editor;
|
||||
|
||||
public JavaLanguage(CodeEditor editor, AndroidProject project)
|
||||
public JavaLanguage(AndroidProject project)
|
||||
{
|
||||
this.analyzer = new JavaLanguageAnalyzer();
|
||||
this.complete = new JavaAutoComplete(editor, analyzer);
|
||||
this.complete = new JavaAutoComplete(analyzer);
|
||||
this.project = project;
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ public class CompleteThisKeyword extends JavaCompleteMatcherImpl {
|
||||
MethodDescription desc = new MethodDescription(
|
||||
method.getName().toString(),
|
||||
returnType,
|
||||
method.getModifiers().flags,
|
||||
(int) method.getModifiers().flags,
|
||||
paramsStr);
|
||||
setInfo(desc, editor, incomplete);
|
||||
result.add(desc);
|
||||
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
package com.itsaky.androidide.language.java.parser.internal;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import androidx.annotation.NonNull;
|
||||
import com.elvishew.xlog.XLog;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
import com.itsaky.androidide.models.ConstantsBridge;
|
||||
import dalvik.system.DexFile;
|
||||
import dalvik.system.PathClassLoader;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
class CompiledClassLoader {
|
||||
|
||||
@NonNull
|
||||
public ArrayList<Class> getCompiledClassesFromProject(@NonNull AndroidProject project) {
|
||||
ArrayList<Class> classes = new ArrayList<>();
|
||||
try {
|
||||
final List<String> dexes = ConstantsBridge.PROJECT_DEXES;
|
||||
final PathClassLoader loader = new PathClassLoader(TextUtils.join(File.pathSeparator, dexes), ClassLoader.getSystemClassLoader());
|
||||
if (dexes == null || dexes.size() <= 0) return classes;
|
||||
for (String dex : dexes) {
|
||||
final DexFile d = new DexFile(new File(dex));
|
||||
Enumeration<String> entries = d.entries();
|
||||
String name = null;
|
||||
while (entries.hasMoreElements() && (name = entries.nextElement()) != null) {
|
||||
while(name.startsWith(File.separator))
|
||||
name = name.substring(1);
|
||||
if(name.contains(File.separator))
|
||||
name = name.replace(File.separator, ".");
|
||||
Class<?> load = tryLoad(loader, name);
|
||||
if (load != null)
|
||||
classes.add(load);
|
||||
}
|
||||
d.close();
|
||||
}
|
||||
|
||||
ConstantsBridge.CLASS_LOAD_SUCCESS = true;
|
||||
return classes;
|
||||
} catch (Throwable th) {
|
||||
ConstantsBridge.CLASS_LOAD_SUCCESS = false;
|
||||
return classes;
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> tryLoad(ClassLoader loader, String name) {
|
||||
try {
|
||||
return loader.loadClass(name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+236
-10
@@ -1,26 +1,47 @@
|
||||
package com.itsaky.androidide.language.java.parser.internal;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.util.Pair;
|
||||
import com.blankj.utilcode.util.FileIOUtils;
|
||||
import com.blankj.utilcode.util.FileUtils;
|
||||
import com.itsaky.androidide.interfaces.Filter;
|
||||
import com.itsaky.androidide.language.java.parser.JavacParser;
|
||||
import com.itsaky.androidide.language.java.parser.model.ClassDescription;
|
||||
import com.itsaky.androidide.language.java.parser.model.FieldDescription;
|
||||
import com.itsaky.androidide.language.java.parser.model.MethodDescription;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
import com.itsaky.androidide.models.ConstantsBridge;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import dalvik.system.DexFile;
|
||||
import dalvik.system.PathClassLoader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import org.eclipse.jdt.core.Signature;
|
||||
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
|
||||
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
|
||||
import org.eclipse.jdt.internal.compiler.classfmt.FieldInfo;
|
||||
import org.eclipse.jdt.internal.compiler.env.IBinaryField;
|
||||
import org.eclipse.jdt.internal.compiler.env.IBinaryMethod;
|
||||
import org.eclipse.jdt.internal.compiler.env.IBinaryType;
|
||||
import com.itsaky.androidide.language.java.parser.model.ConstructorDescription;
|
||||
import com.itsaky.androidide.utils.Either;
|
||||
import com.blankj.utilcode.util.ThrowableUtils;
|
||||
import com.itsaky.androidide.utils.Environment;
|
||||
|
||||
public class JavaClassManager implements IClassManager {
|
||||
|
||||
private static final String TAG = "JavaClassReader";
|
||||
private static final String JAVA_DOT_LANG_DOT = "java.lang.";
|
||||
private static JavaClassManager INSTANCE;
|
||||
@@ -65,11 +86,9 @@ public class JavaClassManager implements IClassManager {
|
||||
|
||||
public void loadFromProject(AndroidProject project) {
|
||||
try {
|
||||
CompiledClassLoader classLoader = new CompiledClassLoader();
|
||||
ArrayList<Class> classes = classLoader.getCompiledClassesFromProject(project);
|
||||
for (Class clazz : classes) {
|
||||
getClassWrapper(clazz);
|
||||
}
|
||||
try {
|
||||
loadClasspaths(project);
|
||||
} catch (Throwable e) {}
|
||||
JavacParser parser = new JavacParser();
|
||||
List<String> javaSrcDirs = project.getSourcePaths();
|
||||
for (String dir : javaSrcDirs) {
|
||||
@@ -84,9 +103,7 @@ public class JavaClassManager implements IClassManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} catch (Throwable e) {}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,5 +266,214 @@ public class JavaClassManager implements IClassManager {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void loadClasspaths(AndroidProject project) throws IOException, ClassFormatException, ClassNotFoundException {
|
||||
final List<String> classpaths = project.getClassPaths();
|
||||
if(!classpaths.contains(Environment.BOOTCLASSPATH))
|
||||
classpaths.add(Environment.BOOTCLASSPATH.getAbsolutePath());
|
||||
for(int i=0;i<classpaths.size();i++) {
|
||||
final File cp = new File(classpaths.get(i));
|
||||
if(!(cp.exists() && cp.getName().endsWith(".jar"))) continue;
|
||||
final ZipFile zip = new ZipFile(cp);
|
||||
final Enumeration<? extends ZipEntry> entries = zip.entries();
|
||||
|
||||
ZipEntry entry = null;
|
||||
while(entries.hasMoreElements() && ((entry = entries.nextElement()) != null)) {
|
||||
if(!entry.getName().endsWith(".class"))
|
||||
continue;
|
||||
try {
|
||||
ClassFileReader reader = ClassFileReader.read(zip, entry.getName());
|
||||
describe(zip, reader);
|
||||
} catch (Throwable th) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ClassDescription describe(ZipFile zip, IBinaryType type) throws IOException, ClassFormatException, ClassNotFoundException {
|
||||
if(type == null)
|
||||
return null;
|
||||
final char[] name = type.getName();
|
||||
final char[] superClassName = type.getSuperclassName();
|
||||
if(name == null) return null;
|
||||
final int mods = type.getModifiers();
|
||||
final boolean isPremitive = false;
|
||||
final boolean isAnnotation = false;
|
||||
final boolean isEnum = false;
|
||||
final boolean isMember = type.getEnclosingTypeName() != null;
|
||||
final IBinaryField[] fields = type.getFields();
|
||||
final IBinaryMethod[] methods = type.getMethods();
|
||||
final char[][] interfaces = type.getInterfaceNames();
|
||||
ClassDescription clazz = new ClassDescription(bytecodeToSourceType(new String(name)), mods, isPremitive, isAnnotation, isEnum, isMember);
|
||||
|
||||
int index = 0;
|
||||
while(fields != null && index < fields.length) {
|
||||
IBinaryField f = fields[index];
|
||||
if(f == null) continue;
|
||||
FieldDescription field = describe(zip, f);
|
||||
if(field == null) continue;
|
||||
clazz.addField(field);
|
||||
++index;
|
||||
}
|
||||
|
||||
index = 0;
|
||||
while(methods != null && index < methods.length) {
|
||||
IBinaryMethod m = methods[index];
|
||||
if(m == null) continue;
|
||||
Either<MethodDescription, ConstructorDescription> result = describe(zip, m);
|
||||
if(result == null) continue;
|
||||
if(result.isLeft())
|
||||
clazz.addMethod(result.getLeft());
|
||||
else clazz.addConstructor(result.getRight());
|
||||
++index;
|
||||
}
|
||||
|
||||
// TODO: Add interfaces
|
||||
|
||||
clazz.setSuperclass(superClassName == null ? ClassDescription.ofObject() : findType(zip, new String(superClassName)));
|
||||
|
||||
update(clazz);
|
||||
return clazz;
|
||||
}
|
||||
|
||||
private Either<MethodDescription, ConstructorDescription> describe(ZipFile zip, IBinaryMethod m) throws IOException, IllegalArgumentException, ClassFormatException, ClassNotFoundException {
|
||||
if(zip == null || m == null) return null;
|
||||
final char[] descriptor = m.getMethodDescriptor();
|
||||
final char[] signature = m.getGenericSignature();
|
||||
final char[] name = m.getSelector();
|
||||
final int mods = m.getModifiers();
|
||||
if(name == null || descriptor == null) return null;
|
||||
IClass returns = findType(zip, Signature.getReturnType(new String(descriptor)));
|
||||
String[] paramTypes = Signature.getParameterTypes(new String(descriptor));
|
||||
ArrayList<String> params = new ArrayList<>();
|
||||
for(String type : paramTypes) {
|
||||
params.add(bytecodeToSourceType(type));
|
||||
}
|
||||
return m.isConstructor() ? Either.forRight(new ConstructorDescription(new String(name).replace("/", "."), params)) : Either.forLeft(new MethodDescription(new String(name).replace("/", "."), returns, mods, params));
|
||||
}
|
||||
|
||||
private FieldDescription describe(ZipFile zip, IBinaryField field) throws IOException, ClassFormatException, ClassNotFoundException {
|
||||
if(field == null)
|
||||
return null;
|
||||
final char[] n = field.getName();
|
||||
final char[] t = field.getTypeName();
|
||||
if(n == null || t == null) return null;
|
||||
final String name = new String(n);
|
||||
final String type = new String(t);
|
||||
final int mods = field.getModifiers();
|
||||
final String value = field instanceof FieldInfo && ((FieldInfo) field).hasConstant() ? field.getConstant().stringValue() : "";
|
||||
IClass typeClass = findType(zip, type);
|
||||
if(typeClass == null)
|
||||
typeClass = new ClassDescription(Object.class);
|
||||
FieldDescription f = new FieldDescription(mods, typeClass, name.replace("/", "."), value);
|
||||
return f;
|
||||
}
|
||||
|
||||
private IClass findType(ZipFile zip, String name) throws IOException, ClassFormatException, ClassNotFoundException {
|
||||
if(zip == null || name == null)
|
||||
return null;
|
||||
final int arrayCount = Signature.getArrayCount(name);
|
||||
if(arrayCount == 0) {
|
||||
if(name.length() == 1) {
|
||||
// A primitive type
|
||||
final Class load = primitiveType(name.charAt(0));
|
||||
if(load != null) {
|
||||
ClassDescription desc = new ClassDescription(load);
|
||||
desc.initMembers(load);
|
||||
update(desc);
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
IClass found = getParsedClass(bytecodeToSourceType(name));
|
||||
if(found != null) {
|
||||
return found;
|
||||
} else {
|
||||
ClassFileReader reader = ClassFileReader.read(zip, name);
|
||||
if(reader == null)
|
||||
return null;
|
||||
else {
|
||||
// This class will be updated in describe() method
|
||||
return describe(zip, reader);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(Signature.getElementType(name).length() == 1) {
|
||||
// This type may be an array of primitive
|
||||
Class load = Class.forName(name);
|
||||
if(load != null) {
|
||||
ClassDescription desc = new ClassDescription(load);
|
||||
desc.initMembers(load);
|
||||
update(desc);
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
// TODO: Load array type classes of objects
|
||||
// Return without loading members of this class
|
||||
return new ClassDescription(Void.TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
private String bytecodeToSourceType(String type) {
|
||||
type = Signature.getElementType(type);
|
||||
if(type.startsWith("L") && type.endsWith(";")) {
|
||||
type = type.substring(1, type.lastIndexOf(";"));
|
||||
}
|
||||
return type.replace("/", ".");
|
||||
}
|
||||
|
||||
public Class primitiveType(char typeId) {
|
||||
switch (typeId) {
|
||||
case 'I' : return int.class;
|
||||
case 'B' : return byte.class;
|
||||
case 'S' : return short.class;
|
||||
case 'C' : return char.class;
|
||||
case 'F' : return float.class;
|
||||
case 'D' : return double.class;
|
||||
case 'Z' : return boolean.class;
|
||||
case 'J' : return long.class;
|
||||
default : return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Kept as a backup if loading jar files fails for some reasons
|
||||
*/
|
||||
private ArrayList<Class> loadFromDexes(AndroidProject project) {
|
||||
ArrayList<Class> classes = new ArrayList<>();
|
||||
try {
|
||||
final List<String> dexes = ConstantsBridge.PROJECT_DEXES;
|
||||
final PathClassLoader loader = new PathClassLoader(TextUtils.join(File.pathSeparator, dexes), ClassLoader.getSystemClassLoader());
|
||||
if (dexes == null || dexes.size() <= 0) return classes;
|
||||
for (String dex : dexes) {
|
||||
final DexFile d = new DexFile(new File(dex));
|
||||
Enumeration<String> entries = d.entries();
|
||||
String name = null;
|
||||
while (entries.hasMoreElements() && (name = entries.nextElement()) != null) {
|
||||
while(name.startsWith(File.separator))
|
||||
name = name.substring(1);
|
||||
if(name.contains(File.separator))
|
||||
name = name.replace(File.separator, ".");
|
||||
Class<?> load = tryLoad(loader, name);
|
||||
if (load != null)
|
||||
classes.add(load);
|
||||
}
|
||||
d.close();
|
||||
}
|
||||
|
||||
ConstantsBridge.CLASS_LOAD_SUCCESS = true;
|
||||
return classes;
|
||||
} catch (Throwable th) {
|
||||
ConstantsBridge.CLASS_LOAD_SUCCESS = false;
|
||||
return classes;
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> tryLoad(ClassLoader loader, String name) {
|
||||
try {
|
||||
return loader.loadClass(name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-24
@@ -56,6 +56,12 @@ public class ClassDescription extends JavaSuggestItemImpl implements IClass {
|
||||
this.isMemberClass = isMember;
|
||||
this.mEum = isEnum;
|
||||
}
|
||||
|
||||
public static ClassDescription ofObject() {
|
||||
ClassDescription desc = new ClassDescription(Object.class);
|
||||
desc.initMembers(Object.class);
|
||||
return desc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMemberClass() {
|
||||
@@ -251,7 +257,6 @@ public class ClassDescription extends JavaSuggestItemImpl implements IClass {
|
||||
for (Class anInterface : interfaces) {
|
||||
IClass implement = JavaClassManager.getInstance().getClassWrapper(anInterface);
|
||||
mImplements.add(implement);
|
||||
// initClassMembers(implement);
|
||||
}
|
||||
|
||||
if (c.getSuperclass() != null) {
|
||||
@@ -262,8 +267,6 @@ public class ClassDescription extends JavaSuggestItemImpl implements IClass {
|
||||
mSuperClass = JavaClassManager.getInstance().getParsedClass(Object.class.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// initClassMembers(mSuperClass);
|
||||
|
||||
for (Constructor constructor : c.getConstructors()) {
|
||||
if (Modifier.isPublic(constructor.getModifiers())) {
|
||||
@@ -288,26 +291,5 @@ public class ClassDescription extends JavaSuggestItemImpl implements IClass {
|
||||
|
||||
public void setSuperclass(IClass superclass) {
|
||||
this.mSuperClass = superclass;
|
||||
// initClassMembers(mSuperClass);
|
||||
}
|
||||
|
||||
// private void initClassMembers(IClass clazz) {
|
||||
// if(clazz.getFields() != null) {
|
||||
// for(IField f : clazz.getFields()) {
|
||||
// addField(f);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if(clazz.getMethods() != null) {
|
||||
// for(IMethod method : clazz.getMethods()) {
|
||||
// addMethod(method);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if(clazz.getConstructors() != null) {
|
||||
// for(ConstructorDescription desc : clazz.getConstructors()) {
|
||||
// addConstructor(desc);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
+12
-7
@@ -13,21 +13,26 @@ import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ConstructorDescription extends JavaSuggestItemImpl implements IJavaDocCommentable {
|
||||
private ArrayList<IClass> mParameterTypes = new ArrayList<>();
|
||||
private ArrayList<String> mParameterTypes = new ArrayList<>();
|
||||
private String mConstructorName;
|
||||
|
||||
public ConstructorDescription(Constructor constructor) {
|
||||
mConstructorName = constructor.getName();
|
||||
Class[] parameterTypes = constructor.getParameterTypes();
|
||||
for (Class parameterType : parameterTypes) {
|
||||
IClass type = JavaClassManager.getInstance().getClassWrapper(parameterType);
|
||||
mParameterTypes.add(type);
|
||||
mParameterTypes.add(parameterType.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public ConstructorDescription(String name, List<IClass> paramTypes) {
|
||||
mConstructorName = name;
|
||||
mParameterTypes.addAll(paramTypes.stream().filter(c -> c != null).map(c -> c.getFullClassName()).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
public ConstructorDescription(String name, ArrayList<String> paramTypes) {
|
||||
mConstructorName = name;
|
||||
mParameterTypes.addAll(paramTypes);
|
||||
}
|
||||
@@ -95,21 +100,21 @@ public class ConstructorDescription extends JavaSuggestItemImpl implements IJava
|
||||
return getSimpleName() + "()";
|
||||
}
|
||||
|
||||
private String paramsToString(@NonNull ArrayList<IClass> parameterTypes) {
|
||||
private String paramsToString(@NonNull ArrayList<String> parameterTypes) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
boolean firstTime = true;
|
||||
for (IClass parameterType : parameterTypes) {
|
||||
for (String parameterType : parameterTypes) {
|
||||
if (firstTime) {
|
||||
firstTime = false;
|
||||
} else {
|
||||
result.append(",");
|
||||
}
|
||||
result.append(parameterType.getSimpleName());
|
||||
result.append(parameterType);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public ArrayList<IClass> getParameterTypes() {
|
||||
public ArrayList<String> getParameterTypes() {
|
||||
return mParameterTypes;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -28,10 +28,10 @@ public class MethodDescription extends JavaSuggestItemImpl implements Member, Su
|
||||
private IClass mReturnType;
|
||||
|
||||
public MethodDescription(String name, IClass returnType,
|
||||
long modifiers, ArrayList<String> parameterTypes) {
|
||||
int modifiers, ArrayList<String> parameterTypes) {
|
||||
mMethodName = name;
|
||||
mReturnType = returnType;
|
||||
mModifiers = (int) modifiers;
|
||||
mModifiers = modifiers;
|
||||
mParameterTypes = parameterTypes;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -44,7 +44,7 @@ public class JavaCompletionProvider {
|
||||
this.mPackageManager = new PackageManager();
|
||||
this.mJavaParser = new JavacParser();
|
||||
|
||||
addAutoComplete();
|
||||
addMatchers();
|
||||
}
|
||||
|
||||
public boolean loadProject(AndroidProject project) {
|
||||
@@ -57,7 +57,7 @@ public class JavaCompletionProvider {
|
||||
return initiated;
|
||||
}
|
||||
|
||||
private void addAutoComplete() {
|
||||
private void addMatchers() {
|
||||
mCompletors.add(new CompleteExpression(mClassLoader));
|
||||
mCompletors.add(new CompleteNewKeyword(mClassLoader));
|
||||
mCompletors.add(new CompletePackage(mPackageManager));
|
||||
|
||||
@@ -10,6 +10,7 @@ import io.github.rosemoe.editor.interfaces.NewlineHandler;
|
||||
import io.github.rosemoe.editor.struct.CompletionItem;
|
||||
import io.github.rosemoe.editor.text.TextAnalyzeResult;
|
||||
import io.github.rosemoe.editor.text.TextAnalyzer;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import io.github.rosemoe.editor.widget.SymbolPairMatch;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -90,7 +91,7 @@ public class LogLanguageImpl implements EditorLanguage {
|
||||
private static class LogCompletor implements AutoCompleteProvider {
|
||||
|
||||
@Override
|
||||
public List<Either<SuggestItem, CompletionItem>> getAutoCompleteItems(String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
|
||||
public List<Either<SuggestItem, CompletionItem>> getAutoCompleteItems(CodeEditor editor, String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
|
||||
return new ArrayList<Either<SuggestItem, CompletionItem>>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@ import com.itsaky.androidide.utils.EitherOfResult;
|
||||
|
||||
public class XMLAutoComplete implements AutoCompleteProvider {
|
||||
|
||||
private CodeEditor editor;
|
||||
|
||||
private Comparator<CompletionItem> RESULTS_SORTER = new Comparator<CompletionItem>(){
|
||||
|
||||
@Override
|
||||
@@ -27,13 +25,9 @@ public class XMLAutoComplete implements AutoCompleteProvider {
|
||||
return ((CompletionListItem) p1).getSortText().compareTo(((CompletionListItem) p2).getSortText());
|
||||
}
|
||||
};
|
||||
|
||||
public XMLAutoComplete(CodeEditor editor) {
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Either<SuggestItem, CompletionItem>> getAutoCompleteItems(String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
|
||||
public List<Either<SuggestItem, CompletionItem>> getAutoCompleteItems(CodeEditor editor, String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
|
||||
// We do not store the completion service here in this class
|
||||
// This is because, if we modify the instance of the service in app,
|
||||
// it'll be updated here
|
||||
|
||||
@@ -19,8 +19,8 @@ public class XMLLanguage implements EditorLanguage {
|
||||
private XMLAutoComplete completer;
|
||||
private NewlineHandler[] newlineHandlers = new NewlineHandler[] {};
|
||||
|
||||
public XMLLanguage(CodeEditor editor) {
|
||||
this.completer = new XMLAutoComplete(editor);
|
||||
public XMLLanguage() {
|
||||
this.completer = new XMLAutoComplete();
|
||||
this.analyzer = new XMLAnalyzer();
|
||||
this.newlineHandlers = new NewlineHandler[]{new IndentHandler()};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.itsaky.androidide.models;
|
||||
import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
public class DiagnosticGroup {
|
||||
public int icon;
|
||||
public String text;
|
||||
public File file;
|
||||
public List<CompilerDiagnostic> diagnostics;
|
||||
|
||||
public DiagnosticGroup(int icon, File file, List<CompilerDiagnostic> diagnostics) {
|
||||
this.icon = icon;
|
||||
this.file = file;
|
||||
this.text = file.getName();
|
||||
this.diagnostics = diagnostics;
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,9 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import com.itsaky.androidide.models.LogLine;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import com.elvishew.xlog.XLog;
|
||||
|
||||
public class LogReceiver extends BroadcastReceiver {
|
||||
|
||||
@@ -37,7 +37,7 @@ public class LogReceiver extends BroadcastReceiver {
|
||||
Matcher m = LOG_HEAD_PATTERN.matcher(line);
|
||||
LogLine log = null;
|
||||
final boolean find = line != null && line.trim().length() > 2 /*if length() == 2 then only [] is available*/ && m.find();
|
||||
XLog.i("line -> " + line + "\nlogTag find() -> " + find);
|
||||
Logger.instance().i("line -> " + line + "\nlogTag find() -> " + find);
|
||||
if(find) {
|
||||
String date = "";
|
||||
String time = "";
|
||||
@@ -84,9 +84,9 @@ public class LogReceiver extends BroadcastReceiver {
|
||||
log = new LogLine("", "", "", "", "", "", line);
|
||||
}
|
||||
|
||||
XLog.i("log = " + log + "\nlogListener = " + listener);
|
||||
Logger.instance().i("log = " + log + "\nlogListener = " + listener);
|
||||
if(log != null && listener != null) {
|
||||
XLog.i("listener.appendLogLine(...) log ->\n" + log);
|
||||
Logger.instance().i("listener.appendLogLine(...) log ->\n" + log);
|
||||
listener.appendLogLine(log);
|
||||
logTag = null;
|
||||
}
|
||||
|
||||
@@ -207,6 +207,8 @@ public class IDEService extends Service implements ShellServer.Callback {
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
// Delete the tmp directory. This directory is not needed anymore...
|
||||
FileUtils.delete(Environment.TMP_DIR);
|
||||
isRunning = false;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,12 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A utility class to convert JAR files to DEX
|
||||
*
|
||||
* @deprecated This is replaced by the ClassFileReader. There is no need to dex the JAR files in order to load them.
|
||||
*/
|
||||
@Deprecated
|
||||
public class Jar2DexService implements ShellServer.Callback {
|
||||
|
||||
private final List<String> mClasspaths;
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.itsaky.androidide.services.compiler;
|
||||
|
||||
import org.eclipse.jdt.core.compiler.CompilationProgress;
|
||||
|
||||
public class CompilerProgress extends CompilationProgress {
|
||||
|
||||
private boolean canceled = false;
|
||||
|
||||
public void setCanceled(boolean canceled) {
|
||||
this.canceled = canceled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void begin(int p1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void done() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCanceled() {
|
||||
return canceled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTaskName(String p1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void worked(int p1, int p2) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.itsaky.androidide.services.compiler;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Locale;
|
||||
import javax.tools.Diagnostic;
|
||||
import javax.tools.DiagnosticListener;
|
||||
import javax.tools.JavaFileObject;
|
||||
import org.eclipse.jdt.core.compiler.CategorizedProblem;
|
||||
import org.eclipse.jdt.internal.compiler.CompilationResult;
|
||||
import org.eclipse.jdt.internal.compiler.ICompilerRequestor;
|
||||
import org.eclipse.jdt.internal.compiler.problem.DefaultProblem;
|
||||
import org.eclipse.jdt.internal.compiler.tool.EclipseCompilerImpl;
|
||||
import org.eclipse.jdt.internal.compiler.tool.EclipseFileManager;
|
||||
import org.eclipse.jdt.internal.compiler.tool.EclipseFileObject;
|
||||
|
||||
public class CompilerRequestor implements ICompilerRequestor {
|
||||
|
||||
private DiagnosticListener<? super JavaFileObject> listener;
|
||||
|
||||
public CompilerRequestor(DiagnosticListener<? super JavaFileObject> listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptResult(CompilationResult p1) {
|
||||
if (p1 != null && p1.hasProblems()) {
|
||||
for (CategorizedProblem problem : p1.getAllProblems()) {
|
||||
report(problem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void report(final CategorizedProblem problem) {
|
||||
this.listener.report(new Diagnostic<JavaFileObject>() {
|
||||
@Override
|
||||
public String getCode() {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public long getColumnNumber() {
|
||||
if (problem instanceof DefaultProblem) {
|
||||
return ((DefaultProblem) problem).column;
|
||||
}
|
||||
return Diagnostic.NOPOS;
|
||||
}
|
||||
@Override
|
||||
public long getEndPosition() {
|
||||
if (problem instanceof DefaultProblem) {
|
||||
return ((DefaultProblem) problem).getSourceEnd();
|
||||
}
|
||||
return Diagnostic.NOPOS;
|
||||
}
|
||||
@Override
|
||||
public Kind getKind() {
|
||||
if (problem.isError()) {
|
||||
return Diagnostic.Kind.ERROR;
|
||||
}
|
||||
if (problem.isWarning()) {
|
||||
return Diagnostic.Kind.WARNING;
|
||||
} else if (problem instanceof DefaultProblem && ((DefaultProblem) problem).isInfo()) {
|
||||
return Diagnostic.Kind.NOTE;
|
||||
}
|
||||
return Diagnostic.Kind.OTHER;
|
||||
}
|
||||
@Override
|
||||
public long getLineNumber() {
|
||||
if (problem instanceof DefaultProblem) {
|
||||
return ((DefaultProblem) problem).getSourceLineNumber();
|
||||
}
|
||||
return Diagnostic.NOPOS;
|
||||
}
|
||||
@Override
|
||||
public String getMessage(Locale locale) {
|
||||
return problem.getMessage();
|
||||
}
|
||||
@Override
|
||||
public long getPosition() {
|
||||
if (problem instanceof DefaultProblem) {
|
||||
return ((DefaultProblem) problem).getSourceStart();
|
||||
}
|
||||
return Diagnostic.NOPOS;
|
||||
}
|
||||
@Override
|
||||
public JavaFileObject getSource() {
|
||||
if (problem instanceof DefaultProblem) {
|
||||
File f = new File(new String(problem.getOriginatingFileName()));
|
||||
if (f.exists()) {
|
||||
return new EclipseFileObject(null, f.toURI(), JavaFileObject.Kind.SOURCE, null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public long getStartPosition() {
|
||||
return getPosition();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package com.itsaky.androidide.services.compiler;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import org.eclipse.jdt.core.compiler.CompilationProgress;
|
||||
import org.eclipse.jdt.internal.compiler.CompilationResult;
|
||||
import org.eclipse.jdt.internal.compiler.Compiler;
|
||||
import org.eclipse.jdt.internal.compiler.ICompilerRequestor;
|
||||
import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
|
||||
import org.eclipse.jdt.internal.compiler.IProblemFactory;
|
||||
import org.eclipse.jdt.internal.compiler.ReadManager;
|
||||
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
|
||||
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
|
||||
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
|
||||
import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
|
||||
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeCollisionException;
|
||||
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
|
||||
import org.eclipse.jdt.internal.compiler.util.Messages;
|
||||
|
||||
public class CompilerWrapper extends Compiler {
|
||||
|
||||
public CompilerWrapper(INameEnvironment env,
|
||||
IErrorHandlingPolicy errorHandlingPolicy,
|
||||
CompilerOptions options,
|
||||
ICompilerRequestor compilerRequestor,
|
||||
IProblemFactory problemFactory,
|
||||
PrintWriter out,
|
||||
CompilationProgress progress) {
|
||||
super(env, errorHandlingPolicy, options, compilerRequestor, problemFactory, out, progress);
|
||||
this.useSingleThread = true;
|
||||
}
|
||||
|
||||
public CompilerWrapper setProgress(CompilationProgress progress) {
|
||||
this.progress = progress;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compile(ICompilationUnit[] sourceUnits) {
|
||||
compileInternal(sourceUnits, false);
|
||||
}
|
||||
|
||||
private void compileInternal(ICompilationUnit[] sourceUnits, boolean lastRound) {
|
||||
this.stats.startTime = System.currentTimeMillis();
|
||||
try {
|
||||
reportProgress(Messages.compilation_beginningToCompile);
|
||||
if (this.annotationProcessorManager == null) {
|
||||
beginToCompile(sourceUnits);
|
||||
} else {
|
||||
ICompilationUnit[] originalUnits = sourceUnits.clone(); // remember source units in case a source type collision occurs
|
||||
try {
|
||||
beginToCompile(sourceUnits);
|
||||
if (!lastRound) processAnnotations();
|
||||
if (!this.options.generateClassFiles) return;
|
||||
} catch (SourceTypeCollisionException e) {
|
||||
backupAptProblems();
|
||||
reset();
|
||||
int originalLength = originalUnits.length;
|
||||
int newProcessedLength = e.newAnnotationProcessorUnits.length;
|
||||
ICompilationUnit[] combinedUnits = new ICompilationUnit[originalLength + newProcessedLength];
|
||||
System.arraycopy(originalUnits, 0, combinedUnits, 0, originalLength);
|
||||
System.arraycopy(e.newAnnotationProcessorUnits, 0, combinedUnits, originalLength, newProcessedLength);
|
||||
this.annotationProcessorStartIndex = originalLength;
|
||||
compileInternal(combinedUnits, e.isLastRound);
|
||||
return;
|
||||
}
|
||||
}
|
||||
restoreAptProblems();
|
||||
processCompiledUnits(0, lastRound);
|
||||
} catch (AbortCompilation e) {
|
||||
this.handleInternalException(e, null);
|
||||
} catch (Throwable th) {
|
||||
AbortCompilation a = new AbortCompilation();
|
||||
a.isSilent = true;
|
||||
handleInternalException(a, null);
|
||||
}
|
||||
if (this.options.verbose) {
|
||||
if (this.totalUnits > 1) {
|
||||
this.out.println(Messages.bind(Messages.compilation_units, String.valueOf(this.totalUnits)));
|
||||
} else {
|
||||
this.out.println(Messages.bind(Messages.compilation_unit, String.valueOf(this.totalUnits)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void internalBeginToCompile(ICompilationUnit[] sourceUnits, int maxUnits) {
|
||||
if (!this.useSingleThread && maxUnits >= ReadManager.THRESHOLD)
|
||||
this.parser.readManager = new ReadManager(sourceUnits, maxUnits);
|
||||
|
||||
for (int i = 0; i < maxUnits; i++) {
|
||||
CompilationResult unitResult = null;
|
||||
try {
|
||||
if (this.options.verbose) {
|
||||
this.out.println(
|
||||
Messages.bind(Messages.compilation_request,
|
||||
new String[] {
|
||||
String.valueOf(i + 1),
|
||||
String.valueOf(maxUnits),
|
||||
new String(sourceUnits[i].getFileName())
|
||||
}));
|
||||
}
|
||||
CompilationUnitDeclaration parsedUnit;
|
||||
unitResult = new CompilationResult(sourceUnits[i], i, maxUnits, this.options.maxProblemsPerUnit);
|
||||
long parseStart = System.currentTimeMillis();
|
||||
if (this.totalUnits < this.parseThreshold) {
|
||||
parsedUnit = this.parser.parse(sourceUnits[i], unitResult);
|
||||
} else {
|
||||
parsedUnit = this.parser.dietParse(sourceUnits[i], unitResult);
|
||||
}
|
||||
long resolveStart = System.currentTimeMillis();
|
||||
this.stats.parseTime += resolveStart - parseStart;
|
||||
this.lookupEnvironment.buildTypeBindings(parsedUnit, null);
|
||||
this.stats.resolveTime += System.currentTimeMillis() - resolveStart;
|
||||
addCompilationUnit(sourceUnits[i], parsedUnit);
|
||||
ImportReference currentPackage = parsedUnit.currentPackage;
|
||||
if (currentPackage != null) {
|
||||
unitResult.recordPackageName(currentPackage.tokens);
|
||||
}
|
||||
if (this.parser.readManager != null) {
|
||||
this.parser.readManager.shutdown();
|
||||
this.parser.readManager = null;
|
||||
}
|
||||
this.lookupEnvironment.completeTypeBindings();
|
||||
} catch (AbortCompilation a) {
|
||||
if (a.compilationResult == null)
|
||||
a.compilationResult = unitResult;
|
||||
throw a;
|
||||
} catch (Throwable th) {
|
||||
AbortCompilation a = new AbortCompilation();
|
||||
a.isSilent = true;
|
||||
throw a;
|
||||
} finally {
|
||||
sourceUnits[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+359
-79
@@ -2,13 +2,18 @@ package com.itsaky.androidide.services.compiler;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import com.blankj.utilcode.util.FileUtils;
|
||||
import com.blankj.utilcode.util.ThreadUtils;
|
||||
import com.itsaky.androidide.interfaces.CompileListener;
|
||||
import com.itsaky.androidide.models.AndroidProject;
|
||||
import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
|
||||
import com.itsaky.androidide.services.compiler.model.CompilerDiagnosticImpl;
|
||||
import com.itsaky.androidide.services.compiler.request.CompilationRequest;
|
||||
import com.itsaky.androidide.tools.SourceJavaFileObject;
|
||||
import com.itsaky.androidide.utils.Environment;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -18,20 +23,41 @@ import javax.tools.Diagnostic;
|
||||
import javax.tools.DiagnosticListener;
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.JavaFileObject;
|
||||
import org.eclipse.jdt.core.compiler.CategorizedProblem;
|
||||
import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
|
||||
import org.eclipse.jdt.internal.compiler.ICompilerRequestor;
|
||||
import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
|
||||
import org.eclipse.jdt.internal.compiler.IProblemFactory;
|
||||
import org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
|
||||
import org.eclipse.jdt.internal.compiler.batch.FileSystem;
|
||||
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
|
||||
import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
|
||||
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
|
||||
import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler;
|
||||
import org.eclipse.jdt.internal.compiler.tool.EclipseFileManager;
|
||||
import com.elvishew.xlog.XLog;
|
||||
import org.eclipse.jdt.internal.compiler.tool.EclipseFileObject;
|
||||
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
|
||||
|
||||
public class JavaCompilerService implements DiagnosticListener<JavaFileObject> {
|
||||
|
||||
private AndroidProject project;
|
||||
|
||||
private CompileListener listener;
|
||||
private EclipseCompiler compiler;
|
||||
private EclipseFileManager fileManager;
|
||||
|
||||
private List<String> options;
|
||||
private final Map<File, List<CompilerDiagnostic>> diagnostics = new HashMap<>();
|
||||
private AndroidProject mProject;
|
||||
|
||||
private EclipseCompiler mCompiler;
|
||||
private CompilerWrapper mSingleFileCompiler;
|
||||
private EclipseFileManager mFileManager;
|
||||
|
||||
private FileSystem sf_nameEnvironment;
|
||||
private CompilerOptions sf_options;
|
||||
private IProblemFactory sf_problemFactory;
|
||||
private ICompilerRequestor sf_compilerRequest;
|
||||
private CompilerProgress sf_progress;
|
||||
private IErrorHandlingPolicy sf_policy;
|
||||
private PrintWriter sf_outWriter;
|
||||
|
||||
private List<String> mOptions;
|
||||
private List<File> mSources;
|
||||
|
||||
private static final Map<File, List<CompilerDiagnostic>> mDiagnostics = new HashMap<>();
|
||||
|
||||
private FileFilter JAVA_FILTER = new FileFilter(){
|
||||
|
||||
@@ -42,90 +68,344 @@ public class JavaCompilerService implements DiagnosticListener<JavaFileObject> {
|
||||
};
|
||||
|
||||
public JavaCompilerService(AndroidProject project) {
|
||||
this.project = project;
|
||||
|
||||
getCompiler();
|
||||
this.mProject = project;
|
||||
this.createOptions();
|
||||
}
|
||||
|
||||
public JavaCompilerService setListener(CompileListener listener) {
|
||||
this.listener = listener;
|
||||
return this;
|
||||
|
||||
private void createOptions() {
|
||||
this.mOptions = new ArrayList<>();
|
||||
|
||||
mOptions.add("-1.8");
|
||||
mOptions.add("-d");
|
||||
mOptions.add(Environment.TMP_DIR.getAbsolutePath());
|
||||
mOptions.add("-proc:none");
|
||||
mOptions.add("-warn:all");
|
||||
mOptions.add("-warn:-nls");
|
||||
mOptions.add("-g");
|
||||
mOptions.add("-bootclasspath");
|
||||
mOptions.add(Environment.BOOTCLASSPATH.getAbsolutePath());
|
||||
mOptions.add("-cp");
|
||||
mOptions.add(TextUtils.join(File.pathSeparator, mProject.getClassPaths()));
|
||||
}
|
||||
|
||||
private EclipseCompiler getCompiler() {
|
||||
return getCompiler(false);
|
||||
}
|
||||
|
||||
private EclipseCompiler getCompiler() {
|
||||
if(compiler == null) {
|
||||
this.compiler = new EclipseCompiler();
|
||||
this.fileManager = (EclipseFileManager) compiler.getStandardFileManager(this, Locale.US, null);
|
||||
this.options = new ArrayList<>();
|
||||
|
||||
options.add("-1.8");
|
||||
options.add("-d");
|
||||
options.add("none");
|
||||
options.add("-proc:none");
|
||||
options.add("-warn:all");
|
||||
options.add("-g");
|
||||
options.add("-bootclasspath");
|
||||
options.add(Environment.BOOTCLASSPATH.getAbsolutePath());
|
||||
options.add("-cp");
|
||||
options.add(TextUtils.join(File.pathSeparator, project.getClassPaths()));
|
||||
}
|
||||
|
||||
return compiler;
|
||||
|
||||
private EclipseCompiler getCompiler(boolean forceCreate) {
|
||||
if(mCompiler == null || forceCreate) {
|
||||
this.mCompiler = new EclipseCompiler();
|
||||
this.mFileManager = (EclipseFileManager) mCompiler.getStandardFileManager(this, Locale.US, null);
|
||||
}
|
||||
return mCompiler;
|
||||
}
|
||||
|
||||
public boolean compileSingle(File file) {
|
||||
List<String> ops = new ArrayList<String>(options);
|
||||
ops.add("-sourcepath");
|
||||
ops.add(TextUtils.join(File.pathSeparator, project.getSourcePaths()));
|
||||
|
||||
JavaCompiler.CompilationTask task = getCompiler().getTask(null, fileManager, this, ops, null, fileManager.getJavaFileObjects(file));
|
||||
diagnostics.clear();
|
||||
boolean success = task.call();
|
||||
if(this.listener != null) {
|
||||
this.listener.onCompilationResult(success, diagnostics);
|
||||
}
|
||||
|
||||
private EclipseFileManager getFileManager() {
|
||||
if(mFileManager == null)
|
||||
getCompiler(true);
|
||||
|
||||
return mFileManager;
|
||||
}
|
||||
|
||||
private CompilerWrapper getSingleFileCompiler() {
|
||||
if(this.sf_nameEnvironment == null)
|
||||
this.sf_nameEnvironment = new FileSystem(createClasspaths(mProject.getClassPaths()), null, null);
|
||||
|
||||
if(this.sf_compilerRequest == null)
|
||||
this.sf_compilerRequest = new CompilerRequestor(this);
|
||||
|
||||
if(this.sf_problemFactory == null)
|
||||
this.sf_problemFactory = getProblemFactory();
|
||||
|
||||
if(this.sf_options == null) {
|
||||
this.sf_options = new CompilerOptions();
|
||||
putOptions(sf_options);
|
||||
}
|
||||
|
||||
if(sf_policy == null)
|
||||
this.sf_policy = DefaultErrorHandlingPolicies.exitAfterAllProblems();
|
||||
|
||||
if(sf_outWriter == null)
|
||||
sf_outWriter = new PrintWriter(System.out);
|
||||
|
||||
if(mSingleFileCompiler == null) {
|
||||
mSingleFileCompiler = new CompilerWrapper(
|
||||
sf_nameEnvironment,
|
||||
sf_policy,
|
||||
sf_options,
|
||||
sf_compilerRequest,
|
||||
sf_problemFactory,
|
||||
sf_outWriter,
|
||||
sf_progress = new CompilerProgress()
|
||||
);
|
||||
}
|
||||
return mSingleFileCompiler;
|
||||
}
|
||||
|
||||
private void putOptions(CompilerOptions options) {
|
||||
final long languageLevel = ClassFileConstants.JDK1_8;
|
||||
options.sourceLevel = options.targetJDK = options.complianceLevel = languageLevel;
|
||||
options.processAnnotations = false;
|
||||
Map<String, String> map = options.getMap();
|
||||
for(String warn : CompilerOptions.warningTokens) {
|
||||
map.put(warn, CompilerOptions.ENABLED);
|
||||
}
|
||||
map.put(CompilerOptions.OPTION_ReportNonExternalizedStringLiteral, CompilerOptions.DISABLED);
|
||||
map.put(CompilerOptions.OPTION_LocalVariableAttribute, CompilerOptions.DO_NOT_GENERATE);
|
||||
map.put(CompilerOptions.OPTION_LineNumberAttribute, CompilerOptions.DO_NOT_GENERATE);
|
||||
map.put(CompilerOptions.OPTION_SourceFileAttribute, CompilerOptions.DO_NOT_GENERATE);
|
||||
options.set(map);
|
||||
}
|
||||
|
||||
private String[] createClasspaths(List<String> classpaths) {
|
||||
if (classpaths == null) {
|
||||
classpaths = new ArrayList<>();
|
||||
}
|
||||
|
||||
if(!classpaths.contains(Environment.BOOTCLASSPATH.getAbsolutePath())) {
|
||||
classpaths.add(Environment.BOOTCLASSPATH.getAbsolutePath());
|
||||
}
|
||||
|
||||
String[] result = new String[classpaths.size()];
|
||||
return classpaths.toArray(result);
|
||||
}
|
||||
|
||||
private CompilationRequest createCompilationRequestForAll(CompileListener listener) {
|
||||
if(mSources == null || mSources.size() <= 0) {
|
||||
loadSources();
|
||||
}
|
||||
return CompilationRequest.Builder
|
||||
.newInstance()
|
||||
.withCallback(listener)
|
||||
.withFiles(mSources)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void notifyCompiled(CompileListener listener, Map<File, List<CompilerDiagnostic>> diags) {
|
||||
ThreadUtils.runOnUiThread(() -> {
|
||||
listener.onCompilationResult(true, diags);
|
||||
});
|
||||
}
|
||||
|
||||
protected void cancelCompilation() {
|
||||
getCompiler().setCompilationCancelled(true);
|
||||
if(sf_progress != null) {
|
||||
sf_progress.setCanceled(true);
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getClasspaths() {
|
||||
return mProject.getClassPaths();
|
||||
}
|
||||
|
||||
public void loadSources() {
|
||||
mSources = new ArrayList<File>();
|
||||
for(String path : mProject.getSourcePaths()) {
|
||||
if(path == null || path.trim().length() <= 0) continue;
|
||||
File p = new File(path);
|
||||
if(!p.exists() || !p.isDirectory()) continue;
|
||||
mSources.addAll(FileUtils.listFilesInDirWithFilter(p, JAVA_FILTER, true));
|
||||
}
|
||||
}
|
||||
|
||||
public void compileAllAsync(CompileListener listener) {
|
||||
new Thread(() -> {
|
||||
compile(createCompilationRequestForAll(listener));
|
||||
}).start();
|
||||
}
|
||||
|
||||
public void compileAsync(CompilationRequest request) {
|
||||
new Thread(() -> {
|
||||
compile(request);
|
||||
}).start();
|
||||
}
|
||||
|
||||
public void compileSingleAsync(SourceJavaFileObject source, CompileListener listener) {
|
||||
new Thread(() -> {
|
||||
compileSingle(source, listener);
|
||||
}).start();
|
||||
}
|
||||
|
||||
public boolean compile(final CompilationRequest request) {
|
||||
this.mDiagnostics.clear();
|
||||
final List<JavaFileObject> fileObjects = (ArrayList<JavaFileObject>) getFileManager().getJavaFileObjectsFromFiles(request.files());
|
||||
JavaCompiler.CompilationTask task = getCompiler()
|
||||
.setCompilationCancelled()
|
||||
.newProgress()
|
||||
.getTask(null, getFileManager(), this, mOptions, null, fileObjects);
|
||||
boolean success = task.call();
|
||||
notifyCompiled(request.callback(), mDiagnostics);
|
||||
return success;
|
||||
}
|
||||
|
||||
public boolean compileAll() {
|
||||
List<File> files = new ArrayList<File>();
|
||||
for(String path : project.getSourcePaths()) {
|
||||
if(path == null || path.trim().length() <= 0) continue;
|
||||
File p = new File(path);
|
||||
if(!p.exists() || !p.isDirectory()) continue;
|
||||
files.addAll(FileUtils.listFilesInDirWithFilter(p, JAVA_FILTER, true));
|
||||
}
|
||||
return compile(files);
|
||||
}
|
||||
|
||||
public boolean compile(List<File> files) {
|
||||
JavaCompiler.CompilationTask task = getCompiler().getTask(null, fileManager, this, options, null, fileManager.getJavaFileObjectsFromFiles(files));
|
||||
diagnostics.clear();
|
||||
final long s = System.nanoTime();
|
||||
boolean success = task.call();
|
||||
XLog.i("[JavaCompiler]"
|
||||
+ "\ncompiled: " + files.size() + " files"
|
||||
+ "\ntimeElapsed: " + (System.nanoTime() - s) + "ns");
|
||||
return success;
|
||||
}
|
||||
|
||||
public Map<File, List<CompilerDiagnostic>> getDiagnostics() {
|
||||
return diagnostics;
|
||||
}
|
||||
}
|
||||
|
||||
public void compileSingle(final SourceJavaFileObject source, final CompileListener listener) {
|
||||
mDiagnostics.clear();
|
||||
cancelCompilation();
|
||||
if(mDiagnostics != null) mDiagnostics.clear();
|
||||
getSingleFileCompiler()
|
||||
.setProgress(this.sf_progress = new CompilerProgress())
|
||||
.compile(new CompilationUnit[]{source.asUnit()});
|
||||
notifyCompiled(listener, mDiagnostics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
|
||||
CompilerDiagnostic d = CompilerDiagnosticImpl.fromDiagnostic(diagnostic);
|
||||
if(diagnostics.containsKey(d.file())) {
|
||||
List<CompilerDiagnostic> diags = diagnostics.get(d.file());
|
||||
if(mDiagnostics.containsKey(d.file())) {
|
||||
List<CompilerDiagnostic> diags = mDiagnostics.get(d.file());
|
||||
if(diags == null)
|
||||
diags = new ArrayList<>();
|
||||
diags.add(d);
|
||||
diagnostics.put(d.file(), diags);
|
||||
mDiagnostics.put(d.file(), diags);
|
||||
} else {
|
||||
List<CompilerDiagnostic> diags = new ArrayList<>();
|
||||
diags.add(d);
|
||||
diagnostics.put(d.file(), diags);
|
||||
mDiagnostics.put(d.file(), diags);
|
||||
}
|
||||
}
|
||||
|
||||
private IProblemFactory getProblemFactory() {
|
||||
return new DefaultProblemFactory() {
|
||||
@Override
|
||||
public CategorizedProblem createProblem(
|
||||
final char[] originatingFileName,
|
||||
final int problemId,
|
||||
final String[] problemArguments,
|
||||
final String[] messageArguments,
|
||||
final int severity,
|
||||
final int startPosition,
|
||||
final int endPosition,
|
||||
final int lineNumber,
|
||||
final int columnNumber) {
|
||||
|
||||
report(new Diagnostic<JavaFileObject>() {
|
||||
@Override
|
||||
public String getCode() {
|
||||
return Integer.toString(problemId);
|
||||
}
|
||||
@Override
|
||||
public long getColumnNumber() {
|
||||
return columnNumber;
|
||||
}
|
||||
@Override
|
||||
public long getEndPosition() {
|
||||
return endPosition;
|
||||
}
|
||||
@Override
|
||||
public Kind getKind() {
|
||||
if ((severity & ProblemSeverities.Error) != 0) {
|
||||
return Diagnostic.Kind.ERROR;
|
||||
}
|
||||
if ((severity & ProblemSeverities.Optional) != 0) {
|
||||
return Diagnostic.Kind.WARNING;
|
||||
}
|
||||
if ((severity & ProblemSeverities.Warning) != 0) {
|
||||
return Diagnostic.Kind.MANDATORY_WARNING;
|
||||
}
|
||||
return Diagnostic.Kind.OTHER;
|
||||
}
|
||||
@Override
|
||||
public long getLineNumber() {
|
||||
return lineNumber;
|
||||
}
|
||||
@Override
|
||||
public String getMessage(Locale locale) {
|
||||
if (locale != null) {
|
||||
setLocale(locale);
|
||||
}
|
||||
return getLocalizedMessage(problemId, problemArguments);
|
||||
}
|
||||
@Override
|
||||
public long getPosition() {
|
||||
return startPosition;
|
||||
}
|
||||
@Override
|
||||
public JavaFileObject getSource() {
|
||||
File f = new File(new String(originatingFileName));
|
||||
if (f.exists()) {
|
||||
return new EclipseFileObject(null, f.toURI(), JavaFileObject.Kind.SOURCE, null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public long getStartPosition() {
|
||||
return startPosition;
|
||||
}
|
||||
});
|
||||
return super.createProblem(originatingFileName, problemId, problemArguments, messageArguments, severity, startPosition, endPosition, lineNumber, columnNumber);
|
||||
}
|
||||
@Override
|
||||
public CategorizedProblem createProblem(
|
||||
final char[] originatingFileName,
|
||||
final int problemId,
|
||||
final String[] problemArguments,
|
||||
final int elaborationID,
|
||||
final String[] messageArguments,
|
||||
final int severity,
|
||||
final int startPosition,
|
||||
final int endPosition,
|
||||
final int lineNumber,
|
||||
final int columnNumber) {
|
||||
|
||||
report(new Diagnostic<JavaFileObject>() {
|
||||
@Override
|
||||
public String getCode() {
|
||||
return Integer.toString(problemId);
|
||||
}
|
||||
@Override
|
||||
public long getColumnNumber() {
|
||||
return columnNumber;
|
||||
}
|
||||
@Override
|
||||
public long getEndPosition() {
|
||||
return endPosition;
|
||||
}
|
||||
@Override
|
||||
public Kind getKind() {
|
||||
if ((severity & ProblemSeverities.Error) != 0) {
|
||||
return Diagnostic.Kind.ERROR;
|
||||
}
|
||||
if ((severity & ProblemSeverities.Info) != 0) {
|
||||
return Diagnostic.Kind.NOTE;
|
||||
}
|
||||
if ((severity & ProblemSeverities.Optional) != 0) {
|
||||
return Diagnostic.Kind.WARNING;
|
||||
}
|
||||
if ((severity & ProblemSeverities.Warning) != 0) {
|
||||
return Diagnostic.Kind.MANDATORY_WARNING;
|
||||
}
|
||||
return Diagnostic.Kind.OTHER;
|
||||
}
|
||||
@Override
|
||||
public long getLineNumber() {
|
||||
return lineNumber;
|
||||
}
|
||||
@Override
|
||||
public String getMessage(Locale locale) {
|
||||
if (locale != null) {
|
||||
setLocale(locale);
|
||||
}
|
||||
return getLocalizedMessage(problemId, problemArguments);
|
||||
}
|
||||
@Override
|
||||
public long getPosition() {
|
||||
return startPosition;
|
||||
}
|
||||
@Override
|
||||
public JavaFileObject getSource() {
|
||||
File f = new File(new String(originatingFileName));
|
||||
if (f.exists()) {
|
||||
return new EclipseFileObject(null, f.toURI(), JavaFileObject.Kind.SOURCE, null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public long getStartPosition() {
|
||||
return startPosition;
|
||||
}
|
||||
});
|
||||
return super.createProblem(originatingFileName, problemId, problemArguments, elaborationID, messageArguments, severity, startPosition, endPosition, lineNumber, columnNumber);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+9
-7
@@ -4,11 +4,13 @@ import java.io.File;
|
||||
import javax.tools.Diagnostic;
|
||||
|
||||
public interface CompilerDiagnostic {
|
||||
File file();
|
||||
long line();
|
||||
long column();
|
||||
long start();
|
||||
long end();
|
||||
String message();
|
||||
Diagnostic.Kind kind();
|
||||
File file();
|
||||
long line();
|
||||
long column();
|
||||
long start();
|
||||
long end();
|
||||
String message();
|
||||
Diagnostic.Kind kind();
|
||||
|
||||
int color();
|
||||
}
|
||||
|
||||
+32
-3
@@ -15,16 +15,20 @@ public class CompilerDiagnosticImpl implements CompilerDiagnostic {
|
||||
protected long end = 0;
|
||||
|
||||
protected Diagnostic.Kind kind;
|
||||
|
||||
public static final int COLOR_ERROR = 0xfff44336;
|
||||
public static final int COLOR_WARNING = 0xffffb300;
|
||||
public static final int COLOR_NOTE = 0xffffffff;
|
||||
|
||||
private CompilerDiagnosticImpl(){}
|
||||
|
||||
private CompilerDiagnosticImpl(Diagnostic<? extends JavaFileObject> diagnostic) {
|
||||
this.file = new File(diagnostic.getSource().toUri());
|
||||
this.message = diagnostic.getMessage(Locale.US);
|
||||
this.line = diagnostic.getLineNumber();
|
||||
this.line = diagnostic.getLineNumber() - 1;
|
||||
this.column = diagnostic.getColumnNumber();
|
||||
this.start = diagnostic.getStartPosition();
|
||||
this.end = diagnostic.getEndPosition();
|
||||
this.end = diagnostic.getEndPosition() + 1;
|
||||
this.kind = diagnostic.getKind();
|
||||
}
|
||||
|
||||
@@ -80,9 +84,34 @@ public class CompilerDiagnosticImpl implements CompilerDiagnostic {
|
||||
public String message() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int color() {
|
||||
if(kind() == Diagnostic.Kind.ERROR) {
|
||||
return COLOR_ERROR;
|
||||
} else if(kind() == Diagnostic.Kind.WARNING || kind() == Diagnostic.Kind.MANDATORY_WARNING) {
|
||||
return COLOR_WARNING;
|
||||
}
|
||||
return COLOR_NOTE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CompilerDiagnosticImpl(file=" + file() + ", line=" + line() + ", column=" + column() + ", start=" + start() + ", end=" + end() + ", message=" + message() + ")";
|
||||
return "CompilerDiagnosticImpl(file=" + file() + ", line=" + line() + ", column=" + column() + ", start=" + start() + ", end=" + end() + ", message=" + message() + ", kind=" + kind().toString() + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if(obj instanceof CompilerDiagnosticImpl) {
|
||||
CompilerDiagnosticImpl that = (CompilerDiagnosticImpl) obj;
|
||||
return this.file().equals(that.file()) &&
|
||||
this.line() == that.line() &&
|
||||
this.column() == that.column() &&
|
||||
this.start() == that.start() &&
|
||||
this.end() == that.end() &&
|
||||
this.message().equals(that.message()) &&
|
||||
this.kind() == that.kind();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package com.itsaky.androidide.services.compiler.request;
|
||||
|
||||
import com.itsaky.androidide.interfaces.CompileListener;
|
||||
import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
|
||||
import com.itsaky.androidide.tools.SourceJavaFileObject;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface CompilationRequest {
|
||||
|
||||
List<File> files();
|
||||
CompileListener callback();
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private List<File> files;
|
||||
private CompileListener callback;
|
||||
|
||||
private Builder(){
|
||||
files = new ArrayList<>();
|
||||
}
|
||||
|
||||
public static Builder newInstance() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public Builder withFiles(File... files) {
|
||||
if(files == null) {
|
||||
this.files = new ArrayList<>();
|
||||
return this;
|
||||
}
|
||||
this.files = new ArrayList<>();
|
||||
for(File file : files) {
|
||||
this.files.add(file);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withFiles(List<File> files) {
|
||||
if(files == null) {
|
||||
this.files = new ArrayList<>();
|
||||
return this;
|
||||
}
|
||||
this.files = files;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withCallback(CompileListener callback) {
|
||||
this.callback = callback;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CompilationRequest build() {
|
||||
return new CompilationRequest(){
|
||||
|
||||
@Override
|
||||
public List<File> files() {
|
||||
return files;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompileListener callback() {
|
||||
return callback;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package com.itsaky.androidide.shell;
|
||||
|
||||
import com.blankj.utilcode.util.CloseUtils;
|
||||
import com.blankj.utilcode.util.ThrowableUtils;
|
||||
import com.elvishew.xlog.XLog;
|
||||
import com.itsaky.androidide.utils.Environment;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.Closeable;
|
||||
@@ -38,25 +37,30 @@ public class ShellServer extends Thread {
|
||||
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");
|
||||
|
||||
// Provide paths to .so files needed to execute JDK
|
||||
final String lib = Environment.LIBDIR.getAbsolutePath();
|
||||
stringBuilder.append("\nexport LD_LIBRARY_PATH=" + lib);
|
||||
stringBuilder.append(System.getenv().containsKey("LD_LIBRARY_PATH") ? ":" + System.getenv().get("LD_LIBRARY_PATH") : "");
|
||||
|
||||
if(android.os.Build.VERSION.SDK_INT >= 30) {
|
||||
// Hook calls to malloc() in native binaries
|
||||
// Required only in Android 11 and up
|
||||
stringBuilder.append("\nexport LD_PRELOAD=" + Environment.LIBHOOKSO.getAbsolutePath());
|
||||
}
|
||||
|
||||
// Monotonic clock fix, needed for JDK
|
||||
stringBuilder.append("\nls -l /system/lib64/libc.so && " +
|
||||
"ln -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");
|
||||
stringBuilder.append("\njava -XshowSettings:all > /sdcard/androidide_jdk_settings.txt");
|
||||
|
||||
// Execute all commands...
|
||||
append(stringBuilder.toString(), false);
|
||||
} catch (Throwable th) {
|
||||
if (callback != null) {
|
||||
String out = ThrowableUtils.getFullStackTrace(th).concat("\n");
|
||||
XLog.e("[ShellServer] error setting up server ->\n" + out);
|
||||
callback.output(out);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.itsaky.androidide.tools;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import javax.tools.SimpleJavaFileObject;
|
||||
import org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
|
||||
|
||||
public class SourceJavaFileObject extends SimpleJavaFileObject {
|
||||
|
||||
private File file;
|
||||
private String source;
|
||||
|
||||
public SourceJavaFileObject(File file, String source) {
|
||||
super(URI.create("source"), Kind.SOURCE);
|
||||
this.file = file;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return this.file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getCharContent(boolean p1) {
|
||||
return source;
|
||||
}
|
||||
|
||||
public CompilationUnit asUnit() {
|
||||
return new CompilationUnit(
|
||||
getCharContent(true).toString().toCharArray(),
|
||||
getFile().getAbsolutePath(),
|
||||
"UTF-8");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package com.itsaky.androidide.utils;
|
||||
|
||||
import com.elvishew.xlog.LogConfiguration;
|
||||
import com.elvishew.xlog.LogLevel;
|
||||
import com.elvishew.xlog.XLog;
|
||||
import com.elvishew.xlog.printer.Printer;
|
||||
import com.elvishew.xlog.printer.file.FilePrinter;
|
||||
import com.elvishew.xlog.printer.file.naming.LevelFileNameGenerator;
|
||||
|
||||
public class Logger {
|
||||
|
||||
private static Logger instance;
|
||||
private static com.elvishew.xlog.Logger xLogger;
|
||||
private static String TAG = "AndroidIDE";
|
||||
|
||||
public static Logger instance() {
|
||||
return instance == null ? createInstance(TAG) : instance;
|
||||
}
|
||||
|
||||
public static Logger instance(String tag) {
|
||||
return instance == null ? createInstance(tag) : instance;
|
||||
}
|
||||
|
||||
private Logger(String tag) {
|
||||
this.TAG = tag;
|
||||
}
|
||||
|
||||
private static Logger createInstance(String tag) {
|
||||
LogConfiguration config = new LogConfiguration.Builder()
|
||||
.logLevel(com.itsaky.androidide.BuildConfig.DEBUG ? LogLevel.ALL : LogLevel.NONE)
|
||||
.tag("AndroidIDE")
|
||||
.enableThreadInfo()
|
||||
.enableStackTrace(10)
|
||||
.disableBorder()
|
||||
.build();
|
||||
Printer filePrinter = new FilePrinter
|
||||
.Builder(FileUtil.getExternalStorageDir() + "/ide_xlog")
|
||||
.fileNameGenerator(new LevelFileNameGenerator())
|
||||
.cleanStrategy(f -> false)
|
||||
.build();
|
||||
XLog.init(config, filePrinter);
|
||||
xLogger = XLog.tag("AndroidIDE").build();
|
||||
|
||||
instance = new Logger(tag);
|
||||
return instance;
|
||||
}
|
||||
|
||||
public Logger d(Object... messages) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("[");
|
||||
sb.append(TAG);
|
||||
sb.append("]");
|
||||
sb.append("\n");
|
||||
if(messages == null) {
|
||||
xLogger.d("null");
|
||||
return this;
|
||||
}
|
||||
for(Object msg : messages) {
|
||||
sb.append(msg);
|
||||
sb.append("\n");
|
||||
}
|
||||
xLogger.d(sb.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
public Logger e(Object... messages) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("[");
|
||||
sb.append(TAG);
|
||||
sb.append("]");
|
||||
sb.append("\n");
|
||||
if(messages == null) {
|
||||
xLogger.e("null");
|
||||
return this;
|
||||
}
|
||||
for(Object msg : messages) {
|
||||
sb.append(msg);
|
||||
sb.append("\n");
|
||||
}
|
||||
xLogger.e(sb.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
public Logger v(Object... messages) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("[");
|
||||
sb.append(TAG);
|
||||
sb.append("]");
|
||||
sb.append("\n");
|
||||
if(messages == null) {
|
||||
xLogger.v("null");
|
||||
return this;
|
||||
}
|
||||
for(Object msg : messages) {
|
||||
sb.append(msg);
|
||||
sb.append("\n");
|
||||
}
|
||||
xLogger.v(sb.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
public Logger i(Object... messages) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("[");
|
||||
sb.append(TAG);
|
||||
sb.append("]");
|
||||
sb.append("\n");
|
||||
if(messages == null) {
|
||||
xLogger.i("null");
|
||||
return this;
|
||||
}
|
||||
for(Object msg : messages) {
|
||||
sb.append(msg);
|
||||
sb.append("\n");
|
||||
}
|
||||
xLogger.i(sb.toString());
|
||||
return this;
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -19,6 +19,7 @@ import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.itsaky.androidide.utils.Either;
|
||||
import io.github.rosemoe.editor.struct.CompletionItem;
|
||||
import io.github.rosemoe.editor.text.TextAnalyzeResult;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -37,7 +38,7 @@ public interface AutoCompleteProvider {
|
||||
* @param line The line of cursor
|
||||
* @return Analyzed items
|
||||
*/
|
||||
List<Either<SuggestItem, CompletionItem>> getAutoCompleteItems(String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line);
|
||||
List<Either<SuggestItem, CompletionItem>> getAutoCompleteItems(CodeEditor editor, String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line);
|
||||
|
||||
}
|
||||
|
||||
+2
-1
@@ -24,6 +24,7 @@ import io.github.rosemoe.editor.interfaces.NewlineHandler;
|
||||
import io.github.rosemoe.editor.struct.CompletionItem;
|
||||
import io.github.rosemoe.editor.text.TextAnalyzeResult;
|
||||
import io.github.rosemoe.editor.text.TextAnalyzer.AnalyzeThread.Delegate;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
import io.github.rosemoe.editor.widget.SymbolPairMatch;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -79,7 +80,7 @@ public class EmptyLanguage implements EditorLanguage {
|
||||
public static class EmptyAutoCompleteProvider implements AutoCompleteProvider {
|
||||
|
||||
@Override
|
||||
public List<Either<SuggestItem, CompletionItem>> getAutoCompleteItems(String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
|
||||
public List<Either<SuggestItem, CompletionItem>> getAutoCompleteItems(CodeEditor editor, String prefix, boolean isInCodeBlock, TextAnalyzeResult colors, int line) {
|
||||
return new ArrayList<Either<SuggestItem, CompletionItem>>();
|
||||
}
|
||||
|
||||
+163
-5
@@ -56,7 +56,7 @@ import android.widget.Toast;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.Px;
|
||||
import io.github.rosemoe.editor.R;
|
||||
import com.itsaky.androidide.R;
|
||||
import io.github.rosemoe.editor.interfaces.EditorEventListener;
|
||||
import io.github.rosemoe.editor.interfaces.EditorLanguage;
|
||||
import io.github.rosemoe.editor.interfaces.NewlineHandler;
|
||||
@@ -82,6 +82,13 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import com.blankj.utilcode.util.DeviceUtils;
|
||||
import io.github.rosemoe.editor.struct.HexColor;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
|
||||
import javax.tools.Diagnostic;
|
||||
import java.util.stream.Collectors;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
/**
|
||||
* CodeEditor is a editor that can highlight text regions by doing basic syntax analyzing
|
||||
@@ -219,6 +226,7 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
private EditorLanguage mLanguage;
|
||||
private long mLastMakeVisible = 0;
|
||||
private EditorAutoCompleteWindow mCompletionWindow;
|
||||
private DiagnosticWindow mDiagnosticWindow;
|
||||
private EditorTouchEventHandler mEventHandler;
|
||||
private Paint.Align mLineNumberAlign;
|
||||
private GestureDetector mBasicDetector;
|
||||
@@ -243,7 +251,8 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
KeyMetaStates mKeyMetaStates = new KeyMetaStates(this);
|
||||
|
||||
private HashMap<HexColor, Integer> mLineColors;
|
||||
|
||||
private HashMap<Long, List<CompilerDiagnostic>> mDiagnostics;
|
||||
|
||||
public CodeEditor(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
@@ -431,6 +440,7 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
setFocusableInTouchMode(true);
|
||||
mConnection = new EditorInputConnection(this);
|
||||
mCompletionWindow = new EditorAutoCompleteWindow(this);
|
||||
mDiagnosticWindow = new DiagnosticWindow(this);
|
||||
mVerticalEdgeGlow = new MaterialEdgeEffect();
|
||||
mHorizontalGlow = new MaterialEdgeEffect();
|
||||
mOverrideSymbolPairs = new SymbolPairMatch();
|
||||
@@ -462,6 +472,57 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
mErrorColor = Color.parseColor("#FF5252");
|
||||
mWarningColor = Color.parseColor("#FFD740");
|
||||
mRect2.set(-1, -1, -1, -1);
|
||||
|
||||
mDiagnostics = new HashMap<>();
|
||||
}
|
||||
|
||||
public void showOrHideDiagnosticWindow() {
|
||||
int line = getCursor().getLeftLine();
|
||||
int index = getCursor().getLeft();
|
||||
boolean hide = false;
|
||||
boolean has = mDiagnostics.containsKey(Long.valueOf(line));
|
||||
if(has) {
|
||||
List<CompilerDiagnostic> diags = mDiagnostics.get(Long.valueOf(line));
|
||||
if(diags != null) {
|
||||
diags = diags.stream().filter(d -> d.start() <= index && d.end() >= index).collect(Collectors.toList());
|
||||
if(diags != null && diags.size() > 0){
|
||||
CompilerDiagnostic d = diags.get(0);
|
||||
mDiagnosticWindow.setDiagnostic(d);
|
||||
if(!mDiagnosticWindow.isShowing())
|
||||
mDiagnosticWindow.show();
|
||||
else updateDiagnosticWindowPosition();
|
||||
} else hide = true;
|
||||
} else hide = true;
|
||||
} else hide = true;
|
||||
if(hide && mDiagnosticWindow.isShowing()) {
|
||||
mDiagnosticWindow.hide();
|
||||
}
|
||||
}
|
||||
|
||||
public void setDiagnostics(List<CompilerDiagnostic> diags) {
|
||||
this.mDiagnostics = new HashMap<>();
|
||||
mapDiagnostics(diags);
|
||||
}
|
||||
|
||||
private void mapDiagnostics(List<CompilerDiagnostic> diags) {
|
||||
if(diags == null)
|
||||
return;
|
||||
for(int i=0;i<diags.size();i++) {
|
||||
CompilerDiagnostic d = diags.get(i);
|
||||
if(d == null)
|
||||
continue;
|
||||
if(mDiagnostics.containsKey(d.line())) {
|
||||
List<CompilerDiagnostic> ds = mDiagnostics.get(d.line());
|
||||
if(ds == null)
|
||||
ds = new ArrayList<>();
|
||||
if(!ds.contains(d))
|
||||
ds.add(d);
|
||||
} else {
|
||||
List<CompilerDiagnostic> ds = new ArrayList<>();
|
||||
ds.add(d);
|
||||
mDiagnostics.put(Long.valueOf(d.line()), ds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CodeEditor setLineColors(HashMap<HexColor, Integer> map) {
|
||||
@@ -1141,7 +1202,7 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
}
|
||||
}
|
||||
|
||||
// Draw hex color values
|
||||
// Draw hex colors
|
||||
if(isLineColorsEnabled() && mLineColors != null && mLineColors.size() > 0) {
|
||||
for(HexColor c : mLineColors.keySet()) {
|
||||
if(c != null && c.isSameLine(line) && mLineColors.get(c) != null) {
|
||||
@@ -1159,7 +1220,35 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Draw errors and warnings
|
||||
if(mDiagnostics != null && mDiagnostics.containsKey(Long.valueOf(line))) {
|
||||
List<CompilerDiagnostic> diags = mDiagnostics.get(Long.valueOf(line));
|
||||
if(diags != null && diags.size() > 0) {
|
||||
for(int i=0;i<diags.size();i++) {
|
||||
CompilerDiagnostic d = diags.get(i);
|
||||
if(d == null) continue;
|
||||
CharPosition start = null, end = null;
|
||||
try {
|
||||
start = getText().getIndexer().getCharPosition((int) d.start());
|
||||
end = getText().getIndexer().getCharPosition((int) d.end());
|
||||
} catch (Throwable th) {
|
||||
continue;
|
||||
}
|
||||
final int paintStart = Math.max(firstVisibleChar, start.column);
|
||||
final int paintEnd = Math.min(lastVisibleChar, end.column);
|
||||
final float width = measureText(mBuffer, paintStart, paintEnd - paintStart);
|
||||
final RectF r = new RectF();
|
||||
r.bottom = getRowBottom(line) - getOffsetY() - mDpUnit * 1;
|
||||
r.top = r.bottom - getRowHeight() * 0.2f;
|
||||
r.left = paintingOffset + measureText(mBuffer, firstVisibleChar, paintStart - firstVisibleChar);
|
||||
r.right = r.left + width;
|
||||
mPaint.setColor(d.color());
|
||||
drawZigzagUnderline(r, canvas, mPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw cursors
|
||||
if (mCursor.isSelected()) {
|
||||
if (mTextActionPresenter.shouldShowCursor()) {
|
||||
@@ -1179,6 +1268,34 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void drawZigzagUnderline(RectF boundaries, Canvas canvas, Paint paint) {
|
||||
Path path = new Path();
|
||||
path.moveTo(boundaries.right, boundaries.bottom);
|
||||
|
||||
float left = boundaries.left;
|
||||
float right = boundaries.right;
|
||||
float y = boundaries.bottom;
|
||||
float h = boundaries.height();
|
||||
float seed = 2 * h;
|
||||
float width = right - left;
|
||||
int count = (int) (width / seed);
|
||||
float diff = width - seed * count;
|
||||
float sideDiff = diff / 2;
|
||||
float halfSeed = seed / 2;
|
||||
float innerHeight = y - h;
|
||||
for (int i=count;i>=1;i--) {
|
||||
float startSeed = i * seed + sideDiff + left;
|
||||
float endSeed = startSeed - seed;
|
||||
if (i == 1) {
|
||||
endSeed -= sideDiff;
|
||||
}
|
||||
path.lineTo(startSeed - halfSeed, innerHeight);
|
||||
path.lineTo(endSeed, y);
|
||||
}
|
||||
|
||||
canvas.drawPath(path, paint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw code block lines on screen
|
||||
@@ -2141,6 +2258,36 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
mTextActionPresenter.onExit();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDiagnosticWindowPosition() {
|
||||
float panelX = updateCursorAnchor() + mDpUnit * 20;
|
||||
float[] rightLayoutOffset = mLayout.getCharLayoutOffset(mCursor.getRightLine(), mCursor.getRightColumn());
|
||||
float panelY = rightLayoutOffset[0] - getOffsetY() + getRowHeight() / 2f;
|
||||
float restY = getHeight() - panelY;
|
||||
if (restY > mDpUnit * 120) {
|
||||
restY = mDpUnit * 120;
|
||||
} else if (restY < mDpUnit * 100) {
|
||||
float offset = 0;
|
||||
while (restY < mDpUnit * 100) {
|
||||
restY += getRowHeight();
|
||||
panelY -= getRowHeight();
|
||||
offset += getRowHeight();
|
||||
}
|
||||
getScroller().startScroll(getOffsetX(), getOffsetY(), 0, (int) offset, 0);
|
||||
}
|
||||
mDiagnosticWindow.setExtendedX(panelX);
|
||||
mDiagnosticWindow.setExtendedY(panelY);
|
||||
if (getWidth() < 500 * mDpUnit) {
|
||||
mDiagnosticWindow.setWidth(getWidth() * 7 / 8);
|
||||
mDiagnosticWindow.setExtendedX(getWidth() / 8f / 2f);
|
||||
} else {
|
||||
int w = getWidth() / 3;
|
||||
mDiagnosticWindow.setWidth(w * 2);
|
||||
}
|
||||
|
||||
mDiagnosticWindow.setHeight((int) restY);
|
||||
mDiagnosticWindow.updatePosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply new position of auto completion window
|
||||
@@ -3227,6 +3374,8 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
if (mTextActionPresenter != null) {
|
||||
mTextActionPresenter.onExit();
|
||||
}
|
||||
|
||||
showOrHideDiagnosticWindow();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3302,6 +3451,7 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
mTextActionPresenter.onBeginTextSelect();
|
||||
}
|
||||
mEventHandler.notifyTouchedSelectionHandlerLater();
|
||||
showOrHideDiagnosticWindow();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3767,7 +3917,9 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
| EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE
|
||||
| EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS
|
||||
| EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
|
||||
|
||||
|
||||
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI|EditorInfo.IME_FLAG_NO_FULLSCREEN;
|
||||
|
||||
outAttrs.initialSelStart = getCursor() != null ? getCursor().getLeft() : 0;
|
||||
outAttrs.initialSelEnd = getCursor() != null ? getCursor().getRight() : 0;
|
||||
outAttrs.initialCapsMode = mConnection.getCursorCapsMode(0);
|
||||
@@ -4075,6 +4227,9 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
|
||||
@Override
|
||||
public void afterInsert(Content content, int startLine, int startColumn, int endLine, int endColumn, CharSequence insertedContent) {
|
||||
if(mDiagnosticWindow != null)
|
||||
mDiagnosticWindow.hide();
|
||||
|
||||
// Update spans
|
||||
if (isSpanMapPrepared(true, endLine - startLine)) {
|
||||
if (startLine == endLine) {
|
||||
@@ -4135,6 +4290,9 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
|
||||
@Override
|
||||
public void afterDelete(Content content, int startLine, int startColumn, int endLine, int endColumn, CharSequence deletedContent) {
|
||||
if(mDiagnosticWindow != null)
|
||||
mDiagnosticWindow.hide();
|
||||
|
||||
if (isSpanMapPrepared(false, endLine - startLine)) {
|
||||
if (startLine == endLine) {
|
||||
SpanMapUpdater.shiftSpansOnSingleLineDelete(mSpanner.getResult().getSpanMap(), startLine, startColumn, endColumn);
|
||||
+1
-1
@@ -24,7 +24,7 @@ import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.itsaky.androidide.utils.Either;
|
||||
import io.github.rosemoe.editor.R;
|
||||
import com.itsaky.androidide.R;
|
||||
import io.github.rosemoe.editor.struct.CompletionItem;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
@@ -0,0 +1,79 @@
|
||||
package io.github.rosemoe.editor.widget;
|
||||
|
||||
import android.graphics.PorterDuff;
|
||||
import android.view.LayoutInflater;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.databinding.LayoutDiagnosticInfoBinding;
|
||||
import com.itsaky.androidide.services.compiler.model.CompilerDiagnostic;
|
||||
import javax.tools.Diagnostic;
|
||||
|
||||
public class DiagnosticWindow extends EditorBasePopupWindow {
|
||||
|
||||
private CodeEditor editor;
|
||||
private CompilerDiagnostic diagnostic;
|
||||
private LayoutDiagnosticInfoBinding binding;
|
||||
|
||||
public DiagnosticWindow(CodeEditor editor) {
|
||||
super(editor);
|
||||
this.editor = editor;
|
||||
this.binding = LayoutDiagnosticInfoBinding.inflate(LayoutInflater.from(editor.getContext()));
|
||||
|
||||
setContentView(this.binding.getRoot());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
super.show();
|
||||
setupData();
|
||||
}
|
||||
|
||||
public void setMaxHeight(int height) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePosition() {
|
||||
super.updatePosition();
|
||||
setupData();
|
||||
}
|
||||
|
||||
private void setupData() {
|
||||
if(diagnostic != null) {
|
||||
binding.icon.setImageResource(getDiagnosticIconId());
|
||||
binding.icon.setColorFilter(ContextCompat.getColor(binding.icon.getContext(), R.color.primaryIconColor), PorterDuff.Mode.SRC_ATOP);
|
||||
binding.errorType.setText(getDiagnosticTypeString());
|
||||
binding.errorPosition.setText(
|
||||
editor.getContext().getString(
|
||||
com.itsaky.androidide.R.string.diagnostic_position,
|
||||
""+ diagnostic.line(),
|
||||
""+ diagnostic.column()
|
||||
)
|
||||
);
|
||||
|
||||
binding.msg.setText(diagnostic.message());
|
||||
binding.msg.setTextIsSelectable(true);
|
||||
}
|
||||
}
|
||||
|
||||
public DiagnosticWindow setDiagnostic(CompilerDiagnostic diag) {
|
||||
this.diagnostic = diag;
|
||||
return this;
|
||||
}
|
||||
|
||||
private int getDiagnosticIconId() {
|
||||
if(diagnostic.kind() == Diagnostic.Kind.ERROR)
|
||||
return R.drawable.ic_compilation_error;
|
||||
return R.drawable.ic_info;
|
||||
}
|
||||
|
||||
private String getDiagnosticTypeString() {
|
||||
if(diagnostic.kind() == Diagnostic.Kind.ERROR) {
|
||||
return editor.getContext().getString(com.itsaky.androidide.R.string.diagnostic_error);
|
||||
} else if(diagnostic.kind() == Diagnostic.Kind.WARNING || diagnostic.kind() == Diagnostic.Kind.MANDATORY_WARNING) {
|
||||
return editor.getContext().getString(com.itsaky.androidide.R.string.diagnostic_warning);
|
||||
}
|
||||
return editor.getContext().getString(com.itsaky.androidide.R.string.diagnostic_note);
|
||||
}
|
||||
|
||||
}
|
||||
+3
-5
@@ -19,14 +19,12 @@ import android.content.Context;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
import com.blankj.utilcode.util.DeviceUtils;
|
||||
import com.itsaky.androidide.databinding.LayoutCompletionWindowBinding;
|
||||
import com.itsaky.androidide.language.java.parser.internal.SuggestItem;
|
||||
import com.itsaky.androidide.models.CompletionEdit;
|
||||
import com.itsaky.androidide.models.CompletionListItem;
|
||||
import com.itsaky.androidide.utils.Either;
|
||||
import io.github.rosemoe.editor.databinding.LayoutCompletionWindowBinding;
|
||||
import io.github.rosemoe.editor.interfaces.AutoCompleteProvider;
|
||||
import io.github.rosemoe.editor.struct.CompletionItem;
|
||||
import io.github.rosemoe.editor.text.CharPosition;
|
||||
@@ -276,7 +274,7 @@ public class EditorAutoCompleteWindow extends EditorBasePopupWindow {
|
||||
mAdapter.attachAttributes(this, results);
|
||||
mBinding.list.setAdapter(mAdapter);
|
||||
mCurrent = 0;
|
||||
float newHeight = mEditor.getDpUnit() * 40 * results.size();
|
||||
float newHeight = mEditor.getDpUnit() * mAdapter.getItemHeight() * results.size();
|
||||
if (isShowing()) {
|
||||
update(getWidth(), (int) Math.min(newHeight, mMaxHeight));
|
||||
}
|
||||
@@ -307,7 +305,7 @@ public class EditorAutoCompleteWindow extends EditorBasePopupWindow {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
displayResults(mLocalProvider.getAutoCompleteItems(mPrefix, mInner, mColors, mLine), mTime);
|
||||
displayResults(mLocalProvider.getAutoCompleteItems(mEditor, mPrefix, mInner, mColors, mLine), mTime);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
displayResults(new ArrayList<>(), mTime);
|
||||
+1
-1
@@ -17,7 +17,7 @@ package io.github.rosemoe.editor.widget;
|
||||
|
||||
import android.view.Gravity;
|
||||
import android.widget.PopupWindow;
|
||||
import io.github.rosemoe.editor.R;
|
||||
import com.itsaky.androidide.R;
|
||||
|
||||
/**
|
||||
* Editor base panel class
|
||||
+1
-1
@@ -24,7 +24,7 @@ import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
import io.github.rosemoe.editor.R;
|
||||
import com.itsaky.androidide.R;
|
||||
|
||||
/**
|
||||
* This will show when selecting text
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user