Skip to content

Commit f2b8059

Browse files
committed
chore(idea): refactor project sync components
Signed-off-by: Dario Valdespino <dvaldespino00@gmail.com>
1 parent 8ccfb03 commit f2b8059

File tree

13 files changed

+328
-213
lines changed

13 files changed

+328
-213
lines changed

packages/plugin-idea/src/main/kotlin/dev/elide/intellij/project/ElideProjectAware.kt

Lines changed: 0 additions & 70 deletions
This file was deleted.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright (c) 2024-2025 Elide Technologies, Inc.
3+
*
4+
* Licensed under the MIT license (the "License"); you may not use this file except in compliance
5+
* with the License. You may obtain a copy of the License at
6+
*
7+
* https://opensource.org/license/mit/
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11+
* License for the specific language governing permissions and limitations under the License.
12+
*/
13+
14+
package dev.elide.intellij.project
15+
16+
import com.intellij.openapi.application.EDT
17+
import com.intellij.openapi.externalSystem.action.DetachExternalProjectAction
18+
import com.intellij.openapi.externalSystem.importing.AbstractOpenProjectProvider
19+
import com.intellij.openapi.externalSystem.model.ProjectSystemId
20+
import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode
21+
import com.intellij.openapi.externalSystem.service.project.trusted.ExternalSystemTrustedProjectDialog
22+
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
23+
import com.intellij.openapi.externalSystem.util.ExternalSystemUtil
24+
import com.intellij.openapi.project.Project
25+
import com.intellij.openapi.util.io.toCanonicalPath
26+
import com.intellij.openapi.vfs.VirtualFile
27+
import dev.elide.intellij.Constants
28+
import dev.elide.intellij.settings.ElideProjectSettings
29+
import kotlinx.coroutines.Dispatchers
30+
import kotlinx.coroutines.withContext
31+
32+
/** Service used to link an Elide project with the IDE, enabling auto-import, sync, and other features. */
33+
@Suppress("UnstableApiUsage") class ElideOpenProjectProvider : AbstractOpenProjectProvider() {
34+
override val systemId: ProjectSystemId = Constants.SYSTEM_ID
35+
36+
override fun isProjectFile(file: VirtualFile): Boolean = !file.isDirectory && file.name == Constants.MANIFEST_NAME
37+
38+
override suspend fun linkProject(projectFile: VirtualFile, project: Project) {
39+
val projectPath = getProjectDirectory(projectFile).toNioPath()
40+
if (!ExternalSystemTrustedProjectDialog.confirmLinkingUntrustedProjectAsync(
41+
project = project,
42+
systemId = Constants.SYSTEM_ID,
43+
projectRoot = projectPath,
44+
)
45+
) return
46+
47+
val settings = ElideProjectSettings()
48+
settings.externalProjectPath = projectPath.toCanonicalPath()
49+
50+
ExternalSystemUtil.linkExternalProject(
51+
/* externalSystemId = */ systemId,
52+
/* projectSettings = */ settings,
53+
/* project = */ project,
54+
/* importResultCallback = */ { },
55+
/* isPreviewMode = */ false,
56+
/* progressExecutionMode = */ ProgressExecutionMode.IN_BACKGROUND_ASYNC,
57+
)
58+
}
59+
60+
override suspend fun unlinkProject(project: Project, externalProjectPath: String) {
61+
val projectData = ExternalSystemApiUtil.findProjectNode(project, systemId, externalProjectPath)?.data ?: return
62+
withContext(Dispatchers.EDT) {
63+
DetachExternalProjectAction.detachProject(project, projectData.owner, projectData, null)
64+
}
65+
}
66+
}

