modular toolchain config

This commit is contained in:
FalsePattern 2025-04-10 17:28:56 +02:00
parent ab20a57e9e
commit 137977f691
Signed by: falsepattern
GPG key ID: E930CDEC50C50E23
6 changed files with 112 additions and 51 deletions

View file

@ -26,9 +26,6 @@ import com.falsepattern.zigbrains.project.toolchain.ZigToolchainListService.MySt
import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
import com.falsepattern.zigbrains.project.toolchain.base.resolve import com.falsepattern.zigbrains.project.toolchain.base.resolve
import com.falsepattern.zigbrains.project.toolchain.base.toRef import com.falsepattern.zigbrains.project.toolchain.base.toRef
import com.falsepattern.zigbrains.shared.AccessibleStorage
import com.falsepattern.zigbrains.shared.ChangeTrackingStorage
import com.falsepattern.zigbrains.shared.IterableStorage
import com.falsepattern.zigbrains.shared.UUIDMapSerializable import com.falsepattern.zigbrains.shared.UUIDMapSerializable
import com.falsepattern.zigbrains.shared.UUIDStorage import com.falsepattern.zigbrains.shared.UUIDStorage
import com.intellij.openapi.components.* import com.intellij.openapi.components.*
@ -38,25 +35,20 @@ import com.intellij.openapi.components.*
name = "ZigToolchainList", name = "ZigToolchainList",
storages = [Storage("zigbrains.xml")] storages = [Storage("zigbrains.xml")]
) )
class ZigToolchainListService: UUIDMapSerializable.Converting<ZigToolchain, ZigToolchain.Ref, MyState>(MyState()), IZigToolchainListService { class ZigToolchainListService: UUIDMapSerializable.Converting<ZigToolchain, ZigToolchain.Ref, MyState>(MyState()) {
override fun serialize(value: ZigToolchain) = value.toRef() override fun serialize(value: ZigToolchain) = value.toRef()
override fun deserialize(value: ZigToolchain.Ref) = value.resolve() override fun deserialize(value: ZigToolchain.Ref) = value.resolve()
override fun getStorage(state: MyState) = state.toolchains override fun getStorage(state: MyState) = state.toolchains
override fun updateStorage(state: MyState, storage: ToolchainStorage) = state.copy(toolchains = storage) override fun updateStorage(state: MyState, storage: ToolchainStorage) = state.copy(toolchains = storage)
data class MyState( data class MyState(@JvmField val toolchains: ToolchainStorage = emptyMap())
@JvmField
val toolchains: ToolchainStorage = emptyMap(),
)
companion object { companion object {
@JvmStatic @JvmStatic
fun getInstance(): IZigToolchainListService = service<ZigToolchainListService>() fun getInstance(): ZigToolchainListService = service<ZigToolchainListService>()
} }
} }
inline val zigToolchainList: IZigToolchainListService get() = ZigToolchainListService.getInstance() inline val zigToolchainList: ZigToolchainListService get() = ZigToolchainListService.getInstance()
sealed interface IZigToolchainListService: ChangeTrackingStorage, AccessibleStorage<ZigToolchain>, IterableStorage<ZigToolchain>
private typealias ToolchainStorage = UUIDStorage<ZigToolchain.Ref> private typealias ToolchainStorage = UUIDStorage<ZigToolchain.Ref>

View file

@ -22,7 +22,6 @@
package com.falsepattern.zigbrains.project.toolchain.base package com.falsepattern.zigbrains.project.toolchain.base
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainListService
import com.falsepattern.zigbrains.project.toolchain.zigToolchainList import com.falsepattern.zigbrains.project.toolchain.zigToolchainList
import com.intellij.openapi.ui.NamedConfigurable import com.intellij.openapi.ui.NamedConfigurable
import com.intellij.openapi.util.NlsContexts import com.intellij.openapi.util.NlsContexts
@ -39,19 +38,21 @@ abstract class ZigToolchainConfigurable<T: ZigToolchain>(
zigToolchainList[uuid] = value zigToolchainList[uuid] = value
field = value field = value
} }
private var myView: ZigToolchainPanel<T>? = null private var myViews: List<ZigToolchainPanel<T>> = emptyList()
abstract fun createPanel(): ZigToolchainPanel<T> abstract fun createPanel(): ZigToolchainPanel<T>
override fun createOptionsPanel(): JComponent? { override fun createOptionsPanel(): JComponent? {
var view = myView var views = myViews
if (view == null) { if (views.isEmpty()) {
view = createPanel() views = ArrayList<ZigToolchainPanel<T>>()
view.reset(toolchain) views.add(createPanel())
myView = view views.addAll(createZigToolchainExtensionPanels())
views.forEach { it.reset(toolchain) }
myViews = views
} }
return panel { return panel {
view.attach(this) views.forEach { it.attach(this@panel) }
}.withMinimumWidth(20) }.withMinimumWidth(20)
} }
@ -68,20 +69,20 @@ abstract class ZigToolchainConfigurable<T: ZigToolchain>(
} }
override fun isModified(): Boolean { override fun isModified(): Boolean {
return myView?.isModified(toolchain) == true return myViews.any { it.isModified(toolchain) }
} }
override fun apply() { override fun apply() {
myView?.apply(toolchain)?.let { toolchain = it } toolchain = myViews.fold(toolchain) { tc, view -> view.apply(tc) ?: tc }
} }
override fun reset() { override fun reset() {
myView?.reset(toolchain) myViews.forEach { it.reset(toolchain) }
} }
override fun disposeUIResources() { override fun disposeUIResources() {
myView?.dispose() myViews.forEach { it.dispose() }
myView = null myViews = emptyList()
super.disposeUIResources() super.disposeUIResources()
} }
} }

