From 3e9fd6b5fb923ed94f24c4ad19b461917a47b60e Mon Sep 17 00:00:00 2001 From: Akash Yadav Date: Wed, 30 Mar 2022 19:27:06 +0530 Subject: [PATCH] Wrote tests for JavaSelectionProvider; Formatted code --- .idea/compiler.xml | 2 +- .idea/google-java-format.xml | 1 + .idea/misc.xml | 2 +- CHANGELOG.md | 3 +- .../com/itsaky/androidide/AboutActivity.java | 3 - .../itsaky/androidide/DesignerActivity.java | 774 +++++++++--------- .../com/itsaky/androidide/EditorActivity.java | 11 +- .../itsaky/androidide/TerminalActivity.java | 6 +- .../adapters/AttrValueFormatTabAdapter.java | 3 - .../adapters/EditorBottomSheetTabAdapter.java | 2 - .../adapters/TextActionItemAdapter.java | 3 - .../com/itsaky/androidide/app/StudioApp.java | 199 +++-- .../fragments/CrashReportFragment.java | 2 - .../fragments/FileTreeFragment.java | 3 - .../fragments/NonEditableEditorFragment.java | 3 - .../fragments/SimpleOutputFragment.java | 3 - .../attr/BaseValueEditorFragment.java | 3 - .../preferences/BuildPreferences.java | 3 - .../preferences/EditorPreferences.java | 2 - .../fragments/preferences/IDEPreferences.java | 2 - .../fragments/sheets/AttrEditorSheet.java | 554 +++++++------ .../sheets/AttrValueEditorSheet.java | 6 +- .../fragments/sheets/AttributeListSheet.java | 3 - .../sheets/BaseBottomSheetFragment.java | 2 - .../fragments/sheets/OptionsListFragment.java | 3 - .../fragments/sheets/ProgressSheet.java | 2 - .../fragments/sheets/TextSheetFragment.java | 3 - .../handlers/BuildServiceHandler.java | 2 - .../language/CommonCompletionProvider.java | 16 +- .../androidide/language/IAnalyzeManager.java | 2 - .../androidide/language/IDELanguage.java | 5 +- .../language/groovy/GroovyLanguage.java | 11 +- .../language/java/JavaAnalyzer.java | 20 +- .../language/java/JavaLanguage.java | 19 +- .../androidide/language/xml/XMLLanguage.java | 15 +- .../androidide/lsp/IDELanguageClientImpl.java | 5 +- .../androidide/models/XMLAttribute.java | 256 +++--- .../views/editor/BaseEditorWindow.java | 3 - .../views/editor/CodeEditorView.java | 15 +- .../views/editor/DiagnosticWindow.java | 1 - .../views/editor/EditorTextActionMode.java | 10 +- .../views/editor/EditorTextActionWindow.java | 16 +- .../androidide/views/editor/IDEEditor.java | 17 +- .../com/itsaky/androidide/utils/Logger.java | 27 +- .../views/virtualkeys/VirtualKeysInfo.java | 18 +- .../com/itsaky/lsp/api/IDocumentHandler.java | 2 - .../com/itsaky/lsp/util/DiagnosticUtil.java | 1 - .../itsaky/lsp/util/NoDocumentHandler.java | 2 - lsp/java/build.gradle | 14 + .../itsaky/lsp/java/JavaLanguageServer.java | 8 +- .../lsp/java/compiler/SynchronizedTask.java | 3 - .../java/providers/CodeActionProvider.java | 7 +- .../java/providers/CompletionProvider.java | 2 +- .../java/providers/DefinitionProvider.java | 6 +- .../java/providers/DiagnosticsProvider.java | 3 - .../providers/JavaDiagnosticProvider.java | 2 +- .../java/providers/JavaSelectionProvider.java | 4 +- .../lsp/java/providers/ReferenceProvider.java | 6 +- .../lsp/java/providers/SignatureProvider.java | 2 +- .../itsaky/lsp/java/rewrite/AddException.java | 6 +- .../itsaky/lsp/java/rewrite/AddImport.java | 2 - .../rewrite/AddSuppressWarningAnnotation.java | 3 - .../lsp/java/rewrite/ConvertFieldToBlock.java | 3 - .../rewrite/ConvertVariableToStatement.java | 2 - .../lsp/java/rewrite/CreateMissingMethod.java | 3 - .../rewrite/GenerateRecordConstructor.java | 3 - .../rewrite/ImplementAbstractMethods.java | 3 - .../itsaky/lsp/java/rewrite/NoRewrite.java | 1 - .../java/rewrite/OverrideInheritedMethod.java | 2 - .../itsaky/lsp/java/rewrite/RemoveClass.java | 2 - .../lsp/java/rewrite/RemoveException.java | 4 +- .../itsaky/lsp/java/rewrite/RemoveMethod.java | 4 +- .../com/itsaky/lsp/java/rewrite/Rewrite.java | 2 - .../lsp/java/rewrite/RewriteNotSupported.java | 1 - .../itsaky/lsp/java/utils/AnalyzeTimer.java | 2 - .../com/itsaky/lsp/java/utils/EditHelper.java | 2 - .../java/visitors/FindTypeDeclarationAt.java | 4 +- .../itsaky/lsp/java/CursorDependentTest.kt | 42 + .../java/com/itsaky/lsp/java/FileProvider.kt | 85 ++ .../itsaky/lsp/java/LanguageServerProvider.kt | 54 ++ .../java/com/itsaky/lsp/java/LoggingTest.kt | 49 ++ .../providers/JavaSelectionProviderTest.kt | 77 ++ .../lsp/java/util/ReflectiveLogListener.kt | 30 + .../SimpleSelectionExpansionTest.java | 7 + .../src/test/resources/SourceFileTest.java | 7 + .../resources/package/SourceFileTest.java | 7 + .../lsp/xml/providers/CompletionProvider.java | 12 +- .../com/itsaky/inflater/ILayoutInflater.java | 281 ++++--- .../itsaky/inflater/XMLLayoutInflater.java | 2 +- 89 files changed, 1535 insertions(+), 1290 deletions(-) create mode 100644 lsp/java/src/test/java/com/itsaky/lsp/java/CursorDependentTest.kt create mode 100644 lsp/java/src/test/java/com/itsaky/lsp/java/FileProvider.kt create mode 100644 lsp/java/src/test/java/com/itsaky/lsp/java/LanguageServerProvider.kt create mode 100644 lsp/java/src/test/java/com/itsaky/lsp/java/LoggingTest.kt create mode 100644 lsp/java/src/test/java/com/itsaky/lsp/java/providers/JavaSelectionProviderTest.kt create mode 100644 lsp/java/src/test/java/com/itsaky/lsp/java/util/ReflectiveLogListener.kt create mode 100644 lsp/java/src/test/resources/SimpleSelectionExpansionTest.java create mode 100644 lsp/java/src/test/resources/SourceFileTest.java create mode 100644 lsp/java/src/test/resources/package/SourceFileTest.java diff --git a/.idea/compiler.xml b/.idea/compiler.xml index b516012a7..96f1abef4 100755 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,7 +1,7 @@ - + diff --git a/.idea/google-java-format.xml b/.idea/google-java-format.xml index 8b57f4527..05946a82a 100644 --- a/.idea/google-java-format.xml +++ b/.idea/google-java-format.xml @@ -2,5 +2,6 @@ \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index aee29d5b4..d01bdfaf0 100755 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -130,7 +130,7 @@ - + diff --git a/CHANGELOG.md b/CHANGELOG.md index 854bcf30e..c06cf1cc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,7 @@ This is the changelog of all the releases of AndroidIDE. - Code formatter for Java source code. - Support for - [`GestureOverlayView`](https://developer.android.com/reference/android/gesture/GestureOverlayView) - , + [`GestureOverlayView`](https://developer.android.com/reference/android/gesture/GestureOverlayView), [`ToggleButton`](https://developer.android.com/reference/android/widget/ToggleButton), [`Switch`](https://developer.android.com/reference/android/widget/Switch), [`GridLayout`](https://developer.android.com/reference/android/widget/GridLayout) in layout diff --git a/app/src/main/java/com/itsaky/androidide/AboutActivity.java b/app/src/main/java/com/itsaky/androidide/AboutActivity.java index 35cd4114b..58412f46e 100755 --- a/app/src/main/java/com/itsaky/androidide/AboutActivity.java +++ b/app/src/main/java/com/itsaky/androidide/AboutActivity.java @@ -21,15 +21,12 @@ import android.os.Bundle; import android.text.Html; import android.text.method.LinkMovementMethod; import android.view.View; - import androidx.appcompat.widget.TooltipCompat; import androidx.core.util.Pair; - import com.itsaky.androidide.app.StudioActivity; import com.itsaky.androidide.databinding.ActivityAboutBinding; import com.itsaky.androidide.databinding.LayoutAboutItemsBinding; import com.itsaky.androidide.models.License; - import java.util.ArrayList; import java.util.List; diff --git a/app/src/main/java/com/itsaky/androidide/DesignerActivity.java b/app/src/main/java/com/itsaky/androidide/DesignerActivity.java index ae48b2a7a..e52d38bb8 100755 --- a/app/src/main/java/com/itsaky/androidide/DesignerActivity.java +++ b/app/src/main/java/com/itsaky/androidide/DesignerActivity.java @@ -41,7 +41,6 @@ import android.widget.RelativeLayout; import android.widget.SeekBar; import android.widget.Spinner; import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.core.content.ContextCompat; @@ -51,7 +50,6 @@ import androidx.transition.Fade; import androidx.transition.Slide; import androidx.transition.TransitionManager; import androidx.transition.TransitionSet; - import com.blankj.utilcode.util.DeviceUtils; import com.blankj.utilcode.util.ThreadUtils; import com.blankj.utilcode.util.ThrowableUtils; @@ -76,9 +74,6 @@ import com.itsaky.inflater.IView; import com.itsaky.inflater.IViewGroup; import com.itsaky.inflater.LayoutInflaterConfiguration; import com.itsaky.inflater.impl.BaseView; - -import org.jetbrains.annotations.Contract; - import java.io.File; import java.util.ArrayList; import java.util.List; @@ -86,418 +81,445 @@ import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; +import org.jetbrains.annotations.Contract; public class DesignerActivity extends StudioActivity - implements WidgetItemAdapter.OnDragStartListener, - AttrEditorSheet.OnViewDeletionFailedListener, - ILayoutInflater.ContextProvider { + implements WidgetItemAdapter.OnDragStartListener, + AttrEditorSheet.OnViewDeletionFailedListener, + ILayoutInflater.ContextProvider { - public static final String KEY_LAYOUT_PATH = "designer_layoutPath"; - public static final String KEY_RES_DIRS = "designer_resDirs"; - public static final String KEY_GENERATED_CODE = "designer_xmlCode"; - public static final String DRAGGING_WIDGET_TAG = "DRAGGING_WIDGET"; - public static final String DRAGGING_WIDGET_MIME = "application/ide_widget"; - private static final Logger LOG = Logger.instance("DesignerActivity"); - public final String EDITOR_SHEET_DIALOG_TAG = "attribute_editor_dialog"; - private final boolean isTablet = DeviceUtils.isTablet(); - private final List widgetGroups = new ArrayList<>(); - private ActivityDesignerBinding mBinding; - private UIWidgetGroup checkedWidgetCategory; - private ILayoutInflater layoutInflater; - private IViewGroup inflatedRoot; - private File layout; - private boolean inflationFailed = false; - private AttrEditorSheet mEditorSheet; - // This will make sure to apply listeners, background and data to view that are inflated from - // XML. - private final IInflateListener mInflateListener = - new IInflateListener() { + public static final String KEY_LAYOUT_PATH = "designer_layoutPath"; + public static final String KEY_RES_DIRS = "designer_resDirs"; + public static final String KEY_GENERATED_CODE = "designer_xmlCode"; + public static final String DRAGGING_WIDGET_TAG = "DRAGGING_WIDGET"; + public static final String DRAGGING_WIDGET_MIME = "application/ide_widget"; + private static final Logger LOG = Logger.instance("DesignerActivity"); + public final String EDITOR_SHEET_DIALOG_TAG = "attribute_editor_dialog"; + private final boolean isTablet = DeviceUtils.isTablet(); + private final List widgetGroups = new ArrayList<>(); + private ActivityDesignerBinding mBinding; + private UIWidgetGroup checkedWidgetCategory; + private ILayoutInflater layoutInflater; + private IViewGroup inflatedRoot; + private File layout; + private boolean inflationFailed = false; + private AttrEditorSheet mEditorSheet; + // This will make sure to apply listeners, background and data to view that are inflated from + // XML. + private final IInflateListener mInflateListener = + new IInflateListener() { - @Override - public void onBeginInflate() {} + @Override + public void onBeginInflate() {} - @Override - public void onInflateView(IView view, IViewGroup parent) { - if (view instanceof IViewGroup) { - View v = view.asView(); - v.setBackgroundResource(R.drawable.bg_design_container_background); - v.setOnDragListener(getOnDragListener((IViewGroup) view)); - } + @Override + public void onInflateView(IView view, IViewGroup parent) { + if (view instanceof IViewGroup) { + View v = view.asView(); + v.setBackgroundResource(R.drawable.bg_design_container_background); + v.setOnDragListener(getOnDragListener((IViewGroup) view)); + } - setupInflatedView(view); - setDragDataToInflatedView(view); - } - - @Override - public void onApplyAttribute(IAttribute attr, IView view) {} - - @Override - public void onFinishInflate(IView rootView) { - if (rootView instanceof IViewGroup) { - inflatedRoot = (IViewGroup) rootView; - } - } - }; - - @Override - protected View bindLayout() { - this.mBinding = ActivityDesignerBinding.inflate(getLayoutInflater()); - return mBinding.getRoot(); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setSupportActionBar(mBinding.toolbar); - Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true); - - final var toggle = - new ActionBarDrawerToggle( - this, mBinding.getRoot(), mBinding.toolbar, R.string.app_name, R.string.app_name) { - - @Override - public void onDrawerSlide(View drawerView, float slideOffset) { - super.onDrawerSlide(drawerView, slideOffset); - if (isTablet) { - mBinding.mainContainer.setTranslationX(drawerView.getWidth() * slideOffset); - } - } - }; - mBinding.getRoot().addDrawerListener(toggle); - toggle.syncState(); - - final var extras = getIntent().getExtras(); - final var path = extras.getString(KEY_LAYOUT_PATH, null); - final var name = path.substring(path.lastIndexOf(File.separator) + 1); - final var resDirs = extras.getStringArrayList(KEY_RES_DIRS); - getSupportActionBar().setTitle(name); - - try { - this.layout = new File(path); - LOG.debug("Inflating layout file:", this.layout); - LOG.debug("Resource directories:", TextUtils.join(",\n", resDirs)); - - //noinspection deprecation - final var pd = ProgressDialog.show(this, null, getString(R.string.please_wait), true, false); - getApp() - .createInflaterConfig(this, resDirs.stream().map(File::new).collect(Collectors.toSet())) - .whenComplete( - (config, throwable) -> { - if (config == null || throwable != null) { - LOG.error("Unable to create inflater configuration", throwable); - return; + setupInflatedView(view); + setDragDataToInflatedView(view); } - ThreadUtils.runOnUiThread( - () -> { - pd.dismiss(); - inflatePath(path, config); - }); - }); - } catch (Throwable th) { - mBinding.layoutContainer.removeAllViews(); - mBinding.layoutContainer.addView(createErrorText(th)); - LOG.error(getString(R.string.err_cannot_inflate_layout), th); - inflationFailed = true; + @Override + public void onApplyAttribute(IAttribute attr, IView view) {} + + @Override + public void onFinishInflate(IView rootView) { + if (rootView instanceof IViewGroup) { + inflatedRoot = (IViewGroup) rootView; + } + } + }; + + @Override + protected View bindLayout() { + this.mBinding = ActivityDesignerBinding.inflate(getLayoutInflater()); + return mBinding.getRoot(); } - setupWidgets(); + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); - // Always open the drawer if this device is a tablet. - if (isTablet) { - mBinding.getRoot().setScrimColor(Color.TRANSPARENT); - mBinding.getRoot().openDrawer(GravityCompat.START); - mBinding.getRoot().setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN); - } - } + setSupportActionBar(mBinding.toolbar); + Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true); - private void inflatePath(String path, LayoutInflaterConfiguration config) { - this.layoutInflater = ILayoutInflater.newInstance(config); - this.layoutInflater.resetContextProvider(this); - this.layoutInflater.registerInflateListener(this.mInflateListener); - final IView view = this.layoutInflater.inflatePath(path, mBinding.layoutContainer); + final var toggle = + new ActionBarDrawerToggle( + this, + mBinding.getRoot(), + mBinding.toolbar, + R.string.app_name, + R.string.app_name) { - if (this.inflatedRoot != null) { - this.inflatedRoot.asView().setOnDragListener(getOnDragListener(this.inflatedRoot)); + @Override + public void onDrawerSlide(View drawerView, float slideOffset) { + super.onDrawerSlide(drawerView, slideOffset); + if (isTablet) { + mBinding.mainContainer.setTranslationX( + drawerView.getWidth() * slideOffset); + } + } + }; + mBinding.getRoot().addDrawerListener(toggle); + toggle.syncState(); + + final var extras = getIntent().getExtras(); + final var path = extras.getString(KEY_LAYOUT_PATH, null); + final var name = path.substring(path.lastIndexOf(File.separator) + 1); + final var resDirs = extras.getStringArrayList(KEY_RES_DIRS); + getSupportActionBar().setTitle(name); + + try { + this.layout = new File(path); + LOG.debug("Inflating layout file:", this.layout); + LOG.debug("Resource directories:", TextUtils.join(",\n", resDirs)); + + //noinspection deprecation + final var pd = + ProgressDialog.show(this, null, getString(R.string.please_wait), true, false); + getApp().createInflaterConfig( + this, resDirs.stream().map(File::new).collect(Collectors.toSet())) + .whenComplete( + (config, throwable) -> { + if (config == null || throwable != null) { + LOG.error("Unable to create inflater configuration", throwable); + return; + } + + ThreadUtils.runOnUiThread( + () -> { + pd.dismiss(); + inflatePath(path, config); + }); + }); + } catch (Throwable th) { + mBinding.layoutContainer.removeAllViews(); + mBinding.layoutContainer.addView(createErrorText(th)); + LOG.error(getString(R.string.err_cannot_inflate_layout), th); + inflationFailed = true; + } + + setupWidgets(); + + // Always open the drawer if this device is a tablet. + if (isTablet) { + mBinding.getRoot().setScrimColor(Color.TRANSPARENT); + mBinding.getRoot().openDrawer(GravityCompat.START); + mBinding.getRoot().setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN); + } } - mBinding.layoutContainer.addView(view.asView()); - mBinding.layoutContainer.setOnClickListener( - v -> mBinding.getRoot().openDrawer(GravityCompat.START)); + private void inflatePath(String path, LayoutInflaterConfiguration config) { + this.layoutInflater = ILayoutInflater.newInstance(config); + this.layoutInflater.resetContextProvider(this); + this.layoutInflater.registerInflateListener(this.mInflateListener); + final IView view = this.layoutInflater.inflatePath(path, mBinding.layoutContainer); - tryDismiss(); - } + if (this.inflatedRoot != null) { + this.inflatedRoot.asView().setOnDragListener(getOnDragListener(this.inflatedRoot)); + } - private void tryDismiss() { - final var manager = getSupportFragmentManager(); - final var frag = manager.findFragmentByTag(EDITOR_SHEET_DIALOG_TAG); - if (frag != null) { - final var transaction = manager.beginTransaction(); - transaction.remove(frag); - transaction.commit(); - } - } + mBinding.layoutContainer.addView(view.asView()); + mBinding.layoutContainer.setOnClickListener( + v -> mBinding.getRoot().openDrawer(GravityCompat.START)); - @Override - public void onBackPressed() { - - if (inflationFailed) { - super.onBackPressed(); - return; + tryDismiss(); } - // When the user presses the back button, set the activity result and finish this activity + private void tryDismiss() { + final var manager = getSupportFragmentManager(); + final var frag = manager.findFragmentByTag(EDITOR_SHEET_DIALOG_TAG); + if (frag != null) { + final var transaction = manager.beginTransaction(); + transaction.remove(frag); + transaction.commit(); + } + } - final var progress = new ProgressSheet(); - progress.setSubMessageEnabled(true); - progress.setShowTitle(false); - progress.setMessage(getString(R.string.title_generating_xml)); - progress.setSubMessage(getString(R.string.please_wait)); - progress.show(getSupportFragmentManager(), "generate_code_progress"); + @Override + public void onBackPressed() { - final var future = - CompletableFuture.supplyAsync( - () -> BaseView.XML_HEADER.concat("\n").concat(inflatedRoot.generateCode())); - future.whenComplete( - (s, throwable) -> { - progress.dismiss(); - - if (throwable != null) { - notifyXmlGenerationFailed(throwable); + if (inflationFailed) { + super.onBackPressed(); return; - } + } - try { - finishWithResult(future.get()); - } catch (ExecutionException | InterruptedException e) { - notifyXmlGenerationFailed(e); - } - }); - } + // When the user presses the back button, set the activity result and finish this activity - private void notifyXmlGenerationFailed(Throwable error) { - final var errorMessage = getString(R.string.msg_generate_xml_failed); - LOG.error(errorMessage, error); + final var progress = new ProgressSheet(); + progress.setSubMessageEnabled(true); + progress.setShowTitle(false); + progress.setMessage(getString(R.string.title_generating_xml)); + progress.setSubMessage(getString(R.string.please_wait)); + progress.show(getSupportFragmentManager(), "generate_code_progress"); - DialogUtils.newYesNoDialog( - this, - getString(R.string.title_code_generation_failed), - getString(R.string.msg_code_generation_failed), - (dialog1, which) -> finishWithError(), - (dialog1, which) -> dialog1.dismiss()) - .show(); - } + final var future = + CompletableFuture.supplyAsync( + () -> BaseView.XML_HEADER.concat("\n").concat(inflatedRoot.generateCode())); + future.whenComplete( + (s, throwable) -> { + progress.dismiss(); - private void finishWithResult(String result) { - final var intent = new Intent(); - intent.putExtra(KEY_GENERATED_CODE, result); - setResult(RESULT_OK, intent); - finish(); - } + if (throwable != null) { + notifyXmlGenerationFailed(throwable); + return; + } - private void finishWithError() { - final var intent = new Intent(); - setResult(-123, intent); - finish(); - } - - @Override - protected void onDestroy() { - LOG.logThis(); - if (isAttrEditorShowing()) { - getAttrEditorSheet().dismiss(); + try { + finishWithResult(future.get()); + } catch (ExecutionException | InterruptedException e) { + notifyXmlGenerationFailed(e); + } + }); } - this.mEditorSheet = null; + private void notifyXmlGenerationFailed(Throwable error) { + final var errorMessage = getString(R.string.msg_generate_xml_failed); + LOG.error(errorMessage, error); - // Release the reference to inflate listener and context from the layout inflater - // Failing to do so will lead getSupportFragmentManager() to return a destroyed fragment - // manager - try { - if (layoutInflater == null) { - LOG.debug("Skipping layout inflater context reference release. Inflater is null"); - return; - } - - this.layoutInflater.unregisterListener(mInflateListener); - this.layoutInflater.resetContextProvider(null); - } catch (Throwable e) { - LOG.error("Unable to release activity references from ILayoutInflater", e); + DialogUtils.newYesNoDialog( + this, + getString(R.string.title_code_generation_failed), + getString(R.string.msg_code_generation_failed), + (dialog1, which) -> finishWithError(), + (dialog1, which) -> dialog1.dismiss()) + .show(); } - super.onDestroy(); - } - - private void setupInflatedView(@NonNull IView view) { - final var listener = - new WidgetTouchListener(view, this::onLayoutViewClick, this::onLayoutViewLongClick); - view.asView().setOnTouchListener(listener); - - setDragDataToInflatedView(view); - } - - private boolean onLayoutViewLongClick(@NonNull IView view) { - final var shadow = new WidgetDragShadowBuilder(view.asView()); - final var item = new ClipData.Item(DesignerActivity.DRAGGING_WIDGET_TAG); - final var clip = - new ClipData( - DesignerActivity.DRAGGING_WIDGET_TAG, - new String[] {DesignerActivity.DRAGGING_WIDGET_MIME}, - item); - final var data = view.getExtraData(); - - view.asView().startDragAndDrop(clip, shadow, data, 0); - view.removeFromParent(); - - return true; - } - - private void onLayoutViewClick(@NonNull IView view) { - if (isAttrEditorShowing()) { - getAttrEditorSheet().dismiss(); + private void finishWithResult(String result) { + final var intent = new Intent(); + intent.putExtra(KEY_GENERATED_CODE, result); + setResult(RESULT_OK, intent); + finish(); } - getAttrEditorSheet().setSelectedView(view); - - if (!getSupportFragmentManager().isDestroyed()) { - getAttrEditorSheet().show(getSupportFragmentManager(), EDITOR_SHEET_DIALOG_TAG); + private void finishWithError() { + final var intent = new Intent(); + setResult(-123, intent); + finish(); } - } - private boolean isAttrEditorShowing() { - return getAttrEditorSheet().getDialog() != null && getAttrEditorSheet().getDialog().isShowing(); - } + @Override + protected void onDestroy() { + LOG.logThis(); + if (isAttrEditorShowing()) { + getAttrEditorSheet().dismiss(); + } - private void setupWidgets() { - final var android = new UIWidgetGroup(getString(R.string.widget_group_android)); - android.addChild( - new UIWidget(getString(R.string.widget_button), R.drawable.ic_widget_button, Button.class)); - android.addChild( - new UIWidget( - getString(R.string.widget_checkbox), R.drawable.ic_widget_checkbox, CheckBox.class)); - android.addChild( - new UIWidget( - getString(R.string.widget_checked_textview), - R.drawable.ic_widget_checked_textview, - CheckedTextView.class)); - android.addChild( - new UIWidget( - getString(R.string.widget_edittext), R.drawable.ic_widget_edittext, EditText.class)); - android.addChild( - new UIWidget( - getString(R.string.widget_image_button), - R.drawable.ic_widget_image, - ImageButton.class)); - android.addChild( - new UIWidget( - getString(R.string.widget_image_view), R.drawable.ic_widget_image, ImageView.class)); - android.addChild( - new UIWidget( - getString(R.string.widget_progressbar), - R.drawable.ic_widget_progress, - ProgressBar.class)); - android.addChild( - new UIWidget( - getString(R.string.widget_radio_button), - R.drawable.ic_widget_radio_button, - RadioButton.class)); - android.addChild( - new UIWidget( - getString(R.string.widget_seekbar), R.drawable.ic_widget_seekbar, SeekBar.class)); - android.addChild( - new UIWidget( - getString(R.string.widget_spinner), R.drawable.ic_widget_spinner, Spinner.class)); - android.addChild( - new UIWidget( - getString(R.string.widget_textview), R.drawable.ic_widget_textview, TextView.class)); + this.mEditorSheet = null; - // IMPORTANT: Do not select any other groups here - android.setSelected(true); - this.checkedWidgetCategory = android; - widgetGroups.add(android); + // Release the reference to inflate listener and context from the layout inflater + // Failing to do so will lead getSupportFragmentManager() to return a destroyed fragment + // manager + try { + if (layoutInflater == null) { + LOG.debug("Skipping layout inflater context reference release. Inflater is null"); + return; + } - final var layouts = new UIWidgetGroup(getString(R.string.widget_group_layouts)); - layouts.addChild( - new UIWidget( - getString(R.string.layout_linear), R.drawable.ic_widget_linear, LinearLayout.class)); - layouts.addChild( - new UIWidget( - getString(R.string.layout_relative), - R.drawable.ic_widget_relative, - RelativeLayout.class)); + this.layoutInflater.unregisterListener(mInflateListener); + this.layoutInflater.resetContextProvider(null); + } catch (Throwable e) { + LOG.error("Unable to release activity references from ILayoutInflater", e); + } - widgetGroups.add(layouts); - - final var adapter = new WidgetGroupItemAdapter(widgetGroups, this::showWidgetGroup); - mBinding.groupItems.setAdapter(adapter); - - showWidgetGroup(android); - } - - @SuppressLint("NotifyDataSetChanged") - private void showWidgetGroup(@NonNull UIWidgetGroup group) { - final var children = group.getChildren(); - this.mBinding.widgetItems.setAdapter(new WidgetItemAdapter(children, this)); - this.checkedWidgetCategory.setSelected(false); - this.checkedWidgetCategory = group; - this.checkedWidgetCategory.setSelected(true); - - TransitionManager.beginDelayedTransition( - mBinding.navigation, - new TransitionSet().addTransition(new Slide(Gravity.END)).addTransition(new Fade())); - - //noinspection ConstantConditions - this.mBinding.groupItems.getAdapter().notifyDataSetChanged(); - } - - @NonNull - private TextView createErrorText(Throwable th) { - final TextView error = new TextView(this); - error.setText(ThrowableUtils.getFullStackTrace(th)); - error.setLayoutParams(new ViewGroup.LayoutParams(-2, -2)); - error.setTextColor(ContextCompat.getColor(this, android.R.color.black)); - error.setBackgroundColor(ContextCompat.getColor(this, android.R.color.white)); - return error; - } - - @Override - public void onDragStarted(View view) { - mBinding.getRoot().closeDrawer(GravityCompat.START); - } - - @NonNull - @Contract("_ -> new") - private View.OnDragListener getOnDragListener(IViewGroup group) { - // WidgetDragListener cannot be reused - // This is because they keep a reference to the view group in which dragged view will be - // added - - return new WidgetDragListener(this, group, this::setupInflatedView); - } - - private void setDragDataToInflatedView(@NonNull IView view) { - view.setExtraData(new WidgetDragData(true, view, null)); - } - - private AttrEditorSheet getAttrEditorSheet() { - return this.mEditorSheet == null - ? mEditorSheet = new AttrEditorSheet().setLayout(this.layout) - : mEditorSheet; - } - - @Override - public boolean onDeletionFailed(@NonNull IView view) { - final var v = view.asView(); - if (mBinding.layoutContainer.indexOfChild(v) >= 0) { - mBinding.layoutContainer.removeView(v); - return true; + super.onDestroy(); } - return false; - } - @Override - public Context getContext() { - return this; - } + private void setupInflatedView(@NonNull IView view) { + final var listener = + new WidgetTouchListener(view, this::onLayoutViewClick, this::onLayoutViewLongClick); + view.asView().setOnTouchListener(listener); + + setDragDataToInflatedView(view); + } + + private boolean onLayoutViewLongClick(@NonNull IView view) { + final var shadow = new WidgetDragShadowBuilder(view.asView()); + final var item = new ClipData.Item(DesignerActivity.DRAGGING_WIDGET_TAG); + final var clip = + new ClipData( + DesignerActivity.DRAGGING_WIDGET_TAG, + new String[] {DesignerActivity.DRAGGING_WIDGET_MIME}, + item); + final var data = view.getExtraData(); + + view.asView().startDragAndDrop(clip, shadow, data, 0); + view.removeFromParent(); + + return true; + } + + private void onLayoutViewClick(@NonNull IView view) { + if (isAttrEditorShowing()) { + getAttrEditorSheet().dismiss(); + } + + getAttrEditorSheet().setSelectedView(view); + + if (!getSupportFragmentManager().isDestroyed()) { + getAttrEditorSheet().show(getSupportFragmentManager(), EDITOR_SHEET_DIALOG_TAG); + } + } + + private boolean isAttrEditorShowing() { + return getAttrEditorSheet().getDialog() != null + && getAttrEditorSheet().getDialog().isShowing(); + } + + private void setupWidgets() { + final var android = new UIWidgetGroup(getString(R.string.widget_group_android)); + android.addChild( + new UIWidget( + getString(R.string.widget_button), + R.drawable.ic_widget_button, + Button.class)); + android.addChild( + new UIWidget( + getString(R.string.widget_checkbox), + R.drawable.ic_widget_checkbox, + CheckBox.class)); + android.addChild( + new UIWidget( + getString(R.string.widget_checked_textview), + R.drawable.ic_widget_checked_textview, + CheckedTextView.class)); + android.addChild( + new UIWidget( + getString(R.string.widget_edittext), + R.drawable.ic_widget_edittext, + EditText.class)); + android.addChild( + new UIWidget( + getString(R.string.widget_image_button), + R.drawable.ic_widget_image, + ImageButton.class)); + android.addChild( + new UIWidget( + getString(R.string.widget_image_view), + R.drawable.ic_widget_image, + ImageView.class)); + android.addChild( + new UIWidget( + getString(R.string.widget_progressbar), + R.drawable.ic_widget_progress, + ProgressBar.class)); + android.addChild( + new UIWidget( + getString(R.string.widget_radio_button), + R.drawable.ic_widget_radio_button, + RadioButton.class)); + android.addChild( + new UIWidget( + getString(R.string.widget_seekbar), + R.drawable.ic_widget_seekbar, + SeekBar.class)); + android.addChild( + new UIWidget( + getString(R.string.widget_spinner), + R.drawable.ic_widget_spinner, + Spinner.class)); + android.addChild( + new UIWidget( + getString(R.string.widget_textview), + R.drawable.ic_widget_textview, + TextView.class)); + + // IMPORTANT: Do not select any other groups here + android.setSelected(true); + this.checkedWidgetCategory = android; + widgetGroups.add(android); + + final var layouts = new UIWidgetGroup(getString(R.string.widget_group_layouts)); + layouts.addChild( + new UIWidget( + getString(R.string.layout_linear), + R.drawable.ic_widget_linear, + LinearLayout.class)); + layouts.addChild( + new UIWidget( + getString(R.string.layout_relative), + R.drawable.ic_widget_relative, + RelativeLayout.class)); + + widgetGroups.add(layouts); + + final var adapter = new WidgetGroupItemAdapter(widgetGroups, this::showWidgetGroup); + mBinding.groupItems.setAdapter(adapter); + + showWidgetGroup(android); + } + + @SuppressLint("NotifyDataSetChanged") + private void showWidgetGroup(@NonNull UIWidgetGroup group) { + final var children = group.getChildren(); + this.mBinding.widgetItems.setAdapter(new WidgetItemAdapter(children, this)); + this.checkedWidgetCategory.setSelected(false); + this.checkedWidgetCategory = group; + this.checkedWidgetCategory.setSelected(true); + + TransitionManager.beginDelayedTransition( + mBinding.navigation, + new TransitionSet() + .addTransition(new Slide(Gravity.END)) + .addTransition(new Fade())); + + //noinspection ConstantConditions + this.mBinding.groupItems.getAdapter().notifyDataSetChanged(); + } + + @NonNull + private TextView createErrorText(Throwable th) { + final TextView error = new TextView(this); + error.setText(ThrowableUtils.getFullStackTrace(th)); + error.setLayoutParams(new ViewGroup.LayoutParams(-2, -2)); + error.setTextColor(ContextCompat.getColor(this, android.R.color.black)); + error.setBackgroundColor(ContextCompat.getColor(this, android.R.color.white)); + return error; + } + + @Override + public void onDragStarted(View view) { + mBinding.getRoot().closeDrawer(GravityCompat.START); + } + + @NonNull + @Contract("_ -> new") + private View.OnDragListener getOnDragListener(IViewGroup group) { + // WidgetDragListener cannot be reused + // This is because they keep a reference to the view group in which dragged view will be + // added + + return new WidgetDragListener(this, group, this::setupInflatedView); + } + + private void setDragDataToInflatedView(@NonNull IView view) { + view.setExtraData(new WidgetDragData(true, view, null)); + } + + private AttrEditorSheet getAttrEditorSheet() { + return this.mEditorSheet == null + ? mEditorSheet = new AttrEditorSheet().setLayout(this.layout) + : mEditorSheet; + } + + @Override + public boolean onDeletionFailed(@NonNull IView view) { + final var v = view.asView(); + if (mBinding.layoutContainer.indexOfChild(v) >= 0) { + mBinding.layoutContainer.removeView(v); + return true; + } + return false; + } + + @Override + public Context getContext() { + return this; + } } diff --git a/app/src/main/java/com/itsaky/androidide/EditorActivity.java b/app/src/main/java/com/itsaky/androidide/EditorActivity.java index b8492c76e..b6f690426 100755 --- a/app/src/main/java/com/itsaky/androidide/EditorActivity.java +++ b/app/src/main/java/com/itsaky/androidide/EditorActivity.java @@ -35,7 +35,6 @@ import android.view.View; import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.LinearLayout; - import androidx.activity.result.ActivityResult; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; @@ -51,7 +50,6 @@ import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.transition.Slide; import androidx.transition.TransitionManager; - import com.blankj.utilcode.util.ClipboardUtils; import com.blankj.utilcode.util.FileUtils; import com.blankj.utilcode.util.IntentUtils; @@ -116,9 +114,8 @@ import com.itsaky.lsp.models.InitializeParams; import com.itsaky.lsp.models.Range; import com.itsaky.toaster.Toaster; import com.unnamed.b.atv.model.TreeNode; - -import org.jetbrains.annotations.Contract; - +import io.github.rosemoe.sora.event.ContentChangeEvent; +import io.github.rosemoe.sora.event.Unsubscribe; import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; @@ -132,11 +129,9 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.regex.Pattern; import java.util.stream.Collectors; - -import io.github.rosemoe.sora.event.ContentChangeEvent; -import io.github.rosemoe.sora.event.Unsubscribe; import me.piruin.quickaction.ActionItem; import me.piruin.quickaction.QuickAction; +import org.jetbrains.annotations.Contract; public class EditorActivity extends StudioActivity implements FileTreeFragment.FileActionListener, diff --git a/app/src/main/java/com/itsaky/androidide/TerminalActivity.java b/app/src/main/java/com/itsaky/androidide/TerminalActivity.java index 5751bb6b3..c9dec2d54 100755 --- a/app/src/main/java/com/itsaky/androidide/TerminalActivity.java +++ b/app/src/main/java/com/itsaky/androidide/TerminalActivity.java @@ -29,9 +29,7 @@ import android.view.MotionEvent; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; - import androidx.annotation.NonNull; - import com.blankj.utilcode.util.ClipboardUtils; import com.blankj.utilcode.util.KeyboardUtils; import com.blankj.utilcode.util.SizeUtils; @@ -56,12 +54,10 @@ import com.itsaky.terminal.TerminalSessionClient; import com.itsaky.terminal.TextStyle; import com.itsaky.terminal.view.TerminalView; import com.itsaky.terminal.view.TerminalViewClient; - -import org.json.JSONException; - import java.io.File; import java.io.FileOutputStream; import java.util.Map; +import org.json.JSONException; public class TerminalActivity extends StudioActivity { diff --git a/app/src/main/java/com/itsaky/androidide/adapters/AttrValueFormatTabAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/AttrValueFormatTabAdapter.java index cfdd440c8..7192cde10 100644 --- a/app/src/main/java/com/itsaky/androidide/adapters/AttrValueFormatTabAdapter.java +++ b/app/src/main/java/com/itsaky/androidide/adapters/AttrValueFormatTabAdapter.java @@ -18,17 +18,14 @@ package com.itsaky.androidide.adapters; import android.os.Bundle; import android.text.TextUtils; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentStatePagerAdapter; - import com.itsaky.androidide.fragments.attr.BaseValueEditorFragment; import com.itsaky.androidide.models.XMLAttribute; import com.itsaky.androidide.utils.Logger; - import java.util.ArrayList; import java.util.List; import java.util.Objects; diff --git a/app/src/main/java/com/itsaky/androidide/adapters/EditorBottomSheetTabAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/EditorBottomSheetTabAdapter.java index 5a434f50c..edf7f5ab5 100644 --- a/app/src/main/java/com/itsaky/androidide/adapters/EditorBottomSheetTabAdapter.java +++ b/app/src/main/java/com/itsaky/androidide/adapters/EditorBottomSheetTabAdapter.java @@ -25,7 +25,6 @@ import androidx.collection.LongSparseArray; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import androidx.viewpager2.adapter.FragmentStateAdapter; - import com.itsaky.androidide.R; import com.itsaky.androidide.fragments.DiagnosticsListFragment; import com.itsaky.androidide.fragments.IDELogFragment; @@ -33,7 +32,6 @@ import com.itsaky.androidide.fragments.LogViewFragment; import com.itsaky.androidide.fragments.SearchResultFragment; import com.itsaky.androidide.fragments.SimpleOutputFragment; import com.itsaky.androidide.utils.Logger; - import java.util.ArrayList; import java.util.List; diff --git a/app/src/main/java/com/itsaky/androidide/adapters/TextActionItemAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/TextActionItemAdapter.java index 3e83d7893..a8fc518e4 100644 --- a/app/src/main/java/com/itsaky/androidide/adapters/TextActionItemAdapter.java +++ b/app/src/main/java/com/itsaky/androidide/adapters/TextActionItemAdapter.java @@ -19,13 +19,10 @@ package com.itsaky.androidide.adapters; import android.view.LayoutInflater; import android.view.ViewGroup; - import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; - import com.itsaky.androidide.databinding.LayoutTextActionItemBinding; import com.itsaky.androidide.views.editor.IDEEditor; - import java.util.List; import java.util.Objects; import java.util.function.Consumer; diff --git a/app/src/main/java/com/itsaky/androidide/app/StudioApp.java b/app/src/main/java/com/itsaky/androidide/app/StudioApp.java index b947893b8..17716fcfa 100755 --- a/app/src/main/java/com/itsaky/androidide/app/StudioApp.java +++ b/app/src/main/java/com/itsaky/androidide/app/StudioApp.java @@ -18,9 +18,7 @@ package com.itsaky.androidide.app; import android.content.Intent; - import androidx.annotation.NonNull; - import com.blankj.utilcode.util.ThrowableUtils; import com.google.firebase.messaging.FirebaseMessaging; import com.itsaky.androidide.CrashHandlerActivity; @@ -38,7 +36,6 @@ import com.itsaky.lsp.java.JavaLanguageServer; import com.itsaky.lsp.xml.XMLLanguageServer; import com.itsaky.sdk.SDKInfo; import com.itsaky.widgets.WidgetInfo; - import java.io.File; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -46,114 +43,114 @@ import java.util.concurrent.CompletionException; public class StudioApp extends BaseApplication { - private static final Logger LOG = Logger.instance("StudioApp"); - private static StudioApp instance; - private static SDKInfo sdkInfo; - private final ILanguageServer mJavaLanguageServer = new JavaLanguageServer(); - private final ILanguageServer mXMLLanguageServer = new XMLLanguageServer(); - private IResourceTable mResTable; - private Thread.UncaughtExceptionHandler uncaughtExceptionHandler; + private static final Logger LOG = Logger.instance("StudioApp"); + private static StudioApp instance; + private static SDKInfo sdkInfo; + private final ILanguageServer mJavaLanguageServer = new JavaLanguageServer(); + private final ILanguageServer mXMLLanguageServer = new XMLLanguageServer(); + private IResourceTable mResTable; + private Thread.UncaughtExceptionHandler uncaughtExceptionHandler; - public static StudioApp getInstance() { - return instance; - } - - @Override - public void onCreate() { - instance = this; - this.uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); - Thread.setDefaultUncaughtExceptionHandler(this::handleCrash); - super.onCreate(); - - FirebaseMessaging.getInstance().subscribeToTopic(MessagingService.TOPIC_UPDATE); - FirebaseMessaging.getInstance().subscribeToTopic(MessagingService.TOPIC_DEV_MSGS); - - getApiInformation(); - } - - @NonNull - public ILanguageServer getJavaLanguageServer() { - return mJavaLanguageServer; - } - - @NonNull - public ILanguageServer getXMLLanguageServer() { - return mXMLLanguageServer; - } - - private void handleCrash(Thread thread, Throwable th) { - writeException(th); - - try { - final var intent = new Intent(); - intent.setAction(CrashHandlerActivity.REPORT_ACTION); - intent.putExtra(CrashHandlerActivity.TRACE_KEY, ThrowableUtils.getFullStackTrace(th)); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); - - if (this.uncaughtExceptionHandler != null) { - this.uncaughtExceptionHandler.uncaughtException(thread, th); - } - System.exit(1); - } catch (Throwable error) { - LOG.error("Unable to show crash handler activity", error); + public static StudioApp getInstance() { + return instance; } - } - public CompletableFuture createInflaterConfig( - ILayoutInflater.ContextProvider contextProvider, Set resDirs) { + @Override + public void onCreate() { + instance = this; + this.uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); + Thread.setDefaultUncaughtExceptionHandler(this::handleCrash); + super.onCreate(); - return CompletableFuture.supplyAsync( - () -> { - try { - final var sdkInfo = getApiInformation().get(); - return new LayoutInflaterConfiguration.Builder() - .setAttrInfo(sdkInfo.getAttrInfo()) - .setWidgetInfo(sdkInfo.getWidgetInfo()) - .setResourceFinder(getResourceTable()) - .setResourceDirectories(resDirs) - .setContextProvider(contextProvider) - .create(); - } catch (Throwable e) { - throw new CompletionException(e); - } - }); - } + FirebaseMessaging.getInstance().subscribeToTopic(MessagingService.TOPIC_UPDATE); + FirebaseMessaging.getInstance().subscribeToTopic(MessagingService.TOPIC_DEV_MSGS); - public IResourceTable getResourceTable() { - return mResTable == null ? mResTable = new ProjectResourceTable() : mResTable; - } + getApiInformation(); + } - /** Reads API version information from api-versions.xml */ - public CompletableFuture getApiInformation() { - return CompletableFuture.supplyAsync( - () -> { - if (sdkInfo == null) { - try { - sdkInfo = new SDKInfo(StudioApp.this); - ((XMLLanguageServer) mXMLLanguageServer).setupSDK(sdkInfo); - } catch (Throwable th) { - LOG.error(getString(R.string.err_init_sdkinfo), th); + @NonNull + public ILanguageServer getJavaLanguageServer() { + return mJavaLanguageServer; + } + + @NonNull + public ILanguageServer getXMLLanguageServer() { + return mXMLLanguageServer; + } + + private void handleCrash(Thread thread, Throwable th) { + writeException(th); + + try { + final var intent = new Intent(); + intent.setAction(CrashHandlerActivity.REPORT_ACTION); + intent.putExtra(CrashHandlerActivity.TRACE_KEY, ThrowableUtils.getFullStackTrace(th)); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + + if (this.uncaughtExceptionHandler != null) { + this.uncaughtExceptionHandler.uncaughtException(thread, th); } - } + System.exit(1); + } catch (Throwable error) { + LOG.error("Unable to show crash handler activity", error); + } + } - return sdkInfo; - }); - } + public CompletableFuture createInflaterConfig( + ILayoutInflater.ContextProvider contextProvider, Set resDirs) { - public void stopAllDaemons() { - newShell(null).bgAppend("gradle --stop"); - } + return CompletableFuture.supplyAsync( + () -> { + try { + final var sdkInfo = getApiInformation().get(); + return new LayoutInflaterConfiguration.Builder() + .setAttrInfo(sdkInfo.getAttrInfo()) + .setWidgetInfo(sdkInfo.getWidgetInfo()) + .setResourceFinder(getResourceTable()) + .setResourceDirectories(resDirs) + .setContextProvider(contextProvider) + .create(); + } catch (Throwable e) { + throw new CompletionException(e); + } + }); + } - public ApiInfo apiInfo() { - return sdkInfo.getApiInfo(); - } + public IResourceTable getResourceTable() { + return mResTable == null ? mResTable = new ProjectResourceTable() : mResTable; + } - public AttrInfo attrInfo() { - return sdkInfo.getAttrInfo(); - } + /** Reads API version information from api-versions.xml */ + public CompletableFuture getApiInformation() { + return CompletableFuture.supplyAsync( + () -> { + if (sdkInfo == null) { + try { + sdkInfo = new SDKInfo(StudioApp.this); + ((XMLLanguageServer) mXMLLanguageServer).setupSDK(sdkInfo); + } catch (Throwable th) { + LOG.error(getString(R.string.err_init_sdkinfo), th); + } + } - public WidgetInfo widgetInfo() { - return sdkInfo.getWidgetInfo(); - } + return sdkInfo; + }); + } + + public void stopAllDaemons() { + newShell(null).bgAppend("gradle --stop"); + } + + public ApiInfo apiInfo() { + return sdkInfo.getApiInfo(); + } + + public AttrInfo attrInfo() { + return sdkInfo.getAttrInfo(); + } + + public WidgetInfo widgetInfo() { + return sdkInfo.getWidgetInfo(); + } } diff --git a/app/src/main/java/com/itsaky/androidide/fragments/CrashReportFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/CrashReportFragment.java index 1d5ed9524..7b1a6f9df 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/CrashReportFragment.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/CrashReportFragment.java @@ -27,11 +27,9 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - import com.blankj.utilcode.util.ClipboardUtils; import com.itsaky.androidide.R; import com.itsaky.androidide.app.BaseApplication; diff --git a/app/src/main/java/com/itsaky/androidide/fragments/FileTreeFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/FileTreeFragment.java index 019f8d430..6b936a6ac 100755 --- a/app/src/main/java/com/itsaky/androidide/fragments/FileTreeFragment.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/FileTreeFragment.java @@ -26,11 +26,9 @@ import android.view.View; import android.view.ViewGroup; import android.widget.HorizontalScrollView; import android.widget.ProgressBar; - import androidx.annotation.NonNull; import androidx.transition.ChangeBounds; import androidx.transition.TransitionManager; - import com.blankj.utilcode.util.FileIOUtils; import com.blankj.utilcode.util.FileUtils; import com.blankj.utilcode.util.IntentUtils; @@ -47,7 +45,6 @@ import com.itsaky.androidide.views.editor.CodeEditorView; import com.itsaky.toaster.Toaster; import com.unnamed.b.atv.model.TreeNode; import com.unnamed.b.atv.view.AndroidTreeView; - import java.io.File; import java.util.Arrays; diff --git a/app/src/main/java/com/itsaky/androidide/fragments/NonEditableEditorFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/NonEditableEditorFragment.java index d247686b1..b01708922 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/NonEditableEditorFragment.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/NonEditableEditorFragment.java @@ -23,17 +23,14 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - import com.itsaky.androidide.databinding.FragmentNonEditableEditorBinding; import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE; import com.itsaky.androidide.utils.Logger; import com.itsaky.androidide.utils.TypefaceUtils; import com.itsaky.androidide.views.editor.IDEEditor; - import io.github.rosemoe.sora.lang.EmptyLanguage; public abstract class NonEditableEditorFragment extends Fragment { diff --git a/app/src/main/java/com/itsaky/androidide/fragments/SimpleOutputFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/SimpleOutputFragment.java index 19c848477..921bac5e1 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/SimpleOutputFragment.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/SimpleOutputFragment.java @@ -19,12 +19,9 @@ package com.itsaky.androidide.fragments; import android.os.Bundle; import android.view.View; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.blankj.utilcode.util.ThreadUtils; - import java.util.ArrayList; import java.util.List; import java.util.Objects; diff --git a/app/src/main/java/com/itsaky/androidide/fragments/attr/BaseValueEditorFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/attr/BaseValueEditorFragment.java index ed38c0153..2c349a2b3 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/attr/BaseValueEditorFragment.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/attr/BaseValueEditorFragment.java @@ -20,14 +20,11 @@ package com.itsaky.androidide.fragments.attr; import android.content.Context; import android.os.Bundle; import android.view.View; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - import com.itsaky.androidide.models.XMLAttribute; import com.itsaky.inflater.IAttribute; - import java.util.Objects; /** diff --git a/app/src/main/java/com/itsaky/androidide/fragments/preferences/BuildPreferences.java b/app/src/main/java/com/itsaky/androidide/fragments/preferences/BuildPreferences.java index c89cee221..2cca027bf 100755 --- a/app/src/main/java/com/itsaky/androidide/fragments/preferences/BuildPreferences.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/preferences/BuildPreferences.java @@ -21,11 +21,9 @@ package com.itsaky.androidide.fragments.preferences; import android.os.Bundle; - import androidx.preference.Preference; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceScreen; - import com.blankj.utilcode.util.FileUtils; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.itsaky.androidide.R; @@ -33,7 +31,6 @@ import com.itsaky.androidide.fragments.sheets.ProgressSheet; import com.itsaky.androidide.tasks.TaskExecutor; import com.itsaky.androidide.utils.DialogUtils; import com.itsaky.androidide.utils.Environment; - import java.io.File; public class BuildPreferences extends BasePreferenceFragment diff --git a/app/src/main/java/com/itsaky/androidide/fragments/preferences/EditorPreferences.java b/app/src/main/java/com/itsaky/androidide/fragments/preferences/EditorPreferences.java index df9d1dd2c..7fa6ee9d7 100755 --- a/app/src/main/java/com/itsaky/androidide/fragments/preferences/EditorPreferences.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/preferences/EditorPreferences.java @@ -35,12 +35,10 @@ import static com.itsaky.lsp.java.models.JavaServerSettings.KEY_JAVA_PREF_GOOGLE import android.os.Bundle; import android.view.LayoutInflater; - import androidx.annotation.NonNull; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; import androidx.preference.SwitchPreference; - import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.itsaky.androidide.R; import com.itsaky.androidide.databinding.LayoutTextSizeSliderBinding; diff --git a/app/src/main/java/com/itsaky/androidide/fragments/preferences/IDEPreferences.java b/app/src/main/java/com/itsaky/androidide/fragments/preferences/IDEPreferences.java index 1edcd1695..db8146792 100755 --- a/app/src/main/java/com/itsaky/androidide/fragments/preferences/IDEPreferences.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/preferences/IDEPreferences.java @@ -22,11 +22,9 @@ import static com.itsaky.androidide.utils.Logger.instance; import android.content.Intent; import android.net.Uri; import android.os.Bundle; - import androidx.annotation.NonNull; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; - import com.itsaky.androidide.AboutActivity; import com.itsaky.androidide.BuildConfig; import com.itsaky.androidide.R; diff --git a/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttrEditorSheet.java b/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttrEditorSheet.java index 1966310ee..c2e2d47c1 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttrEditorSheet.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttrEditorSheet.java @@ -22,11 +22,9 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.transition.TransitionManager; - import com.google.android.material.bottomsheet.BottomSheetDialogFragment; import com.itsaky.androidide.R; import com.itsaky.androidide.adapters.SimpleIconTextAdapter; @@ -44,9 +42,6 @@ import com.itsaky.inflater.IAttribute; import com.itsaky.inflater.IView; import com.itsaky.inflater.impl.UiAttribute; import com.itsaky.toaster.Toaster; - -import org.jetbrains.annotations.Contract; - import java.io.File; import java.util.ArrayList; import java.util.Collections; @@ -56,302 +51,315 @@ import java.util.Objects; import java.util.TreeSet; import java.util.function.Consumer; import java.util.stream.Collectors; +import org.jetbrains.annotations.Contract; public class AttrEditorSheet extends BottomSheetDialogFragment - implements SimpleIconTextAdapter.OnBindListener, - Consumer, - BaseValueEditorFragment.OnValueChangeListener { + implements SimpleIconTextAdapter.OnBindListener, + Consumer, + BaseValueEditorFragment.OnValueChangeListener { - private static final List VIEW_ACTIONS = new ArrayList<>(); - private static final Logger LOG = Logger.instance("AttrBottomSheet"); - private AttributeListSheet mAttrListSheet; - private AttrValueEditorSheet mValueEditorSheet; - private IView selectedView; + private static final List VIEW_ACTIONS = new ArrayList<>(); + private static final Logger LOG = Logger.instance("AttrBottomSheet"); + private AttributeListSheet mAttrListSheet; + private AttrValueEditorSheet mValueEditorSheet; + private IView selectedView; - @SuppressWarnings({"FieldCanBeLocal", "unused"}) - private File layout; + @SuppressWarnings({"FieldCanBeLocal", "unused"}) + private File layout; - private LayoutAttrEditorSheetBinding binding; - private OnViewDeletionFailedListener mDeletionFailedListener; + private LayoutAttrEditorSheetBinding binding; + private OnViewDeletionFailedListener mDeletionFailedListener; - @Nullable - @Override - public View onCreateView( - @NonNull LayoutInflater inflater, - @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - this.binding = - LayoutAttrEditorSheetBinding.inflate(LayoutInflater.from(getContext()), container, false); - return binding.getRoot(); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - if (VIEW_ACTIONS.isEmpty()) { - Collections.addAll( - VIEW_ACTIONS, - IconTextListItem.create(getString(R.string.msg_viewaction_add_attr), R.drawable.ic_add), - IconTextListItem.create( - getString(R.string.title_viewaction_delete), R.drawable.ic_delete), - IconTextListItem.create( - getString(R.string.msg_viewaction_select_parent), R.drawable.ic_view_select_parent)); + @Nullable + @Override + public View onCreateView( + @NonNull LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + this.binding = + LayoutAttrEditorSheetBinding.inflate( + LayoutInflater.from(getContext()), container, false); + return binding.getRoot(); } - setupViewData(); - } + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); - @Override - public void onAttach(@NonNull Context context) { - super.onAttach(context); - mDeletionFailedListener = (OnViewDeletionFailedListener) context; - } - - public AttrEditorSheet setLayout(File layout) { - Objects.requireNonNull(layout); - - this.layout = layout; - return this; - } - - private void setupViewData() { - binding.actionsList.setAdapter(new SimpleIconTextAdapter(VIEW_ACTIONS).setOnBindListener(this)); - - if (dismissIfSelectedNull()) { - LOG.error("Cannot edit attributes of a null view."); - return; - } - - binding.widgetName.setText(this.selectedView.getXmlTag()); - binding.attrList.setAdapter( - new XMLAttributeListAdapter( - this.selectedView.getAttributes().stream() - .map(XMLAttribute::new) - .collect(Collectors.toList()), - this::onAttrClick)); - } - - public void setSelectedView(IView view) { - this.selectedView = view; - } - - private void onAttrClick( - LayoutAttrEditorSheetItemBinding binding, @NonNull XMLAttribute attribute) { - - if (dismissIfSelectedNull()) { - LOG.error("Cannot edit attributes of a null view."); - return; - } - - final var format = attribute.findFormat(); - if (format == -1) { - StudioApp.getInstance().toast(getString(R.string.msg_no_attr_format), Toaster.Type.ERROR); - LOG.error(getString(R.string.msg_no_attr_format), attribute); - return; - } - - showValueEditorSheet(attribute); - } - - private void showValueEditorSheet(@NonNull XMLAttribute attribute) { - getValueEditorSheet(attribute).show(getChildFragmentManager(), "attr_value_editor_sheet"); - } - - @Override - public void postBind( - IconTextListItem item, @NonNull SimpleIconTextAdapter.VH holder, int position) { - final var binding = holder.binding; - binding.getRoot().setOnClickListener(v -> onViewActionClick(position)); - } - - private void onViewActionClick(int position) { - if (dismissIfSelectedNull()) { - return; - } - - if (position == 0) { // Add attribute - var tag = this.selectedView.getXmlTag(); - if ("include".equals(tag) || "merge".equals(tag)) { - tag = "View"; - } - - final var attrs = StudioApp.getInstance().attrInfo(); - final var style = attrs.getStyle(tag); - if (style == null) { - LOG.error("Unable to retrieve attributes for tag:", tag); - return; - } - - final var attributes = new TreeSet(Comparator.comparing(attr -> attr.name)); - attributes.addAll(style.attributes); - attributes.addAll(attrs.NO_PARENT.attributes); - - final var widgetInfo = StudioApp.getInstance().widgetInfo(); - final var widget = widgetInfo.getWidgetBySimpleName(tag); - if (widget != null) { - for (var superclass : widget.superclasses) { - if ("java.lang.Object".equals(superclass)) { - break; - } - - final var simpleName = superclass.substring(superclass.lastIndexOf(".") + 1); - final var superStyle = attrs.getStyle(simpleName); - if (superStyle != null) { - attributes.addAll(superStyle.attributes); - } + if (VIEW_ACTIONS.isEmpty()) { + Collections.addAll( + VIEW_ACTIONS, + IconTextListItem.create( + getString(R.string.msg_viewaction_add_attr), R.drawable.ic_add), + IconTextListItem.create( + getString(R.string.title_viewaction_delete), R.drawable.ic_delete), + IconTextListItem.create( + getString(R.string.msg_viewaction_select_parent), + R.drawable.ic_view_select_parent)); } - } - final var parent = this.selectedView.getParent(); - if (parent != null) { - final var parentTag = parent.getXmlTag(); - final var parentWidget = widgetInfo.getWidgetBySimpleName(parentTag); - if (parentWidget != null) { - final var parentLayoutParams = attrs.getStyle(parentTag + "_Layout"); - if (parentLayoutParams != null) { - attributes.addAll(parentLayoutParams.attributes); - } + setupViewData(); + } - final var paramSuperclasses = widgetInfo.getLayoutParamSuperClasses(parentWidget.name); - if (paramSuperclasses != null) { - for (var superclass : paramSuperclasses) { - if ("java.lang.Object".equals(superclass)) { - continue; - } + @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); + mDeletionFailedListener = (OnViewDeletionFailedListener) context; + } - final var split = superclass.split("\\."); - final var simpleClassName = split[split.length - 2]; - var paramName = split[split.length - 1]; - paramName = paramName.substring(0, paramName.length() - "Params".length()); - final var superParamEntry = attrs.getStyle(simpleClassName + "_" + paramName); - if (superParamEntry != null) { - attributes.addAll(superParamEntry.attributes); - } + public AttrEditorSheet setLayout(File layout) { + Objects.requireNonNull(layout); + + this.layout = layout; + return this; + } + + private void setupViewData() { + binding.actionsList.setAdapter( + new SimpleIconTextAdapter(VIEW_ACTIONS).setOnBindListener(this)); + + if (dismissIfSelectedNull()) { + LOG.error("Cannot edit attributes of a null view."); + return; + } + + binding.widgetName.setText(this.selectedView.getXmlTag()); + binding.attrList.setAdapter( + new XMLAttributeListAdapter( + this.selectedView.getAttributes().stream() + .map(XMLAttribute::new) + .collect(Collectors.toList()), + this::onAttrClick)); + } + + public void setSelectedView(IView view) { + this.selectedView = view; + } + + private void onAttrClick( + LayoutAttrEditorSheetItemBinding binding, @NonNull XMLAttribute attribute) { + + if (dismissIfSelectedNull()) { + LOG.error("Cannot edit attributes of a null view."); + return; + } + + final var format = attribute.findFormat(); + if (format == -1) { + StudioApp.getInstance() + .toast(getString(R.string.msg_no_attr_format), Toaster.Type.ERROR); + LOG.error(getString(R.string.msg_no_attr_format), attribute); + return; + } + + showValueEditorSheet(attribute); + } + + private void showValueEditorSheet(@NonNull XMLAttribute attribute) { + getValueEditorSheet(attribute).show(getChildFragmentManager(), "attr_value_editor_sheet"); + } + + @Override + public void postBind( + IconTextListItem item, @NonNull SimpleIconTextAdapter.VH holder, int position) { + final var binding = holder.binding; + binding.getRoot().setOnClickListener(v -> onViewActionClick(position)); + } + + private void onViewActionClick(int position) { + if (dismissIfSelectedNull()) { + return; + } + + if (position == 0) { // Add attribute + var tag = this.selectedView.getXmlTag(); + if ("include".equals(tag) || "merge".equals(tag)) { + tag = "View"; } - } + + final var attrs = StudioApp.getInstance().attrInfo(); + final var style = attrs.getStyle(tag); + if (style == null) { + LOG.error("Unable to retrieve attributes for tag:", tag); + return; + } + + final var attributes = new TreeSet(Comparator.comparing(attr -> attr.name)); + attributes.addAll(style.attributes); + attributes.addAll(attrs.NO_PARENT.attributes); + + final var widgetInfo = StudioApp.getInstance().widgetInfo(); + final var widget = widgetInfo.getWidgetBySimpleName(tag); + if (widget != null) { + for (var superclass : widget.superclasses) { + if ("java.lang.Object".equals(superclass)) { + break; + } + + final var simpleName = superclass.substring(superclass.lastIndexOf(".") + 1); + final var superStyle = attrs.getStyle(simpleName); + if (superStyle != null) { + attributes.addAll(superStyle.attributes); + } + } + } + + final var parent = this.selectedView.getParent(); + if (parent != null) { + final var parentTag = parent.getXmlTag(); + final var parentWidget = widgetInfo.getWidgetBySimpleName(parentTag); + if (parentWidget != null) { + final var parentLayoutParams = attrs.getStyle(parentTag + "_Layout"); + if (parentLayoutParams != null) { + attributes.addAll(parentLayoutParams.attributes); + } + + final var paramSuperclasses = + widgetInfo.getLayoutParamSuperClasses(parentWidget.name); + if (paramSuperclasses != null) { + for (var superclass : paramSuperclasses) { + if ("java.lang.Object".equals(superclass)) { + continue; + } + + final var split = superclass.split("\\."); + final var simpleClassName = split[split.length - 2]; + var paramName = split[split.length - 1]; + paramName = + paramName.substring(0, paramName.length() - "Params".length()); + final var superParamEntry = + attrs.getStyle(simpleClassName + "_" + paramName); + if (superParamEntry != null) { + attributes.addAll(superParamEntry.attributes); + } + } + } + } + } + + final var sheet = getAttrListSheet(); + sheet.setItems(filterAppliedAttributes(attributes)); + sheet.show(getChildFragmentManager(), "attr_list_sheet"); + + } else if (position == 1) { // Delete + DialogUtils.newYesNoDialog( + getContext(), + (dialog, which) -> { + var handled = selectedView.removeFromParent(); + if (!handled) { + handled = + mDeletionFailedListener != null + && mDeletionFailedListener.onDeletionFailed( + this.selectedView); + } + if (!handled) { + StudioApp.getInstance() + .toast( + getString(R.string.msg_view_deletion_failed), + Toaster.Type.ERROR); + } else { + dismiss(); + } + }, + (dialog, which) -> dialog.dismiss()) + .show(); + } else if (position == 2) { // Select parent + if (this.selectedView.getParent() == null) { + StudioApp.getInstance() + .toast(getString(R.string.msg_no_view_parent), Toaster.Type.ERROR); + return; + } + + this.selectedView = this.selectedView.getParent(); + + TransitionManager.beginDelayedTransition(binding.getRoot()); + setupViewData(); } - } - - final var sheet = getAttrListSheet(); - sheet.setItems(filterAppliedAttributes(attributes)); - sheet.show(getChildFragmentManager(), "attr_list_sheet"); - - } else if (position == 1) { // Delete - DialogUtils.newYesNoDialog( - getContext(), - (dialog, which) -> { - var handled = selectedView.removeFromParent(); - if (!handled) { - handled = - mDeletionFailedListener != null - && mDeletionFailedListener.onDeletionFailed(this.selectedView); - } - if (!handled) { - StudioApp.getInstance() - .toast(getString(R.string.msg_view_deletion_failed), Toaster.Type.ERROR); - } else { - dismiss(); - } - }, - (dialog, which) -> dialog.dismiss()) - .show(); - } else if (position == 2) { // Select parent - if (this.selectedView.getParent() == null) { - StudioApp.getInstance().toast(getString(R.string.msg_no_view_parent), Toaster.Type.ERROR); - return; - } - - this.selectedView = this.selectedView.getParent(); - - TransitionManager.beginDelayedTransition(binding.getRoot()); - setupViewData(); - } - } - - @NonNull - @Contract("_ -> new") - private List filterAppliedAttributes(@NonNull TreeSet attributes) { - attributes.removeIf(attr -> this.selectedView.hasAttribute(attr.namespace, attr.name)); - return new ArrayList<>(attributes); - } - - @Override - public void accept(Attr attr) { - addNewAttribute(attr); - } - - @NonNull - private AttributeListSheet getAttrListSheet() { - if (mAttrListSheet == null) { - mAttrListSheet = new AttributeListSheet(); - mAttrListSheet.setCancelable(true); } - return mAttrListSheet; - } - - @NonNull - public AttrValueEditorSheet getValueEditorSheet(@NonNull XMLAttribute attribute) { - - if (mValueEditorSheet == null) { - mValueEditorSheet = AttrValueEditorSheet.newInstance(attribute); + @NonNull + @Contract("_ -> new") + private List filterAppliedAttributes(@NonNull TreeSet attributes) { + attributes.removeIf(attr -> this.selectedView.hasAttribute(attr.namespace, attr.name)); + return new ArrayList<>(attributes); } - mValueEditorSheet.setAttribute(attribute); - return mValueEditorSheet; - } - - @Override - public void onValueChanged(@NonNull IAttribute attribute, String newValue) { - if (dismissIfSelectedNull()) { - LOG.warn("Cannot update attribute with new value. Selected view is null."); - return; + @Override + public void accept(Attr attr) { + addNewAttribute(attr); } - final var attributeName = attribute.getAttributeName(); - final var namespace = attribute.getNamespace(); + @NonNull + private AttributeListSheet getAttrListSheet() { + if (mAttrListSheet == null) { + mAttrListSheet = new AttributeListSheet(); + mAttrListSheet.setCancelable(true); + } - if (this.selectedView.hasAttribute(namespace, attributeName)) { - this.selectedView.updateAttribute(namespace, attributeName, newValue); - } else { - final var attr = new UiAttribute(namespace, attributeName, newValue); - this.selectedView.addAttribute(attr); + return mAttrListSheet; } - } - private void addNewAttribute(@NonNull Attr attr) { - final XMLAttribute attribute = new XMLAttribute(attr.namespace, attr.name, "", false); - attribute.setAttr(attr); - showValueEditorSheet(attribute); - } + @NonNull + public AttrValueEditorSheet getValueEditorSheet(@NonNull XMLAttribute attribute) { - private boolean dismissIfSelectedNull() { - if (selectedView == null) { - dismiss(); - return true; + if (mValueEditorSheet == null) { + mValueEditorSheet = AttrValueEditorSheet.newInstance(attribute); + } + + mValueEditorSheet.setAttribute(attribute); + return mValueEditorSheet; } - return false; - } - /** - * A listener can be used to get notified when we fail to remove a view from its parent. This is - * used in {@link com.itsaky.androidide.DesignerActivity DesignerActivity}. - * - *

When the user tries to remove the outermost view of the inflated layout, AttrEditorSheet - * fails to remove the view from its parent ({@link IView#getParent()} is null for root XML - * layout). In this case, DesignerActivity check if the view that we were trying to delete is the - * root layout or not. If it is the root layout, then it deletes the layout from the layout - * container. - * - * @author Akash Yadav - */ - public interface OnViewDeletionFailedListener { + @Override + public void onValueChanged(@NonNull IAttribute attribute, String newValue) { + if (dismissIfSelectedNull()) { + LOG.warn("Cannot update attribute with new value. Selected view is null."); + return; + } + + final var attributeName = attribute.getAttributeName(); + final var namespace = attribute.getNamespace(); + + if (this.selectedView.hasAttribute(namespace, attributeName)) { + this.selectedView.updateAttribute(namespace, attributeName, newValue); + } else { + final var attr = new UiAttribute(namespace, attributeName, newValue); + this.selectedView.addAttribute(attr); + } + } + + private void addNewAttribute(@NonNull Attr attr) { + final XMLAttribute attribute = new XMLAttribute(attr.namespace, attr.name, "", false); + attribute.setAttr(attr); + showValueEditorSheet(attribute); + } + + private boolean dismissIfSelectedNull() { + if (selectedView == null) { + dismiss(); + return true; + } + return false; + } /** - * @param view The view that was not removed from its parent. - * @return {@code true} if the listener handled the error. {@code false} otherwise. + * A listener can be used to get notified when we fail to remove a view from its parent. This is + * used in {@link com.itsaky.androidide.DesignerActivity DesignerActivity}. + * + *

When the user tries to remove the outermost view of the inflated layout, AttrEditorSheet + * fails to remove the view from its parent ({@link IView#getParent()} is null for root XML + * layout). In this case, DesignerActivity check if the view that we were trying to delete is + * the root layout or not. If it is the root layout, then it deletes the layout from the layout + * container. + * + * @author Akash Yadav */ - boolean onDeletionFailed(IView view); - } + public interface OnViewDeletionFailedListener { + + /** + * @param view The view that was not removed from its parent. + * @return {@code true} if the listener handled the error. {@code false} otherwise. + */ + boolean onDeletionFailed(IView view); + } } diff --git a/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttrValueEditorSheet.java b/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttrValueEditorSheet.java index fecaec1e5..7c4e1360a 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttrValueEditorSheet.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttrValueEditorSheet.java @@ -34,10 +34,8 @@ import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.google.android.material.bottomsheet.BottomSheetDialogFragment; import com.itsaky.androidide.adapters.AttrValueFormatTabAdapter; import com.itsaky.androidide.databinding.LayoutAttrValueEditorBinding; @@ -55,10 +53,8 @@ import com.itsaky.androidide.fragments.attr.StringEditor; import com.itsaky.androidide.models.XMLAttribute; import com.itsaky.attrinfo.models.Attr; import com.itsaky.inflater.IAttribute; - -import org.jetbrains.annotations.Contract; - import java.util.Objects; +import org.jetbrains.annotations.Contract; /** * Sheet for editing value of an attribute. diff --git a/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttributeListSheet.java b/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttributeListSheet.java index ecf5c440e..ee4147e61 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttributeListSheet.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttributeListSheet.java @@ -23,18 +23,15 @@ import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.SearchView; import androidx.fragment.app.DialogFragment; - import com.google.android.material.bottomsheet.BottomSheetDialogFragment; import com.itsaky.androidide.R; import com.itsaky.androidide.adapters.AttrListAdapter; import com.itsaky.androidide.databinding.LayoutAddAttrSheetBinding; import com.itsaky.attrinfo.models.Attr; - import java.util.ArrayList; import java.util.List; import java.util.Locale; diff --git a/app/src/main/java/com/itsaky/androidide/fragments/sheets/BaseBottomSheetFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/sheets/BaseBottomSheetFragment.java index b663ad0e8..17d2de107 100755 --- a/app/src/main/java/com/itsaky/androidide/fragments/sheets/BaseBottomSheetFragment.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/sheets/BaseBottomSheetFragment.java @@ -23,9 +23,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; - import androidx.annotation.NonNull; - import com.google.android.material.bottomsheet.BottomSheetDialogFragment; import com.itsaky.androidide.databinding.LayoutSheetBinding; diff --git a/app/src/main/java/com/itsaky/androidide/fragments/sheets/OptionsListFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/sheets/OptionsListFragment.java index 8d284cd89..5022c2765 100755 --- a/app/src/main/java/com/itsaky/androidide/fragments/sheets/OptionsListFragment.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/sheets/OptionsListFragment.java @@ -24,14 +24,11 @@ import android.content.Context; import android.os.Bundle; import android.view.View; import android.widget.LinearLayout; - import androidx.annotation.NonNull; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; - import com.itsaky.androidide.adapters.OptionsSheetAdapter; import com.itsaky.androidide.models.SheetOption; - import java.util.ArrayList; import java.util.List; diff --git a/app/src/main/java/com/itsaky/androidide/fragments/sheets/ProgressSheet.java b/app/src/main/java/com/itsaky/androidide/fragments/sheets/ProgressSheet.java index b037399ce..487cc323e 100755 --- a/app/src/main/java/com/itsaky/androidide/fragments/sheets/ProgressSheet.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/sheets/ProgressSheet.java @@ -26,9 +26,7 @@ import android.view.LayoutInflater; import android.view.View; import android.widget.LinearLayout; import android.widget.RelativeLayout; - import androidx.annotation.NonNull; - import com.itsaky.androidide.databinding.LayoutProgressSheetBinding; import com.itsaky.androidide.utils.Logger; diff --git a/app/src/main/java/com/itsaky/androidide/fragments/sheets/TextSheetFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/sheets/TextSheetFragment.java index ad49bbe6d..7a84aaafe 100755 --- a/app/src/main/java/com/itsaky/androidide/fragments/sheets/TextSheetFragment.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/sheets/TextSheetFragment.java @@ -22,13 +22,10 @@ import android.text.SpannableStringBuilder; import android.text.style.ForegroundColorSpan; import android.view.ViewGroup; import android.widget.LinearLayout; - import androidx.annotation.NonNull; - import com.itsaky.androidide.R; import com.itsaky.androidide.utils.TypefaceUtils; import com.itsaky.androidide.views.editor.IDEEditor; - import io.github.rosemoe.sora.lang.EmptyLanguage; public class TextSheetFragment extends BaseBottomSheetFragment { diff --git a/app/src/main/java/com/itsaky/androidide/handlers/BuildServiceHandler.java b/app/src/main/java/com/itsaky/androidide/handlers/BuildServiceHandler.java index 1d5141793..75d04cda8 100755 --- a/app/src/main/java/com/itsaky/androidide/handlers/BuildServiceHandler.java +++ b/app/src/main/java/com/itsaky/androidide/handlers/BuildServiceHandler.java @@ -18,7 +18,6 @@ package com.itsaky.androidide.handlers; import android.view.View; - import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.itsaky.androidide.R; import com.itsaky.androidide.managers.PreferenceManager; @@ -31,7 +30,6 @@ import com.itsaky.androidide.tasks.GradleTask; import com.itsaky.androidide.tasks.gradle.build.ApkGeneratingTask; import com.itsaky.androidide.utils.DialogUtils; import com.itsaky.androidide.utils.Environment; - import java.io.File; import java.util.ArrayList; import java.util.Collection; diff --git a/app/src/main/java/com/itsaky/androidide/language/CommonCompletionProvider.java b/app/src/main/java/com/itsaky/androidide/language/CommonCompletionProvider.java index 347161f21..97c8c5ef1 100644 --- a/app/src/main/java/com/itsaky/androidide/language/CommonCompletionProvider.java +++ b/app/src/main/java/com/itsaky/androidide/language/CommonCompletionProvider.java @@ -18,7 +18,6 @@ package com.itsaky.androidide.language; import androidx.annotation.NonNull; - import com.itsaky.androidide.utils.Logger; import com.itsaky.lsp.api.ICompletionProvider; import com.itsaky.lsp.api.ILanguageServer; @@ -26,9 +25,11 @@ import com.itsaky.lsp.models.CompletionItem; import com.itsaky.lsp.models.CompletionParams; import com.itsaky.lsp.models.CompletionResult; import com.itsaky.lsp.models.Position; - -import org.jetbrains.annotations.Contract; - +import io.github.rosemoe.sora.lang.completion.CompletionCancelledException; +import io.github.rosemoe.sora.lang.completion.CompletionHelper; +import io.github.rosemoe.sora.text.CharPosition; +import io.github.rosemoe.sora.text.ContentReference; +import io.github.rosemoe.sora.util.MyCharacter; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; @@ -37,12 +38,7 @@ import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.function.Predicate; - -import io.github.rosemoe.sora.lang.completion.CompletionCancelledException; -import io.github.rosemoe.sora.lang.completion.CompletionHelper; -import io.github.rosemoe.sora.text.CharPosition; -import io.github.rosemoe.sora.text.ContentReference; -import io.github.rosemoe.sora.util.MyCharacter; +import org.jetbrains.annotations.Contract; /** * Common implementation of completion provider which requests completions to provided language diff --git a/app/src/main/java/com/itsaky/androidide/language/IAnalyzeManager.java b/app/src/main/java/com/itsaky/androidide/language/IAnalyzeManager.java index e6186d481..b485bd2a1 100644 --- a/app/src/main/java/com/itsaky/androidide/language/IAnalyzeManager.java +++ b/app/src/main/java/com/itsaky/androidide/language/IAnalyzeManager.java @@ -18,9 +18,7 @@ package com.itsaky.androidide.language; import androidx.annotation.NonNull; - import com.itsaky.lsp.models.DiagnosticItem; - import java.util.List; /** diff --git a/app/src/main/java/com/itsaky/androidide/language/IDELanguage.java b/app/src/main/java/com/itsaky/androidide/language/IDELanguage.java index 721612aa2..e2b712aaa 100644 --- a/app/src/main/java/com/itsaky/androidide/language/IDELanguage.java +++ b/app/src/main/java/com/itsaky/androidide/language/IDELanguage.java @@ -17,17 +17,14 @@ package com.itsaky.androidide.language; import androidx.annotation.NonNull; - import com.itsaky.androidide.app.StudioApp; import com.itsaky.lsp.api.ILanguageServer; import com.itsaky.lsp.models.DiagnosticItem; - +import io.github.rosemoe.sora.lang.Language; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import io.github.rosemoe.sora.lang.Language; - /** * Base class for language implementations in the IDE. * diff --git a/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyLanguage.java b/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyLanguage.java index bae17cc38..accab28e9 100755 --- a/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyLanguage.java +++ b/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyLanguage.java @@ -18,19 +18,11 @@ package com.itsaky.androidide.language.groovy; import android.os.Bundle; - import androidx.annotation.NonNull; - import com.itsaky.androidide.language.IDELanguage; import com.itsaky.androidide.lexers.groovy.GroovyLexer; import com.itsaky.androidide.utils.CharSequenceReader; import com.itsaky.androidide.utils.Logger; - -import org.antlr.v4.runtime.CharStreams; -import org.antlr.v4.runtime.Token; - -import java.io.StringReader; - import io.github.rosemoe.sora.lang.analysis.AnalyzeManager; import io.github.rosemoe.sora.lang.completion.CompletionCancelledException; import io.github.rosemoe.sora.lang.completion.CompletionPublisher; @@ -40,6 +32,9 @@ import io.github.rosemoe.sora.text.CharPosition; import io.github.rosemoe.sora.text.ContentReference; import io.github.rosemoe.sora.text.TextUtils; import io.github.rosemoe.sora.widget.SymbolPairMatch; +import java.io.StringReader; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.Token; public class GroovyLanguage extends IDELanguage { diff --git a/app/src/main/java/com/itsaky/androidide/language/java/JavaAnalyzer.java b/app/src/main/java/com/itsaky/androidide/language/java/JavaAnalyzer.java index c30b9a837..10ce197e1 100644 --- a/app/src/main/java/com/itsaky/androidide/language/java/JavaAnalyzer.java +++ b/app/src/main/java/com/itsaky/androidide/language/java/JavaAnalyzer.java @@ -28,7 +28,6 @@ import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.get; import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.withoutCompletion; import androidx.annotation.NonNull; - import com.itsaky.androidide.language.IAnalyzeManager; import com.itsaky.androidide.lexers.java.JavaLexer; import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE; @@ -38,22 +37,19 @@ import com.itsaky.lsp.models.DiagnosticItem; import com.itsaky.lsp.models.DiagnosticSeverity; import com.itsaky.lsp.models.Position; import com.itsaky.lsp.models.Range; - -import org.antlr.v4.runtime.CharStreams; -import org.antlr.v4.runtime.CodePointCharStream; -import org.antlr.v4.runtime.Token; -import org.jetbrains.annotations.Contract; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Stack; - import io.github.rosemoe.sora.lang.analysis.SimpleAnalyzeManager; import io.github.rosemoe.sora.lang.styling.CodeBlock; import io.github.rosemoe.sora.lang.styling.MappedSpans; import io.github.rosemoe.sora.lang.styling.Span; import io.github.rosemoe.sora.lang.styling.Styles; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CodePointCharStream; +import org.antlr.v4.runtime.Token; +import org.jetbrains.annotations.Contract; /** * Code analyzer for the Java Language diff --git a/app/src/main/java/com/itsaky/androidide/language/java/JavaLanguage.java b/app/src/main/java/com/itsaky/androidide/language/java/JavaLanguage.java index b12aabb21..ba5bd0856 100755 --- a/app/src/main/java/com/itsaky/androidide/language/java/JavaLanguage.java +++ b/app/src/main/java/com/itsaky/androidide/language/java/JavaLanguage.java @@ -18,9 +18,7 @@ package com.itsaky.androidide.language.java; import android.os.Bundle; - import androidx.annotation.NonNull; - import com.itsaky.androidide.app.StudioApp; import com.itsaky.androidide.language.CommonCompletionProvider; import com.itsaky.androidide.language.IDELanguage; @@ -30,16 +28,6 @@ import com.itsaky.androidide.utils.Logger; import com.itsaky.androidide.views.editor.IDEEditor; import com.itsaky.lsp.api.ILanguageServer; import com.itsaky.lsp.models.DiagnosticItem; - -import org.antlr.v4.runtime.CharStreams; -import org.antlr.v4.runtime.Token; - -import java.io.StringReader; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import io.github.rosemoe.sora.lang.analysis.AnalyzeManager; import io.github.rosemoe.sora.lang.completion.CompletionCancelledException; import io.github.rosemoe.sora.lang.completion.CompletionPublisher; @@ -49,6 +37,13 @@ import io.github.rosemoe.sora.text.CharPosition; import io.github.rosemoe.sora.text.ContentReference; import io.github.rosemoe.sora.text.TextUtils; import io.github.rosemoe.sora.widget.SymbolPairMatch; +import java.io.StringReader; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.Token; public class JavaLanguage extends IDELanguage { diff --git a/app/src/main/java/com/itsaky/androidide/language/xml/XMLLanguage.java b/app/src/main/java/com/itsaky/androidide/language/xml/XMLLanguage.java index a6c9a2064..6a86fb8f4 100755 --- a/app/src/main/java/com/itsaky/androidide/language/xml/XMLLanguage.java +++ b/app/src/main/java/com/itsaky/androidide/language/xml/XMLLanguage.java @@ -18,9 +18,7 @@ package com.itsaky.androidide.language.xml; import android.os.Bundle; - import androidx.annotation.NonNull; - import com.itsaky.androidide.app.StudioApp; import com.itsaky.androidide.language.CommonCompletionProvider; import com.itsaky.androidide.language.IDELanguage; @@ -29,14 +27,6 @@ import com.itsaky.androidide.utils.JavaCharacter; import com.itsaky.androidide.utils.Logger; import com.itsaky.androidide.views.editor.IDEEditor; import com.itsaky.lsp.api.ILanguageServer; - -import org.antlr.v4.runtime.CharStreams; -import org.antlr.v4.runtime.Token; - -import java.io.StringReader; -import java.nio.file.Paths; -import java.util.ArrayList; - import io.github.rosemoe.sora.lang.analysis.AnalyzeManager; import io.github.rosemoe.sora.lang.completion.CompletionCancelledException; import io.github.rosemoe.sora.lang.completion.CompletionPublisher; @@ -44,6 +34,11 @@ import io.github.rosemoe.sora.lang.smartEnter.NewlineHandler; import io.github.rosemoe.sora.text.CharPosition; import io.github.rosemoe.sora.text.ContentReference; import io.github.rosemoe.sora.widget.SymbolPairMatch; +import java.io.StringReader; +import java.nio.file.Paths; +import java.util.ArrayList; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.Token; public class XMLLanguage extends IDELanguage { diff --git a/app/src/main/java/com/itsaky/androidide/lsp/IDELanguageClientImpl.java b/app/src/main/java/com/itsaky/androidide/lsp/IDELanguageClientImpl.java index 9cd43b9b0..0e7c431d6 100755 --- a/app/src/main/java/com/itsaky/androidide/lsp/IDELanguageClientImpl.java +++ b/app/src/main/java/com/itsaky/androidide/lsp/IDELanguageClientImpl.java @@ -20,7 +20,6 @@ package com.itsaky.androidide.lsp; import android.view.View; - import com.blankj.utilcode.util.FileIOUtils; import com.blankj.utilcode.util.FileUtils; import com.google.android.material.dialog.MaterialAlertDialogBuilder; @@ -50,7 +49,7 @@ import com.itsaky.lsp.models.Location; import com.itsaky.lsp.models.Range; import com.itsaky.lsp.models.TextEdit; import com.itsaky.toaster.Toaster; - +import io.github.rosemoe.sora.text.Content; import java.io.File; import java.util.ArrayList; import java.util.HashMap; @@ -58,8 +57,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import io.github.rosemoe.sora.text.Content; - /** AndroidIDE specific implementation of the LanguageClient */ public class IDELanguageClientImpl implements ILanguageClient { diff --git a/app/src/main/java/com/itsaky/androidide/models/XMLAttribute.java b/app/src/main/java/com/itsaky/androidide/models/XMLAttribute.java index c2163f4a1..8c87e05b5 100644 --- a/app/src/main/java/com/itsaky/androidide/models/XMLAttribute.java +++ b/app/src/main/java/com/itsaky/androidide/models/XMLAttribute.java @@ -19,18 +19,14 @@ package com.itsaky.androidide.models; import android.os.Parcel; import android.os.Parcelable; - import androidx.annotation.NonNull; - import com.itsaky.androidide.app.StudioApp; import com.itsaky.attrinfo.models.Attr; import com.itsaky.inflater.IAttribute; import com.itsaky.inflater.impl.UiAttribute; import com.itsaky.xml.INamespace; - -import org.jetbrains.annotations.Contract; - import java.util.Objects; +import org.jetbrains.annotations.Contract; /** * A model that is used to show attributes list of a view. @@ -39,140 +35,144 @@ import java.util.Objects; */ public class XMLAttribute extends UiAttribute implements Parcelable { - public static final Creator CREATOR = - new Creator<>() { - @NonNull - @Contract("_ -> new") - @Override - public XMLAttribute createFromParcel(Parcel in) { - return new XMLAttribute(in); - } + public static final Creator CREATOR = + new Creator<>() { + @NonNull + @Contract("_ -> new") + @Override + public XMLAttribute createFromParcel(Parcel in) { + return new XMLAttribute(in); + } - @NonNull - @Contract(value = "_ -> new", pure = true) - @Override - public XMLAttribute[] newArray(int size) { - return new XMLAttribute[size]; - } - }; - public Attr attr; - /** Is this attribute applied to any view? */ - private boolean isApplied; - /** - * Format of this attribute. May be stored here so we don't have to look in {@link - * com.itsaky.attrinfo.AttrInfo AttrInfo} again and again. - */ - private int format = -1; + @NonNull + @Contract(value = "_ -> new", pure = true) + @Override + public XMLAttribute[] newArray(int size) { + return new XMLAttribute[size]; + } + }; + public Attr attr; + /** Is this attribute applied to any view? */ + private boolean isApplied; + /** + * Format of this attribute. May be stored here so we don't have to look in {@link + * com.itsaky.attrinfo.AttrInfo AttrInfo} again and again. + */ + private int format = -1; - public XMLAttribute(IAttribute attribute) { - this(attribute, true); - } - - public XMLAttribute(@NonNull IAttribute attribute, boolean isApplied) { - this(attribute.getNamespace(), attribute.getAttributeName(), attribute.getValue(), isApplied); - } - - public XMLAttribute(INamespace namespace, String name, String value, boolean isApplied) { - super(namespace, name, value); - this.isApplied = isApplied; - } - - private XMLAttribute(@NonNull Parcel in) { - this( - in.readParcelable(INamespace.class.getClassLoader()), - in.readString(), - in.readString(), - in.readByte() != 0); - this.attr = in.readParcelable(Attr.class.getClassLoader()); - this.format = in.readInt(); - } - - /** - * Create a new attribute instance with the android namespace. {@link #isApplied()} will return - * true by default. - * - * @param name The name of the attribute. - * @param value The value of the attribute. - * @return A new instance of this class with the provided data. - */ - @NonNull - @Contract("_, _ -> new") - public static XMLAttribute newAndroidAttribute(String name, String value) { - return new XMLAttribute(INamespace.ANDROID, name, value, true); - } - - /** - * Create a new attribute instance with the android namespace. The {@link #isApplied()} will - * return {@code false} by default. - * - * @param name The name of the attribute. - * @return The new attribute instance. - */ - @NonNull - @Contract("_ -> new") - public static XMLAttribute newAndroidAttribute(String name) { - return new XMLAttribute(INamespace.ANDROID, name, "", false); - } - - public int getFormat() { - return format; - } - - public void setFormat(int format) { - this.format = format; - } - - public int findFormat() { - - if (getFormat() > 0) { - return getFormat(); + public XMLAttribute(IAttribute attribute) { + this(attribute, true); } - final var attrs = StudioApp.getInstance().attrInfo(); - this.attr = attrs.getAttribute(getAttributeName()); - if (this.attr != null) { - this.format = this.attr.format; + public XMLAttribute(@NonNull IAttribute attribute, boolean isApplied) { + this( + attribute.getNamespace(), + attribute.getAttributeName(), + attribute.getValue(), + isApplied); } - return getFormat(); - } + public XMLAttribute(INamespace namespace, String name, String value, boolean isApplied) { + super(namespace, name, value); + this.isApplied = isApplied; + } - public boolean hasFormat(int format) { - return (this.format & format) != 0; - } + private XMLAttribute(@NonNull Parcel in) { + this( + in.readParcelable(INamespace.class.getClassLoader()), + in.readString(), + in.readString(), + in.readByte() != 0); + this.attr = in.readParcelable(Attr.class.getClassLoader()); + this.format = in.readInt(); + } - public Attr getAttr() { - return attr; - } + /** + * Create a new attribute instance with the android namespace. {@link #isApplied()} will return + * true by default. + * + * @param name The name of the attribute. + * @param value The value of the attribute. + * @return A new instance of this class with the provided data. + */ + @NonNull + @Contract("_, _ -> new") + public static XMLAttribute newAndroidAttribute(String name, String value) { + return new XMLAttribute(INamespace.ANDROID, name, value, true); + } - public void setAttr(Attr attr) { - Objects.requireNonNull(attr); - this.attr = attr; - setFormat(attr.format); - } + /** + * Create a new attribute instance with the android namespace. The {@link #isApplied()} will + * return {@code false} by default. + * + * @param name The name of the attribute. + * @return The new attribute instance. + */ + @NonNull + @Contract("_ -> new") + public static XMLAttribute newAndroidAttribute(String name) { + return new XMLAttribute(INamespace.ANDROID, name, "", false); + } - @Override - public void apply(String value) { - super.apply(value); - isApplied = true; - } + public int getFormat() { + return format; + } - public boolean isApplied() { - return isApplied; - } + public void setFormat(int format) { + this.format = format; + } - @Override - public int describeContents() { - return 0; - } + public int findFormat() { - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeParcelable(getNamespace(), flags); - dest.writeString(getAttributeName()); - dest.writeString(getValue()); - dest.writeByte((byte) (isApplied ? 1 : 0)); - dest.writeParcelable(getAttr(), flags); - dest.writeInt(this.format); - } + if (getFormat() > 0) { + return getFormat(); + } + + final var attrs = StudioApp.getInstance().attrInfo(); + this.attr = attrs.getAttribute(getAttributeName()); + if (this.attr != null) { + this.format = this.attr.format; + } + + return getFormat(); + } + + public boolean hasFormat(int format) { + return (this.format & format) != 0; + } + + public Attr getAttr() { + return attr; + } + + public void setAttr(Attr attr) { + Objects.requireNonNull(attr); + this.attr = attr; + setFormat(attr.format); + } + + @Override + public void apply(String value) { + super.apply(value); + isApplied = true; + } + + public boolean isApplied() { + return isApplied; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeParcelable(getNamespace(), flags); + dest.writeString(getAttributeName()); + dest.writeString(getValue()); + dest.writeByte((byte) (isApplied ? 1 : 0)); + dest.writeParcelable(getAttr(), flags); + dest.writeInt(this.format); + } } diff --git a/app/src/main/java/com/itsaky/androidide/views/editor/BaseEditorWindow.java b/app/src/main/java/com/itsaky/androidide/views/editor/BaseEditorWindow.java index ba343bc1d..13cd0160f 100644 --- a/app/src/main/java/com/itsaky/androidide/views/editor/BaseEditorWindow.java +++ b/app/src/main/java/com/itsaky/androidide/views/editor/BaseEditorWindow.java @@ -24,13 +24,10 @@ import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; - import com.blankj.utilcode.util.SizeUtils; import com.itsaky.androidide.R; - import io.github.rosemoe.sora.widget.base.EditorPopupWindow; /** diff --git a/app/src/main/java/com/itsaky/androidide/views/editor/CodeEditorView.java b/app/src/main/java/com/itsaky/androidide/views/editor/CodeEditorView.java index adfd501fb..3e44cac7b 100644 --- a/app/src/main/java/com/itsaky/androidide/views/editor/CodeEditorView.java +++ b/app/src/main/java/com/itsaky/androidide/views/editor/CodeEditorView.java @@ -38,10 +38,8 @@ import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.widget.FrameLayout; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.blankj.utilcode.util.FileIOUtils; import com.blankj.utilcode.util.SizeUtils; import com.itsaky.androidide.adapters.CompletionListAdapter; @@ -59,10 +57,9 @@ import com.itsaky.androidide.utils.Logger; import com.itsaky.androidide.utils.TypefaceUtils; import com.itsaky.inflater.values.ValuesTableFactory; import com.itsaky.lsp.models.Range; - -import org.antlr.v4.runtime.CharStreams; -import org.antlr.v4.runtime.Token; - +import io.github.rosemoe.sora.event.ContentChangeEvent; +import io.github.rosemoe.sora.lang.EmptyLanguage; +import io.github.rosemoe.sora.text.Content; import java.io.File; import java.io.StringReader; import java.nio.file.Files; @@ -70,10 +67,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; - -import io.github.rosemoe.sora.event.ContentChangeEvent; -import io.github.rosemoe.sora.lang.EmptyLanguage; -import io.github.rosemoe.sora.text.Content; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.Token; /** * A view that handles opened code editors. diff --git a/app/src/main/java/com/itsaky/androidide/views/editor/DiagnosticWindow.java b/app/src/main/java/com/itsaky/androidide/views/editor/DiagnosticWindow.java index 4d2d88b74..f40b116d2 100644 --- a/app/src/main/java/com/itsaky/androidide/views/editor/DiagnosticWindow.java +++ b/app/src/main/java/com/itsaky/androidide/views/editor/DiagnosticWindow.java @@ -19,7 +19,6 @@ package com.itsaky.androidide.views.editor; import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.itsaky.lsp.models.DiagnosticItem; /** diff --git a/app/src/main/java/com/itsaky/androidide/views/editor/EditorTextActionMode.java b/app/src/main/java/com/itsaky/androidide/views/editor/EditorTextActionMode.java index 81c774ee0..ebf908537 100644 --- a/app/src/main/java/com/itsaky/androidide/views/editor/EditorTextActionMode.java +++ b/app/src/main/java/com/itsaky/androidide/views/editor/EditorTextActionMode.java @@ -19,24 +19,20 @@ package com.itsaky.androidide.views.editor; import android.annotation.SuppressLint; import android.view.Menu; import android.view.MenuItem; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.view.ActionMode; import androidx.appcompat.view.menu.MenuBuilder; - import com.itsaky.androidide.utils.Logger; - +import io.github.rosemoe.sora.event.ClickEvent; +import io.github.rosemoe.sora.event.SelectionChangeEvent; +import io.github.rosemoe.sora.widget.component.EditorTextActionWindow; import java.util.Objects; import java.util.Set; import java.util.TreeSet; import java.util.stream.Collectors; -import io.github.rosemoe.sora.event.ClickEvent; -import io.github.rosemoe.sora.event.SelectionChangeEvent; -import io.github.rosemoe.sora.widget.component.EditorTextActionWindow; - /** * Text action mode actions for the editor. * diff --git a/app/src/main/java/com/itsaky/androidide/views/editor/EditorTextActionWindow.java b/app/src/main/java/com/itsaky/androidide/views/editor/EditorTextActionWindow.java index 9339deb7a..7d4c62480 100644 --- a/app/src/main/java/com/itsaky/androidide/views/editor/EditorTextActionWindow.java +++ b/app/src/main/java/com/itsaky/androidide/views/editor/EditorTextActionWindow.java @@ -24,24 +24,14 @@ import android.graphics.drawable.GradientDrawable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; - import com.blankj.utilcode.util.SizeUtils; import com.itsaky.androidide.R; import com.itsaky.androidide.adapters.TextActionItemAdapter; import com.itsaky.androidide.databinding.LayoutEditorActionsBinding; import com.itsaky.androidide.utils.Logger; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.TreeSet; -import java.util.stream.Collectors; - import io.github.rosemoe.sora.event.HandleStateChangeEvent; import io.github.rosemoe.sora.event.ScrollEvent; import io.github.rosemoe.sora.event.SelectionChangeEvent; @@ -50,6 +40,12 @@ import io.github.rosemoe.sora.event.Unsubscribe; import io.github.rosemoe.sora.widget.CodeEditor; import io.github.rosemoe.sora.widget.EditorTouchEventHandler; import io.github.rosemoe.sora.widget.base.EditorPopupWindow; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; /** * Presents text actions in a popup widow. diff --git a/app/src/main/java/com/itsaky/androidide/views/editor/IDEEditor.java b/app/src/main/java/com/itsaky/androidide/views/editor/IDEEditor.java index f11ec8ebd..234f6b7fe 100644 --- a/app/src/main/java/com/itsaky/androidide/views/editor/IDEEditor.java +++ b/app/src/main/java/com/itsaky/androidide/views/editor/IDEEditor.java @@ -23,12 +23,10 @@ import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.inputmethod.EditorInfo; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.core.content.ContextCompat; - import com.blankj.utilcode.util.ThreadUtils; import com.itsaky.androidide.R; import com.itsaky.androidide.app.StudioApp; @@ -60,20 +58,17 @@ import com.itsaky.lsp.models.SignatureHelp; import com.itsaky.lsp.models.SignatureHelpParams; import com.itsaky.lsp.util.DiagnosticUtil; import com.itsaky.toaster.Toaster; - -import org.jetbrains.annotations.Contract; - -import java.io.File; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; - import io.github.rosemoe.sora.event.ContentChangeEvent; import io.github.rosemoe.sora.event.SelectionChangeEvent; import io.github.rosemoe.sora.event.Unsubscribe; import io.github.rosemoe.sora.widget.CodeEditor; import io.github.rosemoe.sora.widget.component.EditorAutoCompletion; +import java.io.File; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import org.jetbrains.annotations.Contract; public class IDEEditor extends CodeEditor { diff --git a/common/src/main/java/com/itsaky/androidide/utils/Logger.java b/common/src/main/java/com/itsaky/androidide/utils/Logger.java index f73b1ba7f..f5476da32 100755 --- a/common/src/main/java/com/itsaky/androidide/utils/Logger.java +++ b/common/src/main/java/com/itsaky/androidide/utils/Logger.java @@ -40,10 +40,10 @@ import java.util.Objects; public class Logger { public static final int DEBUG = 0; - public static final int WARNING = 0; - public static final int ERROR = 0; - public static final int INFO = 0; - public static final int VERBOSE = 0; + public static final int WARNING = 1; + public static final int ERROR = 2; + public static final int INFO = 3; + public static final int VERBOSE = 4; private static final String MSG_SEPARATOR = " "; // Separate messages with a space. private static final List logListeners = new ArrayList<>(); private static Logger instance; @@ -77,6 +77,25 @@ public class Logger { return createInstance(tag); } + @NonNull + @Contract(pure = true) + public static String priorityText(int priority) { + switch (priority) { + case DEBUG: + return "DEBUG"; + case INFO: + return "INFO"; + case VERBOSE: + return "VERBOSE"; + case ERROR: + return "ERROR"; + case WARNING: + return "WARNING"; + default: + return ""; + } + } + public Logger warn(Object... messages) { final var msg = generateMessage(messages); Log.w(TAG, msg); diff --git a/emulatorview/src/main/java/com/itsaky/androidide/views/virtualkeys/VirtualKeysInfo.java b/emulatorview/src/main/java/com/itsaky/androidide/views/virtualkeys/VirtualKeysInfo.java index 1d3aa70a0..b6383a7cd 100755 --- a/emulatorview/src/main/java/com/itsaky/androidide/views/virtualkeys/VirtualKeysInfo.java +++ b/emulatorview/src/main/java/com/itsaky/androidide/views/virtualkeys/VirtualKeysInfo.java @@ -33,19 +33,19 @@ import org.json.JSONObject; * *

Examples: {@code # Empty: [] * - *

# Single row: [[ESC, TAB, CTRL, ALT, {key: '-', popup: '|'}, DOWN, UP]] + *

# Single row: [[ESC, TAB, CTRL, ALT, {key: '-', popup: '|'}, DOWN, UP]] * - *

# 2 row: [['ESC','/',{key: '-', popup: '|'},'HOME','UP','END','PGUP'], + *

# 2 row: [['ESC','/',{key: '-', popup: '|'},'HOME','UP','END','PGUP'], * ['TAB','CTRL','ALT','LEFT','DOWN','RIGHT','PGDN']] * - *

# Advance: [[ {key: ESC, popup: {macro: "CTRL f d", display: "tmux exit"}}, - * {key: CTRL, popup: {macro: "CTRL f BKSP", display: "tmux ←"}}, {key: ALT, popup: {macro: "CTRL f - * TAB", display: "tmux →"}}, {key: TAB, popup: {macro: "ALT a", display: A-a}}, {key: LEFT, popup: - * HOME}, {key: DOWN, popup: PGDN}, {key: UP, popup: PGUP}, {key: RIGHT, popup: END}, {macro: "ALT - * j", display: A-j, popup: {macro: "ALT g", display: A-g}}, {key: KEYBOARD, popup: {macro: "CTRL - * d", display: exit}} ]] + *

# Advance: [[ {key: ESC, popup: {macro: "CTRL f d", display: "tmux + * exit"}}, {key: CTRL, popup: {macro: "CTRL f BKSP", display: "tmux ←"}}, {key: ALT, popup: {macro: + * "CTRL f TAB", display: "tmux →"}}, {key: TAB, popup: {macro: "ALT a", display: A-a}}, {key: LEFT, + * popup: HOME}, {key: DOWN, popup: PGDN}, {key: UP, popup: PGUP}, {key: RIGHT, popup: END}, {macro: + * "ALT j", display: A-j, popup: {macro: "ALT g", display: A-g}}, {key: KEYBOARD, popup: {macro: + * "CTRL d", display: exit}} ]] * - *

} + *

} * *

Aliases are also allowed for the keys that you can pass as {@code extraKeyAliasMap}. Check * {@link VirtualKeysConstants#CONTROL_CHARS_ALIASES}. diff --git a/lsp/api/src/main/java/com/itsaky/lsp/api/IDocumentHandler.java b/lsp/api/src/main/java/com/itsaky/lsp/api/IDocumentHandler.java index 79e4071bc..bc65626d0 100644 --- a/lsp/api/src/main/java/com/itsaky/lsp/api/IDocumentHandler.java +++ b/lsp/api/src/main/java/com/itsaky/lsp/api/IDocumentHandler.java @@ -18,12 +18,10 @@ package com.itsaky.lsp.api; import androidx.annotation.NonNull; - import com.itsaky.lsp.models.DocumentChangeEvent; import com.itsaky.lsp.models.DocumentCloseEvent; import com.itsaky.lsp.models.DocumentOpenEvent; import com.itsaky.lsp.models.DocumentSaveEvent; - import java.nio.file.Path; /** diff --git a/lsp/api/src/main/java/com/itsaky/lsp/util/DiagnosticUtil.java b/lsp/api/src/main/java/com/itsaky/lsp/util/DiagnosticUtil.java index 223f67e2d..1b2d60745 100644 --- a/lsp/api/src/main/java/com/itsaky/lsp/util/DiagnosticUtil.java +++ b/lsp/api/src/main/java/com/itsaky/lsp/util/DiagnosticUtil.java @@ -19,7 +19,6 @@ package com.itsaky.lsp.util; import com.itsaky.lsp.models.DiagnosticItem; import com.itsaky.lsp.models.Position; - import java.util.List; /** diff --git a/lsp/api/src/main/java/com/itsaky/lsp/util/NoDocumentHandler.java b/lsp/api/src/main/java/com/itsaky/lsp/util/NoDocumentHandler.java index ba1e854cc..47ede0467 100644 --- a/lsp/api/src/main/java/com/itsaky/lsp/util/NoDocumentHandler.java +++ b/lsp/api/src/main/java/com/itsaky/lsp/util/NoDocumentHandler.java @@ -18,13 +18,11 @@ package com.itsaky.lsp.util; import androidx.annotation.NonNull; - import com.itsaky.lsp.api.IDocumentHandler; import com.itsaky.lsp.models.DocumentChangeEvent; import com.itsaky.lsp.models.DocumentCloseEvent; import com.itsaky.lsp.models.DocumentOpenEvent; import com.itsaky.lsp.models.DocumentSaveEvent; - import java.nio.file.Path; /** diff --git a/lsp/java/build.gradle b/lsp/java/build.gradle index daa4c32ee..a38862ca5 100644 --- a/lsp/java/build.gradle +++ b/lsp/java/build.gradle @@ -15,6 +15,7 @@ * along with AndroidIDE. If not, see . */ apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' android { compileSdkVersion rootProject.ext.compileSdk @@ -37,6 +38,7 @@ android { targetCompatibility JavaVersion.VERSION_1_8 } } + dependencies { implementation common.editor implementation common.utilcode @@ -53,6 +55,18 @@ dependencies { implementation project(path: ':lsp:api') implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar']) + + implementation "androidx.core:core-ktx:1.7.0" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + + testImplementation project(':common') + testImplementation 'com.google.truth:truth:1.1.3' + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.7.3' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + androidTestImplementation 'com.google.truth:truth:1.1.3' + testImplementation common.utilcode } repositories { diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/JavaLanguageServer.java b/lsp/java/src/main/java/com/itsaky/lsp/java/JavaLanguageServer.java index e58dc9b0f..77f15e64c 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/JavaLanguageServer.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/JavaLanguageServer.java @@ -19,7 +19,6 @@ package com.itsaky.lsp.java; import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.itsaky.androidide.utils.Logger; import com.itsaky.lsp.api.ICompletionProvider; import com.itsaky.lsp.api.IDocumentHandler; @@ -57,7 +56,6 @@ import com.itsaky.lsp.models.ServerCapabilities; import com.itsaky.lsp.models.SignatureHelp; import com.itsaky.lsp.models.SignatureHelpParams; import com.itsaky.lsp.util.NoCompletionsProvider; - import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; @@ -108,7 +106,7 @@ public class JavaLanguageServer implements ILanguageServer, IDocumentHandler { if (diagnostics == null) { diagnostics = new ArrayList<>(0); } - + client.publishDiagnostics( new DiagnosticResult(this.selectedFile, diagnostics)); } @@ -149,6 +147,10 @@ public class JavaLanguageServer implements ILanguageServer, IDocumentHandler { initialized = true; } + boolean isInitialized() { + return initialized; + } + @NonNull @Override public ServerCapabilities getCapabilities() { diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/compiler/SynchronizedTask.java b/lsp/java/src/main/java/com/itsaky/lsp/java/compiler/SynchronizedTask.java index 94af695c2..2173fa0ce 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/compiler/SynchronizedTask.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/compiler/SynchronizedTask.java @@ -37,12 +37,9 @@ package com.itsaky.lsp.java.compiler; import static com.itsaky.androidide.utils.Logger.instance; import androidx.annotation.NonNull; - import com.itsaky.androidide.utils.Logger; - import java.util.concurrent.Semaphore; import java.util.function.Consumer; - import kotlin.jvm.functions.Function1; public class SynchronizedTask { diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/CodeActionProvider.java b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/CodeActionProvider.java index c382ddbdf..997fcf523 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/CodeActionProvider.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/CodeActionProvider.java @@ -19,10 +19,8 @@ package com.itsaky.lsp.java.providers; import android.text.TextUtils; import android.util.Pair; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.itsaky.androidide.utils.Logger; import com.itsaky.lsp.java.compiler.CompileTask; import com.itsaky.lsp.java.compiler.CompilerProvider; @@ -59,9 +57,6 @@ import com.sun.source.util.TreePath; import com.sun.source.util.Trees; import com.sun.tools.javac.api.ClientCodeWrapper; import com.sun.tools.javac.util.JCDiagnostic; - -import org.jetbrains.annotations.Contract; - import java.io.IOException; import java.nio.file.Path; import java.time.Duration; @@ -74,7 +69,6 @@ import java.util.Map; import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; - import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; @@ -86,6 +80,7 @@ import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.Diagnostic; import javax.tools.JavaFileObject; +import org.jetbrains.annotations.Contract; public class CodeActionProvider { diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/CompletionProvider.java b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/CompletionProvider.java index 4e4a3beb3..ea10bba0f 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/CompletionProvider.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/CompletionProvider.java @@ -215,7 +215,7 @@ public class CompletionProvider extends AbstractServiceProvider implements IComp String partial = partialIdentifier(contents, (int) cursor); boolean endsWithParen = endsWithParen(contents, (int) cursor); SynchronizedTask synchronizedTask = compiler.compile(Collections.singletonList(source)); - return synchronizedTask.get ( + return synchronizedTask.get( task -> { LOG.info( "...compiled in " diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/DefinitionProvider.java b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/DefinitionProvider.java index ced0a924c..9bfffdc2c 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/DefinitionProvider.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/DefinitionProvider.java @@ -69,7 +69,7 @@ public class DefinitionProvider { public List find() { SynchronizedTask synchronizedTask = compiler.compile(file); - return synchronizedTask.get ( + return synchronizedTask.get( task -> { Element element = NavigationHelper.findElement(task, file, line, column); if (element == null) { @@ -138,7 +138,7 @@ public class DefinitionProvider { List locations = new ArrayList<>(); SynchronizedTask synchronizedTask = compiler.compile(sources); - synchronizedTask.run ( + synchronizedTask.run( task -> { Trees trees = Trees.instance(task.task); Elements elements = task.task.getElements(); @@ -170,7 +170,7 @@ public class DefinitionProvider { private List findRemoteDefinitions(JavaFileObject otherFile) { SynchronizedTask synchronizedTask = compiler.compile(Arrays.asList(new SourceFileObject(file), otherFile)); - return synchronizedTask.get ( + return synchronizedTask.get( task -> { Element element = NavigationHelper.findElement(task, file, line, column); return findDefinitions(task, element); diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/DiagnosticsProvider.java b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/DiagnosticsProvider.java index b75df1a91..d888223fc 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/DiagnosticsProvider.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/DiagnosticsProvider.java @@ -18,7 +18,6 @@ package com.itsaky.lsp.java.providers; import androidx.annotation.NonNull; - import com.itsaky.androidide.app.BaseApplication; import com.itsaky.lsp.java.FileStore; import com.itsaky.lsp.java.R; @@ -38,7 +37,6 @@ import com.sun.source.tree.VariableTree; import com.sun.source.util.SourcePositions; import com.sun.source.util.TreePath; import com.sun.source.util.Trees; - import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -48,7 +46,6 @@ import java.util.Locale; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; - import javax.lang.model.element.Element; import javax.lang.model.element.Name; import javax.tools.Diagnostic; diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/JavaDiagnosticProvider.java b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/JavaDiagnosticProvider.java index 6dc0dacbd..a4044a743 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/JavaDiagnosticProvider.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/JavaDiagnosticProvider.java @@ -46,7 +46,7 @@ public class JavaDiagnosticProvider { @NonNull public List analyze(@NonNull Path file) { final SynchronizedTask synchronizedTask = compiler.compile(file); - return synchronizedTask.get ( + return synchronizedTask.get( task -> { if (!isTaskValid(task)) { // Do not use Collections.emptyList () diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/JavaSelectionProvider.java b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/JavaSelectionProvider.java index a3a53ca34..cccb98224 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/JavaSelectionProvider.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/JavaSelectionProvider.java @@ -42,7 +42,7 @@ public class JavaSelectionProvider { @NonNull public Range expandSelection(@NonNull ExpandSelectionParams params) { return compiler.compile(params.getFile()) - .get ( + .get( task -> { final CompilationUnitTree root = task.root(params.getFile()); final FindBiggerRange rangeFinder = @@ -50,7 +50,7 @@ public class JavaSelectionProvider { final Range range = rangeFinder.scan(root, params.getSelection()); if (range != null) { - LOG.debug("Expanding selection to range", range); + LOG.verbose("Expanding selection to range", range); return range; } diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/ReferenceProvider.java b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/ReferenceProvider.java index 28ce4da9b..a5fcddf01 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/ReferenceProvider.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/ReferenceProvider.java @@ -59,7 +59,7 @@ public class ReferenceProvider { public List find() { final SynchronizedTask synchronizedTask = compiler.compile(file); - return synchronizedTask.get ( + return synchronizedTask.get( task -> { Element element = NavigationHelper.findElement(task, file, line, column); if (element == null) return NOT_SUPPORTED; @@ -89,13 +89,13 @@ public class ReferenceProvider { private List findTypeReferences(String className) { Path[] files = compiler.findTypeReferences(className); if (files.length == 0) return Collections.emptyList(); - return compiler.compile(files).get (this::findReferences); + return compiler.compile(files).get(this::findReferences); } private List findMemberReferences(String className, String memberName) { Path[] files = compiler.findMemberReferences(className, memberName); if (files.length == 0) return Collections.emptyList(); - return compiler.compile(files).get (this::findReferences); + return compiler.compile(files).get(this::findReferences); } private List findReferences(CompileTask task) { diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/SignatureProvider.java b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/SignatureProvider.java index fca362cb7..7b888960d 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/SignatureProvider.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/SignatureProvider.java @@ -91,7 +91,7 @@ public class SignatureProvider { // TODO prune SynchronizedTask synchronizedTask = compiler.compile(file); - return synchronizedTask.get ( + return synchronizedTask.get( task -> { long cursor = task.root().getLineMap().getPosition(line, column); TreePath path = new FindInvocationAt(task.task).scan(task.root(), cursor); diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/AddException.java b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/AddException.java index 1e0367bf2..8dd2e88c0 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/AddException.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/AddException.java @@ -18,7 +18,6 @@ package com.itsaky.lsp.java.rewrite; import androidx.annotation.NonNull; - import com.itsaky.lsp.java.compiler.CompilerProvider; import com.itsaky.lsp.java.compiler.SynchronizedTask; import com.itsaky.lsp.java.utils.FindHelper; @@ -29,11 +28,9 @@ import com.sun.source.tree.LineMap; import com.sun.source.tree.MethodTree; import com.sun.source.util.SourcePositions; import com.sun.source.util.Trees; - import java.nio.file.Path; import java.util.Collections; import java.util.Map; - import javax.lang.model.element.ExecutableElement; public class AddException extends Rewrite { @@ -53,8 +50,7 @@ public class AddException extends Rewrite { } @Override - public Map rewrite ( - @NonNull CompilerProvider compiler) { + public Map rewrite(@NonNull CompilerProvider compiler) { Path file = compiler.findTypeDeclaration(className); if (file == CompilerProvider.NOT_FOUND) { return CANCELLED; diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/AddImport.java b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/AddImport.java index 2e338cad7..1dbef545b 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/AddImport.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/AddImport.java @@ -18,7 +18,6 @@ package com.itsaky.lsp.java.rewrite; import androidx.annotation.NonNull; - import com.itsaky.lsp.java.compiler.CompilerProvider; import com.itsaky.lsp.java.parser.ParseTask; import com.itsaky.lsp.models.Position; @@ -28,7 +27,6 @@ import com.sun.source.tree.ImportTree; import com.sun.source.tree.Tree; import com.sun.source.util.SourcePositions; import com.sun.source.util.Trees; - import java.nio.file.Path; import java.util.Collections; import java.util.List; diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/AddSuppressWarningAnnotation.java b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/AddSuppressWarningAnnotation.java index 1a7969097..c072e8330 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/AddSuppressWarningAnnotation.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/AddSuppressWarningAnnotation.java @@ -18,7 +18,6 @@ package com.itsaky.lsp.java.rewrite; import androidx.annotation.NonNull; - import com.itsaky.lsp.java.compiler.CompilerProvider; import com.itsaky.lsp.java.compiler.SynchronizedTask; import com.itsaky.lsp.java.utils.EditHelper; @@ -30,11 +29,9 @@ import com.sun.source.tree.LineMap; import com.sun.source.tree.MethodTree; import com.sun.source.util.SourcePositions; import com.sun.source.util.Trees; - import java.nio.file.Path; import java.util.Collections; import java.util.Map; - import javax.lang.model.element.ExecutableElement; public class AddSuppressWarningAnnotation extends Rewrite { diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/ConvertFieldToBlock.java b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/ConvertFieldToBlock.java index 9082ec9c8..458474db0 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/ConvertFieldToBlock.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/ConvertFieldToBlock.java @@ -18,7 +18,6 @@ package com.itsaky.lsp.java.rewrite; import androidx.annotation.NonNull; - import com.itsaky.lsp.java.compiler.CompilerProvider; import com.itsaky.lsp.java.parser.ParseTask; import com.itsaky.lsp.models.Position; @@ -29,11 +28,9 @@ import com.sun.source.tree.LineMap; import com.sun.source.tree.VariableTree; import com.sun.source.util.SourcePositions; import com.sun.source.util.Trees; - import java.nio.file.Path; import java.util.Collections; import java.util.Map; - import javax.lang.model.element.Modifier; public class ConvertFieldToBlock extends Rewrite { diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/ConvertVariableToStatement.java b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/ConvertVariableToStatement.java index 826459b54..18f3940a4 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/ConvertVariableToStatement.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/ConvertVariableToStatement.java @@ -18,7 +18,6 @@ package com.itsaky.lsp.java.rewrite; import androidx.annotation.NonNull; - import com.itsaky.lsp.java.compiler.CompilerProvider; import com.itsaky.lsp.java.parser.ParseTask; import com.itsaky.lsp.java.visitors.FindVariableAtCursor; @@ -31,7 +30,6 @@ import com.sun.source.tree.Tree; import com.sun.source.tree.VariableTree; import com.sun.source.util.SourcePositions; import com.sun.source.util.Trees; - import java.nio.file.Path; import java.util.Collections; import java.util.Map; diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/CreateMissingMethod.java b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/CreateMissingMethod.java index 52d59ba5a..ef377d87f 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/CreateMissingMethod.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/CreateMissingMethod.java @@ -18,7 +18,6 @@ package com.itsaky.lsp.java.rewrite; import androidx.annotation.NonNull; - import com.itsaky.androidide.utils.Logger; import com.itsaky.lsp.java.compiler.CompileTask; import com.itsaky.lsp.java.compiler.CompilerProvider; @@ -39,13 +38,11 @@ import com.sun.source.tree.MethodTree; import com.sun.source.tree.Tree; import com.sun.source.util.TreePath; import com.sun.source.util.Trees; - import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collections; import java.util.Map; import java.util.StringJoiner; - import javax.lang.model.element.Modifier; import javax.lang.model.element.Name; import javax.lang.model.type.DeclaredType; diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/GenerateRecordConstructor.java b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/GenerateRecordConstructor.java index 991f1c751..f683c9217 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/GenerateRecordConstructor.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/GenerateRecordConstructor.java @@ -18,7 +18,6 @@ package com.itsaky.lsp.java.rewrite; import androidx.annotation.NonNull; - import com.itsaky.androidide.utils.Logger; import com.itsaky.lsp.java.compiler.CompileTask; import com.itsaky.lsp.java.compiler.CompilerProvider; @@ -33,7 +32,6 @@ import com.sun.source.tree.Tree; import com.sun.source.tree.VariableTree; import com.sun.source.util.SourcePositions; import com.sun.source.util.Trees; - import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; @@ -42,7 +40,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringJoiner; - import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/ImplementAbstractMethods.java b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/ImplementAbstractMethods.java index fd50850e0..42a9f1cda 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/ImplementAbstractMethods.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/ImplementAbstractMethods.java @@ -18,7 +18,6 @@ package com.itsaky.lsp.java.rewrite; import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.itsaky.androidide.utils.Logger; import com.itsaky.lsp.java.compiler.CompileTask; import com.itsaky.lsp.java.compiler.CompilerProvider; @@ -36,12 +35,10 @@ import com.sun.source.tree.ClassTree; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.util.Trees; import com.sun.tools.javac.util.JCDiagnostic; - import java.nio.file.Path; import java.util.Collections; import java.util.Map; import java.util.StringJoiner; - import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/NoRewrite.java b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/NoRewrite.java index 7c643f9e3..565f15cdb 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/NoRewrite.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/NoRewrite.java @@ -20,7 +20,6 @@ package com.itsaky.lsp.java.rewrite; import com.itsaky.lsp.java.compiler.CompilerProvider; import com.itsaky.lsp.models.CodeActionItem; import com.itsaky.lsp.models.TextEdit; - import java.nio.file.Path; import java.util.List; import java.util.Map; diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/OverrideInheritedMethod.java b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/OverrideInheritedMethod.java index 74e984640..57291c219 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/OverrideInheritedMethod.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/OverrideInheritedMethod.java @@ -32,12 +32,10 @@ import com.sun.source.tree.Tree; import com.sun.source.util.SourcePositions; import com.sun.source.util.TreePath; import com.sun.source.util.Trees; - import java.nio.file.Path; import java.util.Collections; import java.util.Map; import java.util.Optional; - import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/RemoveClass.java b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/RemoveClass.java index a56edd494..55b244a29 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/RemoveClass.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/RemoveClass.java @@ -18,14 +18,12 @@ package com.itsaky.lsp.java.rewrite; import androidx.annotation.NonNull; - import com.itsaky.lsp.java.compiler.CompilerProvider; import com.itsaky.lsp.java.parser.ParseTask; import com.itsaky.lsp.java.utils.EditHelper; import com.itsaky.lsp.java.visitors.FindTypeDeclarationAt; import com.itsaky.lsp.models.TextEdit; import com.sun.source.tree.ClassTree; - import java.nio.file.Path; import java.util.Collections; import java.util.Map; diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/RemoveException.java b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/RemoveException.java index de94c89d0..72baced6a 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/RemoveException.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/RemoveException.java @@ -31,20 +31,18 @@ import com.sun.source.util.JavacTask; import com.sun.source.util.SourcePositions; import com.sun.source.util.TreePath; import com.sun.source.util.Trees; - import java.io.IOException; import java.nio.file.Path; import java.util.Collections; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; - import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; public class RemoveException extends Rewrite { - + private static final Pattern THROWS = Pattern.compile("\\s*\\bthrows\\b"); final String className, methodName; final String[] erasedParameterTypes; diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/RemoveMethod.java b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/RemoveMethod.java index 63ad79423..d64204f86 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/RemoveMethod.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/RemoveMethod.java @@ -22,15 +22,13 @@ import com.itsaky.lsp.java.utils.FindHelper; import com.itsaky.lsp.models.TextEdit; import com.sun.source.tree.MethodTree; import com.sun.source.util.Trees; - import java.nio.file.Path; import java.util.Collections; import java.util.Map; - import javax.lang.model.element.ExecutableElement; public class RemoveMethod extends Rewrite { - + final String className, methodName; final String[] erasedParameterTypes; diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/Rewrite.java b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/Rewrite.java index d5affacf6..0571446fc 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/Rewrite.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/Rewrite.java @@ -18,13 +18,11 @@ package com.itsaky.lsp.java.rewrite; import androidx.annotation.NonNull; - import com.itsaky.lsp.java.compiler.CompilerProvider; import com.itsaky.lsp.models.CodeActionItem; import com.itsaky.lsp.models.CodeActionKind; import com.itsaky.lsp.models.DocumentChange; import com.itsaky.lsp.models.TextEdit; - import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/RewriteNotSupported.java b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/RewriteNotSupported.java index 3ef605431..d1a0c9daa 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/RewriteNotSupported.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/RewriteNotSupported.java @@ -19,7 +19,6 @@ package com.itsaky.lsp.java.rewrite; import com.itsaky.lsp.java.compiler.CompilerProvider; import com.itsaky.lsp.models.TextEdit; - import java.nio.file.Path; import java.util.Map; diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/utils/AnalyzeTimer.java b/lsp/java/src/main/java/com/itsaky/lsp/java/utils/AnalyzeTimer.java index 3d68c74e6..7a1f720c4 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/utils/AnalyzeTimer.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/utils/AnalyzeTimer.java @@ -18,9 +18,7 @@ package com.itsaky.lsp.java.utils; import android.os.Handler; - import androidx.annotation.NonNull; - import java.util.Objects; /** diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/utils/EditHelper.java b/lsp/java/src/main/java/com/itsaky/lsp/java/utils/EditHelper.java index 07e85d686..bf4e0e2df 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/utils/EditHelper.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/utils/EditHelper.java @@ -28,10 +28,8 @@ import com.sun.source.tree.Tree; import com.sun.source.util.JavacTask; import com.sun.source.util.SourcePositions; import com.sun.source.util.Trees; - import java.util.List; import java.util.StringJoiner; - import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.Name; diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/visitors/FindTypeDeclarationAt.java b/lsp/java/src/main/java/com/itsaky/lsp/java/visitors/FindTypeDeclarationAt.java index 130b0e4ad..5dd3d3d6b 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/visitors/FindTypeDeclarationAt.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/visitors/FindTypeDeclarationAt.java @@ -34,7 +34,7 @@ public class FindTypeDeclarationAt extends TreePathScanner { public FindTypeDeclarationAt(JavacTask task) { pos = Trees.instance(task).getSourcePositions(); } - + @Override public ClassTree visitCompilationUnit(CompilationUnitTree t, Long find) { root = t; @@ -60,7 +60,7 @@ public class FindTypeDeclarationAt extends TreePathScanner { return b; } - public TreePath getStoredPath () { + public TreePath getStoredPath() { return stored; } } diff --git a/lsp/java/src/test/java/com/itsaky/lsp/java/CursorDependentTest.kt b/lsp/java/src/test/java/com/itsaky/lsp/java/CursorDependentTest.kt new file mode 100644 index 000000000..5760a14fa --- /dev/null +++ b/lsp/java/src/test/java/com/itsaky/lsp/java/CursorDependentTest.kt @@ -0,0 +1,42 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ +package com.itsaky.lsp.java + +import com.google.common.truth.Truth.assertThat + +/** + * A cursor dependent test is a test that needs to have cursor positions specified in the content. + * `@@cursor@@` denotes the position of the cursor in source files. + * + * @author Akash Yadav + */ +open class CursorDependentTest : LoggingTest() { + + protected val CURSOR = "@@cursor@@" + + fun requireCursor(content: String): Int { + val cursor = content.indexOf(CURSOR) + assertThat(cursor).isGreaterThan(-1) + return cursor + } + + fun requireCursor(content: StringBuilder): Int { + val cursor = content.indexOf(CURSOR) + assertThat(cursor).isGreaterThan(-1) + return cursor + } +} \ No newline at end of file diff --git a/lsp/java/src/test/java/com/itsaky/lsp/java/FileProvider.kt b/lsp/java/src/test/java/com/itsaky/lsp/java/FileProvider.kt new file mode 100644 index 000000000..ca933f181 --- /dev/null +++ b/lsp/java/src/test/java/com/itsaky/lsp/java/FileProvider.kt @@ -0,0 +1,85 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ +package com.itsaky.lsp.java + +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import java.nio.charset.Charset +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths + +/** + * Provides paths to source files in 'resources' directory. + * + * @author Akash Yadav + */ +class FileProvider { + @Test + fun testPath() { + val path = sourceFile("SourceFileTest") + assertThat(Files.exists(path)).isTrue() + assertThat(path.fileName.toString()).isEqualTo("SourceFileTest.java") + } + + @Test + fun testNested() { + val path = sourceFile("package/SourceFileTest") + assertThat(Files.exists(path)).isTrue() + assertThat(path.fileName.toString()).isEqualTo("SourceFileTest.java") + } + + companion object { + /** + * Get the path to the 'resources' directory. + * + * @return The the resources directory. + */ + @JvmStatic + fun resources(): Path { + return Paths.get(".", "src", "test", "resources") + } + + /** + * Get the path to the file in resources. + * + * @param name The name of the file. Nested file paths can be separated using '/'. + * @return The path to the file. + */ + @JvmStatic + fun sourceFile(name: String): Path { + return resources().resolve("$name.java") + } + + @JvmStatic + fun contents(file: Path): StringBuilder { + try { + Files.newBufferedReader(file, Charset.defaultCharset()).use { reader -> + val sb = StringBuilder() + var line: String? + while (reader.readLine().also { line = it } != null) { + sb.append(line) + sb.append("\n") + } + return sb + } + } catch (e: Throwable) { + throw RuntimeException(e) + } + } + } +} \ No newline at end of file diff --git a/lsp/java/src/test/java/com/itsaky/lsp/java/LanguageServerProvider.kt b/lsp/java/src/test/java/com/itsaky/lsp/java/LanguageServerProvider.kt new file mode 100644 index 000000000..e0729a36f --- /dev/null +++ b/lsp/java/src/test/java/com/itsaky/lsp/java/LanguageServerProvider.kt @@ -0,0 +1,54 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ +package com.itsaky.lsp.java + +import com.google.common.truth.Truth.assertThat +import com.itsaky.androidide.utils.Environment +import com.itsaky.lsp.java.FileProvider.Companion.resources +import com.itsaky.lsp.models.InitializeParams +import org.jetbrains.annotations.Contract +import java.io.File + +/** + * Provides instance to the java language server to test classes. + * + * @author Akash Yadav + */ +object LanguageServerProvider { + private val server = JavaLanguageServer() + fun server(): JavaLanguageServer { + + if (Environment.COMPILER_MODULE == null) { + val javaHome = System.getProperty("java.home") + assertThat(javaHome).isNotEmpty() + Environment.COMPILER_MODULE = File(javaHome) + } + + return server + } + + fun initIfNecessary() { + if (!server().isInitialized) { + server().initialize(createInitParams()) + } + } + + @Contract(" -> new") + private fun createInitParams(): InitializeParams { + return InitializeParams(setOf(resources())) + } +} \ No newline at end of file diff --git a/lsp/java/src/test/java/com/itsaky/lsp/java/LoggingTest.kt b/lsp/java/src/test/java/com/itsaky/lsp/java/LoggingTest.kt new file mode 100644 index 000000000..60cc7ee4d --- /dev/null +++ b/lsp/java/src/test/java/com/itsaky/lsp/java/LoggingTest.kt @@ -0,0 +1,49 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ +package com.itsaky.lsp.java + +import com.itsaky.androidide.utils.Logger +import com.itsaky.lsp.java.LoggingTest +import com.itsaky.lsp.java.util.ReflectiveLogListener +import java.util.* + +/** + * This class provides an efficient way to log messages. + * + * @author Akash Yadav + */ +open class LoggingTest { + companion object { + private var logListener: ReflectiveLogListener? = null + + init { + logListener = ReflectiveLogListener(LoggingTest::class.java) + Logger.addLogListener(logListener) + } + + @Suppress("unused") + @JvmStatic + fun log(priority: Int, tag: String, message: String) { + System.out.printf(Locale.ROOT, "%-8s%-26s%-20s", Logger.priorityText(priority), tag, message) + println() + } + } + + @Suppress("PropertyName") + @JvmField + protected val LOG = Logger.instance(javaClass.simpleName) +} \ No newline at end of file diff --git a/lsp/java/src/test/java/com/itsaky/lsp/java/providers/JavaSelectionProviderTest.kt b/lsp/java/src/test/java/com/itsaky/lsp/java/providers/JavaSelectionProviderTest.kt new file mode 100644 index 000000000..d3d82ad01 --- /dev/null +++ b/lsp/java/src/test/java/com/itsaky/lsp/java/providers/JavaSelectionProviderTest.kt @@ -0,0 +1,77 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ +package com.itsaky.lsp.java.providers + +import com.google.common.truth.Truth.assertThat +import com.itsaky.lsp.java.CursorDependentTest +import com.itsaky.lsp.java.FileProvider.Companion.contents +import com.itsaky.lsp.java.FileProvider.Companion.sourceFile +import com.itsaky.lsp.java.LanguageServerProvider +import com.itsaky.lsp.models.* +import io.github.rosemoe.sora.text.Content +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import java.nio.file.Path + +/** + * @author Akash Yadav + */ +@RunWith(RobolectricTestRunner::class) +@Config(manifest = Config.NONE) +class JavaSelectionProviderTest : CursorDependentTest() { + private var file: Path? = null + private var contents: StringBuilder? = null + private var cursor: Int = -1 + + @Before + fun init() { + LOG.debug("Initializing JavaSelectionProviderTest") + LanguageServerProvider.initIfNecessary() + } + + @Test + fun testSimpleSelectionExpansion() { + openFile("SimpleSelectionExpansionTest") + cursor = requireCursor(contents!!) + contents!!.delete(cursor, cursor + CURSOR.length) + assertThat(contents!!.indexOf(CURSOR)).isEqualTo(-1) + LanguageServerProvider.server().onContentChange(DocumentChangeEvent(file!!, contents.toString(), 1)) + + val range = findRange() + val expanded = LanguageServerProvider.server() + .expandSelection(ExpandSelectionParams(file!!, range)) + + assertThat(expanded).isEqualTo(Range(Position(4, 27), Position(4, 41))) + } + + private fun findRange(): Range { + val pos = Content(contents!!).indexer.getCharPosition(cursor) + val position = Position(pos.line, pos.column, pos.index) + return Range(position, position) + } + + private fun openFile(fileName: String) { + file = sourceFile(fileName) + contents = contents(file!!) + + LanguageServerProvider.server() + .onFileOpened(DocumentOpenEvent(file!!, contents.toString(), 0)) + } +} \ No newline at end of file diff --git a/lsp/java/src/test/java/com/itsaky/lsp/java/util/ReflectiveLogListener.kt b/lsp/java/src/test/java/com/itsaky/lsp/java/util/ReflectiveLogListener.kt new file mode 100644 index 000000000..913c102c6 --- /dev/null +++ b/lsp/java/src/test/java/com/itsaky/lsp/java/util/ReflectiveLogListener.kt @@ -0,0 +1,30 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ +package com.itsaky.lsp.java.util + +import com.itsaky.androidide.utils.Logger + +/** + * @author Akash Yadav + */ +class ReflectiveLogListener(private val receiver: Class) : Logger.LogListener { + override fun log(priority: Int, tag: String, message: String) { + val method = receiver.getDeclaredMethod("log", Int::class.java, String::class.java, String::class.java) + method.isAccessible = true + method.invoke(null, priority, tag, message) + } +} \ No newline at end of file diff --git a/lsp/java/src/test/resources/SimpleSelectionExpansionTest.java b/lsp/java/src/test/resources/SimpleSelectionExpansionTest.java new file mode 100644 index 000000000..21c2299bc --- /dev/null +++ b/lsp/java/src/test/resources/SimpleSelectionExpansionTest.java @@ -0,0 +1,7 @@ +package com.itsaky.androidide.test; + +public class Main { + public static void main(String[] args) { + System.out.println(@@cursor@@"Hello World!"); + } +} \ No newline at end of file diff --git a/lsp/java/src/test/resources/SourceFileTest.java b/lsp/java/src/test/resources/SourceFileTest.java new file mode 100644 index 000000000..79beff99c --- /dev/null +++ b/lsp/java/src/test/resources/SourceFileTest.java @@ -0,0 +1,7 @@ +package com.itsaky.androidide.test; + +public class Main { + public static void main(String[] args) { + System.out.println("Hello World!"); + } +} diff --git a/lsp/java/src/test/resources/package/SourceFileTest.java b/lsp/java/src/test/resources/package/SourceFileTest.java new file mode 100644 index 000000000..79beff99c --- /dev/null +++ b/lsp/java/src/test/resources/package/SourceFileTest.java @@ -0,0 +1,7 @@ +package com.itsaky.androidide.test; + +public class Main { + public static void main(String[] args) { + System.out.println("Hello World!"); + } +} diff --git a/lsp/xml/src/main/java/com/itsaky/lsp/xml/providers/CompletionProvider.java b/lsp/xml/src/main/java/com/itsaky/lsp/xml/providers/CompletionProvider.java index e8d7bfff4..ee89d5846 100644 --- a/lsp/xml/src/main/java/com/itsaky/lsp/xml/providers/CompletionProvider.java +++ b/lsp/xml/src/main/java/com/itsaky/lsp/xml/providers/CompletionProvider.java @@ -19,7 +19,6 @@ package com.itsaky.lsp.xml.providers; import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.itsaky.androidide.app.BaseApplication; import com.itsaky.androidide.lexers.xml.XMLLexer; import com.itsaky.androidide.utils.CharSequenceReader; @@ -38,19 +37,16 @@ import com.itsaky.lsp.models.InsertTextFormat; import com.itsaky.lsp.xml.R; import com.itsaky.sdk.SDKInfo; import com.itsaky.widgets.models.Widget; - -import org.antlr.v4.runtime.CharStreams; -import org.antlr.v4.runtime.Token; -import org.jetbrains.annotations.Contract; - +import io.github.rosemoe.sora.lang.completion.CompletionCancelledException; import java.nio.file.Path; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.regex.Pattern; - -import io.github.rosemoe.sora.lang.completion.CompletionCancelledException; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.Token; +import org.jetbrains.annotations.Contract; /** * Completion provider for the XML Language diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/ILayoutInflater.java b/xml-inflater/src/main/java/com/itsaky/inflater/ILayoutInflater.java index 4f75a2870..b6ac833d9 100644 --- a/xml-inflater/src/main/java/com/itsaky/inflater/ILayoutInflater.java +++ b/xml-inflater/src/main/java/com/itsaky/inflater/ILayoutInflater.java @@ -19,14 +19,11 @@ package com.itsaky.inflater; import android.content.Context; import android.view.ViewGroup; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.blankj.utilcode.util.FileIOUtils; import com.blankj.utilcode.util.FileUtils; import com.itsaky.inflater.util.Preconditions; - import java.io.File; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -38,148 +35,148 @@ import java.util.regex.Pattern; */ public abstract class ILayoutInflater { - private static final Pattern XML_DECL = Pattern.compile("<\\?xml.*\\?>"); + private static final Pattern XML_DECL = Pattern.compile("<\\?xml.*\\?>"); - /** The file whose content is currently being inflated. */ - private File currentlyInflatingFile; - - /** - * Creates an instance of {@code ILayoutInflater} - * - * @param config The configuration of the LayoutInflater - * @return A new layout inflater instance - */ - @NonNull - public static ILayoutInflater newInstance(LayoutInflaterConfiguration config) { - return new XMLLayoutInflater(config); - } - - /** - * Acatual implementation of the layout inflation - * - * @param layout The layout to inflate - * @return The inflated view - * @throws InflateException when there was an error inflating the layout - */ - @NonNull - protected abstract IView doInflate(String layout, ViewGroup parent) throws InflateException; - - /** - * Reset the {@link ContextProvider} of this inflater - * - * @param provider The new {@link ContextProvider} - */ - public abstract void resetContextProvider(ContextProvider provider); - - /** - * Register the inflate listener to this inflater. - * - *

NOTE: Do not register too much listeners as they may affect the inflation time. - * - * @param listener The listener to register. - */ - public abstract void registerInflateListener(IInflateListener listener); - - /** - * Unregister the inflate listener if it is registered. - * - * @param listener The listener to unregister - */ - public abstract void unregisterListener(IInflateListener listener); - - /** - * Get the resource finder attached to this layout inflater. - * - * @return The attached resource finder. - */ - @NonNull - protected abstract IResourceTable requireResourceTable(); - - /** - * Inflate the layout from the given file path - * - * @param path The file path to inflate layout from - * @return The inflated layout - * @throws InflateException when there was an error inflating the layout - */ - @NonNull - public IView inflatePath(String path, ViewGroup parent) throws InflateException { - Preconditions.assertNotBlank(path, "Layout file path is blank!"); - return inflate(new File(path), parent); - } - - /** - * Inflate the layout from the given file - * - * @param file The file to inflate layout from - * @return The inflated layout - * @throws InflateException when there was an error inflating the layout - */ - @NonNull - public IView inflate(File file, ViewGroup parent) throws InflateException { - - Preconditions.assertNotnull(file, "Cannot inflate null file"); - - // How can we inflate a non existent file? - if (!file.exists()) { - throw new InflateException("File does not exist!"); - } - - if (!file.canRead()) { - throw new InflateException("Cannot read file. Permission denied!"); - } - - // Cannot inflate a non UTF-8 too. This will also be true if the file is empty. - if (!FileUtils.isUtf8(file)) { - throw new InflateException("File is not UTF-8 or is empty!"); - } - - requireResourceTable().setInflatingFile(file); - this.currentlyInflatingFile = file; - return inflate(FileIOUtils.readFile2String(file), parent); - } - - /** - * Inflate the layout from the provided XML layout code - * - * @param layout The XML layout code - * @return The inflated layout - * @throws InflateException when there was an error inflating the layout - */ - @NonNull - protected IView inflate(String layout, ViewGroup parent) throws InflateException { - - Preconditions.assertNotBlank(layout, "Layout is blank!"); - - // Trim the layout code - layout = layout.trim(); - - final Matcher matcher = XML_DECL.matcher(layout); - if (matcher.find()) { - layout = layout.substring(matcher.end()).trim(); - } - - return doInflate(layout, parent); - } - - /** - * Get the currently inflating file. - * - * @return The file. - */ - @Nullable - protected File getFile() { - return currentlyInflatingFile; - } - - /** Provides a reference to {@link Context}. This will be used to create original view. */ - public interface ContextProvider { + /** The file whose content is currently being inflated. */ + private File currentlyInflatingFile; /** - * Provide the context to this inflater. + * Creates an instance of {@code ILayoutInflater} * - * @return The context + * @param config The configuration of the LayoutInflater + * @return A new layout inflater instance */ - Context getContext(); - } + @NonNull + public static ILayoutInflater newInstance(LayoutInflaterConfiguration config) { + return new XMLLayoutInflater(config); + } + + /** + * Acatual implementation of the layout inflation + * + * @param layout The layout to inflate + * @return The inflated view + * @throws InflateException when there was an error inflating the layout + */ + @NonNull + protected abstract IView doInflate(String layout, ViewGroup parent) throws InflateException; + + /** + * Reset the {@link ContextProvider} of this inflater + * + * @param provider The new {@link ContextProvider} + */ + public abstract void resetContextProvider(ContextProvider provider); + + /** + * Register the inflate listener to this inflater. + * + *

NOTE: Do not register too much listeners as they may affect the inflation time. + * + * @param listener The listener to register. + */ + public abstract void registerInflateListener(IInflateListener listener); + + /** + * Unregister the inflate listener if it is registered. + * + * @param listener The listener to unregister + */ + public abstract void unregisterListener(IInflateListener listener); + + /** + * Get the resource finder attached to this layout inflater. + * + * @return The attached resource finder. + */ + @NonNull + protected abstract IResourceTable requireResourceTable(); + + /** + * Inflate the layout from the given file path + * + * @param path The file path to inflate layout from + * @return The inflated layout + * @throws InflateException when there was an error inflating the layout + */ + @NonNull + public IView inflatePath(String path, ViewGroup parent) throws InflateException { + Preconditions.assertNotBlank(path, "Layout file path is blank!"); + return inflate(new File(path), parent); + } + + /** + * Inflate the layout from the given file + * + * @param file The file to inflate layout from + * @return The inflated layout + * @throws InflateException when there was an error inflating the layout + */ + @NonNull + public IView inflate(File file, ViewGroup parent) throws InflateException { + + Preconditions.assertNotnull(file, "Cannot inflate null file"); + + // How can we inflate a non existent file? + if (!file.exists()) { + throw new InflateException("File does not exist!"); + } + + if (!file.canRead()) { + throw new InflateException("Cannot read file. Permission denied!"); + } + + // Cannot inflate a non UTF-8 too. This will also be true if the file is empty. + if (!FileUtils.isUtf8(file)) { + throw new InflateException("File is not UTF-8 or is empty!"); + } + + requireResourceTable().setInflatingFile(file); + this.currentlyInflatingFile = file; + return inflate(FileIOUtils.readFile2String(file), parent); + } + + /** + * Inflate the layout from the provided XML layout code + * + * @param layout The XML layout code + * @return The inflated layout + * @throws InflateException when there was an error inflating the layout + */ + @NonNull + protected IView inflate(String layout, ViewGroup parent) throws InflateException { + + Preconditions.assertNotBlank(layout, "Layout is blank!"); + + // Trim the layout code + layout = layout.trim(); + + final Matcher matcher = XML_DECL.matcher(layout); + if (matcher.find()) { + layout = layout.substring(matcher.end()).trim(); + } + + return doInflate(layout, parent); + } + + /** + * Get the currently inflating file. + * + * @return The file. + */ + @Nullable + protected File getFile() { + return currentlyInflatingFile; + } + + /** Provides a reference to {@link Context}. This will be used to create original view. */ + public interface ContextProvider { + + /** + * Provide the context to this inflater. + * + * @return The context + */ + Context getContext(); + } } diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/XMLLayoutInflater.java b/xml-inflater/src/main/java/com/itsaky/inflater/XMLLayoutInflater.java index a4913ebc3..b4f76e974 100644 --- a/xml-inflater/src/main/java/com/itsaky/inflater/XMLLayoutInflater.java +++ b/xml-inflater/src/main/java/com/itsaky/inflater/XMLLayoutInflater.java @@ -130,7 +130,7 @@ class XMLLayoutInflater extends BaseLayoutInflater { @NonNull @Override - protected IResourceTable requireResourceTable () { + protected IResourceTable requireResourceTable() { return this.resFinder; }