packages/plugin-idea/src/main/kotlin/dev/elide/intellij/project/ElideProjectResolver.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,6 @@ import elide.tooling.project.SourceSetFactory
4040
* A service capable of using the Elide manifest and lockfile to build a project model that can be understood by the
4141
* IDE. Generally, the model can be built without calling the Elide CLI; however, in cases where the lockfile is out of
4242
* date, or dependencies are not installed, a command invocation will take place in a background task.
43-
*
44-
* The resolver is part of the auto-import feature, providing the actual import logic for the [ElideProjectAware]
45-
* tracker.
4643
*/
4744
class ElideProjectResolver : ExternalSystemProjectResolver<ElideExecutionSettings> {
4845
private fun ExternalSystemTaskNotificationListener.onStep(taskId: ExternalSystemTaskId, text: String) {
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (c) 2024-2025 Elide Technologies, Inc.
3+
*
4+
* Licensed under the MIT license (the "License"); you may not use this file except in compliance
5+
* with the License. You may obtain a copy of the License at
6+
*
7+
* https://opensource.org/license/mit/
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11+
* License for the specific language governing permissions and limitations under the License.
12+
*/
13+
14+
package dev.elide.intellij.project
15+
16+
import com.intellij.openapi.Disposable
17+
import com.intellij.openapi.externalSystem.autolink.ExternalSystemProjectLinkListener
18+
import com.intellij.openapi.externalSystem.autolink.ExternalSystemUnlinkedProjectAware
19+
import com.intellij.openapi.externalSystem.model.ProjectSystemId
20+
import com.intellij.openapi.externalSystem.settings.ExternalSystemSettingsListener
21+
import com.intellij.openapi.project.Project
22+
import com.intellij.openapi.vfs.VirtualFile
23+
import dev.elide.intellij.Constants
24+
import dev.elide.intellij.settings.ElideProjectSettings
25+
import dev.elide.intellij.settings.ElideSettings
26+
import java.util.concurrent.ConcurrentSkipListSet
27+
28+
/**
29+
* Tracking service used to link Elide projects when they are opened by the IDE. This class enables the auto-import
30+
* and project sync features automatically, by configuring which files should be tracked by Intellij.
31+
*/
32+
@Suppress("UnstableApiUsage") class ElideUnlinkedProjectAware : ExternalSystemUnlinkedProjectAware {
33+
private val linkedProjects = ConcurrentSkipListSet<String>()
34+
override val systemId: ProjectSystemId = Constants.SYSTEM_ID
35+
36+
override fun isBuildFile(project: Project, buildFile: VirtualFile): Boolean {
37+
return buildFile.name == Constants.MANIFEST_NAME
38+
}
39+
40+
override fun isLinkedProject(project: Project, externalProjectPath: String): Boolean {
41+
val settings = project.getService(ElideSettings::class.java)
42+
return settings.getLinkedProjectSettings(externalProjectPath) != null
43+
}
44+
45+
override fun subscribe(project: Project, listener: ExternalSystemProjectLinkListener, parentDisposable: Disposable) {
46+
project.getService(ElideSettings::class.java).subscribe(
47+
object : ExternalSystemSettingsListener<ElideProjectSettings?> {
48+
override fun onProjectsLinked(settings: Collection<ElideProjectSettings?>) {
49+
settings.forEach { if (it != null) listener.onProjectLinked(it.externalProjectPath) }
50+
}
51+
52+
override fun onProjectsUnlinked(linkedProjectPaths: Set<String?>) {
53+
linkedProjectPaths.forEach { if (it != null) listener.onProjectUnlinked(it) }
54+
}
55+
},
56+
parentDisposable,
57+
)
58+
}
59+
60+
override suspend fun linkAndLoadProjectAsync(project: Project, externalProjectPath: String) {
61+
linkedProjects.add(externalProjectPath)
62+
ElideOpenProjectProvider().linkToExistingProjectAsync(externalProjectPath, project)
63+
64+
}
65+
66+
override suspend fun unlinkProject(project: Project, externalProjectPath: String) {
67+
ElideOpenProjectProvider().unlinkProject(project, externalProjectPath)
68+
linkedProjects.remove(externalProjectPath)
69+
}
70+
}

packages/plugin-idea/src/main/kotlin/dev/elide/intellij/project/model/BuiltinLibraryContributor.kt

Lines changed: 0 additions & 47 deletions
This file was deleted.

packages/plugin-idea/src/main/kotlin/dev/elide/intellij/project/model/CompositeLibraryContributor.kt

Lines changed: 0 additions & 35 deletions
This file was deleted.

packages/plugin-idea/src/main/kotlin/dev/elide/intellij/project/model/MavenLibraryContributor.kt renamed to packages/plugin-idea/src/main/kotlin/dev/elide/intellij/project/model/ElideDependenciesContributor.kt

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,73 @@
1313

1414
package dev.elide.intellij.project.model
1515

16-
import com.intellij.openapi.externalSystem.model.project.LibraryData
17-
import com.intellij.openapi.externalSystem.model.project.LibraryPathType
16+
import com.intellij.openapi.externalSystem.model.DataNode
17+
import com.intellij.openapi.externalSystem.model.ProjectKeys
18+
import com.intellij.openapi.externalSystem.model.project.*
1819
import dev.elide.intellij.Constants
1920
import java.nio.file.Path
2021
import kotlin.io.path.nameWithoutExtension
2122
import kotlin.io.path.pathString
2223
import elide.tooling.ClasspathSpec
2324
import elide.tooling.JvmMultiPathEntryType
25+
import elide.tooling.jvm.JvmLibraries
2426
import elide.tooling.jvm.resolver.MavenLockfileResolver
2527
import elide.tooling.lockfile.ElideLockfile
2628
import elide.tooling.project.ElideConfiguredProject
2729
import elide.tooling.project.SourceSet
30+
import elide.tooling.project.SourceSetLanguage
2831
import elide.tooling.project.SourceSetType
2932

3033
/**
31-
* Provides library data from the resolved classpath stored in the project's lockfile, using a [MavenLockfileResolver].
34+
* Adds library dependencies to a project and its modules, using the resolved dependency data from the Elide lockfile.
35+
* Where applicable, built-in dependencies shipped with the runtime are also added.
3236
*/
33-
class MavenLibraryContributor : LibraryDependencyContributor {
34-
override suspend fun collectDependencies(
35-
projectPath: Path,
37+
class ElideDependenciesContributor : ElideProjectModelContributor {
38+
override suspend fun enhanceModule(
39+
moduleNode: DataNode<ModuleData>,
3640
elideProject: ElideConfiguredProject,
37-
sourceSet: SourceSet
41+
elideSourceSet: SourceSet,
42+
projectPath: Path
43+
) {
44+
// add dependencies
45+
val libraries = collectMavenDependencies(elideProject, elideSourceSet, projectPath) +
46+
collectBuiltinDependencies(elideProject, elideSourceSet)
47+
48+
libraries.forEach {
49+
moduleNode.createChild(
50+
ProjectKeys.LIBRARY_DEPENDENCY,
51+
LibraryDependencyData(moduleNode.data, it, LibraryLevel.MODULE),
52+
)
53+
}
54+
}
55+
56+
/** Provides library data for dependencies that are bundled with Elide for JVM projects. */
57+
private fun collectBuiltinDependencies(
58+
elideProject: ElideConfiguredProject,
59+
sourceSet: SourceSet,
60+
): Sequence<LibraryData> {
61+
if (sourceSet.languages?.contains(SourceSetLanguage.Kotlin) != true) return emptySequence()
62+
63+
return JvmLibraries.builtinClasspath(
64+
path = elideProject.resourcesPath,
65+
tests = sourceSet.type == SourceSetType.Tests,
66+
kotlin = true,
67+
).asSequence().map {
68+
val library = LibraryData(Constants.SYSTEM_ID, it.path.pathString, false)
69+
library.addPath(LibraryPathType.BINARY, it.path.pathString)
70+
71+
library
72+
}
73+
}
74+
75+
/**
76+
* Provides library data from the resolved classpath stored in the project's lockfile, using a
77+
* [MavenLockfileResolver].
78+
*/
79+
private suspend fun collectMavenDependencies(
80+
elideProject: ElideConfiguredProject,
81+
sourceSet: SourceSet,
82+
projectPath: Path
3883
): Sequence<LibraryData> {
3984
val mavenLockfile = elideProject.activeLockfile
4085
?.lockfile
@@ -58,8 +103,8 @@ class MavenLibraryContributor : LibraryDependencyContributor {
58103
val basePath = it.path.parent.resolve(it.path.nameWithoutExtension)
59104

60105
library.addPath(LibraryPathType.BINARY, it.path.pathString)
61-
library.addPath(LibraryPathType.SOURCE, "${basePath.pathString}$SUFFIX_SOURCES")
62-
library.addPath(LibraryPathType.DOC, "${basePath.pathString}$SUFFIX_JAVADOC")
106+
library.addPath(LibraryPathType.SOURCE, "${basePath.pathString}${SUFFIX_SOURCES}")
107+
library.addPath(LibraryPathType.DOC, "${basePath.pathString}${SUFFIX_JAVADOC}")
63108

64109
library
65110
}.orEmpty()

0 commit comments

Comments
 (0)