View file

@ -0,0 +1,38 @@
/*
* 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.base
import com.intellij.openapi.extensions.ExtensionPointName
private val EXTENSION_POINT_NAME = ExtensionPointName.create<ZigToolchainExtensionsProvider>("com.falsepattern.zigbrains.toolchainExtensionsProvider")
internal interface ZigToolchainExtensionsProvider {
fun <T : ZigToolchain> createExtensionPanel(): ZigToolchainPanel<T>?
val index: Int
}
fun <T: ZigToolchain> createZigToolchainExtensionPanels(): List<ZigToolchainPanel<T>> {
return EXTENSION_POINT_NAME.extensionList.sortedBy{ it.index }.mapNotNull {
it.createExtensionPanel()
}
}

View file

@ -22,29 +22,15 @@
package com.falsepattern.zigbrains.project.toolchain.base package com.falsepattern.zigbrains.project.toolchain.base
import com.falsepattern.zigbrains.ZigBrainsBundle
import com.intellij.openapi.Disposable import com.intellij.openapi.Disposable
import com.intellij.ui.components.JBTextField
import com.intellij.ui.dsl.builder.AlignX
import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.Panel
import com.intellij.ui.util.preferredHeight
import java.awt.Dimension
abstract class ZigToolchainPanel<T: ZigToolchain>: Disposable { interface ZigToolchainPanel<T: ZigToolchain>: Disposable {
private val nameField = JBTextField(25) fun attach(p: Panel)
fun isModified(toolchain: T): Boolean
protected var nameFieldValue: String? /**
get() = nameField.text.ifBlank { null } * Returned object must be the exact same class as the provided one.
set(value) {nameField.text = value ?: ""} */
fun apply(toolchain: T): T?
open fun attach(p: Panel): Unit = with(p) { fun reset(toolchain: T)
row(ZigBrainsBundle.message("settings.toolchain.base.name.label")) {
cell(nameField).resizableColumn().align(AlignX.FILL)
}
separator()
}
abstract fun isModified(toolchain: T): Boolean
abstract fun apply(toolchain: T): T?
abstract fun reset(toolchain: T)
} }

View file

@ -0,0 +1,46 @@
/*
* 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.base
import com.falsepattern.zigbrains.ZigBrainsBundle
import com.intellij.openapi.Disposable
import com.intellij.ui.components.JBTextField
import com.intellij.ui.dsl.builder.AlignX
import com.intellij.ui.dsl.builder.Panel
import com.intellij.ui.util.preferredHeight
import java.awt.Dimension
abstract class ZigToolchainPanelBase<T: ZigToolchain>: ZigToolchainPanel<T> {
private val nameField = JBTextField(25)
protected var nameFieldValue: String?
get() = nameField.text.ifBlank { null }
set(value) {nameField.text = value ?: ""}
override fun attach(p: Panel): Unit = with(p) {
row(ZigBrainsBundle.message("settings.toolchain.base.name.label")) {
cell(nameField).resizableColumn().align(AlignX.FILL)
}
separator()
}
}

View file

@ -23,10 +23,9 @@
package com.falsepattern.zigbrains.project.toolchain.local package com.falsepattern.zigbrains.project.toolchain.local
import com.falsepattern.zigbrains.ZigBrainsBundle import com.falsepattern.zigbrains.ZigBrainsBundle
import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchainPanel import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchainPanelBase
import com.falsepattern.zigbrains.shared.coroutine.withEDTContext import com.falsepattern.zigbrains.shared.coroutine.withEDTContext
import com.falsepattern.zigbrains.shared.zigCoroutineScope import com.falsepattern.zigbrains.shared.zigCoroutineScope
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ModalityState import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Disposer
@ -35,7 +34,6 @@ import com.intellij.ui.DocumentAdapter
import com.intellij.ui.JBColor import com.intellij.ui.JBColor
import com.intellij.ui.components.JBCheckBox import com.intellij.ui.components.JBCheckBox
import com.intellij.ui.components.JBTextArea import com.intellij.ui.components.JBTextArea
import com.intellij.ui.components.JBTextField
import com.intellij.ui.components.textFieldWithBrowseButton import com.intellij.ui.components.textFieldWithBrowseButton
import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.AlignX
import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.Panel
@ -46,7 +44,7 @@ import kotlinx.coroutines.launch
import javax.swing.event.DocumentEvent import javax.swing.event.DocumentEvent
import kotlin.io.path.pathString import kotlin.io.path.pathString
class LocalZigToolchainPanel() : ZigToolchainPanel<LocalZigToolchain>() { class LocalZigToolchainPanel() : ZigToolchainPanelBase<LocalZigToolchain>() {
private val pathToToolchain = textFieldWithBrowseButton( private val pathToToolchain = textFieldWithBrowseButton(
null, null,
FileChooserDescriptorFactory.createSingleFolderDescriptor().withTitle(ZigBrainsBundle.message("dialog.title.zig-toolchain")) FileChooserDescriptorFactory.createSingleFolderDescriptor().withTitle(ZigBrainsBundle.message("dialog.title.zig-toolchain"))