From 2c500d40a5515bc5c492580a790aae3f7b4c8992 Mon Sep 17 00:00:00 2001 From: FalsePattern Date: Sun, 6 Apr 2025 02:25:22 +0200 Subject: [PATCH] work work work --- .../project/toolchain/LocalZigToolchain.kt | 9 +- .../toolchain/LocalZigToolchainProvider.kt | 25 ++ .../toolchain/ZigToolchainListEditor.kt | 336 ++++++++++++++---- .../toolchain/ZigToolchainListService.kt | 1 - .../project/toolchain/ZigToolchainProvider.kt | 7 + .../project/toolchain/ZigVersionInfo.kt | 106 ++++++ .../toolchain/tools/ZigCompilerTool.kt | 3 + .../shared/coroutine/CoroutinesUtil.kt | 6 + 8 files changed, 412 insertions(+), 81 deletions(-) create mode 100644 core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/ZigVersionInfo.kt diff --git a/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/LocalZigToolchain.kt b/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/LocalZigToolchain.kt index 2eeca5fe..397bd5f3 100644 --- a/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/LocalZigToolchain.kt +++ b/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/LocalZigToolchain.kt @@ -32,6 +32,7 @@ import com.intellij.openapi.util.KeyWithDefaultValue import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.util.io.toNioPathOrNull import com.intellij.openapi.vfs.toNioPathOrNull +import kotlinx.coroutines.runBlocking import java.nio.file.Path import java.util.UUID import kotlin.io.path.pathString @@ -71,10 +72,16 @@ data class LocalZigToolchain(val location: Path, val std: Path? = null, val name } fun tryFromPath(path: Path): LocalZigToolchain? { - val tc = LocalZigToolchain(path) + var tc = LocalZigToolchain(path) if (!tc.zig.fileValid()) { return null } + tc.zig + .getEnvBlocking(null) + .getOrNull() + ?.version + ?.let { "Zig $it" } + ?.let { tc = tc.copy(name = it) } return tc } } diff --git a/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/LocalZigToolchainProvider.kt b/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/LocalZigToolchainProvider.kt index d49f652b..d83e8419 100644 --- a/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/LocalZigToolchainProvider.kt +++ b/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/LocalZigToolchainProvider.kt @@ -30,8 +30,13 @@ 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.FileUtil import com.intellij.openapi.util.io.toNioPathOrNull +import com.intellij.openapi.util.text.StringUtil +import com.intellij.ui.SimpleColoredComponent +import com.intellij.ui.SimpleTextAttributes import com.intellij.util.EnvironmentUtil +import com.intellij.util.IconUtil import com.intellij.util.system.OS import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.asFlow @@ -98,4 +103,24 @@ class LocalZigToolchainProvider: ZigToolchainProvider { EnvironmentUtil.getValue("PATH")?.split(File.pathSeparatorChar)?.let { res.addAll(it.toList()) } return res.mapNotNull { LocalZigToolchain.tryFromPathString(it) } } + + override fun render(toolchain: AbstractZigToolchain, component: SimpleColoredComponent) { + toolchain as LocalZigToolchain + component.append(presentDetectedPath(toolchain.location.pathString)) + if (toolchain.name != null) { + component.append(" ") + component.append(toolchain.name, SimpleTextAttributes.GRAYED_ATTRIBUTES) + } + } +} + + +private fun presentDetectedPath(home: String, maxLength: Int = 50, suffixLength: Int = 30): String { + //for macOS, let's try removing Bundle internals + var home = home + home = StringUtil.trimEnd(home, "/Contents/Home") //NON-NLS + home = StringUtil.trimEnd(home, "/Contents/MacOS") //NON-NLS + home = FileUtil.getLocationRelativeToUserHome(home, false) + home = StringUtil.shortenTextWithEllipsis(home, maxLength, suffixLength) + return home } \ No newline at end of file diff --git a/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/ZigToolchainListEditor.kt b/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/ZigToolchainListEditor.kt index 778b1b64..a006aed4 100644 --- a/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/ZigToolchainListEditor.kt +++ b/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/ZigToolchainListEditor.kt @@ -22,111 +22,94 @@ package com.falsepattern.zigbrains.project.toolchain +import com.falsepattern.zigbrains.Icons 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.falsepattern.zigbrains.shared.coroutine.withEDTContext +import com.falsepattern.zigbrains.shared.zigCoroutineScope +import com.intellij.icons.AllIcons import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.Presentation +import com.intellij.openapi.application.ModalityState +import com.intellij.openapi.application.PathManager +import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory +import com.intellij.openapi.progress.coroutineToIndicator 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.ComboBox +import com.intellij.openapi.ui.DialogBuilder import com.intellij.openapi.ui.MasterDetailsComponent -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.openapi.util.Disposer +import com.intellij.openapi.util.io.toNioPathOrNull +import com.intellij.platform.ide.progress.ModalTaskOwner +import com.intellij.platform.ide.progress.TaskCancellation +import com.intellij.platform.ide.progress.withModalProgress +import com.intellij.platform.util.progress.withProgressText +import com.intellij.ui.* +import com.intellij.ui.components.JBList +import com.intellij.ui.components.panels.OpaquePanel +import com.intellij.ui.components.textFieldWithBrowseButton +import com.intellij.ui.dsl.builder.Align +import com.intellij.ui.dsl.builder.AlignX +import com.intellij.ui.dsl.builder.Cell 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.Consumer import com.intellij.util.IconUtil +import com.intellij.util.download.DownloadableFileService +import com.intellij.util.system.CpuArch +import com.intellij.util.text.SemVer import com.intellij.util.ui.EmptyIcon -import com.intellij.util.ui.UIUtil.FontColor +import com.intellij.util.ui.JBUI +import com.intellij.util.ui.UIUtil +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.withContext +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.decodeFromStream +import java.awt.BorderLayout import java.awt.Component -import java.awt.LayoutManager +import java.nio.file.Path import java.util.* -import java.util.function.Consumer -import javax.swing.AbstractListModel -import javax.swing.BoxLayout -import javax.swing.DefaultListModel +import javax.accessibility.AccessibleContext +import javax.swing.DefaultComboBoxModel 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.border.Border +import javax.swing.event.DocumentEvent import javax.swing.tree.DefaultTreeModel -import kotlin.io.path.pathString -class ZigToolchainListEditor(): MasterDetailsComponent() { +class ZigToolchainListEditor() : MasterDetailsComponent() { private var isTreeInitialized = false + private var myComponent: JComponent? = null override fun createComponent(): JComponent { if (!isTreeInitialized) { initTree() isTreeInitialized = true } - return super.createComponent() + val comp = super.createComponent() + myComponent = comp + return comp } - class ToolchainContext(private val project: Project?, private val model: ListModel): ComboBoxPopup.Context { - override fun getProject(): Project? { - return project - } - - override fun getModel(): ListModel { - return model - } - - override fun getRenderer(): ListCellRenderer { - return object: ColoredListCellRenderer() { - override fun customizeCellRenderer( - list: JList, - 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 - ): ComboBoxPopup(context, selected, onItemSelected) { - - } - - override fun createActions(fromPopup: Boolean): List? { - val add = object : DumbAwareAction({"lmaoo"}, Presentation.NULL_STRING, IconUtil.addIcon) { + override fun createActions(fromPopup: Boolean): List { + val add = object : DumbAwareAction({ "lmaoo" }, Presentation.NULL_STRING, IconUtil.addIcon) { override fun actionPerformed(e: AnActionEvent) { val toolchains = suggestZigToolchains(zigToolchainList.toolchains.map { it.second }.toList()) - val final = ArrayList() - final.addAll(toolchains) - val popup = ToolchainPopup(ToolchainContext(null, CollectionListModel(final)), null, {}) + val final = ArrayList() + final.add(TCListElem.Download) + final.add(TCListElem.FromDisk) + final.add(Separator("Detected toolchains", true)) + final.addAll(toolchains.map { TCListElem.Toolchain(it) }) + val model = TCModel(final) + val context = TCContext(null, model) + val popup = TCPopup(context, null, ::onItemSelected) popup.showInBestPositionFor(e.dataContext) } } @@ -140,6 +123,61 @@ class ZigToolchainListEditor(): MasterDetailsComponent() { super.onItemDeleted(item) } + private fun onItemSelected(elem: TCListElem) { + when (elem) { + is TCListElem.Toolchain -> { + val uuid = UUID.randomUUID() + zigToolchainList.setToolchain(uuid, elem.toolchain) + addToolchain(uuid, elem.toolchain) + (myTree.model as DefaultTreeModel).reload() + } + + is TCListElem.Download -> { + zigCoroutineScope.async { + withEDTContext(ModalityState.stateForComponent(myComponent!!)) { + val info = withModalProgress(myComponent?.let { ModalTaskOwner.component(it) } ?: ModalTaskOwner.guess(), "Fetching zig version information", TaskCancellation.cancellable()) { + ZigVersionInfo.download() + } + val dialog = DialogBuilder() + val theList = ComboBox(DefaultComboBoxModel(info.map { it.first }.toTypedArray())) + val outputPath = textFieldWithBrowseButton( + null, + FileChooserDescriptorFactory.createSingleFolderDescriptor().withTitle(ZigBrainsBundle.message("dialog.title.zig-toolchain")) + ).also { + Disposer.register(dialog, it) + } + var archiveSizeCell: Cell<*>? = null + fun detect(item: String) { + outputPath.text = System.getProperty("user.home") + "/.zig/" + item + val data = info.firstOrNull { it.first == item } ?: return + val size = data.second.dist.size + val sizeMb = size / (1024f * 1024f) + archiveSizeCell?.comment?.text = "Archive size: %.2fMB".format(sizeMb) + } + theList.addItemListener { + detect(it.item as String) + } + val center = panel { + row("Version:") { + cell(theList).resizableColumn().align(AlignX.FILL) + } + row("Location:") { + cell(outputPath).resizableColumn().align(AlignX.FILL).apply { archiveSizeCell = comment("") } + } + } + detect(info[0].first) + dialog.centerPanel(center) + dialog.setTitle("Version Selector") + dialog.addCancelAction() + dialog.showAndGet() + } + } + } + + is TCListElem.FromDisk -> {} + } + } + override fun reset() { reloadTree() super.reset() @@ -149,20 +187,160 @@ class ZigToolchainListEditor(): MasterDetailsComponent() { override fun getDisplayName() = ZigBrainsBundle.message("settings.toolchains.title") - private fun addLocalToolchain(uuid: UUID, toolchain: LocalZigToolchain) { - val node = MyNode(LocalZigToolchainConfigurable(uuid, toolchain, ProjectManager.getInstance().defaultProject)) + private fun addToolchain(uuid: UUID, toolchain: AbstractZigToolchain) { + val node = MyNode(toolchain.createNamedConfigurable(uuid, ProjectManager.getInstance().defaultProject)) addNode(node, myRoot) } private fun reloadTree() { myRoot.removeAllChildren() zigToolchainList.toolchains.forEach { (uuid, toolchain) -> - if (toolchain is LocalZigToolchain) { - addLocalToolchain(uuid, toolchain) - } + addToolchain(uuid, toolchain) } (myTree.model as DefaultTreeModel).reload() } + + override fun disposeUIResources() { + super.disposeUIResources() + myComponent = null + } +} + +private sealed interface TCListElemIn + +private sealed interface TCListElem : TCListElemIn { + @JvmRecord + data class Toolchain(val toolchain: AbstractZigToolchain) : TCListElem + object Download : TCListElem + object FromDisk : TCListElem +} + +@JvmRecord +private data class Separator(val text: String, val separatorBar: Boolean) : TCListElemIn + +private class TCPopup( + context: TCContext, + selected: TCListElem?, + onItemSelected: Consumer, +) : ComboBoxPopup(context, selected, onItemSelected) + +private class TCModel private constructor(elements: List, private val separators: Map) : CollectionListModel(elements) { + companion object { + operator fun invoke(input: List): TCModel { + val separators = IdentityHashMap() + var lastSeparator: Separator? = null + val elements = ArrayList() + input.forEach { + when (it) { + is TCListElem -> { + if (lastSeparator != null) { + separators[it] = lastSeparator + lastSeparator = null + } + elements.add(it) + } + + is Separator -> lastSeparator = it + } + } + val model = TCModel(elements, separators) + return model + } + } + + fun separatorAbove(elem: TCListElem) = separators[elem] +} + +private class TCContext(private val project: Project?, private val model: TCModel) : ComboBoxPopup.Context { + override fun getProject(): Project? { + return project + } + + override fun getModel(): TCModel { + return model + } + + override fun getRenderer(): ListCellRenderer { + return TCCellRenderer(::getModel) + } +} + +private class TCCellRenderer(val getModel: () -> TCModel) : ColoredListCellRenderer() { + + override fun getListCellRendererComponent( + list: JList?, + value: TCListElem?, + index: Int, + selected: Boolean, + hasFocus: Boolean + ): Component? { + val component = super.getListCellRendererComponent(list, value, index, selected, hasFocus) as SimpleColoredComponent + val panel = object : CellRendererPanel(BorderLayout()) { + val myContext = component.accessibleContext + + override fun getAccessibleContext(): AccessibleContext? { + return myContext + } + + override fun setBorder(border: Border?) { + component.border = border + } + } + panel.add(component, BorderLayout.CENTER) + + component.isOpaque = true + list?.let { background = if (selected) it.selectionBackground else it.background } + + val model = getModel() + + val separator = value?.let { model.separatorAbove(it) } + + if (separator != null) { + val separatorText = separator.text + val vGap = if (UIUtil.isUnderNativeMacLookAndFeel()) 1 else 3 + val separatorComponent = GroupHeaderSeparator(JBUI.insets(vGap, 10, vGap, 0)) + separatorComponent.isHideLine = !separator.separatorBar + if (separatorText.isNotBlank()) { + separatorComponent.caption = separatorText + } + + val wrapper = OpaquePanel(BorderLayout()) + wrapper.add(separatorComponent, BorderLayout.CENTER) + list?.let { wrapper.background = it.background } + panel.add(wrapper, BorderLayout.NORTH) + } + + return panel + } + + override fun customizeCellRenderer( + list: JList, + value: TCListElem?, + index: Int, + selected: Boolean, + hasFocus: Boolean + ) { + icon = EMPTY_ICON + when (value) { + is TCListElem.Toolchain -> { + icon = Icons.Zig + val toolchain = value.toolchain + toolchain.render(this) + } + + is TCListElem.Download -> { + icon = AllIcons.Actions.Download + append("Download Zig\u2026") + } + + is TCListElem.FromDisk -> { + icon = AllIcons.General.OpenDisk + append("Add Zig from disk\u2026") + } + + null -> {} + } + } } private val EMPTY_ICON = EmptyIcon.create(1, 16) \ No newline at end of file diff --git a/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/ZigToolchainListService.kt b/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/ZigToolchainListService.kt index b999d1b5..b38bca87 100644 --- a/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/ZigToolchainListService.kt +++ b/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/ZigToolchainListService.kt @@ -63,7 +63,6 @@ class ZigToolchainListService: SerializablePersistentStateComponent = emptyMap(), ) } diff --git a/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/ZigToolchainProvider.kt b/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/ZigToolchainProvider.kt index f6c7eccf..7c4ded3b 100644 --- a/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/ZigToolchainProvider.kt +++ b/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/ZigToolchainProvider.kt @@ -26,6 +26,7 @@ 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.ui.SimpleColoredComponent import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.filter @@ -46,6 +47,7 @@ sealed interface ZigToolchainProvider { fun matchesSuggestion(toolchain: AbstractZigToolchain, suggestion: AbstractZigToolchain): Boolean fun createConfigurable(uuid: UUID, toolchain: AbstractZigToolchain, project: Project): NamedConfigurable fun suggestToolchains(): List + fun render(toolchain: AbstractZigToolchain, component: SimpleColoredComponent) } fun AbstractZigToolchain.Ref.resolve(): AbstractZigToolchain? { @@ -75,4 +77,9 @@ fun suggestZigToolchains(existing: List): List compatibleExisting.none { existing -> ext.matchesSuggestion(existing, suggestion) } } } +} + +fun AbstractZigToolchain.render(component: SimpleColoredComponent) { + val provider = EXTENSION_POINT_NAME.extensionList.find { it.isCompatible(this) } ?: throw IllegalStateException() + return provider.render(this, component) } \ No newline at end of file diff --git a/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/ZigVersionInfo.kt b/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/ZigVersionInfo.kt new file mode 100644 index 00000000..dcfc7758 --- /dev/null +++ b/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/ZigVersionInfo.kt @@ -0,0 +1,106 @@ +/* + * 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 . + */ + +package com.falsepattern.zigbrains.project.toolchain + +import com.intellij.openapi.application.PathManager +import com.intellij.openapi.progress.coroutineToIndicator +import com.intellij.openapi.util.io.toNioPathOrNull +import com.intellij.platform.util.progress.withProgressText +import com.intellij.util.asSafely +import com.intellij.util.download.DownloadableFileService +import com.intellij.util.system.CpuArch +import com.intellij.util.system.OS +import com.intellij.util.text.SemVer +import com.jetbrains.rd.util.firstOrNull +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.decodeFromJsonElement +import kotlinx.serialization.json.decodeFromStream +import java.nio.file.Path + +@JvmRecord +data class ZigVersionInfo(val date: String, val docs: String, val notes: String, val src: Tarball?, val dist: Tarball) { + companion object { + @OptIn(ExperimentalSerializationApi::class) + suspend fun download(): List> { + return withProgressText("Fetching zig version information") { + withContext(Dispatchers.IO) { + val service = DownloadableFileService.getInstance() + val desc = service.createFileDescription("https://ziglang.org/download/index.json", "index.json") + val downloader = service.createDownloader(listOf(desc), "Zig version information downloading") + val downloadDirectory = tempPluginDir.toFile() + val downloadResults = coroutineToIndicator { + downloader.download(downloadDirectory) + } + var info: JsonObject? = null + for (result in downloadResults) { + if (result.second.defaultFileName == "index.json") { + info = result.first.inputStream().use { Json.decodeFromStream(it) } + } + } + info?.mapNotNull getVersions@{ (version, data) -> + data as? JsonObject ?: return@getVersions null + val date = data["date"]?.asSafely()?.content ?: "" + val docs = data["docs"]?.asSafely()?.content ?: "" + val notes = data["notes"]?.asSafely()?.content ?: "" + val src = data["src"]?.asSafely()?.let { Json.decodeFromJsonElement(it) } + val dist = data.firstNotNullOfOrNull findCompatible@{ (dist, tb) -> + if (!dist.contains('-')) + return@findCompatible null + val (arch, os) = dist.split('-', limit = 2) + val theArch = when(arch) { + "x86_64" -> CpuArch.X86_64 + "i386" -> CpuArch.X86 + "armv7a" -> CpuArch.ARM32 + "aarch64" -> CpuArch.ARM64 + else -> return@findCompatible null + } + val theOS = when(os) { + "linux" -> OS.Linux + "windows" -> OS.Windows + "macos" -> OS.macOS + "freebsd" -> OS.FreeBSD + else -> return@findCompatible null + } + if (theArch == CpuArch.CURRENT && theOS == OS.CURRENT) { + Json.decodeFromJsonElement(tb) + } else null + } ?: return@getVersions null + Pair(version, ZigVersionInfo(date, docs, notes, src, dist)) + } ?.toList() ?: emptyList() + } + } + } + } +} + +@JvmRecord +@Serializable +data class Tarball(val tarball: String, val shasum: String, val size: Int) + +private val tempPluginDir get(): Path = PathManager.getTempPath().toNioPathOrNull()!!.resolve("zigbrains") \ No newline at end of file diff --git a/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/tools/ZigCompilerTool.kt b/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/tools/ZigCompilerTool.kt index 85e2af65..1f67b4a7 100644 --- a/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/tools/ZigCompilerTool.kt +++ b/core/src/main/kotlin/com/falsepattern/zigbrains/project/toolchain/tools/ZigCompilerTool.kt @@ -25,6 +25,7 @@ package com.falsepattern.zigbrains.project.toolchain.tools import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain import com.falsepattern.zigbrains.project.toolchain.ZigToolchainEnvironmentSerializable import com.intellij.openapi.project.Project +import kotlinx.coroutines.runBlocking import kotlinx.serialization.SerializationException import kotlinx.serialization.json.Json import java.nio.file.Path @@ -45,6 +46,8 @@ class ZigCompilerTool(toolchain: AbstractZigToolchain) : ZigTool(toolchain) { Result.failure(IllegalStateException("could not deserialize zig env", e)) } } + + fun getEnvBlocking(project: Project?) = runBlocking { getEnv(project) } } private val envJson = Json { diff --git a/core/src/main/kotlin/com/falsepattern/zigbrains/shared/coroutine/CoroutinesUtil.kt b/core/src/main/kotlin/com/falsepattern/zigbrains/shared/coroutine/CoroutinesUtil.kt index cc69c88a..7a8e4c24 100644 --- a/core/src/main/kotlin/com/falsepattern/zigbrains/shared/coroutine/CoroutinesUtil.kt +++ b/core/src/main/kotlin/com/falsepattern/zigbrains/shared/coroutine/CoroutinesUtil.kt @@ -49,6 +49,12 @@ suspend inline fun withCurrentEDTModalityContext(noinline block: suspend Cor } } +fun T.letBlocking(targetAction: suspend CoroutineScope.(T) -> R): R { + return runBlocking { + targetAction(this@letBlocking) + } +} + suspend inline fun runInterruptibleEDT(state: ModalityState, noinline targetAction: () -> T): T { return runInterruptible(Dispatchers.EDT + state.asContextElement(), block = targetAction) }