more progress

This commit is contained in:
FalsePattern 2025-04-05 19:46:48 +02:00
parent f53b0e3283
commit e737058cb5
Signed by: falsepattern
GPG key ID: E930CDEC50C50E23
14 changed files with 246 additions and 244 deletions

View file

@ -26,6 +26,7 @@ import com.falsepattern.zigbrains.direnv.DirenvCmd
import com.falsepattern.zigbrains.project.settings.zigProjectSettings
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainProvider
import com.falsepattern.zigbrains.project.toolchain.suggestZigToolchain
import com.intellij.ide.BrowserUtil
import com.intellij.ide.plugins.PluginManager
import com.intellij.notification.Notification
@ -80,7 +81,7 @@ class ZBStartup: ProjectActivity {
data.putUserData(LocalZigToolchain.DIRENV_KEY,
DirenvCmd.direnvInstalled() && !project.isDefault && zigProjectState.direnv
)
val tc = ZigToolchainProvider.suggestToolchain(project, data) ?: return
val tc = project.suggestZigToolchain(data) ?: return
if (tc is LocalZigToolchain) {
zigProjectState.toolchainPath = tc.location.pathString
project.zigProjectSettings.state = zigProjectState

View file

@ -26,6 +26,7 @@ import com.falsepattern.zigbrains.ZigBrainsBundle
import com.falsepattern.zigbrains.direnv.DirenvCmd
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainProvider
import com.falsepattern.zigbrains.project.toolchain.suggestZigToolchain
import com.falsepattern.zigbrains.shared.coroutine.launchWithEDT
import com.falsepattern.zigbrains.shared.coroutine.withEDTContext
import com.falsepattern.zigbrains.shared.zigCoroutineScope
@ -107,7 +108,7 @@ class ZigProjectSettingsPanel(private val holder: ZigProjectConfigurationProvide
return
val data = UserDataHolderBase()
data.putUserData(LocalZigToolchain.DIRENV_KEY, !project.isDefault && direnv.isSelected && DirenvCmd.direnvInstalled())
val tc = ZigToolchainProvider.suggestToolchain(project, data) ?: return
val tc = project.suggestZigToolchain(project) ?: return
if (tc !is LocalZigToolchain) {
TODO("Implement non-local zig toolchain in config")
}

View file

@ -25,7 +25,12 @@ package com.falsepattern.zigbrains.project.toolchain
import com.falsepattern.zigbrains.project.toolchain.tools.ZigCompilerTool
import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.openapi.project.Project
import com.intellij.util.xmlb.Converter
import com.intellij.util.xmlb.annotations.Attribute
import com.intellij.util.xmlb.annotations.MapAnnotation
import com.intellij.util.xmlb.annotations.OptionTag
import java.nio.file.Path
import java.util.UUID
abstract class AbstractZigToolchain {
@ -36,4 +41,13 @@ abstract class AbstractZigToolchain {
abstract suspend fun patchCommandLine(commandLine: GeneralCommandLine, project: Project? = null): GeneralCommandLine
abstract fun pathToExecutable(toolName: String, project: Project? = null): Path
data class Ref(
@JvmField
@Attribute
val marker: String? = null,
@JvmField
@MapAnnotation(surroundWithTag = false)
val data: Map<String, String>? = null,
)
}

View file

@ -33,8 +33,10 @@ import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.util.io.toNioPathOrNull
import com.intellij.openapi.vfs.toNioPathOrNull
import java.nio.file.Path
import java.util.UUID
import kotlin.io.path.pathString
class LocalZigToolchain(var location: Path, var std: Path? = null, var name: String? = null): AbstractZigToolchain() {
data class LocalZigToolchain(val location: Path, val std: Path? = null, val name: String? = null): AbstractZigToolchain() {
override fun workingDirectory(project: Project?): Path? {
return project?.guessProjectDir()?.toNioPathOrNull()
}

View file

@ -25,22 +25,28 @@ package com.falsepattern.zigbrains.project.toolchain
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.NamedConfigurable
import com.intellij.openapi.util.NlsContexts
import com.intellij.openapi.util.NlsSafe
import com.intellij.ui.dsl.builder.panel
import kotlinx.coroutines.runBlocking
import java.util.UUID
import javax.swing.JComponent
class LocalZigToolchainConfigurable(
val toolchain: LocalZigToolchain,
val uuid: UUID,
toolchain: LocalZigToolchain,
private val project: Project
): NamedConfigurable<LocalZigToolchain>() {
): NamedConfigurable<UUID>() {
var toolchain: LocalZigToolchain = toolchain
set(value) {
zigToolchainList.setToolchain(uuid, value)
field = value
}
private var myView: LocalZigToolchainPanel? = null
override fun setDisplayName(name: @NlsSafe String?) {
toolchain.name = name
override fun setDisplayName(name: String?) {
toolchain = toolchain.copy(name = name)
}
override fun getEditableObject(): LocalZigToolchain? {
return toolchain
override fun getEditableObject(): UUID {
return uuid
}
override fun getBannerSlogan(): @NlsContexts.DetailedDescription String? {
@ -65,7 +71,7 @@ class LocalZigToolchainConfigurable(
val version = toolchain.zig.let { runBlocking { it.getEnv(project) } }.getOrNull()?.version
if (version != null) {
theName = "Zig $version"
toolchain.name = theName
toolchain = toolchain.copy(name = theName)
}
}
return theName

View file

@ -101,12 +101,10 @@ class LocalZigToolchainPanel() : Disposable {
}
fun apply(cfg: LocalZigToolchainConfigurable): Boolean {
cfg.displayName = nameField.text
val tc = cfg.toolchain
val location = this.pathToToolchain.text.ifBlank { null }?.toNioPathOrNull() ?: return false
val std = if (stdFieldOverride.isSelected) pathToStd.text.ifBlank { null }?.toNioPathOrNull() else null
tc.location = location
tc.std = std
cfg.toolchain = tc.copy(location = location, std = std, name = nameField.text ?: "")
return true
}

View file

@ -26,12 +26,22 @@ import com.falsepattern.zigbrains.direnv.DirenvCmd
import com.falsepattern.zigbrains.direnv.emptyEnv
import com.falsepattern.zigbrains.project.settings.zigProjectSettings
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.ui.configuration.SdkPopupBuilder
import com.intellij.openapi.ui.NamedConfigurable
import com.intellij.openapi.util.UserDataHolder
import com.intellij.openapi.util.io.toNioPathOrNull
import com.intellij.util.EnvironmentUtil
import com.intellij.util.system.OS
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.serialization.json.*
import java.io.File
import java.util.UUID
import kotlin.io.path.pathString
class LocalZigToolchainProvider: ZigToolchainProvider<LocalZigToolchain> {
class LocalZigToolchainProvider: ZigToolchainProvider {
override suspend fun suggestToolchain(project: Project?, extraData: UserDataHolder): LocalZigToolchain? {
val env = if (project != null && (extraData.getUserData(LocalZigToolchain.DIRENV_KEY) ?: project.zigProjectSettings.state.direnv)) {
DirenvCmd.importDirenv(project)
@ -45,22 +55,47 @@ class LocalZigToolchainProvider: ZigToolchainProvider<LocalZigToolchain> {
override val serialMarker: String
get() = "local"
override fun deserialize(data: JsonElement): LocalZigToolchain? {
if (data !is JsonObject)
return null
val loc = data["location"] as? JsonPrimitive ?: return null
val path = loc.content.toNioPathOrNull() ?: return null
return LocalZigToolchain(path)
override fun deserialize(data: Map<String, String>): AbstractZigToolchain? {
val location = data["location"]?.toNioPathOrNull() ?: return null
val std = data["std"]?.toNioPathOrNull()
val name = data["name"]
return LocalZigToolchain(location, std, name)
}
override fun canSerialize(toolchain: AbstractZigToolchain): Boolean {
override fun isCompatible(toolchain: AbstractZigToolchain): Boolean {
return toolchain is LocalZigToolchain
}
override fun serialize(toolchain: LocalZigToolchain): JsonElement {
return buildJsonObject {
put("location", toolchain.location.pathString)
}
override fun serialize(toolchain: AbstractZigToolchain): Map<String, String> {
toolchain as LocalZigToolchain
val map = HashMap<String, String>()
toolchain.location.pathString.let { map["location"] = it }
toolchain.std?.pathString?.let { map["std"] = it }
toolchain.name?.let { map["name"] = it }
return map
}
override fun matchesSuggestion(
toolchain: AbstractZigToolchain,
suggestion: AbstractZigToolchain
): Boolean {
toolchain as LocalZigToolchain
suggestion as LocalZigToolchain
return toolchain.location == suggestion.location
}
override fun createConfigurable(
uuid: UUID,
toolchain: AbstractZigToolchain,
project: Project
): NamedConfigurable<UUID> {
toolchain as LocalZigToolchain
return LocalZigToolchainConfigurable(uuid, toolchain, project)
}
override fun suggestToolchains(): List<AbstractZigToolchain> {
val res = HashSet<String>()
EnvironmentUtil.getValue("PATH")?.split(File.pathSeparatorChar)?.let { res.addAll(it.toList()) }
return res.mapNotNull { LocalZigToolchain.tryFromPathString(it) }
}
}

View file

@ -1,92 +0,0 @@
/*
* This file is part of ZigBrains.
*
* Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* ZigBrains is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, only version 3 of the License.
*
* ZigBrains 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with ZigBrains. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.zigbrains.project.toolchain
import com.falsepattern.zigbrains.direnv.emptyEnv
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.AdditionalDataConfigurable
import com.intellij.openapi.projectRoots.SdkAdditionalData
import com.intellij.openapi.projectRoots.SdkModel
import com.intellij.openapi.projectRoots.SdkModificator
import com.intellij.openapi.projectRoots.SdkType
import com.intellij.openapi.util.UserDataHolderBase
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.EnvironmentUtil
import com.intellij.util.asSafely
import com.intellij.util.system.OS
import kotlinx.coroutines.runBlocking
import org.jdom.Element
import org.jetbrains.annotations.Nls
import java.io.File
import kotlin.io.path.pathString
class ZigSDKType: SdkType("Zig") {
override fun suggestHomePath(): String? {
return null
}
private fun getPathEnv(path: String): ZigToolchainEnvironmentSerializable? {
return LocalZigToolchain.tryFromPathString(path)?.zig?.let { runBlocking { it.getEnv(null) } }?.getOrNull()
}
override fun isValidSdkHome(path: String): Boolean {
return LocalZigToolchain.tryFromPathString(path) != null
}
override fun suggestSdkName(currentSdkName: String?, sdkHome: String): String {
return getVersionString(sdkHome)?.let { "Zig $it" } ?: currentSdkName ?: "Zig"
}
override fun getVersionString(sdkHome: String): String? {
return getPathEnv(sdkHome)?.version
}
override fun suggestHomePaths(): Collection<String?> {
val res = HashSet<String>()
EnvironmentUtil.getValue("PATH")?.split(File.pathSeparatorChar)?.let { res.addAll(it.toList()) }
if (OS.CURRENT != OS.Windows) {
EnvironmentUtil.getValue("HOME")?.let { res.add("$it/.local/share/zigup") }
EnvironmentUtil.getValue("XDG_DATA_HOME")?.let { res.add("$it/zigup") }
}
return res
}
override fun createAdditionalDataConfigurable(
sdkModel: SdkModel,
sdkModificator: SdkModificator
): AdditionalDataConfigurable? {
return null
}
override fun getPresentableName(): @Nls(capitalization = Nls.Capitalization.Title) String {
return "Zig"
}
override fun saveAdditionalData(additionalData: SdkAdditionalData, additional: Element) {
}
override fun isRelevantForFile(project: Project, file: VirtualFile): Boolean {
return file.extension == "zig" || file.extension == "zon"
}
}

View file

@ -1,30 +0,0 @@
/*
* This file is part of ZigBrains.
*
* Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* ZigBrains is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, only version 3 of the License.
*
* ZigBrains 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with ZigBrains. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.zigbrains.project.toolchain
import com.intellij.util.xmlb.annotations.OptionTag
data class ZigToolchainList(
@get:OptionTag(converter = ZigToolchainListConverter::class)
var toolchains: List<AbstractZigToolchain> = emptyList(),
)

View file

@ -23,17 +23,51 @@
package com.falsepattern.zigbrains.project.toolchain
import com.falsepattern.zigbrains.ZigBrainsBundle
import com.intellij.ide.projectView.TreeStructureProvider
import com.intellij.ide.util.treeView.AbstractTreeStructure
import com.intellij.ide.util.treeView.AbstractTreeStructureBase
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.Presentation
import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.roots.ui.SdkAppearanceService
import com.intellij.openapi.roots.ui.configuration.SdkListPresenter
import com.intellij.openapi.roots.ui.configuration.SdkPopupFactory
import com.intellij.openapi.ui.MasterDetailsComponent
import com.intellij.openapi.util.io.toNioPathOrNull
import com.intellij.openapi.ui.popup.JBPopupFactory
import com.intellij.openapi.ui.popup.util.BaseTreePopupStep
import com.intellij.openapi.util.NlsContexts
import com.intellij.ui.CollectionListModel
import com.intellij.ui.ColoredListCellRenderer
import com.intellij.ui.SimpleTextAttributes
import com.intellij.ui.components.JBLabel
import com.intellij.ui.components.JBPanel
import com.intellij.ui.components.panels.HorizontalLayout
import com.intellij.ui.dsl.builder.panel
import com.intellij.ui.dsl.gridLayout.UnscaledGaps
import com.intellij.ui.popup.list.ComboBoxPopup
import com.intellij.ui.treeStructure.SimpleNode
import com.intellij.ui.treeStructure.SimpleTreeStructure
import com.intellij.util.IconUtil
import com.intellij.util.ui.EmptyIcon
import com.intellij.util.ui.UIUtil.FontColor
import java.awt.Component
import java.awt.LayoutManager
import java.util.*
import java.util.function.Consumer
import javax.swing.AbstractListModel
import javax.swing.BoxLayout
import javax.swing.DefaultListModel
import javax.swing.JComponent
import javax.swing.JList
import javax.swing.JPanel
import javax.swing.ListCellRenderer
import javax.swing.ListModel
import javax.swing.SwingConstants
import javax.swing.tree.DefaultTreeModel
import kotlin.io.path.pathString
class ZigToolchainListEditor(): MasterDetailsComponent() {
private var isTreeInitialized = false
@ -46,29 +80,62 @@ class ZigToolchainListEditor(): MasterDetailsComponent() {
return super.createComponent()
}
class ToolchainContext(private val project: Project?, private val model: ListModel<Any>): ComboBoxPopup.Context<Any> {
override fun getProject(): Project? {
return project
}
override fun getModel(): ListModel<Any> {
return model
}
override fun getRenderer(): ListCellRenderer<in Any> {
return object: ColoredListCellRenderer<Any>() {
override fun customizeCellRenderer(
list: JList<out Any?>,
value: Any?,
index: Int,
selected: Boolean,
hasFocus: Boolean
) {
icon = EMPTY_ICON
if (value is LocalZigToolchain) {
icon = IconUtil.addIcon
append(SdkListPresenter.presentDetectedSdkPath(value.location.pathString))
if (value.name != null) {
append(" ")
append(value.name, SimpleTextAttributes.GRAYED_ATTRIBUTES)
}
}
}
}
}
}
class ToolchainPopup(context: ToolchainContext,
selected: Any?,
onItemSelected: Consumer<Any>
): ComboBoxPopup<Any>(context, selected, onItemSelected) {
}
override fun createActions(fromPopup: Boolean): List<AnAction?>? {
val add = object : DumbAwareAction({"lmaoo"}, Presentation.NULL_STRING, IconUtil.addIcon) {
override fun actionPerformed(e: AnActionEvent) {
SdkPopupFactory
.newBuilder()
.withSdkTypeFilter { it is ZigSDKType }
.onSdkSelected {
val path = it.homePath?.toNioPathOrNull() ?: return@onSdkSelected
val toolchain = LocalZigToolchain(path)
zigToolchainList.state = ZigToolchainList(zigToolchainList.state.toolchains + listOf(toolchain))
addLocalToolchain(toolchain)
(myTree.model as DefaultTreeModel).reload()
}
.buildPopup()
.showPopup(e)
val toolchains = suggestZigToolchains(zigToolchainList.toolchains.map { it.second }.toList())
val final = ArrayList<Any>()
final.addAll(toolchains)
val popup = ToolchainPopup(ToolchainContext(null, CollectionListModel(final)), null, {})
popup.showInBestPositionFor(e.dataContext)
}
}
return listOf(add, MyDeleteAction())
}
override fun onItemDeleted(item: Any?) {
if (item is AbstractZigToolchain) {
zigToolchainList.state = ZigToolchainList(zigToolchainList.state.toolchains.filter { it != item })
if (item is UUID) {
zigToolchainList.removeToolchain(item)
}
super.onItemDeleted(item)
}
@ -82,18 +149,20 @@ class ZigToolchainListEditor(): MasterDetailsComponent() {
override fun getDisplayName() = ZigBrainsBundle.message("settings.toolchains.title")
private fun addLocalToolchain(toolchain: LocalZigToolchain) {
val node = MyNode(LocalZigToolchainConfigurable(toolchain, ProjectManager.getInstance().defaultProject))
private fun addLocalToolchain(uuid: UUID, toolchain: LocalZigToolchain) {
val node = MyNode(LocalZigToolchainConfigurable(uuid, toolchain, ProjectManager.getInstance().defaultProject))
addNode(node, myRoot)
}
private fun reloadTree() {
myRoot.removeAllChildren()
zigToolchainList.state.toolchains.forEach { toolchain ->
zigToolchainList.toolchains.forEach { (uuid, toolchain) ->
if (toolchain is LocalZigToolchain) {
addLocalToolchain(toolchain)
addLocalToolchain(uuid, toolchain)
}
}
(myTree.model as DefaultTreeModel).reload()
}
}
private val EMPTY_ICON = EmptyIcon.create(1, 16)

View file

@ -22,37 +22,50 @@
package com.falsepattern.zigbrains.project.toolchain
import com.falsepattern.zigbrains.project.settings.ZigProjectSettings
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
import com.intellij.util.xmlb.annotations.MapAnnotation
import java.util.UUID
@Service(Service.Level.APP)
@State(
name = "ZigProjectSettings",
name = "ZigToolchainList",
storages = [Storage("zigbrains.xml")]
)
class ZigToolchainListService(): PersistentStateComponent<ZigToolchainList> {
@Volatile
private var state = ZigToolchainList()
override fun getState(): ZigToolchainList {
return state.copy()
class ZigToolchainListService: SerializablePersistentStateComponent<ZigToolchainListService.State>(State()) {
fun setToolchain(uuid: UUID, toolchain: AbstractZigToolchain) {
updateState {
val newMap = HashMap<String, AbstractZigToolchain.Ref>()
newMap.putAll(it.toolchains)
newMap.put(uuid.toString(), toolchain.toRef())
it.copy(toolchains = newMap)
}
}
fun setState(value: ZigToolchainList) {
this.state = value
fun getToolchain(uuid: UUID): AbstractZigToolchain? {
return state.toolchains[uuid.toString()]?.resolve()
}
override fun loadState(state: ZigToolchainList) {
setState(state)
fun removeToolchain(uuid: UUID) {
val str = uuid.toString()
updateState {
it.copy(toolchains = it.toolchains.filter { it.key != str })
}
}
fun isModified(otherData: ZigToolchainList): Boolean {
return state != otherData
val toolchains: Sequence<Pair<UUID, AbstractZigToolchain>>
get() = state.toolchains
.asSequence()
.mapNotNull {
val uuid = UUID.fromString(it.key) ?: return@mapNotNull null
val tc = it.value.resolve() ?: return@mapNotNull null
uuid to tc
}
data class State(
@JvmField
@MapAnnotation(surroundKeyWithTag = false, surroundValueWithTag = false)
val toolchains: Map<String, AbstractZigToolchain.Ref> = emptyMap(),
)
}
val zigToolchainList get() = service<ZigToolchainListService>()

View file

@ -22,71 +22,57 @@
package com.falsepattern.zigbrains.project.toolchain
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainProvider.Companion.EXTENSION_POINT_NAME
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.NamedConfigurable
import com.intellij.openapi.util.UserDataHolder
import com.intellij.util.asSafely
import com.intellij.util.xmlb.Converter
import kotlinx.serialization.json.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapMerge
import kotlinx.coroutines.flow.flattenConcat
import kotlinx.coroutines.flow.map
import java.util.UUID
sealed interface ZigToolchainProvider<in T: AbstractZigToolchain> {
private val EXTENSION_POINT_NAME = ExtensionPointName.create<ZigToolchainProvider>("com.falsepattern.zigbrains.toolchainProvider")
sealed interface ZigToolchainProvider {
suspend fun suggestToolchain(project: Project?, extraData: UserDataHolder): AbstractZigToolchain?
val serialMarker: String
fun deserialize(data: JsonElement): AbstractZigToolchain?
fun canSerialize(toolchain: AbstractZigToolchain): Boolean
fun serialize(toolchain: T): JsonElement
companion object {
val EXTENSION_POINT_NAME = ExtensionPointName.create<ZigToolchainProvider<*>>("com.falsepattern.zigbrains.toolchainProvider")
suspend fun suggestToolchain(project: Project?, extraData: UserDataHolder): AbstractZigToolchain? {
return EXTENSION_POINT_NAME.extensionList.firstNotNullOfOrNull { it.suggestToolchain(project, extraData) }
fun isCompatible(toolchain: AbstractZigToolchain): Boolean
fun deserialize(data: Map<String, String>): AbstractZigToolchain?
fun serialize(toolchain: AbstractZigToolchain): Map<String, String>
fun matchesSuggestion(toolchain: AbstractZigToolchain, suggestion: AbstractZigToolchain): Boolean
fun createConfigurable(uuid: UUID, toolchain: AbstractZigToolchain, project: Project): NamedConfigurable<UUID>
fun suggestToolchains(): List<AbstractZigToolchain>
}
fun fromJson(json: JsonObject): AbstractZigToolchain? {
val marker = (json["marker"] as? JsonPrimitive)?.contentOrNull ?: return null
val data = json["data"] ?: return null
fun AbstractZigToolchain.Ref.resolve(): AbstractZigToolchain? {
val marker = this.marker ?: return null
val data = this.data ?: return null
val provider = EXTENSION_POINT_NAME.extensionList.find { it.serialMarker == marker } ?: return null
return provider.deserialize(data)
}
fun toJson(tc: AbstractZigToolchain): JsonObject? {
val provider = EXTENSION_POINT_NAME.extensionList.find { it.canSerialize(tc) } ?: return null
return buildJsonObject {
put("marker", provider.serialMarker)
put("data", provider.serialize(tc))
}
}
}
fun AbstractZigToolchain.toRef(): AbstractZigToolchain.Ref {
val provider = EXTENSION_POINT_NAME.extensionList.find { it.isCompatible(this) } ?: throw IllegalStateException()
return AbstractZigToolchain.Ref(provider.serialMarker, provider.serialize(this))
}
@Suppress("UNCHECKED_CAST")
private fun <T: AbstractZigToolchain> ZigToolchainProvider<T>.serialize(toolchain: AbstractZigToolchain) = serialize(toolchain as T)
class ZigToolchainConverter: Converter<AbstractZigToolchain>() {
override fun fromString(value: String): AbstractZigToolchain? {
val json = Json.parseToJsonElement(value) as? JsonObject ?: return null
return ZigToolchainProvider.fromJson(json)
suspend fun Project?.suggestZigToolchain(extraData: UserDataHolder): AbstractZigToolchain? {
return EXTENSION_POINT_NAME.extensionList.firstNotNullOfOrNull { it.suggestToolchain(this, extraData) }
}
override fun toString(value: AbstractZigToolchain): String? {
return ZigToolchainProvider.toJson(value)?.toString()
}
fun AbstractZigToolchain.createNamedConfigurable(uuid: UUID, project: Project): NamedConfigurable<UUID> {
val provider = EXTENSION_POINT_NAME.extensionList.find { it.isCompatible(this) } ?: throw IllegalStateException()
return provider.createConfigurable(uuid, this, project)
}
class ZigToolchainListConverter: Converter<List<AbstractZigToolchain>>() {
override fun fromString(value: String): List<AbstractZigToolchain> {
val json = Json.parseToJsonElement(value) as? JsonArray ?: return emptyList()
return json.mapNotNull { it.asSafely<JsonObject>()?.let { ZigToolchainProvider.fromJson(it) } }
}
override fun toString(value: List<AbstractZigToolchain>): String {
return buildJsonArray {
value.mapNotNull { ZigToolchainProvider.toJson(it) }.forEach {
add(it)
}
}.toString()
fun suggestZigToolchains(existing: List<AbstractZigToolchain>): List<AbstractZigToolchain> {
return EXTENSION_POINT_NAME.extensionList.flatMap { ext ->
val compatibleExisting = existing.filter { ext.isCompatible(it) }
val suggestions = ext.suggestToolchains()
suggestions.filter { suggestion -> compatibleExisting.none { existing -> ext.matchesSuggestion(existing, suggestion) } }
}
}

View file

@ -150,7 +150,6 @@
id="ZigToolchainConfigurable"
displayName="Toolchain"
/>
<sdkType implementation="com.falsepattern.zigbrains.project.toolchain.ZigSDKType"/>
<programRunner
implementation="com.falsepattern.zigbrains.project.run.ZigRegularRunner"

View file

@ -37,7 +37,7 @@ class ToolchainZLSConfigProvider: SuspendingZLSConfigProvider {
override suspend fun getEnvironment(project: Project, previous: ZLSConfig): ZLSConfig {
val svc = project.zigProjectSettings
var state = svc.state
val toolchain = state.toolchain ?: ZigToolchainProvider.suggestToolchain(project, UserDataHolderBase()) ?: return previous
val toolchain = state.toolchain ?: project.suggestZigToolchain(UserDataHolderBase()) ?: return previous
val env = toolchain.zig.getEnv(project).getOrElse { throwable ->
throwable.printStackTrace()