mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
refactor: migrate ProjectManager to interface based API
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
+23
-24
@@ -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<String, String>) {
|
||||
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<String, BuildVariantInfo>()
|
||||
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
|
||||
|
||||
@@ -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<Checkable<GradleTask>>()
|
||||
val tasks = rootProject.tasks.map { Checkable(false, it) }.toMutableList()
|
||||
|
||||
@@ -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())
|
||||
|
||||
+4
-6
@@ -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())
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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 -> ""
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}"
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
+6
-4
@@ -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)
|
||||
|
||||
+3
-2
@@ -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)
|
||||
|
||||
+3
-2
@@ -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))
|
||||
|
||||
+3
-2
@@ -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()
|
||||
|
||||
|
||||
+3
-2
@@ -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 {
|
||||
|
||||
+3
-2
@@ -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 {
|
||||
|
||||
+3
-2
@@ -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)
|
||||
|
||||
+3
-2
@@ -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)
|
||||
|
||||
+3
-2
@@ -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 {
|
||||
|
||||
+3
-2
@@ -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<String>) {
|
||||
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()
|
||||
|
||||
|
||||
+3
-2
@@ -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 =
|
||||
|
||||
+3
-2
@@ -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<String>) {
|
||||
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()
|
||||
|
||||
|
||||
+3
-2
@@ -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<String>) {
|
||||
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()
|
||||
|
||||
|
||||
+7
-4
@@ -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<MethodPtr>) {
|
||||
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
|
||||
|
||||
+3
-2
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+3
-2
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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()
|
||||
}
|
||||
+318
-329
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -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<File> {
|
||||
val project = ProjectManager.rootProject ?: return emptySet()
|
||||
val project = IProjectManager.getInstance().rootProject ?: return emptySet()
|
||||
val result = mutableSetOf<File>()
|
||||
result.addAll(getModuleClasspaths())
|
||||
|
||||
@@ -219,7 +219,7 @@ open class AndroidModule( // Class must be open because BaseXMLTest mocks this..
|
||||
}
|
||||
|
||||
override fun getCompileModuleProjects(): List<ModuleProject> {
|
||||
val root = ProjectManager.rootProject ?: return emptyList()
|
||||
val root = IProjectManager.getInstance().rootProject ?: return emptyList()
|
||||
val result = mutableListOf<ModuleProject>()
|
||||
|
||||
for (library in this.libraries) {
|
||||
|
||||
@@ -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<ModuleProject> {
|
||||
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 }
|
||||
|
||||
+19
-4
@@ -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<File>
|
||||
abstract fun getClassPaths(): Set<File>
|
||||
|
||||
/**
|
||||
* 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<ModuleProject>
|
||||
|
||||
/**
|
||||
* 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()
|
||||
|
||||
|
||||
+32
-29
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -29,8 +29,8 @@ class ModuleProjectModelBuilder(initializationParams: InitializeProjectParams) :
|
||||
AbstractModelBuilder<ModuleProjectModelBuilderParams, IModuleProject>(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 {
|
||||
|
||||
-3
@@ -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<BuildController, IdeaModule>
|
||||
|
||||
class ModuleProjectModelBuilderParams(val controller: BuildController, project: IdeaProject,
|
||||
|
||||
@@ -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<XmlProcesso
|
||||
throw InflateException("File is not a layout file.")
|
||||
}
|
||||
|
||||
if (ProjectManager.rootProject == null) {
|
||||
if (IProjectManager.getInstance().rootProject == null) {
|
||||
throw InflateException("GradleProject is not initialized!")
|
||||
}
|
||||
|
||||
val module =
|
||||
ProjectManager.findModuleForFile(file) as? AndroidModule
|
||||
IProjectManager.getInstance().findModuleForFile(file, false) as? AndroidModule
|
||||
?: throw InflateException("Cannot find module for given file. Is the project initialized?")
|
||||
val resFile =
|
||||
ResourceFile(
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
package com.itsaky.androidide.inflater.utils
|
||||
|
||||
import com.itsaky.androidide.projects.ProjectManager
|
||||
import com.itsaky.androidide.projects.IProjectManager
|
||||
import com.itsaky.androidide.projects.api.AndroidModule
|
||||
import java.io.File
|
||||
|
||||
@@ -33,7 +33,7 @@ fun startParse(file: File) {
|
||||
if (isParsing) {
|
||||
return
|
||||
}
|
||||
(ProjectManager.findModuleForFile(file) as? AndroidModule)?.let {
|
||||
(IProjectManager.getInstance().findModuleForFile(file, false) as? AndroidModule)?.let {
|
||||
startParse(it)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import com.itsaky.androidide.inflater.internal.ViewGroupImpl
|
||||
import com.itsaky.androidide.inflater.internal.ViewImpl
|
||||
import com.itsaky.androidide.inflater.internal.utils.IDTable
|
||||
import com.itsaky.androidide.inflater.utils.newAttribute
|
||||
import com.itsaky.androidide.projects.ProjectManager
|
||||
import com.itsaky.androidide.projects.IProjectManager
|
||||
import com.itsaky.androidide.projects.api.AndroidModule
|
||||
import org.junit.Before
|
||||
import java.io.File
|
||||
@@ -228,7 +228,7 @@ class LayoutInflaterTest {
|
||||
}
|
||||
|
||||
private fun layoutFile(name: String): File {
|
||||
val app = ProjectManager.app ?: throw IllegalStateException("GradleProject is not initialized")
|
||||
val app = IProjectManager.getInstance().app ?: throw IllegalStateException("GradleProject is not initialized")
|
||||
return File(app.projectDir, "src/main/res/layout/$name.xml")
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import com.itsaky.androidide.inflater.utils.endParse
|
||||
import com.itsaky.androidide.inflater.utils.startParse
|
||||
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.builder.BuildService
|
||||
import com.itsaky.androidide.tooling.api.messages.InitializeProjectParams
|
||||
@@ -48,15 +48,16 @@ object XmlInflaterTest {
|
||||
server.initialize(InitializeProjectParams(FileProvider.testProjectRoot().pathString)).get()
|
||||
|
||||
Lookup.getDefault().register(BuildService.KEY_PROJECT_PROXY, project)
|
||||
ProjectManager.setupProject()
|
||||
IProjectManager.getInstance().setupProject()
|
||||
init.set(true)
|
||||
}
|
||||
}
|
||||
|
||||
fun inflaterTest(block: (AndroidModule) -> Unit) {
|
||||
XmlInflaterTest.initIfNeeded()
|
||||
startParse(ProjectManager.app!!)
|
||||
block(ProjectManager.app!!)
|
||||
val app = IProjectManager.getInstance().app!!
|
||||
startParse(app)
|
||||
block(app)
|
||||
endParse()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user