From f5143625efd0c8d25675c698fbcdaf3674e6631c Mon Sep 17 00:00:00 2001 From: Akash Yadav Date: Sun, 27 Aug 2023 18:51:42 +0530 Subject: [PATCH] refactor: migrate ProjectManager to interface based API --- .../actions/build/QuickRunAction.kt | 6 +- .../actions/etc/FindInProjectAction.kt | 4 +- .../androidide/actions/file/SaveFileAction.kt | 4 +- .../actions/filetree/NewFileAction.kt | 4 +- .../actions/sidebar/TerminalSidebarAction.kt | 4 +- .../androidide/activities/MainActivity.kt | 4 +- .../activities/editor/BaseEditorActivity.kt | 9 +- .../editor/EditorHandlerActivity.kt | 4 +- .../editor/ProjectHandlerActivity.kt | 47 +- .../fragments/RunTasksDialogFragment.kt | 4 +- .../fragments/sidebar/FileTreeFragment.kt | 4 +- .../EditorActivityLifecyclerObserver.kt | 10 +- .../services/builder/GradleBuildService.java | 10 +- .../androidide/viewmodel/EditorViewModel.kt | 5 +- .../editor/snippets/FileVariableResolver.kt | 4 +- .../snippets/WorkspaceVariableResolver.kt | 5 +- .../androidide/lsp/api/ILanguageServer.java | 11 +- .../lsp/util/commonCompletionUtils.kt | 18 +- .../lsp/java/JavaLanguageServer.java | 9 +- .../lsp/java/actions/BaseJavaCodeAction.kt | 4 +- .../lsp/java/actions/FieldBasedAction.kt | 6 +- .../actions/diagnostics/AddImportAction.kt | 10 +- .../actions/diagnostics/AddThrowsAction.kt | 5 +- .../diagnostics/CreateMissingMethodAction.kt | 5 +- .../actions/diagnostics/FieldToBlockAction.kt | 5 +- .../actions/diagnostics/RemoveClassAction.kt | 5 +- .../actions/diagnostics/RemoveMethodAction.kt | 5 +- .../diagnostics/RemoveUnusedThrowsAction.kt | 5 +- .../SuppressUncheckedWarningAction.kt | 5 +- .../diagnostics/VariableToStatementAction.kt | 5 +- .../generators/GenerateConstructorAction.kt | 5 +- .../GenerateMissingConstructorAction.kt | 5 +- .../GenerateSettersAndGettersAction.kt | 5 +- .../GenerateToStringMethodAction.kt | 5 +- .../OverrideSuperclassMethodsAction.kt | 11 +- .../lsp/java/edits/AdvancedJavaEditHandler.kt | 5 +- .../androidide/lsp/java/parser/Parser.java | 27 +- .../java/providers/JavaDiagnosticProvider.kt | 5 +- .../lsp/java/JavaCompilerProviderTest.kt | 4 +- .../itsaky/androidide/lsp/java/JavaLSPTest.kt | 4 +- .../com/itsaky/androidide/lsp/api/LSPTest.kt | 9 +- .../itsaky/androidide/lsp/xml/XMLLSPTest.kt | 4 +- subprojects/projects/build.gradle.kts | 68 +- .../androidide/projects/IProjectManager.kt | 145 ++++ ...rojectManager.kt => ProjectManagerImpl.kt} | 647 +++++++++--------- .../androidide/projects/api/AndroidModule.kt | 6 +- .../androidide/projects/api/JavaModule.kt | 4 +- .../androidide/projects/api/ModuleProject.kt | 23 +- .../projects/api/ModuleProjectTest.kt | 61 +- .../impl/sync/ModuleProjectModelBuilder.kt | 4 +- .../androidide/tooling/impl/sync/types.kt | 3 - .../inflater/utils/inflaterUtils.kt | 6 +- .../androidide/inflater/utils/module.kt | 4 +- .../androidide/inflater/LayoutInflaterTest.kt | 4 +- .../androidide/inflater/XmlInflaterTest.kt | 9 +- 55 files changed, 734 insertions(+), 565 deletions(-) create mode 100644 subprojects/projects/src/main/java/com/itsaky/androidide/projects/IProjectManager.kt rename subprojects/projects/src/main/java/com/itsaky/androidide/projects/{ProjectManager.kt => ProjectManagerImpl.kt} (75%) diff --git a/app/src/main/java/com/itsaky/androidide/actions/build/QuickRunAction.kt b/app/src/main/java/com/itsaky/androidide/actions/build/QuickRunAction.kt index 015a489be..856d25f7a 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/build/QuickRunAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/build/QuickRunAction.kt @@ -22,7 +22,7 @@ import androidx.core.content.ContextCompat import com.itsaky.androidide.actions.ActionData import com.itsaky.androidide.actions.BaseBuildAction import com.itsaky.androidide.models.ApkMetadata -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.ProjectManagerImpl import com.itsaky.androidide.resources.R import com.itsaky.androidide.utils.ApkInstaller import com.itsaky.androidide.utils.InstallationResultHandler @@ -53,8 +53,8 @@ class QuickRunAction(context: Context) : BaseBuildAction() { log.debug("Installing APK(s) for variant: debug") // TODO Handle multiple application modules - val projectManager = ProjectManager - val app = projectManager.getApplicationModule() + val projectManager = ProjectManagerImpl.getInstance() + val app = projectManager.app if (app == null) { log.warn("No application module found. Cannot install APKs") return@execTasks diff --git a/app/src/main/java/com/itsaky/androidide/actions/etc/FindInProjectAction.kt b/app/src/main/java/com/itsaky/androidide/actions/etc/FindInProjectAction.kt index e3bbb3c43..49082ad5b 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/etc/FindInProjectAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/etc/FindInProjectAction.kt @@ -23,7 +23,7 @@ import com.itsaky.androidide.resources.R import com.itsaky.androidide.actions.ActionData import com.itsaky.androidide.actions.EditorActivityAction import com.itsaky.androidide.actions.markInvisible -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager /** @author Akash Yadav */ class FindInProjectAction() : EditorActivityAction() { @@ -44,7 +44,7 @@ class FindInProjectAction() : EditorActivityAction() { return } - val project = ProjectManager.rootProject + val project = IProjectManager.getInstance().rootProject if (project == null || project.subProjects.isEmpty()) { markInvisible() return diff --git a/app/src/main/java/com/itsaky/androidide/actions/file/SaveFileAction.kt b/app/src/main/java/com/itsaky/androidide/actions/file/SaveFileAction.kt index eb3aeffe0..7153a3dc5 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/file/SaveFileAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/file/SaveFileAction.kt @@ -22,7 +22,7 @@ import androidx.core.content.ContextCompat import com.itsaky.androidide.actions.ActionData import com.itsaky.androidide.actions.EditorRelatedAction import com.itsaky.androidide.models.SaveResult -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.ProjectManagerImpl import com.itsaky.androidide.resources.R import com.itsaky.androidide.utils.ILogger import com.itsaky.androidide.utils.flashError @@ -77,7 +77,7 @@ class SaveFileAction(context: Context) : EditorRelatedAction() { val saveResult = result.result if (saveResult.xmlSaved) { - ProjectManager.generateSources() + ProjectManagerImpl.getInstance().generateSources() } if (saveResult.gradleSaved) { diff --git a/app/src/main/java/com/itsaky/androidide/actions/filetree/NewFileAction.kt b/app/src/main/java/com/itsaky/androidide/actions/filetree/NewFileAction.kt index fd2a2dc90..03747573b 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/filetree/NewFileAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/filetree/NewFileAction.kt @@ -27,7 +27,7 @@ import com.itsaky.androidide.adapters.viewholders.FileTreeViewHolder import com.itsaky.androidide.databinding.LayoutCreateFileJavaBinding import com.itsaky.androidide.eventbus.events.file.FileCreationEvent import com.itsaky.androidide.preferences.databinding.LayoutDialogTextInputBinding -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.resources.R import com.itsaky.androidide.utils.DialogUtils import com.itsaky.androidide.utils.Environment @@ -82,7 +82,7 @@ class NewFileAction(context: Context) : return } - val projectDir = ProjectManager.getProjectDirPath() + val projectDir = IProjectManager.getInstance().projectDirPath Objects.requireNonNull(projectDir) val isJava = Pattern.compile(Pattern.quote(projectDir) + JAVA_PATH_REGEX).matcher(file.absolutePath).find() diff --git a/app/src/main/java/com/itsaky/androidide/actions/sidebar/TerminalSidebarAction.kt b/app/src/main/java/com/itsaky/androidide/actions/sidebar/TerminalSidebarAction.kt index e228fd347..9a0512774 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/sidebar/TerminalSidebarAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/sidebar/TerminalSidebarAction.kt @@ -25,7 +25,7 @@ import com.itsaky.androidide.R import com.itsaky.androidide.actions.ActionData import com.itsaky.androidide.actions.requireContext import com.itsaky.androidide.activities.TerminalActivity -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import java.util.Objects import kotlin.reflect.KClass @@ -49,7 +49,7 @@ class TerminalSidebarAction(context: Context, override val order: Int) : Abstrac val intent = Intent(context, TerminalActivity::class.java) intent.putExtra( TerminalActivity.KEY_WORKING_DIRECTORY, - Objects.requireNonNull(ProjectManager.getProjectDirPath()) + Objects.requireNonNull(IProjectManager.getInstance().projectDirPath) ) context.startActivity(intent) return true diff --git a/app/src/main/java/com/itsaky/androidide/activities/MainActivity.kt b/app/src/main/java/com/itsaky/androidide/activities/MainActivity.kt index 8cf570a59..7298c9885 100755 --- a/app/src/main/java/com/itsaky/androidide/activities/MainActivity.kt +++ b/app/src/main/java/com/itsaky/androidide/activities/MainActivity.kt @@ -42,7 +42,7 @@ import com.itsaky.androidide.preferences.internal.confirmProjectOpen import com.itsaky.androidide.preferences.internal.lastOpenedProject import com.itsaky.androidide.preferences.internal.statConsentDialogShown import com.itsaky.androidide.preferences.internal.statOptIn -import com.itsaky.androidide.projects.ProjectManager.projectPath +import com.itsaky.androidide.projects.ProjectManagerImpl import com.itsaky.androidide.resources.R.string import com.itsaky.androidide.templates.ITemplateProvider import com.itsaky.androidide.utils.DialogUtils @@ -284,7 +284,7 @@ class MainActivity : LimitlessIDEActivity() { } internal fun openProject(root: File) { - projectPath = root.absolutePath + ProjectManagerImpl.getInstance().projectPath = root.absolutePath startActivity(Intent(this, EditorActivityKt::class.java)) } diff --git a/app/src/main/java/com/itsaky/androidide/activities/editor/BaseEditorActivity.kt b/app/src/main/java/com/itsaky/androidide/activities/editor/BaseEditorActivity.kt index bdd74c94c..8c15289d9 100644 --- a/app/src/main/java/com/itsaky/androidide/activities/editor/BaseEditorActivity.kt +++ b/app/src/main/java/com/itsaky/androidide/activities/editor/BaseEditorActivity.kt @@ -65,8 +65,8 @@ import com.itsaky.androidide.models.DiagnosticGroup import com.itsaky.androidide.models.OpenedFile import com.itsaky.androidide.models.Range import com.itsaky.androidide.models.SearchResult -import com.itsaky.androidide.projects.ProjectManager.getProjectDirPath -import com.itsaky.androidide.projects.ProjectManager.projectPath +import com.itsaky.androidide.projects.IProjectManager +import com.itsaky.androidide.projects.ProjectManagerImpl import com.itsaky.androidide.ui.ContentTranslatingDrawerLayout import com.itsaky.androidide.ui.editor.CodeEditorView import com.itsaky.androidide.uidesigner.UIDesignerActivity @@ -186,7 +186,8 @@ abstract class BaseEditorActivity : registerLanguageServers() if (savedInstanceState != null && savedInstanceState.containsKey(KEY_PROJECT_PATH)) { - projectPath = savedInstanceState.getString(KEY_PROJECT_PATH)!! + ProjectManagerImpl.getInstance().projectPath = savedInstanceState.getString( + KEY_PROJECT_PATH)!! } onBackPressedDispatcher.addCallback(this, onBackPressedCallback) @@ -239,7 +240,7 @@ abstract class BaseEditorActivity : } override fun onSaveInstanceState(outState: Bundle) { - outState.putString(KEY_PROJECT_PATH, getProjectDirPath()) + outState.putString(KEY_PROJECT_PATH, IProjectManager.getInstance().projectDirPath) super.onSaveInstanceState(outState) } diff --git a/app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt b/app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt index 2d6d13085..62549875c 100644 --- a/app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt +++ b/app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt @@ -47,7 +47,7 @@ import com.itsaky.androidide.models.OpenedFile import com.itsaky.androidide.models.OpenedFilesCache import com.itsaky.androidide.models.Range import com.itsaky.androidide.models.SaveResult -import com.itsaky.androidide.projects.ProjectManager.generateSources +import com.itsaky.androidide.projects.ProjectManagerImpl import com.itsaky.androidide.tasks.executeAsync import com.itsaky.androidide.tasks.executeAsyncProvideError import com.itsaky.androidide.ui.editor.CodeEditorView @@ -330,7 +330,7 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler { } if (processResources) { - generateSources() + ProjectManagerImpl.getInstance().generateSources() } return result.gradleSaved diff --git a/app/src/main/java/com/itsaky/androidide/activities/editor/ProjectHandlerActivity.kt b/app/src/main/java/com/itsaky/androidide/activities/editor/ProjectHandlerActivity.kt index 924221b5b..e5abb3ba9 100644 --- a/app/src/main/java/com/itsaky/androidide/activities/editor/ProjectHandlerActivity.kt +++ b/app/src/main/java/com/itsaky/androidide/activities/editor/ProjectHandlerActivity.kt @@ -41,14 +41,7 @@ import com.itsaky.androidide.lsp.IDELanguageClientImpl import com.itsaky.androidide.preferences.internal.NO_OPENED_PROJECT import com.itsaky.androidide.preferences.internal.gradleInstallationDir import com.itsaky.androidide.preferences.internal.lastOpenedProject -import com.itsaky.androidide.projects.ProjectManager -import com.itsaky.androidide.projects.ProjectManager.cachedInitResult -import com.itsaky.androidide.projects.ProjectManager.getProjectDirPath -import com.itsaky.androidide.projects.ProjectManager.notifyProjectUpdate -import com.itsaky.androidide.projects.ProjectManager.projectInitialized -import com.itsaky.androidide.projects.ProjectManager.projectPath -import com.itsaky.androidide.projects.ProjectManager.rootProject -import com.itsaky.androidide.projects.ProjectManager.setupProject +import com.itsaky.androidide.projects.ProjectManagerImpl import com.itsaky.androidide.projects.api.AndroidModule import com.itsaky.androidide.projects.api.GradleProject import com.itsaky.androidide.projects.builder.BuildService @@ -168,7 +161,7 @@ abstract class ProjectHandlerActivity : BaseEditorActivity() { // reset these values here // sometimes, when the IDE closed and reopened instantly, these values prevent initialization // of the project - ProjectManager.destroy() + ProjectManagerImpl.getInstance().destroy() editorViewModel.isInitializing = false editorViewModel.isBuildInProgress = false @@ -353,13 +346,14 @@ abstract class ProjectHandlerActivity : BaseEditorActivity() { * @param buildVariants A map of project paths to the selected build variants. */ fun initializeProject(buildVariants: Map) { - val projectDir = File(projectPath) + val manager = ProjectManagerImpl.getInstance() + val projectDir = File(manager.projectPath) if (!projectDir.exists()) { log.error("GradleProject directory does not exist. Cannot initialize project") return } - val initialized = projectInitialized && cachedInitResult != null + val initialized = manager.projectInitialized && manager.cachedInitResult != null log.debug("Is project initialized: $initialized") // When returning after a configuration change between the initialization process, // we do not want to start another project initialization @@ -392,7 +386,7 @@ abstract class ProjectHandlerActivity : BaseEditorActivity() { log.debug("Using cached initialize result as the project is already initialized") CompletableFuture.supplyAsync { log.warn("GradleProject has already been initialized. Skipping initialization process.") - cachedInitResult + manager.cachedInitResult } } @@ -458,14 +452,15 @@ abstract class ProjectHandlerActivity : BaseEditorActivity() { } protected open fun onProjectInitialized(result: InitializeResult) { - if (isFromSavedInstance && projectInitialized && result == cachedInitResult) { + val manager = ProjectManagerImpl.getInstance() + if (isFromSavedInstance && manager.projectInitialized && result == manager.cachedInitResult) { log.debug("Not setting up project as this a configuration change") return } - cachedInitResult = result - setupProject() - notifyProjectUpdate() + manager.cachedInitResult = result + manager.setupProject() + manager.notifyProjectUpdate() updateBuildVariants() ThreadUtils.runOnUiThread { postProjectInit(true) } @@ -477,18 +472,19 @@ abstract class ProjectHandlerActivity : BaseEditorActivity() { } protected open fun postProjectInit(isSuccessful: Boolean) { + val manager = ProjectManagerImpl.getInstance() if (!isSuccessful) { setStatus(getString(string.msg_project_initialization_failed)) flashError(string.msg_project_initialization_failed) editorViewModel.isInitializing = false - projectInitialized = false + manager.projectInitialized = false return } initialSetup() setStatus(getString(string.msg_project_initialized)) editorViewModel.isInitializing = false - projectInitialized = true + manager.projectInitialized = true if (mFindInProjectDialog?.isShowing == true) { mFindInProjectDialog!!.dismiss() @@ -498,8 +494,9 @@ abstract class ProjectHandlerActivity : BaseEditorActivity() { } private fun updateBuildVariants() { + val manager = ProjectManagerImpl.getInstance() executeAsyncProvideError({ - rootProject?.run { + manager.rootProject?.run { val buildVariants = mutableMapOf() subProjects.forEach { subproject -> if (subproject is AndroidModule) { @@ -533,7 +530,8 @@ abstract class ProjectHandlerActivity : BaseEditorActivity() { } protected open fun createFindInProjectDialog(): AlertDialog? { - if (rootProject == null) { + val manager = ProjectManagerImpl.getInstance() + if (manager.rootProject == null) { log.warn("No root project model found. Is the project initialized?") flashError(getString(string.msg_project_not_initialized)) return null @@ -541,7 +539,7 @@ abstract class ProjectHandlerActivity : BaseEditorActivity() { val moduleDirs = try { - rootProject!!.subProjects.stream().map(GradleProject::projectDir) + manager.rootProject!!.subProjects.stream().map(GradleProject::projectDir) .collect(Collectors.toList()) } catch (e: Throwable) { flashError(getString(string.msg_no_modules)) @@ -633,9 +631,10 @@ abstract class ProjectHandlerActivity : BaseEditorActivity() { } private fun initialSetup() { - lastOpenedProject = getProjectDirPath() + val manager = ProjectManagerImpl.getInstance() + lastOpenedProject = manager.projectDirPath try { - val project = rootProject + val project = manager.rootProject if (project == null) { log.warn("GradleProject not initialized. Skipping initial setup...") return @@ -643,7 +642,7 @@ abstract class ProjectHandlerActivity : BaseEditorActivity() { var projectName = project.rootProject.name if (projectName.isEmpty()) { - projectName = File(getProjectDirPath()).name + projectName = manager.projectDir.name } supportActionBar!!.subtitle = projectName diff --git a/app/src/main/java/com/itsaky/androidide/fragments/RunTasksDialogFragment.kt b/app/src/main/java/com/itsaky/androidide/fragments/RunTasksDialogFragment.kt index 75eb0ca07..bba933f18 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/RunTasksDialogFragment.kt +++ b/app/src/main/java/com/itsaky/androidide/fragments/RunTasksDialogFragment.kt @@ -45,7 +45,7 @@ import com.itsaky.androidide.databinding.LayoutRunTaskBinding import com.itsaky.androidide.databinding.LayoutRunTaskDialogBinding import com.itsaky.androidide.lookup.Lookup import com.itsaky.androidide.models.Checkable -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.projects.builder.BuildService import com.itsaky.androidide.resources.R import com.itsaky.androidide.tasks.executeAsync @@ -183,7 +183,7 @@ class RunTasksDialogFragment : BottomSheetDialogFragment() { viewModel.displayedChild = CHILD_LOADING executeAsync({ - val project = ProjectManager.rootProject + val project = IProjectManager.getInstance().rootProject val rootProject = project?.rootProject ?: return@executeAsync emptyList>() val tasks = rootProject.tasks.map { Checkable(false, it) }.toMutableList() diff --git a/app/src/main/java/com/itsaky/androidide/fragments/sidebar/FileTreeFragment.kt b/app/src/main/java/com/itsaky/androidide/fragments/sidebar/FileTreeFragment.kt index 90f2b4161..bf6893cf9 100755 --- a/app/src/main/java/com/itsaky/androidide/fragments/sidebar/FileTreeFragment.kt +++ b/app/src/main/java/com/itsaky/androidide/fragments/sidebar/FileTreeFragment.kt @@ -35,7 +35,7 @@ import com.itsaky.androidide.eventbus.events.filetree.FileClickEvent import com.itsaky.androidide.eventbus.events.filetree.FileLongClickEvent import com.itsaky.androidide.events.ExpandTreeNodeRequestEvent import com.itsaky.androidide.events.ListProjectFilesRequestEvent -import com.itsaky.androidide.projects.ProjectManager.getProjectDirPath +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.resources.R.drawable import com.itsaky.androidide.tasks.TaskExecutor.executeAsync import com.itsaky.androidide.tasks.callables.FileTreeCallable @@ -211,7 +211,7 @@ class FileTreeFragment : BottomSheetDialogFragment(), TreeNodeClickListener, // Fragment has been destroyed return } - val projectDirPath = getProjectDirPath() + val projectDirPath = IProjectManager.getInstance().projectDirPath val projectDir = File(projectDirPath) val rootNode = TreeNode(File("")) rootNode.viewHolder = FileTreeViewHolder(requireContext()) diff --git a/app/src/main/java/com/itsaky/androidide/handlers/EditorActivityLifecyclerObserver.kt b/app/src/main/java/com/itsaky/androidide/handlers/EditorActivityLifecyclerObserver.kt index f51aab569..72b5db455 100644 --- a/app/src/main/java/com/itsaky/androidide/handlers/EditorActivityLifecyclerObserver.kt +++ b/app/src/main/java/com/itsaky/androidide/handlers/EditorActivityLifecyclerObserver.kt @@ -20,8 +20,6 @@ package com.itsaky.androidide.handlers import android.content.Context import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner -import androidx.navigation.fragment.NavHostFragment -import com.itsaky.androidide.activities.editor.BaseEditorActivity import com.itsaky.androidide.eventbus.events.Event import com.itsaky.androidide.eventbus.events.EventReceiver import com.itsaky.androidide.eventbus.events.editor.OnCreateEvent @@ -30,13 +28,13 @@ import com.itsaky.androidide.eventbus.events.editor.OnPauseEvent import com.itsaky.androidide.eventbus.events.editor.OnResumeEvent import com.itsaky.androidide.eventbus.events.editor.OnStartEvent import com.itsaky.androidide.eventbus.events.editor.OnStopEvent -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.ProjectManagerImpl import com.itsaky.androidide.projects.util.BootClasspathProvider import com.itsaky.androidide.utils.EditorActivityActions import com.itsaky.androidide.utils.EditorSidebarActions import com.itsaky.androidide.utils.Environment -import java.util.concurrent.CompletableFuture import org.greenrobot.eventbus.EventBus +import java.util.concurrent.CompletableFuture /** * Observes lifecycle events if [com.itsaky.androidide.EditorActivityKt]. @@ -55,7 +53,7 @@ class EditorActivityLifecyclerObserver : DefaultLifecycleObserver { override fun onStart(owner: LifecycleOwner) { CompletableFuture.runAsync(this::initBootclasspathProvider) - register(fileActionsHandler, ProjectManager) + register(fileActionsHandler, ProjectManagerImpl.getInstance()) dispatchEvent(OnStartEvent()) } @@ -71,7 +69,7 @@ class EditorActivityLifecyclerObserver : DefaultLifecycleObserver { } override fun onStop(owner: LifecycleOwner) { - unregister(fileActionsHandler, ProjectManager) + unregister(fileActionsHandler, ProjectManagerImpl.getInstance()) dispatchEvent(OnStopEvent()) } diff --git a/app/src/main/java/com/itsaky/androidide/services/builder/GradleBuildService.java b/app/src/main/java/com/itsaky/androidide/services/builder/GradleBuildService.java index 2dd6ce701..2caa6d8f2 100644 --- a/app/src/main/java/com/itsaky/androidide/services/builder/GradleBuildService.java +++ b/app/src/main/java/com/itsaky/androidide/services/builder/GradleBuildService.java @@ -47,7 +47,8 @@ import com.itsaky.androidide.BuildConfig; import com.itsaky.androidide.app.BaseApplication; import com.itsaky.androidide.lookup.Lookup; import com.itsaky.androidide.models.LogLine; -import com.itsaky.androidide.projects.ProjectManager; +import com.itsaky.androidide.projects.IProjectManager; +import com.itsaky.androidide.projects.ProjectManagerImpl; import com.itsaky.androidide.projects.builder.BuildService; import com.itsaky.androidide.resources.R; import com.itsaky.androidide.services.ToolingServerNotStartedException; @@ -220,7 +221,6 @@ public class GradleBuildService extends Service implements BuildService, IToolin this.server = server; Lookup.getDefault().update(BuildService.KEY_PROJECT_PROXY, projectProxy); this.isToolingServerStarted = true; - ProjectManager.INSTANCE.setupProject(projectProxy, false); } @Override @@ -328,12 +328,12 @@ public class GradleBuildService extends Service implements BuildService, IToolin } public boolean isGradleWrapperAvailable() { - final var projectDir = ProjectManager.INSTANCE.getProjectDirPath(); + final var projectDir = IProjectManager.getInstance().getProjectDirPath(); if (TextUtils.isEmpty(projectDir)) { return false; } - final var projectRoot = Objects.requireNonNull(ProjectManager.INSTANCE.getProjectDir()); + final var projectRoot = Objects.requireNonNull(ProjectManagerImpl.getInstance().getProjectDir()); if (!projectRoot.exists()) { return false; } @@ -372,7 +372,7 @@ public class GradleBuildService extends Service implements BuildService, IToolin } try { - final var projectDir = ProjectManager.INSTANCE.getProjectDir(); + final var projectDir = ProjectManagerImpl.getInstance().getProjectDir(); final var files = ZipUtils.unzipFile(extracted, projectDir); if (files != null && !files.isEmpty()) { return new GradleWrapperCheckResult(true); diff --git a/app/src/main/java/com/itsaky/androidide/viewmodel/EditorViewModel.kt b/app/src/main/java/com/itsaky/androidide/viewmodel/EditorViewModel.kt index 9b8e4bd0f..570cad919 100644 --- a/app/src/main/java/com/itsaky/androidide/viewmodel/EditorViewModel.kt +++ b/app/src/main/java/com/itsaky/androidide/viewmodel/EditorViewModel.kt @@ -25,9 +25,8 @@ import androidx.lifecycle.ViewModel import com.blankj.utilcode.util.FileUtils import com.google.gson.GsonBuilder import com.itsaky.androidide.models.OpenedFilesCache -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.tasks.executeAsync -import com.itsaky.androidide.tooling.api.models.BuildVariantInfo import com.itsaky.androidide.utils.Environment import java.io.File @@ -234,7 +233,7 @@ class EditorViewModel : ViewModel() { } private fun getOpenedFilesCache(forWrite: Boolean = false): File { - var file = Environment.getProjectCacheDir(ProjectManager.projectPath) + var file = Environment.getProjectCacheDir(IProjectManager.getInstance().projectDir) file = File(file, "editor/openedFiles.json") if (file.exists() && forWrite) { FileUtils.rename(file, "${file.name}.bak") diff --git a/editor/src/main/java/com/itsaky/androidide/editor/snippets/FileVariableResolver.kt b/editor/src/main/java/com/itsaky/androidide/editor/snippets/FileVariableResolver.kt index b70dee2f8..ec68a3405 100644 --- a/editor/src/main/java/com/itsaky/androidide/editor/snippets/FileVariableResolver.kt +++ b/editor/src/main/java/com/itsaky/androidide/editor/snippets/FileVariableResolver.kt @@ -18,7 +18,7 @@ package com.itsaky.androidide.editor.snippets import com.itsaky.androidide.editor.ui.IDEEditor -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import io.github.rosemoe.sora.widget.snippet.variable.FileBasedSnippetVariableResolver /** @@ -46,7 +46,7 @@ class FileVariableResolver(editor: IDEEditor) : FileBasedSnippetVariableResolver TM_FILENAME_BASE -> file.nameWithoutExtension TM_DIRECTORY -> file.parentFile?.absolutePath ?: "" TM_FILEPATH -> file.absolutePath - RELATIVE_FILEPATH -> file.relativeTo(ProjectManager.getProjectDir()).absolutePath + RELATIVE_FILEPATH -> file.relativeTo(IProjectManager.getInstance().projectDir).absolutePath else -> "" } } diff --git a/editor/src/main/java/com/itsaky/androidide/editor/snippets/WorkspaceVariableResolver.kt b/editor/src/main/java/com/itsaky/androidide/editor/snippets/WorkspaceVariableResolver.kt index 9b18d37d4..98a90226b 100644 --- a/editor/src/main/java/com/itsaky/androidide/editor/snippets/WorkspaceVariableResolver.kt +++ b/editor/src/main/java/com/itsaky/androidide/editor/snippets/WorkspaceVariableResolver.kt @@ -17,7 +17,7 @@ package com.itsaky.androidide.editor.snippets -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import io.github.rosemoe.sora.widget.snippet.variable.WorkspaceBasedSnippetVariableResolver /** @@ -29,12 +29,13 @@ class WorkspaceVariableResolver : WorkspaceBasedSnippetVariableResolver(), AbstractSnippetVariableResolver { companion object { + private const val WORKSPACE_NAME = "WORKSPACE_NAME" private const val WORKSPACE_FOLDER = "WORKSPACE_FOLDER" } override fun resolve(name: String): String { - val directory = ProjectManager.getProjectDir() + val directory = IProjectManager.getInstance().projectDir return when (name) { WORKSPACE_NAME -> directory.name WORKSPACE_FOLDER -> directory.absolutePath diff --git a/lsp/api/src/main/java/com/itsaky/androidide/lsp/api/ILanguageServer.java b/lsp/api/src/main/java/com/itsaky/androidide/lsp/api/ILanguageServer.java index 337328138..43918c0cb 100644 --- a/lsp/api/src/main/java/com/itsaky/androidide/lsp/api/ILanguageServer.java +++ b/lsp/api/src/main/java/com/itsaky/androidide/lsp/api/ILanguageServer.java @@ -35,7 +35,6 @@ package com.itsaky.androidide.lsp.api; import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.itsaky.androidide.lsp.models.CodeFormatResult; import com.itsaky.androidide.lsp.models.CompletionParams; import com.itsaky.androidide.lsp.models.CompletionResult; @@ -51,9 +50,6 @@ import com.itsaky.androidide.lsp.models.SignatureHelp; import com.itsaky.androidide.lsp.models.SignatureHelpParams; import com.itsaky.androidide.models.Range; import com.itsaky.androidide.progress.ICancelChecker; -import com.itsaky.androidide.projects.ProjectManager; -import com.itsaky.androidide.projects.api.GradleProject; - import com.itsaky.androidide.projects.api.Project; import java.nio.file.Path; import java.util.Collections; @@ -105,8 +101,9 @@ public interface ILanguageServer { /** * Setup this language server with the given project. Servers are not expected to keep a reference - * to the provided project. Instead, use {@link ProjectManager#getRootProject()} to obtain the - * project instance. + * to the provided project. Instead, use + * {@link com.itsaky.androidide.projects.IProjectManager#getRootProject getRootProject()} to + * obtain the project instance. * * @param project The initialized project. */ @@ -165,7 +162,7 @@ public interface ILanguageServer { * * @param file The file to analyze. * @return The diagnostic result. Points to {@link DiagnosticResult#NO_UPDATE} if no diagnotic - * items are available. + * items are available. */ @NonNull DiagnosticResult analyze(@NonNull Path file); diff --git a/lsp/api/src/main/java/com/itsaky/androidide/lsp/util/commonCompletionUtils.kt b/lsp/api/src/main/java/com/itsaky/androidide/lsp/util/commonCompletionUtils.kt index fc77ea847..fc18c3ab0 100644 --- a/lsp/api/src/main/java/com/itsaky/androidide/lsp/util/commonCompletionUtils.kt +++ b/lsp/api/src/main/java/com/itsaky/androidide/lsp/util/commonCompletionUtils.kt @@ -18,7 +18,7 @@ package com.itsaky.androidide.lsp.util import com.itsaky.androidide.lookup.Lookup -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.projects.api.AndroidModule import com.itsaky.androidide.projects.api.ModuleProject import com.itsaky.androidide.xml.resources.ResourceTableRegistry @@ -32,37 +32,37 @@ fun setupLookupForCompletion(file: File) { } fun setupLookupForCompletion(file: Path) { - val module = ProjectManager.findModuleForFile(file) ?: return + val module = IProjectManager.getInstance().findModuleForFile(file, false) ?: return val lookup = Lookup.getDefault() - + lookup.update(ModuleProject.COMPLETION_MODULE_KEY, module) - + if (module is AndroidModule) { val versions = module.getApiVersions() if (versions != null) { lookup.update(ApiVersions.COMPLETION_LOOKUP_KEY, versions) } - + val widgets = module.getWidgetTable() if (widgets != null) { lookup.update(WidgetTable.COMPLETION_LOOKUP_KEY, widgets) } - + val frameworkResources = module.getFrameworkResourceTable() if (frameworkResources != null) { lookup.update(ResourceTableRegistry.COMPLETION_FRAMEWORK_RES, frameworkResources) } - + val moduleResources = module.getSourceResourceTables() if (moduleResources.isNotEmpty()) { lookup.update(ResourceTableRegistry.COMPLETION_MODULE_RES, moduleResources) } - + val depResTables = module.getDependencyResourceTables() if (depResTables.isNotEmpty()) { lookup.update(ResourceTableRegistry.COMPLETION_DEP_RES, depResTables) } - + val manifestAttrTable = module.getManifestAttrTable() if (manifestAttrTable != null) { lookup.update(ResourceTableRegistry.COMPLETION_MANIFEST_ATTR_RES, manifestAttrTable) diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/JavaLanguageServer.java b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/JavaLanguageServer.java index 1ea8d80f9..b90302034 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/JavaLanguageServer.java +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/JavaLanguageServer.java @@ -61,9 +61,9 @@ import com.itsaky.androidide.lsp.util.LSPEditorActions; import com.itsaky.androidide.models.Range; import com.itsaky.androidide.progress.ICancelChecker; import com.itsaky.androidide.projects.FileManager; -import com.itsaky.androidide.projects.ProjectManager; -import com.itsaky.androidide.projects.api.ModuleProject; +import com.itsaky.androidide.projects.IProjectManager; import com.itsaky.androidide.projects.api.GradleProject; +import com.itsaky.androidide.projects.api.ModuleProject; import com.itsaky.androidide.projects.api.Project; import com.itsaky.androidide.utils.DocumentUtils; import com.itsaky.androidide.utils.ILogger; @@ -285,7 +285,7 @@ public class JavaLanguageServer implements ILanguageServer { return JavaCompilerService.NO_MODULE_COMPILER; } - final var root = ProjectManager.INSTANCE.getRootProject(); + final var root = IProjectManager.getInstance().getRootProject(); if (root == null) { return JavaCompilerService.NO_MODULE_COMPILER; } @@ -326,7 +326,8 @@ public class JavaLanguageServer implements ILanguageServer { // TODO Find an alternative to efficiently update changeDelta in JavaCompilerService instance JavaCompilerService.NO_MODULE_COMPILER.onDocumentChange(event); - final ModuleProject module = ProjectManager.INSTANCE.findModuleForFile(event.getChangedFile()); + final ModuleProject module = IProjectManager.getInstance() + .findModuleForFile(event.getChangedFile()); if (module != null) { final JavaCompilerService compiler = JavaCompilerProvider.get(module); compiler.onDocumentChange(event); diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/BaseJavaCodeAction.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/BaseJavaCodeAction.kt index 29364edcf..9cae8d15d 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/BaseJavaCodeAction.kt +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/BaseJavaCodeAction.kt @@ -32,7 +32,7 @@ import com.itsaky.androidide.lsp.java.JavaLanguageServer import com.itsaky.androidide.lsp.java.R import com.itsaky.androidide.lsp.java.compiler.JavaCompilerService import com.itsaky.androidide.lsp.java.rewrite.Rewrite -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.utils.DocumentUtils import com.itsaky.androidide.utils.ILogger import com.itsaky.androidide.utils.flashError @@ -104,7 +104,7 @@ abstract class BaseJavaCodeAction : EditorActionItem { } protected fun ActionData.requireCompiler(): JavaCompilerService { - val module = ProjectManager.findModuleForFile(requireFile()) + val module = IProjectManager.getInstance().findModuleForFile(requireFile(), false) requireNotNull(module) { "Cannot get compiler instance. Unable to find module for file: ${requireFile().name}" } diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/FieldBasedAction.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/FieldBasedAction.kt index 58cd11c2f..4be406dd6 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/FieldBasedAction.kt +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/FieldBasedAction.kt @@ -26,7 +26,7 @@ import com.itsaky.androidide.actions.requirePath import com.itsaky.androidide.lsp.java.JavaCompilerProvider import com.itsaky.androidide.lsp.java.compiler.CompileTask import com.itsaky.androidide.lsp.java.visitors.FindTypeDeclarationAt -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.resources.R import com.itsaky.androidide.utils.ILogger import com.itsaky.androidide.utils.flashInfo @@ -57,7 +57,7 @@ abstract class FieldBasedAction : BaseJavaCodeAction() { com.itsaky.androidide.models.Range::class.java, CodeEditor::class.java ) || - ProjectManager.rootProject == null + IProjectManager.getInstance().rootProject == null ) { markInvisible() return @@ -70,7 +70,7 @@ abstract class FieldBasedAction : BaseJavaCodeAction() { override fun execAction(data: ActionData): Any { val range = data[com.itsaky.androidide.models.Range::class.java]!! val file = data.requirePath() - val module = ProjectManager.findModuleForFile(file) ?: return Any() + val module = IProjectManager.getInstance().findModuleForFile(file, false) ?: return Any() return JavaCompilerProvider.get(module).compile(file).get { task -> val triple = findFields(task, file, range) diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/AddImportAction.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/AddImportAction.kt index 3c2bc84b3..7011b0a28 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/AddImportAction.kt +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/AddImportAction.kt @@ -31,7 +31,7 @@ import com.itsaky.androidide.lsp.java.rewrite.AddImport import com.itsaky.androidide.lsp.java.rewrite.Rewrite import com.itsaky.androidide.lsp.models.CodeActionItem import com.itsaky.androidide.lsp.models.DiagnosticItem -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.resources.R import com.itsaky.androidide.utils.ILogger import jdkx.tools.Diagnostic @@ -63,7 +63,7 @@ class AddImportAction : BaseJavaCodeAction() { val file = data.requireFile() val module = - ProjectManager.findModuleForFile(file) + IProjectManager.getInstance().findModuleForFile(file, false) ?: run { markInvisible() return @@ -95,7 +95,7 @@ class AddImportAction : BaseJavaCodeAction() { )!! val file = data.requireFile() val module = - ProjectManager.findModuleForFile(file) + IProjectManager.getInstance().findModuleForFile(file, false) ?: run { markInvisible() return Any() @@ -136,7 +136,7 @@ class AddImportAction : BaseJavaCodeAction() { val file = data.requireFile() val module = - ProjectManager.findModuleForFile(file) + IProjectManager.getInstance().findModuleForFile(file, false) ?: run { markInvisible() return @@ -158,9 +158,11 @@ class AddImportAction : BaseJavaCodeAction() { 0 -> { log.warn("No rewrites found. Cannot perform action") } + 1 -> { client.performCodeAction(actions[0]) } + else -> { val builder = newDialogBuilder(data) builder.setTitle(label) diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/AddThrowsAction.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/AddThrowsAction.kt index eaa16b7d9..53cc08a35 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/AddThrowsAction.kt +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/AddThrowsAction.kt @@ -27,7 +27,7 @@ import com.itsaky.androidide.lsp.java.models.DiagnosticCode import com.itsaky.androidide.lsp.java.rewrite.AddException import com.itsaky.androidide.lsp.java.utils.CodeActionUtils import com.itsaky.androidide.lsp.models.DiagnosticItem -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.resources.R import com.itsaky.androidide.utils.ILogger @@ -58,7 +58,8 @@ class AddThrowsAction : BaseJavaCodeAction() { override fun execAction(data: ActionData): Any { val diagnostic = data[DiagnosticItem::class.java]!! val compiler = - JavaCompilerProvider.get(ProjectManager.findModuleForFile(data.requireFile()) ?: return Any()) + JavaCompilerProvider.get( + IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return Any()) val file = data.requirePath() return compiler.compile(file).get { task -> val needsThrow = CodeActionUtils.findMethod(task, diagnostic.range) diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/CreateMissingMethodAction.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/CreateMissingMethodAction.kt index 86187fcbe..5462ac94b 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/CreateMissingMethodAction.kt +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/CreateMissingMethodAction.kt @@ -27,7 +27,7 @@ import com.itsaky.androidide.lsp.java.actions.BaseJavaCodeAction import com.itsaky.androidide.lsp.java.models.DiagnosticCode import com.itsaky.androidide.lsp.java.rewrite.CreateMissingMethod import com.itsaky.androidide.lsp.java.utils.CodeActionUtils.findPosition -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.utils.ILogger /** @author Akash Yadav */ @@ -60,7 +60,8 @@ class CreateMissingMethodAction : BaseJavaCodeAction() { override fun execAction(data: ActionData): Any { val diagnostic = data[com.itsaky.androidide.lsp.models.DiagnosticItem::class.java]!! val compiler = - JavaCompilerProvider.get(ProjectManager.findModuleForFile(data.requireFile()) ?: return Any()) + JavaCompilerProvider.get( + IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return Any()) val file = data.requirePath() return compiler.compile(file).get { CreateMissingMethod(file, findPosition(it, diagnostic.range.start)) diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/FieldToBlockAction.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/FieldToBlockAction.kt index acbffbcac..43e3cc9fd 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/FieldToBlockAction.kt +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/FieldToBlockAction.kt @@ -28,7 +28,7 @@ import com.itsaky.androidide.lsp.java.models.DiagnosticCode import com.itsaky.androidide.lsp.java.rewrite.ConvertFieldToBlock import com.itsaky.androidide.lsp.java.utils.CodeActionUtils.findPosition import com.itsaky.androidide.lsp.models.DiagnosticItem -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.utils.ILogger /** @author Akash Yadav */ @@ -62,7 +62,8 @@ class FieldToBlockAction : BaseJavaCodeAction() { override fun execAction(data: ActionData): Any { val compiler = - JavaCompilerProvider.get(ProjectManager.findModuleForFile(data.requireFile()) ?: return Any()) + JavaCompilerProvider.get( + IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return Any()) val diagnostic = data[DiagnosticItem::class.java]!! val file = data.requirePath() diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/RemoveClassAction.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/RemoveClassAction.kt index a1413d4ab..6f2e3b2af 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/RemoveClassAction.kt +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/RemoveClassAction.kt @@ -27,7 +27,7 @@ import com.itsaky.androidide.lsp.java.actions.BaseJavaCodeAction import com.itsaky.androidide.lsp.java.models.DiagnosticCode import com.itsaky.androidide.lsp.java.rewrite.RemoveClass import com.itsaky.androidide.lsp.java.utils.CodeActionUtils.findPosition -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.utils.ILogger /** @author Akash Yadav */ @@ -57,7 +57,8 @@ class RemoveClassAction : BaseJavaCodeAction() { override fun execAction(data: ActionData): Any { val diagnostic = data[com.itsaky.androidide.lsp.models.DiagnosticItem::class.java]!! val compiler = - JavaCompilerProvider.get(ProjectManager.findModuleForFile(data.requireFile()) ?: return Any()) + JavaCompilerProvider.get( + IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return Any()) val file = data.requirePath() return compiler.compile(file).get { diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/RemoveMethodAction.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/RemoveMethodAction.kt index dff04bc96..2cb2cceb3 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/RemoveMethodAction.kt +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/RemoveMethodAction.kt @@ -27,7 +27,7 @@ import com.itsaky.androidide.lsp.java.actions.BaseJavaCodeAction import com.itsaky.androidide.lsp.java.models.DiagnosticCode import com.itsaky.androidide.lsp.java.rewrite.RemoveMethod import com.itsaky.androidide.lsp.java.utils.CodeActionUtils.findMethod -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.utils.ILogger /** @author Akash Yadav */ @@ -57,7 +57,8 @@ class RemoveMethodAction : BaseJavaCodeAction() { override fun execAction(data: ActionData): Any { val diagnostic = data[com.itsaky.androidide.lsp.models.DiagnosticItem::class.java]!! val compiler = - JavaCompilerProvider.get(ProjectManager.findModuleForFile(data.requireFile()) ?: return Any()) + JavaCompilerProvider.get( + IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return Any()) val file = data.requirePath() return compiler.compile(file).get { diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/RemoveUnusedThrowsAction.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/RemoveUnusedThrowsAction.kt index 40dcc65de..e3ff27530 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/RemoveUnusedThrowsAction.kt +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/RemoveUnusedThrowsAction.kt @@ -27,7 +27,7 @@ import com.itsaky.androidide.lsp.java.actions.BaseJavaCodeAction import com.itsaky.androidide.lsp.java.models.DiagnosticCode import com.itsaky.androidide.lsp.java.rewrite.RemoveException import com.itsaky.androidide.lsp.java.utils.CodeActionUtils -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.utils.ILogger /** @author Akash Yadav */ @@ -60,7 +60,8 @@ class RemoveUnusedThrowsAction : BaseJavaCodeAction() { override fun execAction(data: ActionData): Any { val d = data[com.itsaky.androidide.lsp.models.DiagnosticItem::class.java]!! val compiler = - JavaCompilerProvider.get(ProjectManager.findModuleForFile(data.requireFile()) ?: return Any()) + JavaCompilerProvider.get( + IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return Any()) val file = data.requirePath() return compiler.compile(file).get { task -> val notThrown = CodeActionUtils.extractNotThrownExceptionName(d.message) diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/SuppressUncheckedWarningAction.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/SuppressUncheckedWarningAction.kt index 0d66f9532..0e0ef13fc 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/SuppressUncheckedWarningAction.kt +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/SuppressUncheckedWarningAction.kt @@ -27,7 +27,7 @@ import com.itsaky.androidide.lsp.java.actions.BaseJavaCodeAction import com.itsaky.androidide.lsp.java.models.DiagnosticCode import com.itsaky.androidide.lsp.java.rewrite.AddSuppressWarningAnnotation import com.itsaky.androidide.lsp.java.utils.CodeActionUtils -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.utils.ILogger /** @author Akash Yadav */ @@ -57,7 +57,8 @@ class SuppressUncheckedWarningAction : BaseJavaCodeAction() { override fun execAction(data: ActionData): Any { val diagnostic = data[com.itsaky.androidide.lsp.models.DiagnosticItem::class.java]!! val compiler = - JavaCompilerProvider.get(ProjectManager.findModuleForFile(data.requireFile()) ?: return Any()) + JavaCompilerProvider.get( + IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return Any()) val file = data.requirePath() return compiler.compile(file).get { task -> val warnedMethod = CodeActionUtils.findMethod(task, diagnostic.range) diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/VariableToStatementAction.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/VariableToStatementAction.kt index 8dfebcc5f..598bcd693 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/VariableToStatementAction.kt +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/diagnostics/VariableToStatementAction.kt @@ -27,7 +27,7 @@ import com.itsaky.androidide.lsp.java.actions.BaseJavaCodeAction import com.itsaky.androidide.lsp.java.models.DiagnosticCode import com.itsaky.androidide.lsp.java.rewrite.ConvertVariableToStatement import com.itsaky.androidide.lsp.java.utils.CodeActionUtils.findPosition -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.utils.ILogger /** @author Akash Yadav */ @@ -65,7 +65,8 @@ class VariableToStatementAction : BaseJavaCodeAction() { override fun execAction(data: ActionData): Any { val diagnostic = data[com.itsaky.androidide.lsp.models.DiagnosticItem::class.java]!! val compiler = - JavaCompilerProvider.get(ProjectManager.findModuleForFile(data.requireFile()) ?: return Any()) + JavaCompilerProvider.get( + IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return Any()) val path = data.requirePath() return compiler.compile(path).get { diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/GenerateConstructorAction.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/GenerateConstructorAction.kt index 523f542e4..1de03374f 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/GenerateConstructorAction.kt +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/GenerateConstructorAction.kt @@ -31,7 +31,7 @@ import com.itsaky.androidide.lsp.java.compiler.CompileTask import com.itsaky.androidide.lsp.java.utils.EditHelper import com.itsaky.androidide.lsp.java.utils.ShortTypePrinter.NO_PACKAGE import com.itsaky.androidide.preferences.utils.indentationString -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.resources.R.string import com.itsaky.androidide.utils.ILogger import com.itsaky.androidide.utils.flashError @@ -72,7 +72,8 @@ class GenerateConstructorAction : FieldBasedAction() { private fun generateConstructor(data: ActionData, selected: MutableSet) { val compiler = - JavaCompilerProvider.get(ProjectManager.findModuleForFile(data.requireFile()) ?: return) + JavaCompilerProvider.get( + IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return) val range = data[com.itsaky.androidide.models.Range::class.java]!! val file = data.requirePath() diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/GenerateMissingConstructorAction.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/GenerateMissingConstructorAction.kt index 1f01f6c89..424a3be67 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/GenerateMissingConstructorAction.kt +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/GenerateMissingConstructorAction.kt @@ -27,7 +27,7 @@ import com.itsaky.androidide.lsp.java.actions.BaseJavaCodeAction import com.itsaky.androidide.lsp.java.models.DiagnosticCode import com.itsaky.androidide.lsp.java.rewrite.GenerateRecordConstructor import com.itsaky.androidide.lsp.java.utils.CodeActionUtils -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.utils.ILogger /** @author Akash Yadav */ @@ -59,7 +59,8 @@ class GenerateMissingConstructorAction : BaseJavaCodeAction() { override fun execAction(data: ActionData): Any { val diagnostic = data[com.itsaky.androidide.lsp.models.DiagnosticItem::class.java]!! val compiler = - JavaCompilerProvider.get(ProjectManager.findModuleForFile(data.requireFile()) ?: return Any()) + JavaCompilerProvider.get( + IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return Any()) val file = data.requirePath() return compiler.compile(file).get { task -> val needsConstructor = diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/GenerateSettersAndGettersAction.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/GenerateSettersAndGettersAction.kt index 938b273dc..1778455e2 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/GenerateSettersAndGettersAction.kt +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/GenerateSettersAndGettersAction.kt @@ -36,7 +36,7 @@ import com.itsaky.androidide.lsp.java.utils.JavaParserUtils import com.itsaky.androidide.lsp.java.utils.TypeUtils.toType import com.itsaky.androidide.preferences.internal.tabSize import com.itsaky.androidide.preferences.utils.indentationString -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.resources.R import com.itsaky.androidide.utils.ILogger import com.itsaky.androidide.utils.flashError @@ -82,7 +82,8 @@ class GenerateSettersAndGettersAction : FieldBasedAction() { private fun generateForFields(data: ActionData, names: MutableSet) { val compiler = - JavaCompilerProvider.get(ProjectManager.findModuleForFile(data.requireFile()) ?: return) + JavaCompilerProvider.get( + IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return) val range = data[com.itsaky.androidide.models.Range::class.java]!! val file = data.requirePath() diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/GenerateToStringMethodAction.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/GenerateToStringMethodAction.kt index 5009e6b1f..40c709934 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/GenerateToStringMethodAction.kt +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/GenerateToStringMethodAction.kt @@ -31,7 +31,7 @@ import com.itsaky.androidide.lsp.java.compiler.CompileTask import com.itsaky.androidide.lsp.java.utils.EditHelper import com.itsaky.androidide.preferences.internal.tabSize import com.itsaky.androidide.preferences.utils.indentationString -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.resources.R import com.itsaky.androidide.resources.R.string import com.itsaky.androidide.utils.ILogger @@ -80,7 +80,8 @@ class GenerateToStringMethodAction : FieldBasedAction() { private fun generateToString(data: ActionData, selected: MutableSet) { val compiler = - JavaCompilerProvider.get(ProjectManager.findModuleForFile(data.requireFile()) ?: return) + JavaCompilerProvider.get( + IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return) val range = data[com.itsaky.androidide.models.Range::class.java]!! val file = data.requirePath() diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/OverrideSuperclassMethodsAction.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/OverrideSuperclassMethodsAction.kt index 6a0b06dbe..dac79c625 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/OverrideSuperclassMethodsAction.kt +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/OverrideSuperclassMethodsAction.kt @@ -38,7 +38,7 @@ import com.itsaky.androidide.lsp.java.visitors.FindTypeDeclarationAt import com.itsaky.androidide.models.Position import com.itsaky.androidide.preferences.internal.tabSize import com.itsaky.androidide.preferences.utils.indentationString -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.resources.R import com.itsaky.androidide.utils.ILogger import com.itsaky.androidide.utils.flashError @@ -90,7 +90,8 @@ class OverrideSuperclassMethodsAction : BaseJavaCodeAction() { override fun execAction(data: ActionData): Any { val range = data[com.itsaky.androidide.models.Range::class.java]!! val compiler = - JavaCompilerProvider.get(ProjectManager.findModuleForFile(data.requireFile()) ?: return Any()) + JavaCompilerProvider.get( + IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return Any()) val file = data.requirePath() return compiler.compile(file).get { task -> @@ -197,7 +198,8 @@ class OverrideSuperclassMethodsAction : BaseJavaCodeAction() { private fun overrideMethods(data: ActionData, checkedMethods: MutableList) { val compiler = - JavaCompilerProvider.get(ProjectManager.findModuleForFile(data.requireFile()) ?: return) + JavaCompilerProvider.get( + IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return) val file = data.requirePath() compiler.compile(file).run { task -> @@ -263,7 +265,8 @@ class OverrideSuperclassMethodsAction : BaseJavaCodeAction() { position: Position, ) { val compiler = - JavaCompilerProvider.get(ProjectManager.findModuleForFile(data.requireFile()) ?: return) + JavaCompilerProvider.get( + IProjectManager.getInstance().findModuleForFile(data.requireFile(), false) ?: return) val editor = data[CodeEditor::class.java]!! val file = data.requirePath() val text = editor.text diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/edits/AdvancedJavaEditHandler.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/edits/AdvancedJavaEditHandler.kt index 60c240499..7b8fed0d0 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/edits/AdvancedJavaEditHandler.kt +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/edits/AdvancedJavaEditHandler.kt @@ -20,7 +20,7 @@ package com.itsaky.androidide.lsp.java.edits import com.itsaky.androidide.lsp.java.JavaCompilerProvider import com.itsaky.androidide.lsp.java.compiler.JavaCompilerService import com.itsaky.androidide.lsp.models.CompletionItem -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import io.github.rosemoe.sora.text.Content import io.github.rosemoe.sora.widget.CodeEditor import java.nio.file.Path @@ -40,7 +40,8 @@ abstract class AdvancedJavaEditHandler(protected val file: Path) : BaseJavaEditH column: Int, index: Int ) { - val compiler = JavaCompilerProvider.get(ProjectManager.findModuleForFile(file) ?: return) + val compiler = JavaCompilerProvider.get( + IProjectManager.getInstance().findModuleForFile(file, false) ?: return) performEdits(compiler, editor, item) executeCommand(editor, item.command) diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/parser/Parser.java b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/parser/Parser.java index 806cff7c9..0ac349068 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/parser/Parser.java +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/parser/Parser.java @@ -21,9 +21,17 @@ import com.itsaky.androidide.lsp.java.compiler.SourceFileManager; import com.itsaky.androidide.lsp.java.compiler.SourceFileObject; import com.itsaky.androidide.models.Position; import com.itsaky.androidide.models.Range; -import com.itsaky.androidide.projects.ProjectManager; +import com.itsaky.androidide.projects.IProjectManager; import com.itsaky.androidide.projects.api.ModuleProject; import com.itsaky.androidide.utils.ILogger; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import jdkx.tools.JavaCompiler; +import jdkx.tools.JavaFileObject; import openjdk.source.tree.ClassTree; import openjdk.source.tree.CompilationUnitTree; import openjdk.source.tree.LineMap; @@ -36,16 +44,6 @@ import openjdk.source.util.TreePath; import openjdk.source.util.Trees; import openjdk.tools.javac.api.JavacTool; -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Collections; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import jdkx.tools.JavaCompiler; -import jdkx.tools.JavaFileObject; - public class Parser { private static final JavaCompiler COMPILER = JavacTool.create(); @@ -75,9 +73,12 @@ public class Parser { this.trees = Trees.instance(task); } - /** Create a task that compiles a single file */ + /** + * Create a task that compiles a single file + */ private static JavacTask singleFileTask(JavaFileObject file) { - final ModuleProject module = ProjectManager.INSTANCE.findModuleForFile(Paths.get(file.toUri())); + final ModuleProject module = IProjectManager.getInstance() + .findModuleForFile(Paths.get(file.toUri())); if (module != null) { FILE_MANAGER = SourceFileManager.forModule(module); } diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/JavaDiagnosticProvider.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/JavaDiagnosticProvider.kt index cc0964efb..1bea96472 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/JavaDiagnosticProvider.kt +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/JavaDiagnosticProvider.kt @@ -24,7 +24,7 @@ import com.itsaky.androidide.lsp.models.DiagnosticResult import com.itsaky.androidide.progress.ProgressManager import com.itsaky.androidide.progress.ProgressManager.Companion.abortIfCancelled import com.itsaky.androidide.projects.FileManager -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.utils.ILogger import java.nio.file.Path import java.time.Instant @@ -45,7 +45,8 @@ class JavaDiagnosticProvider { fun analyze(file: Path): DiagnosticResult { - val module = ProjectManager.findModuleForFile(file) ?: return DiagnosticResult.NO_UPDATE + val module = IProjectManager.getInstance().findModuleForFile(file, false) + ?: return DiagnosticResult.NO_UPDATE val compiler = JavaCompilerService(module) abortIfCancelled() diff --git a/lsp/java/src/test/java/com/itsaky/androidide/lsp/java/JavaCompilerProviderTest.kt b/lsp/java/src/test/java/com/itsaky/androidide/lsp/java/JavaCompilerProviderTest.kt index 64df3da39..fcd804eb1 100644 --- a/lsp/java/src/test/java/com/itsaky/androidide/lsp/java/JavaCompilerProviderTest.kt +++ b/lsp/java/src/test/java/com/itsaky/androidide/lsp/java/JavaCompilerProviderTest.kt @@ -19,7 +19,7 @@ package com.itsaky.androidide.lsp.java import com.google.common.truth.Truth.assertThat import com.itsaky.androidide.lsp.java.compiler.JavaCompilerService -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.projects.api.ModuleProject import org.junit.Before import org.junit.Test @@ -39,7 +39,7 @@ class JavaCompilerProviderTest { @Test fun `test module specific compilers`() { - val rootProject = ProjectManager.rootProject!! + val rootProject = IProjectManager.getInstance().rootProject!! val appModule = rootProject.findByPath(":app")!! as ModuleProject val androidLib = rootProject.findByPath(":android-library")!! as ModuleProject val anotherAndroidLib = rootProject.findByPath(":another-android-library")!! as ModuleProject diff --git a/lsp/java/src/test/java/com/itsaky/androidide/lsp/java/JavaLSPTest.kt b/lsp/java/src/test/java/com/itsaky/androidide/lsp/java/JavaLSPTest.kt index de2f58031..e328251e7 100644 --- a/lsp/java/src/test/java/com/itsaky/androidide/lsp/java/JavaLSPTest.kt +++ b/lsp/java/src/test/java/com/itsaky/androidide/lsp/java/JavaLSPTest.kt @@ -20,7 +20,7 @@ package com.itsaky.androidide.lsp.java import com.itsaky.androidide.lsp.api.ILanguageServerRegistry import com.itsaky.androidide.lsp.api.LSPTest import com.itsaky.androidide.lsp.java.compiler.JavaCompilerService -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import org.junit.Before import org.junit.Ignore @@ -46,7 +46,7 @@ object JavaLSPTest : LSPTest() { override fun getServerId() = JavaLanguageServer.SERVER_ID fun getCompiler(): JavaCompilerService { - return JavaCompilerProvider.get(ProjectManager.app) + return JavaCompilerProvider.get(IProjectManager.getInstance().app) } override fun test() {} diff --git a/lsp/testing/src/main/java/com/itsaky/androidide/lsp/api/LSPTest.kt b/lsp/testing/src/main/java/com/itsaky/androidide/lsp/api/LSPTest.kt index 98d76b79a..d515be92d 100644 --- a/lsp/testing/src/main/java/com/itsaky/androidide/lsp/api/LSPTest.kt +++ b/lsp/testing/src/main/java/com/itsaky/androidide/lsp/api/LSPTest.kt @@ -31,7 +31,8 @@ import com.itsaky.androidide.models.Position import com.itsaky.androidide.models.Range import com.itsaky.androidide.preferences.internal.tabSize import com.itsaky.androidide.projects.FileManager -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager +import com.itsaky.androidide.projects.ProjectManagerImpl import com.itsaky.androidide.projects.builder.BuildService import com.itsaky.androidide.tooling.api.IProject import com.itsaky.androidide.tooling.api.IToolingApiServer @@ -98,14 +99,14 @@ abstract class LSPTest { Environment.ANDROID_JAR = FileProvider.resources().resolve("android.jar").toFile() Environment.JAVA_HOME = File(System.getProperty("java.home")!!) registerServer() - ProjectManager.register() - ProjectManager.setupProject() + ProjectManagerImpl.getInstance().register() + ProjectManagerImpl.getInstance().setupProject() // We need to manually setup the language server with the project here // ProjectManager.notifyProjectUpdate() ILanguageServerRegistry.getDefault() .getServer(getServerId())!! - .setupWithProject(ProjectManager.rootProject!!) + .setupWithProject(IProjectManager.getInstance().rootProject!!) isInitialized = true } diff --git a/lsp/xml/src/test/java/com/itsaky/androidide/lsp/xml/XMLLSPTest.kt b/lsp/xml/src/test/java/com/itsaky/androidide/lsp/xml/XMLLSPTest.kt index 79d7e8d2b..ee77e183c 100644 --- a/lsp/xml/src/test/java/com/itsaky/androidide/lsp/xml/XMLLSPTest.kt +++ b/lsp/xml/src/test/java/com/itsaky/androidide/lsp/xml/XMLLSPTest.kt @@ -19,7 +19,7 @@ package com.itsaky.androidide.lsp.xml import com.itsaky.androidide.lookup.Lookup import com.itsaky.androidide.lsp.api.ILanguageServerRegistry import com.itsaky.androidide.lsp.api.LSPTest -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.projects.api.ModuleProject import com.itsaky.androidide.xml.resources.ResourceTableRegistry import com.itsaky.androidide.xml.versions.ApiVersions @@ -38,7 +38,7 @@ object XMLLSPTest : LSPTest() { } super.initProjectIfNeeded() try { - val module = ProjectManager.app!! + val module = IProjectManager.getInstance().app!! val lookup = Lookup.getDefault() lookup.update(ModuleProject.COMPLETION_MODULE_KEY, module) diff --git a/subprojects/projects/build.gradle.kts b/subprojects/projects/build.gradle.kts index 756f14d11..f852eb8b4 100644 --- a/subprojects/projects/build.gradle.kts +++ b/subprojects/projects/build.gradle.kts @@ -1,44 +1,46 @@ plugins { - id("com.android.library") - id("kotlin-android") - id("kotlin-parcelize") - id("kotlin-kapt") + id("com.android.library") + id("kotlin-android") + id("kotlin-parcelize") + id("kotlin-kapt") } android { - namespace = "${BuildConfig.packageName}.projects" + namespace = "${BuildConfig.packageName}.projects" } kapt { - arguments { - arg ("eventBusIndex", "${BuildConfig.packageName}.events.ProjectsApiEventsIndex") - } + arguments { + arg("eventBusIndex", "${BuildConfig.packageName}.events.ProjectsApiEventsIndex") + } } dependencies { - - kapt(projects.annotationProcessors) - - api(projects.eventbus) - api(projects.eventbusEvents) - api(projects.subprojects.toolingApi) - - implementation(projects.common) - implementation(projects.logger) - implementation(projects.lookup) - implementation(projects.shared) - implementation(projects.subprojects.javacServices) - implementation(projects.subprojects.xmlUtils) - - implementation(libs.common.io) - implementation(libs.google.guava) - - testImplementation(projects.subprojects.toolingApiTesting) - testImplementation(projects.shared) - testImplementation(libs.tests.junit) - testImplementation(libs.tests.google.truth) - testImplementation(libs.tests.robolectric) - androidTestImplementation(libs.tests.androidx.junit) - androidTestImplementation(libs.tests.androidx.espresso) - androidTestImplementation(libs.tests.google.truth) + + kapt(projects.annotationProcessors) + kapt(libs.google.auto.service) + + api(projects.eventbus) + api(projects.eventbusEvents) + api(projects.subprojects.toolingApi) + + implementation(projects.common) + implementation(projects.logger) + implementation(projects.lookup) + implementation(projects.shared) + implementation(projects.subprojects.javacServices) + implementation(projects.subprojects.xmlUtils) + + implementation(libs.common.io) + implementation(libs.google.auto.service.annotations) + implementation(libs.google.guava) + + testImplementation(projects.subprojects.toolingApiTesting) + testImplementation(projects.shared) + testImplementation(libs.tests.junit) + testImplementation(libs.tests.google.truth) + testImplementation(libs.tests.robolectric) + androidTestImplementation(libs.tests.androidx.junit) + androidTestImplementation(libs.tests.androidx.espresso) + androidTestImplementation(libs.tests.google.truth) } \ No newline at end of file diff --git a/subprojects/projects/src/main/java/com/itsaky/androidide/projects/IProjectManager.kt b/subprojects/projects/src/main/java/com/itsaky/androidide/projects/IProjectManager.kt new file mode 100644 index 000000000..1b0d1d029 --- /dev/null +++ b/subprojects/projects/src/main/java/com/itsaky/androidide/projects/IProjectManager.kt @@ -0,0 +1,145 @@ +/* + * 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.androidide.projects + +import androidx.annotation.RestrictTo +import com.itsaky.androidide.lookup.Lookup +import com.itsaky.androidide.projects.api.AndroidModule +import com.itsaky.androidide.projects.api.ModuleProject +import com.itsaky.androidide.projects.api.Project +import com.itsaky.androidide.projects.builder.BuildService +import com.itsaky.androidide.tooling.api.IProject +import com.itsaky.androidide.utils.ServiceLoader +import java.io.File +import java.nio.file.Path + +/** + * Project manager. + * + * @author Akash Yadav + */ +interface IProjectManager { + + companion object { + + private var projectManager: IProjectManager? = null + + /** + * Get the project manager instance. + */ + @JvmStatic + fun getInstance(): IProjectManager { + return projectManager ?: ServiceLoader.load(IProjectManager::class.java).findFirstOrThrow() + .also { + projectManager = it + } + } + } + + /** + * The root project model. + */ + val rootProject: Project? + + /** + * The Android application module in the project. + */ + val app: AndroidModule? + + /** + * The path to the project's root directory. + */ + val projectDirPath: String + + /** + * The project's root directory. + */ + val projectDir: File + get() = File(projectDirPath) + + /** + * Setup the project with the given [project proxy][project] from the Tooling API. + * + * @param project The project proxy. + */ + @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) + fun setupProject( + project: IProject = Lookup.getDefault().lookup(BuildService.KEY_PROJECT_PROXY)!! + ) + + /** + * Find the module for the given file. + * + * @param file The file to find the module for. + * @return The module project, or `null` if not found. + */ + fun findModuleForFile(file: File) : ModuleProject? { + return findModuleForFile(file, true) + } + + /** + * Find the module for the given file. + * + * @param file The file to find the module for. + * @param checkExistance Whether to check if the file exists or not. + * @return The [ModuleProject] for the given file, or `null` if cannot be found. + */ + fun findModuleForFile(file: File, checkExistance: Boolean): ModuleProject? + + /** + * Find the module for the given file. + * + * @param file The file to find the module for. + * @return The module project, or `null` if not found. + */ + fun findModuleForFile(file: Path) : ModuleProject? { + return findModuleForFile(file, true) + } + + /** + * Find the module for the given file. + * + * @param file The file to find the module for. + * @param checkExistance Whether to check if the file exists or not. + * @return The [ModuleProject] for the given file, or `null` if cannot be found. + */ + fun findModuleForFile(file: Path, checkExistance: Boolean): ModuleProject? { + return findModuleForFile(file.toFile(), checkExistance) + } + + /** + * Check if any of the module projects contain the given [file] in their source folder. + * + * @param file The file to check. + * @return `true` if the given [file] is a source file in any of the mdoules, `false` otherwise. + */ + fun containsSourceFile(file: Path): Boolean + + /** + * Checks if the given file is a resource file in any of the included Android modules. + * + * @param file The file to check. + * @return `true` if the given file is a resource file in any of the Android modules, `false` otherwise. + */ + fun isAndroidResource(file: File): Boolean + + /** + * Destroy the project manager. + */ + fun destroy() +} \ No newline at end of file diff --git a/subprojects/projects/src/main/java/com/itsaky/androidide/projects/ProjectManager.kt b/subprojects/projects/src/main/java/com/itsaky/androidide/projects/ProjectManagerImpl.kt similarity index 75% rename from subprojects/projects/src/main/java/com/itsaky/androidide/projects/ProjectManager.kt rename to subprojects/projects/src/main/java/com/itsaky/androidide/projects/ProjectManagerImpl.kt index 7e8d4814a..aa1dd3a43 100644 --- a/subprojects/projects/src/main/java/com/itsaky/androidide/projects/ProjectManager.kt +++ b/subprojects/projects/src/main/java/com/itsaky/androidide/projects/ProjectManagerImpl.kt @@ -1,329 +1,318 @@ -/* - * 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.androidide.projects - -import com.itsaky.androidide.eventbus.events.EventReceiver -import com.itsaky.androidide.eventbus.events.editor.DocumentSaveEvent -import com.itsaky.androidide.eventbus.events.file.FileCreationEvent -import com.itsaky.androidide.eventbus.events.file.FileDeletionEvent -import com.itsaky.androidide.eventbus.events.file.FileEvent -import com.itsaky.androidide.eventbus.events.file.FileRenameEvent -import com.itsaky.androidide.eventbus.events.project.ProjectInitializedEvent -import com.itsaky.androidide.lookup.Lookup -import com.itsaky.androidide.projects.api.AndroidModule -import com.itsaky.androidide.projects.api.ModuleProject -import com.itsaky.androidide.projects.api.Project -import com.itsaky.androidide.projects.builder.BuildService -import com.itsaky.androidide.projects.util.ProjectTransformer -import com.itsaky.androidide.tasks.executeAsync -import com.itsaky.androidide.tooling.api.IAndroidProject -import com.itsaky.androidide.tooling.api.IProject -import com.itsaky.androidide.tooling.api.messages.result.InitializeResult -import com.itsaky.androidide.tooling.api.models.BuildVariantInfo -import com.itsaky.androidide.utils.DocumentUtils -import com.itsaky.androidide.utils.ILogger -import com.itsaky.androidide.utils.flashError -import org.greenrobot.eventbus.EventBus -import org.greenrobot.eventbus.Subscribe -import org.greenrobot.eventbus.ThreadMode.ASYNC -import org.greenrobot.eventbus.ThreadMode.BACKGROUND -import java.io.File -import java.nio.file.Files -import java.nio.file.Path -import kotlin.io.path.extension -import kotlin.io.path.isDirectory -import kotlin.io.path.pathString - -/** - * Manages projects in AndroidIDE. - * - * @author Akash Yadav - */ -object ProjectManager : EventReceiver { - - private val log = ILogger.newInstance(javaClass.simpleName) - lateinit var projectPath: String - - var rootProject: Project? = null - var app: AndroidModule? = null - - var projectInitialized: Boolean = false - var cachedInitResult: InitializeResult? = null - - @JvmOverloads - fun setupProject( - project: IProject = Lookup.getDefault().lookup(BuildService.KEY_PROJECT_PROXY)!!, - isInitialized: Boolean = true - ) { - this.rootProject = if (isInitialized) ProjectTransformer().transform( - CachingProject(project)) else null - if (this.rootProject != null) { - this.app = this.rootProject!!.findFirstAndroidAppModule() - this.rootProject!!.subProjects.filterIsInstance(ModuleProject::class.java).forEach { - it.indexSourcesAndClasspaths() - if (it is AndroidModule) { - it.readResources() - } - } - } - } - - fun destroy() { - log.info("Destroying project manager") - this.rootProject = null - this.app = null - this.cachedInitResult = null - this.projectInitialized = false - } - - fun getProjectDir(): File { - return File(getProjectDirPath()) - } - - fun getProjectDirPath(): String { - return projectPath - } - - @JvmOverloads - fun generateSources( - builder: BuildService? = Lookup.getDefault().lookup(BuildService.KEY_BUILD_SERVICE) - ) { - if (builder == null) { - log.warn("Cannot generate sources. BuildService is null.") - return - } - - if (!builder.isToolingServerStarted()) { - flashError(R.string.msg_tooling_server_unavailable) - return - } - - if (app == null) { - log.warn("Cannot run resource and source generation task. No application module found.") - return - } - - val debug = app!!.getVariant("debug") - if (debug == null) { - log.warn("No debug variant found in application project ${app!!.name}") - return - } - - val mainArtifact = debug.mainArtifact - val genResourcesTask = mainArtifact.resGenTaskName - val genSourcesTask = mainArtifact.sourceGenTaskName - val genDataBinding = // If view binding is enabled, generate the view binding classes too - if (app!!.viewBindingOptions.isEnabled) { - "dataBindingGenBaseClassesDebug" - } else { - "" - } - builder - .executeProjectTasks( - app!!.path, - genResourcesTask ?: "", - genSourcesTask, - "processDebugResources", - genDataBinding - ) - .whenComplete { result, taskErr -> - if (taskErr != null || !result.isSuccessful) { - log.warn( - "Execution for tasks '$genResourcesTask' and '$genSourcesTask' failed.", - taskErr ?: "" - ) - } else { - notifyProjectUpdate() - } - } - } - - fun getApplicationModule(): AndroidModule? { - return app - } - - fun notifyProjectUpdate() { - - executeAsync { - rootProject?.apply { - subProjects.forEach { subproject -> - if (subproject is ModuleProject) { - subproject.indexSources() - } - } - } - - val event = ProjectInitializedEvent() - event.put(Project::class.java, rootProject) - EventBus.getDefault().post(event) - } - } - - @JvmOverloads - fun findModuleForFile(file: File, checkExistance: Boolean = false): ModuleProject? { - if (!checkInit()) { - return null - } - - return this.rootProject!!.findModuleForFile(file, checkExistance) - } - - @JvmOverloads - fun findModuleForFile(file: Path, checkExistance: Boolean = false): ModuleProject? { - return findModuleForFile(file.toFile(), checkExistance) - } - - fun containsSourceFile(file: Path): Boolean { - if (!checkInit()) { - return false - } - - if (!Files.exists(file)) { - return false - } - - for (module in this.rootProject!!.subProjects) { - if (module !is ModuleProject) { - continue - } - - val source = module.compileJavaSourceClasses.findSource(file) - if (source != null) { - return true - } - } - - return false - } - - private fun isInitialized() = rootProject != null - - private fun checkInit(): Boolean { - if (isInitialized()) { - return true - } - - log.warn("GradleProject is not initialized yet!") - return false - } - - @Suppress("unused") - @Subscribe(threadMode = ASYNC) - fun onFileSaved(event: DocumentSaveEvent) { - event.file.apply { - if (isDirectory()) { - return@apply - } - - if (extension != "xml") { - return@apply - } - - val module = findModuleForFile(this) ?: return@apply - if (module !is AndroidModule) { - return@apply - } - - val isResource = - module.mainSourceSet?.sourceProvider?.resDirectories?.any { - this.pathString.contains(it.path) - } - ?: false - - if (isResource) { - module.updateResourceTable() - } - } - } - - @Suppress("unused") - @Subscribe(threadMode = BACKGROUND) - fun onFileCreated(event: FileCreationEvent) { - generateSourcesIfNecessary(event) - - if (DocumentUtils.isJavaFile(event.file.toPath())) { - findModuleForFile(event.file)?.let { - val sourceRoot = it.findSourceRoot(event.file) ?: return@let - - // add the source node entry - it.compileJavaSourceClasses.append(event.file.toPath(), sourceRoot) - } - } - } - - @Suppress("unused") - @Subscribe(threadMode = BACKGROUND) - fun onFileDeleted(event: FileDeletionEvent) { - generateSourcesIfNecessary(event) - - // Remove the source node entry - // Do not check for Java file DocumentUtils.isJavaFile(...) as it checks for file existence as - // well. As the file is already deleted, it will always return false - if (event.file.extension == "java") { - findModuleForFile(event.file) - ?.compileJavaSourceClasses - ?.findSource(event.file.toPath()) - ?.let { it.parent?.removeChild(it) } - } - } - - @Suppress("unused") - @Subscribe(threadMode = BACKGROUND) - fun onFileRenamed(event: FileRenameEvent) { - generateSourcesIfNecessary(event) - - // Do not check for Java file DocumentUtils.isJavaFile(...) as it checks for file existence as - // well. As the file is already renamed to another filename, it will always return false - if (event.file.extension == "java") { - // remove the source node entry - findModuleForFile(event.file) - ?.compileJavaSourceClasses - ?.findSource(event.file.toPath()) - ?.let { it.parent?.removeChild(it) } - } - - if (DocumentUtils.isJavaFile(event.newFile.toPath())) { - findModuleForFile(event.newFile)?.let { - val sourceRoot = it.findSourceRoot(event.newFile) ?: return@let - // add the new source node entry - it.compileJavaSourceClasses.append(event.newFile.toPath(), sourceRoot) - } - } - } - - private fun generateSourcesIfNecessary(event: FileEvent) { - val builder = Lookup.getDefault().lookup(BuildService.KEY_BUILD_SERVICE) ?: return - val file = event.file - if (!isResource(file)) { - return - } - - generateSources(builder) - } - - private fun ModuleProject.findSourceRoot(file: File): Path? { - return getCompileSourceDirectories().find { file.path.startsWith(it.path) }?.toPath() - } - - private fun isResource(file: File): Boolean { - val module = findModuleForFile(file) ?: return false - if (module is AndroidModule) { - return module.getResourceDirectories().find { file.path.startsWith(it.path) } != null - } - return true - } -} +/* + * 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.androidide.projects + +import androidx.annotation.RestrictTo +import com.google.auto.service.AutoService +import com.itsaky.androidide.eventbus.events.EventReceiver +import com.itsaky.androidide.eventbus.events.editor.DocumentSaveEvent +import com.itsaky.androidide.eventbus.events.file.FileCreationEvent +import com.itsaky.androidide.eventbus.events.file.FileDeletionEvent +import com.itsaky.androidide.eventbus.events.file.FileEvent +import com.itsaky.androidide.eventbus.events.file.FileRenameEvent +import com.itsaky.androidide.eventbus.events.project.ProjectInitializedEvent +import com.itsaky.androidide.lookup.Lookup +import com.itsaky.androidide.projects.api.AndroidModule +import com.itsaky.androidide.projects.api.ModuleProject +import com.itsaky.androidide.projects.api.Project +import com.itsaky.androidide.projects.builder.BuildService +import com.itsaky.androidide.projects.util.ProjectTransformer +import com.itsaky.androidide.tasks.executeAsync +import com.itsaky.androidide.tooling.api.IProject +import com.itsaky.androidide.tooling.api.messages.result.InitializeResult +import com.itsaky.androidide.utils.DocumentUtils +import com.itsaky.androidide.utils.ILogger +import com.itsaky.androidide.utils.flashError +import org.greenrobot.eventbus.EventBus +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import java.io.File +import java.nio.file.Files +import java.nio.file.Path +import kotlin.io.path.extension +import kotlin.io.path.isDirectory +import kotlin.io.path.pathString + +/** + * Internal implementation of [IProjectManager]. + * + * @author Akash Yadav + */ +@AutoService(IProjectManager::class) +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) +class ProjectManagerImpl : IProjectManager, EventReceiver { + + lateinit var projectPath: String + var projectInitialized: Boolean = false + var cachedInitResult: InitializeResult? = null + + override var rootProject: Project? = null + override var app: AndroidModule? = null + + override val projectDirPath: String + get() = projectPath + + companion object { + + private val log = ILogger.newInstance("ProjectManagerImpl") + + @JvmStatic + @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) + fun getInstance(): ProjectManagerImpl { + return IProjectManager.getInstance() as ProjectManagerImpl + } + } + + override fun setupProject(project: IProject) { + this.rootProject = ProjectTransformer().transform(CachingProject(project)) + if (this.rootProject == null) { + return + } + + this.app = this.rootProject!!.findFirstAndroidAppModule() + this.rootProject!!.subProjects.filterIsInstance(ModuleProject::class.java).forEach { + it.indexSourcesAndClasspaths() + if (it is AndroidModule) { + it.readResources() + } + } + } + + override fun findModuleForFile(file: File, checkExistance: Boolean): ModuleProject? { + if (!checkInit()) { + return null + } + + return this.rootProject!!.findModuleForFile(file, checkExistance) + } + + override fun containsSourceFile(file: Path): Boolean { + if (!checkInit()) { + return false + } + + if (!Files.exists(file)) { + return false + } + + for (module in this.rootProject!!.subProjects) { + if (module !is ModuleProject) { + continue + } + + val source = module.compileJavaSourceClasses.findSource(file) + if (source != null) { + return true + } + } + + return false + } + + override fun isAndroidResource(file: File): Boolean { + val module = findModuleForFile(file) ?: return false + if (module is AndroidModule) { + return module.getResourceDirectories().find { file.path.startsWith(it.path) } != null + } + return true + } + + override fun destroy() { + log.info("Destroying project manager") + this.rootProject = null + this.app = null + this.cachedInitResult = null + this.projectInitialized = false + } + + @JvmOverloads + fun generateSources( + builder: BuildService? = Lookup.getDefault().lookup(BuildService.KEY_BUILD_SERVICE) + ) { + if (builder == null) { + log.warn("Cannot generate sources. BuildService is null.") + return + } + + if (!builder.isToolingServerStarted()) { + flashError(R.string.msg_tooling_server_unavailable) + return + } + + if (app == null) { + log.warn("Cannot run resource and source generation task. No application module found.") + return + } + + val debug = app!!.getVariant("debug") + if (debug == null) { + log.warn("No debug variant found in application project ${app!!.name}") + return + } + + val mainArtifact = debug.mainArtifact + val genResourcesTask = mainArtifact.resGenTaskName + val genSourcesTask = mainArtifact.sourceGenTaskName + val genDataBinding = // If view binding is enabled, generate the view binding classes too + if (app!!.viewBindingOptions.isEnabled) { + "dataBindingGenBaseClassesDebug" + } else { + "" + } + builder + .executeProjectTasks( + app!!.path, + genResourcesTask ?: "", + genSourcesTask, + "processDebugResources", + genDataBinding + ) + .whenComplete { result, taskErr -> + if (taskErr != null || !result.isSuccessful) { + log.warn( + "Execution for tasks '$genResourcesTask' and '$genSourcesTask' failed.", + taskErr ?: "" + ) + } else { + notifyProjectUpdate() + } + } + } + + fun notifyProjectUpdate() { + + executeAsync { + rootProject?.apply { + subProjects.forEach { subproject -> + if (subproject is ModuleProject) { + subproject.indexSources() + } + } + } + + val event = ProjectInitializedEvent() + event.put(Project::class.java, rootProject) + EventBus.getDefault().post(event) + } + } + + private fun isInitialized() = rootProject != null + + private fun checkInit(): Boolean { + if (isInitialized()) { + return true + } + + log.warn("GradleProject is not initialized yet!") + return false + } + + private fun generateSourcesIfNecessary(event: FileEvent) { + val builder = Lookup.getDefault().lookup(BuildService.KEY_BUILD_SERVICE) ?: return + val file = event.file + if (!isAndroidResource(file)) { + return + } + + generateSources(builder) + } + + @Suppress("unused") + @Subscribe(threadMode = ThreadMode.ASYNC) + fun onFileSaved(event: DocumentSaveEvent) { + event.file.apply { + if (isDirectory()) { + return@apply + } + + if (extension != "xml") { + return@apply + } + + val module = IProjectManager.getInstance().findModuleForFile(this, false) ?: return@apply + if (module !is AndroidModule) { + return@apply + } + + val isResource = + module.mainSourceSet?.sourceProvider?.resDirectories?.any { + this.pathString.contains(it.path) + } + ?: false + + if (isResource) { + module.updateResourceTable() + } + } + } + + @Suppress("unused") + @Subscribe(threadMode = ThreadMode.BACKGROUND) + fun onFileCreated(event: FileCreationEvent) { + generateSourcesIfNecessary(event) + + if (DocumentUtils.isJavaFile(event.file.toPath())) { + IProjectManager.getInstance().findModuleForFile(event.file, false)?.let { + val sourceRoot = it.findSourceRoot(event.file) ?: return@let + + // add the source node entry + it.compileJavaSourceClasses.append(event.file.toPath(), sourceRoot) + } + } + } + + @Suppress("unused") + @Subscribe(threadMode = ThreadMode.BACKGROUND) + fun onFileDeleted(event: FileDeletionEvent) { + generateSourcesIfNecessary(event) + + // Remove the source node entry + // Do not check for Java file DocumentUtils.isJavaFile(...) as it checks for file existence as + // well. As the file is already deleted, it will always return false + if (event.file.extension == "java") { + IProjectManager.getInstance().findModuleForFile(event.file, false) + ?.compileJavaSourceClasses + ?.findSource(event.file.toPath()) + ?.let { it.parent?.removeChild(it) } + } + } + + @Suppress("unused") + @Subscribe(threadMode = ThreadMode.BACKGROUND) + fun onFileRenamed(event: FileRenameEvent) { + generateSourcesIfNecessary(event) + + // Do not check for Java file DocumentUtils.isJavaFile(...) as it checks for file existence as + // well. As the file is already renamed to another filename, it will always return false + if (event.file.extension == "java") { + // remove the source node entry + IProjectManager.getInstance().findModuleForFile(event.file, false) + ?.compileJavaSourceClasses + ?.findSource(event.file.toPath()) + ?.let { it.parent?.removeChild(it) } + } + + if (DocumentUtils.isJavaFile(event.newFile.toPath())) { + IProjectManager.getInstance().findModuleForFile(event.newFile, false)?.let { + val sourceRoot = it.findSourceRoot(event.newFile) ?: return@let + // add the new source node entry + it.compileJavaSourceClasses.append(event.newFile.toPath(), sourceRoot) + } + } + } +} \ No newline at end of file diff --git a/subprojects/projects/src/main/java/com/itsaky/androidide/projects/api/AndroidModule.kt b/subprojects/projects/src/main/java/com/itsaky/androidide/projects/api/AndroidModule.kt index 37fd509f2..8aae06c46 100644 --- a/subprojects/projects/src/main/java/com/itsaky/androidide/projects/api/AndroidModule.kt +++ b/subprojects/projects/src/main/java/com/itsaky/androidide/projects/api/AndroidModule.kt @@ -31,7 +31,7 @@ import com.itsaky.androidide.builder.model.DefaultModelSyncFile import com.itsaky.androidide.builder.model.DefaultSourceSetContainer import com.itsaky.androidide.builder.model.DefaultViewBindingOptions import com.itsaky.androidide.builder.model.UNKNOWN_PACKAGE -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.tooling.api.ProjectType.Android import com.itsaky.androidide.tooling.api.models.BasicAndroidVariantMetadata import com.itsaky.androidide.tooling.api.models.GradleTask @@ -190,7 +190,7 @@ open class AndroidModule( // Class must be open because BaseXMLTest mocks this.. } override fun getCompileClasspaths(): Set { - val project = ProjectManager.rootProject ?: return emptySet() + val project = IProjectManager.getInstance().rootProject ?: return emptySet() val result = mutableSetOf() result.addAll(getModuleClasspaths()) @@ -219,7 +219,7 @@ open class AndroidModule( // Class must be open because BaseXMLTest mocks this.. } override fun getCompileModuleProjects(): List { - val root = ProjectManager.rootProject ?: return emptyList() + val root = IProjectManager.getInstance().rootProject ?: return emptyList() val result = mutableListOf() for (library in this.libraries) { diff --git a/subprojects/projects/src/main/java/com/itsaky/androidide/projects/api/JavaModule.kt b/subprojects/projects/src/main/java/com/itsaky/androidide/projects/api/JavaModule.kt index f4e66efab..7c63f87a1 100644 --- a/subprojects/projects/src/main/java/com/itsaky/androidide/projects/api/JavaModule.kt +++ b/subprojects/projects/src/main/java/com/itsaky/androidide/projects/api/JavaModule.kt @@ -18,7 +18,7 @@ package com.itsaky.androidide.projects.api import com.itsaky.androidide.builder.model.IJavaCompilerSettings -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.tooling.api.ProjectType.Java import com.itsaky.androidide.tooling.api.models.JavaModuleExternalDependency import com.itsaky.androidide.tooling.api.models.JavaModuleProjectDependency @@ -106,7 +106,7 @@ class JavaModule( } override fun getCompileModuleProjects(): List { - val root = ProjectManager.rootProject ?: return emptyList() + val root = IProjectManager.getInstance().rootProject ?: return emptyList() return this.dependencies .filterIsInstance(JavaModuleProjectDependency::class.java) .filter { it.scope == SCOPE_COMPILE } diff --git a/subprojects/projects/src/main/java/com/itsaky/androidide/projects/api/ModuleProject.kt b/subprojects/projects/src/main/java/com/itsaky/androidide/projects/api/ModuleProject.kt index 36da1a11f..08b8f49b6 100644 --- a/subprojects/projects/src/main/java/com/itsaky/androidide/projects/api/ModuleProject.kt +++ b/subprojects/projects/src/main/java/com/itsaky/androidide/projects/api/ModuleProject.kt @@ -18,6 +18,7 @@ package com.itsaky.androidide.projects.api import android.text.TextUtils +import androidx.annotation.RestrictTo import com.itsaky.androidide.builder.model.IJavaCompilerSettings import com.itsaky.androidide.javac.services.fs.CacheFSInfoSingleton import com.itsaky.androidide.lookup.Lookup @@ -62,6 +63,7 @@ abstract class ModuleProject( @JvmField val compileJavaSourceClasses = SourceClassTrie() + @JvmField val compileClasspathClasses = ClassTrie() @@ -84,7 +86,7 @@ abstract class ModuleProject( * Get the classpaths for this module project. The returned list always included the * `classes.jar`. */ - abstract fun getClassPaths() : Set + abstract fun getClassPaths(): Set /** * Get the JAR files for this module. This does not include JAR files of any dependencies. @@ -107,14 +109,26 @@ abstract class ModuleProject( */ abstract fun getCompileModuleProjects(): List + /** + * Find the source root of the given [file]. + * + * @param file The file to find the source root for. + * @return The source root (directory) of the given file, or `null` if not found. + */ + fun findSourceRoot(file: File): Path? { + return getCompileSourceDirectories().find { file.path.startsWith(it.path) }?.toPath() + } + /** Finds the source files and classes from source directories and classpaths and indexes them. */ - internal fun indexSourcesAndClasspaths() { + @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) + fun indexSourcesAndClasspaths() { log.info("Indexing sources and classpaths for project:", path) indexSources() indexClasspaths() } - internal fun indexClasspaths() { + @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) + fun indexClasspaths() { this.compileClasspathClasses.clear() @@ -138,7 +152,8 @@ abstract class ModuleProject( } } - internal fun indexSources() { + @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) + fun indexSources() { this.compileJavaSourceClasses.clear() diff --git a/subprojects/projects/src/test/java/com/itsaky/androidide/projects/api/ModuleProjectTest.kt b/subprojects/projects/src/test/java/com/itsaky/androidide/projects/api/ModuleProjectTest.kt index 82a068b2a..6e3d3fcab 100644 --- a/subprojects/projects/src/test/java/com/itsaky/androidide/projects/api/ModuleProjectTest.kt +++ b/subprojects/projects/src/test/java/com/itsaky/androidide/projects/api/ModuleProjectTest.kt @@ -22,7 +22,8 @@ import com.itsaky.androidide.eventbus.events.file.FileCreationEvent import com.itsaky.androidide.eventbus.events.file.FileDeletionEvent import com.itsaky.androidide.eventbus.events.file.FileRenameEvent import com.itsaky.androidide.lookup.Lookup -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager +import com.itsaky.androidide.projects.ProjectManagerImpl import com.itsaky.androidide.projects.builder.BuildService import com.itsaky.androidide.tooling.api.IAndroidProject import com.itsaky.androidide.tooling.api.messages.InitializeProjectParams @@ -54,7 +55,7 @@ class ModuleProjectTest { assertThat(project).isNotNull() - val rootProject = ProjectManager.rootProject + val rootProject = IProjectManager.getInstance().rootProject assertThat(rootProject).isNotNull() val root = rootProject!!.rootProject @@ -104,10 +105,10 @@ class ModuleProjectTest { ) ) assertThat( - anotherAndroidLib.getCompileClasspaths().firstOrNull { - it.name == "nb-javac-android-17.0.0.0.jar" - } - ) + anotherAndroidLib.getCompileClasspaths().firstOrNull { + it.name == "nb-javac-android-17.0.0.0.jar" + } + ) .isNotNull() assertThat(anotherAndroidLib.getCompileSourceDirectories()) @@ -147,12 +148,12 @@ class ModuleProjectTest { ) ) assertThat( - filter { - it.absolutePath.endsWith("appcompat-1.5.1/jars/classes.jar") || + filter { + it.absolutePath.endsWith("appcompat-1.5.1/jars/classes.jar") || it.absolutePath.endsWith("material-1.8.0-alpha01/jars/classes.jar") || it.absolutePath.endsWith("coordinatorlayout-1.2.0/jars/classes.jar") - } - ) + } + ) .hasSize(3) } @@ -203,9 +204,10 @@ class ModuleProjectTest { Files.delete(testCls_renamed) } - ProjectManager.setupProject() + val projectManager = ProjectManagerImpl.getInstance() + projectManager.setupProject() - val rootProject = ProjectManager.rootProject + val rootProject = IProjectManager.getInstance().rootProject assertThat(rootProject).isNotNull() val root = rootProject?.rootProject @@ -229,28 +231,29 @@ class ModuleProjectTest { assertThat(Files.isSameFile(source, sourceNode.file)).isTrue() // make sure the files are not indexed - assertThat(ProjectManager.containsSourceFile(testCls)).isFalse() - assertThat(ProjectManager.containsSourceFile(testCls_renamed)).isFalse() + assertThat(IProjectManager.getInstance().containsSourceFile(testCls)).isFalse() + assertThat(IProjectManager.getInstance().containsSourceFile(testCls_renamed)).isFalse() testCls.writeText("public class Test { }") - ProjectManager.onFileCreated(FileCreationEvent(testCls.toFile())) - assertThat(ProjectManager.containsSourceFile(testCls)).isTrue() - assertThat(ProjectManager.containsSourceFile(testCls_renamed)).isFalse() - assertThat(ProjectManager.findModuleForFile(testCls, true)).isEqualTo(module) - assertThat(ProjectManager.findModuleForFile(testCls_renamed, true)).isNull() + projectManager.onFileCreated(FileCreationEvent(testCls.toFile())) + assertThat(IProjectManager.getInstance().containsSourceFile(testCls)).isTrue() + assertThat(IProjectManager.getInstance().containsSourceFile(testCls_renamed)).isFalse() + assertThat(IProjectManager.getInstance().findModuleForFile(testCls, true)).isEqualTo(module) + assertThat(IProjectManager.getInstance().findModuleForFile(testCls_renamed, true)).isNull() Files.move(testCls, testCls_renamed) - ProjectManager.onFileRenamed(FileRenameEvent(testCls.toFile(), testCls_renamed.toFile())) - assertThat(ProjectManager.containsSourceFile(testCls)).isFalse() - assertThat(ProjectManager.containsSourceFile(testCls_renamed)).isTrue() - assertThat(ProjectManager.findModuleForFile(testCls, true)).isNull() - assertThat(ProjectManager.findModuleForFile(testCls_renamed, true)).isEqualTo(module) + projectManager.onFileRenamed(FileRenameEvent(testCls.toFile(), testCls_renamed.toFile())) + assertThat(IProjectManager.getInstance().containsSourceFile(testCls)).isFalse() + assertThat(IProjectManager.getInstance().containsSourceFile(testCls_renamed)).isTrue() + assertThat(IProjectManager.getInstance().findModuleForFile(testCls, true)).isNull() + assertThat(IProjectManager.getInstance().findModuleForFile(testCls_renamed, true)).isEqualTo( + module) Files.delete(testCls_renamed) - ProjectManager.onFileDeleted(FileDeletionEvent(testCls_renamed.toFile())) - assertThat(ProjectManager.containsSourceFile(testCls)).isFalse() - assertThat(ProjectManager.containsSourceFile(testCls_renamed)).isFalse() - assertThat(ProjectManager.findModuleForFile(testCls, true)).isNull() - assertThat(ProjectManager.findModuleForFile(testCls_renamed, true)).isNull() + projectManager.onFileDeleted(FileDeletionEvent(testCls_renamed.toFile())) + assertThat(IProjectManager.getInstance().containsSourceFile(testCls)).isFalse() + assertThat(IProjectManager.getInstance().containsSourceFile(testCls_renamed)).isFalse() + assertThat(IProjectManager.getInstance().findModuleForFile(testCls, true)).isNull() + assertThat(IProjectManager.getInstance().findModuleForFile(testCls_renamed, true)).isNull() } } diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/ModuleProjectModelBuilder.kt b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/ModuleProjectModelBuilder.kt index b3f0b26a6..f1ea702a5 100644 --- a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/ModuleProjectModelBuilder.kt +++ b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/ModuleProjectModelBuilder.kt @@ -29,8 +29,8 @@ class ModuleProjectModelBuilder(initializationParams: InitializeProjectParams) : AbstractModelBuilder(initializationParams) { override fun build(param: ModuleProjectModelBuilderParams): IModuleProject { - val isAndroidProject = getAndroidVersions(param.module, param.controller) != null - return if (isAndroidProject) { + val versions = getAndroidVersions(param.module, param.controller) + return if (versions != null) { AndroidProjectModelBuilder(initializationParams).build( param.controller to param.module) } else { diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/types.kt b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/types.kt index 04da5d9a0..6ed5925e3 100644 --- a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/types.kt +++ b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/types.kt @@ -17,13 +17,10 @@ package com.itsaky.androidide.tooling.impl.sync -import com.itsaky.androidide.tooling.api.messages.InitializeProjectParams import org.gradle.tooling.BuildController -import org.gradle.tooling.ProjectConnection import org.gradle.tooling.model.idea.IdeaModule import org.gradle.tooling.model.idea.IdeaProject - typealias BuildControllderAndIdeaModule = Pair class ModuleProjectModelBuilderParams(val controller: BuildController, project: IdeaProject, diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/utils/inflaterUtils.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/utils/inflaterUtils.kt index b110542db..357c4cadb 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/utils/inflaterUtils.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/utils/inflaterUtils.kt @@ -34,7 +34,7 @@ import com.itsaky.androidide.inflater.IView import com.itsaky.androidide.inflater.InflateException import com.itsaky.androidide.inflater.internal.AttributeImpl import com.itsaky.androidide.lookup.Lookup -import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.projects.api.AndroidModule import java.io.File @@ -92,12 +92,12 @@ fun processXmlFile(file: File, expectedType: AaptResourceType): Pair Unit) { XmlInflaterTest.initIfNeeded() - startParse(ProjectManager.app!!) - block(ProjectManager.app!!) + val app = IProjectManager.getInstance().app!! + startParse(app) + block(app) endParse() }