feat: Integrate stdlib into workspace model

This commit is contained in:
FalsePattern 2025-03-10 23:59:07 +01:00
parent 88df5b1426
commit 16fb4b57b7
Signed by: falsepattern
GPG key ID: E930CDEC50C50E23
3 changed files with 79 additions and 15 deletions

View file

@ -19,6 +19,8 @@ Changelog structure reference:
### Added ### Added
- Zig
- Changing the zig standard library path in the project settings now properly updates the dependency
- ZLS - ZLS
- All of the config options are now exposed in the GUI - All of the config options are now exposed in the GUI

View file

@ -22,15 +22,18 @@
package com.falsepattern.zigbrains.project.settings 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.components.*
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import kotlinx.coroutines.launch
@Service(Service.Level.PROJECT) @Service(Service.Level.PROJECT)
@State( @State(
name = "ZigProjectSettings", name = "ZigProjectSettings",
storages = [Storage("zigbrains.xml")] storages = [Storage("zigbrains.xml")]
) )
class ZigProjectSettingsService: PersistentStateComponent<ZigProjectSettings> { class ZigProjectSettingsService(val project: Project): PersistentStateComponent<ZigProjectSettings> {
@Volatile @Volatile
private var state = ZigProjectSettings() private var state = ZigProjectSettings()
@ -40,10 +43,13 @@ class ZigProjectSettingsService: PersistentStateComponent<ZigProjectSettings> {
fun setState(value: ZigProjectSettings) { fun setState(value: ZigProjectSettings) {
this.state = value this.state = value
zigCoroutineScope.launch {
ZigSyntheticLibrary.reload(project, value)
}
} }
override fun loadState(state: ZigProjectSettings) { override fun loadState(state: ZigProjectSettings) {
this.state = state setState(state)
} }
fun isModified(otherData: ZigProjectSettings): Boolean { fun isModified(otherData: ZigProjectSettings): Boolean {

View file

@ -27,28 +27,34 @@ import com.falsepattern.zigbrains.project.settings.ZigProjectSettings
import com.falsepattern.zigbrains.project.settings.zigProjectSettings import com.falsepattern.zigbrains.project.settings.zigProjectSettings
import com.intellij.navigation.ItemPresentation import com.intellij.navigation.ItemPresentation
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.project.guessProjectDir
import com.intellij.openapi.roots.SyntheticLibrary import com.intellij.openapi.roots.SyntheticLibrary
import com.intellij.openapi.util.io.toNioPathOrNull import com.intellij.openapi.util.io.toNioPathOrNull
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.refreshAndFindVirtualDirectory 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 kotlinx.coroutines.runBlocking
import java.util.* import java.util.*
import javax.swing.Icon import javax.swing.Icon
class ZigSyntheticLibrary(val project: Project) : SyntheticLibrary(), ItemPresentation { class ZigSyntheticLibrary(val project: Project) : SyntheticLibrary(), ItemPresentation {
private var state: ZigProjectSettings = project.zigProjectSettings.state.copy()
private val roots by lazy { private val roots by lazy {
getRoots(project.zigProjectSettings.state, project) runBlocking {getRoot(state, project)}?.let { setOf(it) } ?: emptySet()
} }
private val name by lazy { private val name by lazy {
getName(project.zigProjectSettings.state, project) getName(state, project)
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (other !is ZigSyntheticLibrary) if (other !is ZigSyntheticLibrary)
return false return false
return roots == other.roots return state == other.state
} }
override fun hashCode(): Int { override fun hashCode(): Int {
@ -67,9 +73,59 @@ class ZigSyntheticLibrary(val project: Project) : SyntheticLibrary(), ItemPresen
return roots 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( private fun getName(
state: ZigProjectSettings, state: ZigProjectSettings,
@ -80,29 +136,29 @@ private fun getName(
return "Zig $version" return "Zig $version"
} }
private fun getRoots( suspend fun getRoot(
state: ZigProjectSettings, state: ZigProjectSettings,
project: Project project: Project
): Set<VirtualFile> { ): VirtualFile? {
val toolchain = state.toolchain val toolchain = state.toolchain
if (state.overrideStdPath) run { if (state.overrideStdPath) run {
val ePathStr = state.explicitPathToStd ?: return@run val ePathStr = state.explicitPathToStd ?: return@run
val ePath = ePathStr.toNioPathOrNull() ?: return@run val ePath = ePathStr.toNioPathOrNull() ?: return@run
if (ePath.isAbsolute) { if (ePath.isAbsolute) {
val roots = ePath.refreshAndFindVirtualDirectory() ?: return@run val roots = ePath.refreshAndFindVirtualDirectory() ?: return@run
return setOf(roots) return roots
} else if (toolchain != null) { } else if (toolchain != null) {
val stdPath = toolchain.location.resolve(ePath) val stdPath = toolchain.location.resolve(ePath)
if (stdPath.isAbsolute) { if (stdPath.isAbsolute) {
val roots = stdPath.refreshAndFindVirtualDirectory() ?: return@run val roots = stdPath.refreshAndFindVirtualDirectory() ?: return@run
return setOf(roots) return roots
} }
} }
} }
if (toolchain != null) { if (toolchain != null) {
val stdPath = runBlocking { toolchain.zig.getEnv(project) }?.stdPath(toolchain, project) ?: return emptySet() val stdPath = toolchain.zig.getEnv(project)?.stdPath(toolchain, project) ?: return null
val roots = stdPath.refreshAndFindVirtualDirectory() ?: return emptySet() val roots = stdPath.refreshAndFindVirtualDirectory() ?: return null
return setOf(roots) return roots
} }
return emptySet() return null
} }