diff --git a/CHANGELOG.md b/CHANGELOG.md index 53795c33..d0452203 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ Changelog structure reference: ### Added +- Zig + - Changing the zig standard library path in the project settings now properly updates the dependency - ZLS - All of the config options are now exposed in the GUI diff --git a/core/src/main/kotlin/com/falsepattern/zigbrains/project/settings/ZigProjectSettingsService.kt b/core/src/main/kotlin/com/falsepattern/zigbrains/project/settings/ZigProjectSettingsService.kt index 0acce1e2..6820c488 100644 --- a/core/src/main/kotlin/com/falsepattern/zigbrains/project/settings/ZigProjectSettingsService.kt +++ b/core/src/main/kotlin/com/falsepattern/zigbrains/project/settings/ZigProjectSettingsService.kt @@ -22,15 +22,18 @@ package com.falsepattern.zigbrains.project.settings +import com.falsepattern.zigbrains.project.toolchain.stdlib.ZigSyntheticLibrary +import com.falsepattern.zigbrains.shared.zigCoroutineScope import com.intellij.openapi.components.* import com.intellij.openapi.project.Project +import kotlinx.coroutines.launch @Service(Service.Level.PROJECT) @State( name = "ZigProjectSettings", storages = [Storage("zigbrains.xml")] ) -class ZigProjectSettingsService: PersistentStateComponent { +class ZigProjectSettingsService(val project: Project): PersistentStateComponent { @Volatile private var state = ZigProjectSettings() @@ -40,10 +43,13 @@ class ZigProjectSettingsService: PersistentStateComponent { fun setState(value: ZigProjectSettings) { this.state = value + zigCoroutineScope.launch { + ZigSyntheticLibrary.reload(project, value) + } } override fun loadState(state: ZigProjectSettings) { - this.state = state + setState(state) } fun isModified(otherData: ZigProjectSettings): Boolean { diff --git a/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/stdlib/ZigSyntheticLibrary.kt b/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/stdlib/ZigSyntheticLibrary.kt index 7dd8e449..40d3f088 100644 --- a/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/stdlib/ZigSyntheticLibrary.kt +++ b/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/stdlib/ZigSyntheticLibrary.kt @@ -27,28 +27,34 @@ import com.falsepattern.zigbrains.project.settings.ZigProjectSettings import com.falsepattern.zigbrains.project.settings.zigProjectSettings import com.intellij.navigation.ItemPresentation import com.intellij.openapi.project.Project +import com.intellij.openapi.project.guessProjectDir import com.intellij.openapi.roots.SyntheticLibrary import com.intellij.openapi.util.io.toNioPathOrNull import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.refreshAndFindVirtualDirectory +import com.intellij.platform.backend.workspace.WorkspaceModel +import com.intellij.platform.backend.workspace.toVirtualFileUrl +import com.intellij.platform.workspace.jps.entities.* +import com.intellij.workspaceModel.ide.legacyBridge.LegacyBridgeJpsEntitySourceFactory import kotlinx.coroutines.runBlocking import java.util.* import javax.swing.Icon class ZigSyntheticLibrary(val project: Project) : SyntheticLibrary(), ItemPresentation { + private var state: ZigProjectSettings = project.zigProjectSettings.state.copy() private val roots by lazy { - getRoots(project.zigProjectSettings.state, project) + runBlocking {getRoot(state, project)}?.let { setOf(it) } ?: emptySet() } private val name by lazy { - getName(project.zigProjectSettings.state, project) + getName(state, project) } override fun equals(other: Any?): Boolean { if (other !is ZigSyntheticLibrary) return false - return roots == other.roots + return state == other.state } override fun hashCode(): Int { @@ -67,10 +73,60 @@ class ZigSyntheticLibrary(val project: Project) : SyntheticLibrary(), ItemPresen return roots } + companion object { + private const val ZIG_LIBRARY_ID = "Zig SDK" + private const val ZIG_MODULE_ID = "Zig" + suspend fun reload(project: Project, state: ZigProjectSettings) { + val moduleId = ModuleId(ZIG_MODULE_ID) + val workspaceModel = WorkspaceModel.getInstance(project) + if (moduleId !in workspaceModel.currentSnapshot) { + val baseModuleDir = project.guessProjectDir()?.toVirtualFileUrl(workspaceModel.getVirtualFileUrlManager()) ?: return + + val moduleEntitySource = LegacyBridgeJpsEntitySourceFactory.getInstance(project) + .createEntitySourceForModule(baseModuleDir, null) + + val moduleEntity = ModuleEntity(ZIG_MODULE_ID, emptyList(), moduleEntitySource) + workspaceModel.update("Add new module") {builder -> + builder.addEntity(moduleEntity) + } + } + val moduleEntity = workspaceModel.currentSnapshot.resolve(moduleId) ?: return + val root = getRoot(state, project) ?: return + val libRoot = LibraryRoot(root.toVirtualFileUrl(workspaceModel.getVirtualFileUrlManager()), LibraryRootTypeId.SOURCES) + val libraryTableId = LibraryTableId.ProjectLibraryTableId + val libraryId = LibraryId(ZIG_LIBRARY_ID, libraryTableId) + if (libraryId in workspaceModel.currentSnapshot) { + val library = workspaceModel.currentSnapshot.resolve(libraryId) ?: return + workspaceModel.update("Update library") { builder -> + builder.modifyLibraryEntity(library) { + roots.clear() + roots.add(libRoot) + } + } + } else { + val libraryEntitySource = LegacyBridgeJpsEntitySourceFactory + .getInstance(project) + .createEntitySourceForProjectLibrary(null) + val libraryEntity = LibraryEntity( + ZIG_LIBRARY_ID, + libraryTableId, emptyList(), + libraryEntitySource + ) { + roots.add(libRoot) + } + workspaceModel.update("Add new library") { builder -> + builder.addEntity(libraryEntity) + } + } + workspaceModel.update("Link dep") { builder -> + builder.modifyModuleEntity(moduleEntity) { + dependencies.add(LibraryDependency(libraryId, false, DependencyScope.COMPILE)) + } + } + } + } } - - private fun getName( state: ZigProjectSettings, project: Project @@ -80,29 +136,29 @@ private fun getName( return "Zig $version" } -private fun getRoots( +suspend fun getRoot( state: ZigProjectSettings, project: Project -): Set { +): VirtualFile? { val toolchain = state.toolchain if (state.overrideStdPath) run { val ePathStr = state.explicitPathToStd ?: return@run val ePath = ePathStr.toNioPathOrNull() ?: return@run if (ePath.isAbsolute) { val roots = ePath.refreshAndFindVirtualDirectory() ?: return@run - return setOf(roots) + return roots } else if (toolchain != null) { val stdPath = toolchain.location.resolve(ePath) if (stdPath.isAbsolute) { val roots = stdPath.refreshAndFindVirtualDirectory() ?: return@run - return setOf(roots) + return roots } } } if (toolchain != null) { - val stdPath = runBlocking { toolchain.zig.getEnv(project) }?.stdPath(toolchain, project) ?: return emptySet() - val roots = stdPath.refreshAndFindVirtualDirectory() ?: return emptySet() - return setOf(roots) + val stdPath = toolchain.zig.getEnv(project)?.stdPath(toolchain, project) ?: return null + val roots = stdPath.refreshAndFindVirtualDirectory() ?: return null + return roots } - return emptySet() + return null } \ No newline at end of file