more progress
This commit is contained in:
parent
f53b0e3283
commit
e737058cb5
14 changed files with 246 additions and 244 deletions
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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) }
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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(),
|
||||
)
|
|
@ -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)
|
|
@ -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>()
|
|
@ -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 fromJson(json: JsonObject): AbstractZigToolchain? {
|
||||
val marker = (json["marker"] as? JsonPrimitive)?.contentOrNull ?: return null
|
||||
val data = json["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 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>
|
||||
}
|
||||
|
||||
@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)
|
||||
}
|
||||
|
||||
override fun toString(value: AbstractZigToolchain): String? {
|
||||
return ZigToolchainProvider.toJson(value)?.toString()
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
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) } }
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
override fun toString(value: List<AbstractZigToolchain>): String {
|
||||
return buildJsonArray {
|
||||
value.mapNotNull { ZigToolchainProvider.toJson(it) }.forEach {
|
||||
add(it)
|
||||
}
|
||||
}.toString()
|
||||
suspend fun Project?.suggestZigToolchain(extraData: UserDataHolder): AbstractZigToolchain? {
|
||||
return EXTENSION_POINT_NAME.extensionList.firstNotNullOfOrNull { it.suggestToolchain(this, extraData) }
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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) } }
|
||||
}
|
||||
}
|
|
@ -150,7 +150,6 @@
|
|||
id="ZigToolchainConfigurable"
|
||||
displayName="Toolchain"
|
||||
/>
|
||||
<sdkType implementation="com.falsepattern.zigbrains.project.toolchain.ZigSDKType"/>
|
||||
|
||||
<programRunner
|
||||
implementation="com.falsepattern.zigbrains.project.run.ZigRegularRunner"
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Add table
Reference in a new issue