abstract away UUID storage for LSP code sharing
This commit is contained in:
parent
1725b189a4
commit
ab20a57e9e
12 changed files with 323 additions and 163 deletions
|
@ -22,125 +22,31 @@
|
||||||
|
|
||||||
package com.falsepattern.zigbrains.project.toolchain
|
package com.falsepattern.zigbrains.project.toolchain
|
||||||
|
|
||||||
|
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainListService.MyState
|
||||||
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.zigCoroutineScope
|
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.UUIDStorage
|
||||||
import com.intellij.openapi.components.*
|
import com.intellij.openapi.components.*
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import java.lang.ref.WeakReference
|
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
@Service(Service.Level.APP)
|
@Service(Service.Level.APP)
|
||||||
@State(
|
@State(
|
||||||
name = "ZigToolchainList",
|
name = "ZigToolchainList",
|
||||||
storages = [Storage("zigbrains.xml")]
|
storages = [Storage("zigbrains.xml")]
|
||||||
)
|
)
|
||||||
class ZigToolchainListService: SerializablePersistentStateComponent<ZigToolchainListService.State>(State()), IZigToolchainListService {
|
class ZigToolchainListService: UUIDMapSerializable.Converting<ZigToolchain, ZigToolchain.Ref, MyState>(MyState()), IZigToolchainListService {
|
||||||
private val changeListeners = ArrayList<WeakReference<ToolchainListChangeListener>>()
|
override fun serialize(value: ZigToolchain) = value.toRef()
|
||||||
|
override fun deserialize(value: ZigToolchain.Ref) = value.resolve()
|
||||||
|
override fun getStorage(state: MyState) = state.toolchains
|
||||||
|
override fun updateStorage(state: MyState, storage: ToolchainStorage) = state.copy(toolchains = storage)
|
||||||
|
|
||||||
override val toolchains: Sequence<Pair<UUID, ZigToolchain>>
|
data class MyState(
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setToolchain(uuid: UUID, toolchain: ZigToolchain) {
|
|
||||||
val str = uuid.toString()
|
|
||||||
val ref = toolchain.toRef()
|
|
||||||
updateState {
|
|
||||||
val newMap = HashMap<String, ZigToolchain.Ref>()
|
|
||||||
newMap.putAll(it.toolchains)
|
|
||||||
newMap[str] = ref
|
|
||||||
it.copy(toolchains = newMap)
|
|
||||||
}
|
|
||||||
notifyChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun registerNewToolchain(toolchain: ZigToolchain): UUID {
|
|
||||||
val ref = toolchain.toRef()
|
|
||||||
var uuid = UUID.randomUUID()
|
|
||||||
updateState {
|
|
||||||
val newMap = HashMap<String, ZigToolchain.Ref>()
|
|
||||||
newMap.putAll(it.toolchains)
|
|
||||||
var uuidStr = uuid.toString()
|
|
||||||
while (newMap.containsKey(uuidStr)) {
|
|
||||||
uuid = UUID.randomUUID()
|
|
||||||
uuidStr = uuid.toString()
|
|
||||||
}
|
|
||||||
newMap[uuidStr] = ref
|
|
||||||
it.copy(toolchains = newMap)
|
|
||||||
}
|
|
||||||
notifyChanged()
|
|
||||||
return uuid
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getToolchain(uuid: UUID): ZigToolchain? {
|
|
||||||
return state.toolchains[uuid.toString()]?.resolve()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hasToolchain(uuid: UUID): Boolean {
|
|
||||||
return state.toolchains.containsKey(uuid.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeToolchain(uuid: UUID) {
|
|
||||||
val str = uuid.toString()
|
|
||||||
updateState {
|
|
||||||
it.copy(toolchains = it.toolchains.filter { it.key != str })
|
|
||||||
}
|
|
||||||
notifyChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addChangeListener(listener: ToolchainListChangeListener) {
|
|
||||||
synchronized(changeListeners) {
|
|
||||||
changeListeners.add(WeakReference(listener))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeChangeListener(listener: ToolchainListChangeListener) {
|
|
||||||
synchronized(changeListeners) {
|
|
||||||
changeListeners.removeIf {
|
|
||||||
val v = it.get()
|
|
||||||
v == null || v === listener
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T: ZigToolchain> withUniqueName(toolchain: T): T {
|
|
||||||
val baseName = toolchain.name ?: ""
|
|
||||||
var index = 0
|
|
||||||
var currentName = baseName
|
|
||||||
while (toolchains.any { (_, existing) -> existing.name == currentName }) {
|
|
||||||
index++
|
|
||||||
currentName = "$baseName ($index)"
|
|
||||||
}
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
return toolchain.withName(currentName) as T
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun notifyChanged() {
|
|
||||||
synchronized(changeListeners) {
|
|
||||||
var i = 0
|
|
||||||
while (i < changeListeners.size) {
|
|
||||||
val v = changeListeners[i].get()
|
|
||||||
if (v == null) {
|
|
||||||
changeListeners.removeAt(i)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
zigCoroutineScope.launch {
|
|
||||||
v.toolchainListChanged()
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class State(
|
|
||||||
@JvmField
|
@JvmField
|
||||||
val toolchains: Map<String, ZigToolchain.Ref> = emptyMap(),
|
val toolchains: ToolchainStorage = emptyMap(),
|
||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -149,19 +55,8 @@ class ZigToolchainListService: SerializablePersistentStateComponent<ZigToolchain
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@FunctionalInterface
|
inline val zigToolchainList: IZigToolchainListService get() = ZigToolchainListService.getInstance()
|
||||||
interface ToolchainListChangeListener {
|
|
||||||
suspend fun toolchainListChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed interface IZigToolchainListService {
|
sealed interface IZigToolchainListService: ChangeTrackingStorage, AccessibleStorage<ZigToolchain>, IterableStorage<ZigToolchain>
|
||||||
val toolchains: Sequence<Pair<UUID, ZigToolchain>>
|
|
||||||
fun setToolchain(uuid: UUID, toolchain: ZigToolchain)
|
private typealias ToolchainStorage = UUIDStorage<ZigToolchain.Ref>
|
||||||
fun registerNewToolchain(toolchain: ZigToolchain): UUID
|
|
||||||
fun getToolchain(uuid: UUID): ZigToolchain?
|
|
||||||
fun hasToolchain(uuid: UUID): Boolean
|
|
||||||
fun removeToolchain(uuid: UUID)
|
|
||||||
fun addChangeListener(listener: ToolchainListChangeListener)
|
|
||||||
fun removeChangeListener(listener: ToolchainListChangeListener)
|
|
||||||
fun <T: ZigToolchain> withUniqueName(toolchain: T): T
|
|
||||||
}
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ import java.util.UUID
|
||||||
class ZigToolchainService(val project: Project): SerializablePersistentStateComponent<ZigToolchainService.State>(State()), IZigToolchainService {
|
class ZigToolchainService(val project: Project): SerializablePersistentStateComponent<ZigToolchainService.State>(State()), IZigToolchainService {
|
||||||
override var toolchainUUID: UUID?
|
override var toolchainUUID: UUID?
|
||||||
get() = state.toolchain.ifBlank { null }?.let { UUID.fromString(it) }?.takeIf {
|
get() = state.toolchain.ifBlank { null }?.let { UUID.fromString(it) }?.takeIf {
|
||||||
if (ZigToolchainListService.getInstance().hasToolchain(it)) {
|
if (it in zigToolchainList) {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
updateState {
|
updateState {
|
||||||
|
@ -64,7 +64,7 @@ class ZigToolchainService(val project: Project): SerializablePersistentStateComp
|
||||||
}
|
}
|
||||||
|
|
||||||
override val toolchain: ZigToolchain?
|
override val toolchain: ZigToolchain?
|
||||||
get() = toolchainUUID?.let { ZigToolchainListService.getInstance().getToolchain(it) }
|
get() = toolchainUUID?.let { zigToolchainList[it] }
|
||||||
|
|
||||||
data class State(
|
data class State(
|
||||||
@JvmField
|
@JvmField
|
||||||
|
|
|
@ -23,8 +23,10 @@
|
||||||
package com.falsepattern.zigbrains.project.toolchain.base
|
package com.falsepattern.zigbrains.project.toolchain.base
|
||||||
|
|
||||||
import com.falsepattern.zigbrains.project.toolchain.tools.ZigCompilerTool
|
import com.falsepattern.zigbrains.project.toolchain.tools.ZigCompilerTool
|
||||||
|
import com.falsepattern.zigbrains.shared.NamedObject
|
||||||
import com.intellij.execution.configurations.GeneralCommandLine
|
import com.intellij.execution.configurations.GeneralCommandLine
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.util.Key
|
||||||
import com.intellij.util.xmlb.annotations.Attribute
|
import com.intellij.util.xmlb.annotations.Attribute
|
||||||
import com.intellij.util.xmlb.annotations.MapAnnotation
|
import com.intellij.util.xmlb.annotations.MapAnnotation
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
@ -32,10 +34,15 @@ import java.nio.file.Path
|
||||||
/**
|
/**
|
||||||
* These MUST be stateless and interchangeable! (e.g., immutable data class)
|
* These MUST be stateless and interchangeable! (e.g., immutable data class)
|
||||||
*/
|
*/
|
||||||
interface ZigToolchain {
|
interface ZigToolchain: NamedObject<ZigToolchain> {
|
||||||
val zig: ZigCompilerTool get() = ZigCompilerTool(this)
|
val zig: ZigCompilerTool get() = ZigCompilerTool(this)
|
||||||
|
|
||||||
val name: String?
|
fun <T> getUserData(key: Key<T>): T?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returned type must be the same class
|
||||||
|
*/
|
||||||
|
fun <T> withUserData(key: Key<T>, value: T?): ZigToolchain
|
||||||
|
|
||||||
fun workingDirectory(project: Project? = null): Path?
|
fun workingDirectory(project: Project? = null): Path?
|
||||||
|
|
||||||
|
@ -43,11 +50,6 @@ interface ZigToolchain {
|
||||||
|
|
||||||
fun pathToExecutable(toolName: String, project: Project? = null): Path
|
fun pathToExecutable(toolName: String, project: Project? = null): Path
|
||||||
|
|
||||||
/**
|
|
||||||
* Returned object must be the same class.
|
|
||||||
*/
|
|
||||||
fun withName(newName: String?): ZigToolchain
|
|
||||||
|
|
||||||
data class Ref(
|
data class Ref(
|
||||||
@JvmField
|
@JvmField
|
||||||
@Attribute
|
@Attribute
|
||||||
|
|
|
@ -23,10 +23,10 @@
|
||||||
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.ZigToolchainListService
|
||||||
|
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
|
||||||
import com.intellij.ui.dsl.builder.panel
|
import com.intellij.ui.dsl.builder.panel
|
||||||
import com.intellij.ui.util.minimumWidth
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.swing.JComponent
|
import javax.swing.JComponent
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ abstract class ZigToolchainConfigurable<T: ZigToolchain>(
|
||||||
): NamedConfigurable<UUID>() {
|
): NamedConfigurable<UUID>() {
|
||||||
var toolchain: T = tc
|
var toolchain: T = tc
|
||||||
set(value) {
|
set(value) {
|
||||||
ZigToolchainListService.getInstance().setToolchain(uuid, value)
|
zigToolchainList[uuid] = value
|
||||||
field = value
|
field = value
|
||||||
}
|
}
|
||||||
private var myView: ZigToolchainPanel<T>? = null
|
private var myView: ZigToolchainPanel<T>? = null
|
||||||
|
|
|
@ -22,8 +22,7 @@
|
||||||
|
|
||||||
package com.falsepattern.zigbrains.project.toolchain.base
|
package com.falsepattern.zigbrains.project.toolchain.base
|
||||||
|
|
||||||
import com.falsepattern.zigbrains.direnv.DirenvState
|
import com.falsepattern.zigbrains.project.toolchain.zigToolchainList
|
||||||
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainListService
|
|
||||||
import com.intellij.openapi.extensions.ExtensionPointName
|
import com.intellij.openapi.extensions.ExtensionPointName
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
|
@ -37,6 +36,7 @@ import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.flow.flatMapConcat
|
import kotlinx.coroutines.flow.flatMapConcat
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import kotlin.collections.none
|
||||||
|
|
||||||
private val EXTENSION_POINT_NAME = ExtensionPointName.create<ZigToolchainProvider>("com.falsepattern.zigbrains.toolchainProvider")
|
private val EXTENSION_POINT_NAME = ExtensionPointName.create<ZigToolchainProvider>("com.falsepattern.zigbrains.toolchainProvider")
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ fun ZigToolchain.createNamedConfigurable(uuid: UUID): ZigToolchainConfigurable<*
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
fun suggestZigToolchains(project: Project? = null, data: UserDataHolder = emptyData): Flow<ZigToolchain> {
|
fun suggestZigToolchains(project: Project? = null, data: UserDataHolder = emptyData): Flow<ZigToolchain> {
|
||||||
val existing = ZigToolchainListService.getInstance().toolchains.map { (_, tc) -> tc }.toList()
|
val existing = zigToolchainList.map { (_, tc) -> tc }
|
||||||
return EXTENSION_POINT_NAME.extensionList.asFlow().flatMapConcat { ext ->
|
return EXTENSION_POINT_NAME.extensionList.asFlow().flatMapConcat { ext ->
|
||||||
val compatibleExisting = existing.filter { ext.isCompatible(it) }
|
val compatibleExisting = existing.filter { ext.isCompatible(it) }
|
||||||
val suggestions = ext.suggestToolchains(project, data)
|
val suggestions = ext.suggestToolchains(project, data)
|
||||||
|
|
|
@ -27,10 +27,12 @@ import com.falsepattern.zigbrains.ZigBrainsBundle
|
||||||
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainListService
|
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainListService
|
||||||
import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
|
import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
|
||||||
import com.falsepattern.zigbrains.project.toolchain.local.LocalZigToolchain
|
import com.falsepattern.zigbrains.project.toolchain.local.LocalZigToolchain
|
||||||
|
import com.falsepattern.zigbrains.project.toolchain.zigToolchainList
|
||||||
import com.falsepattern.zigbrains.shared.coroutine.asContextElement
|
import com.falsepattern.zigbrains.shared.coroutine.asContextElement
|
||||||
import com.falsepattern.zigbrains.shared.coroutine.launchWithEDT
|
import com.falsepattern.zigbrains.shared.coroutine.launchWithEDT
|
||||||
import com.falsepattern.zigbrains.shared.coroutine.runInterruptibleEDT
|
import com.falsepattern.zigbrains.shared.coroutine.runInterruptibleEDT
|
||||||
import com.falsepattern.zigbrains.shared.coroutine.withEDTContext
|
import com.falsepattern.zigbrains.shared.coroutine.withEDTContext
|
||||||
|
import com.falsepattern.zigbrains.shared.withUniqueName
|
||||||
import com.falsepattern.zigbrains.shared.zigCoroutineScope
|
import com.falsepattern.zigbrains.shared.zigCoroutineScope
|
||||||
import com.intellij.icons.AllIcons
|
import com.intellij.icons.AllIcons
|
||||||
import com.intellij.openapi.application.ModalityState
|
import com.intellij.openapi.application.ModalityState
|
||||||
|
@ -78,9 +80,7 @@ object LocalSelector {
|
||||||
errorMessageBox.text = ZigBrainsBundle.message("settings.toolchain.local-selector.state.invalid")
|
errorMessageBox.text = ZigBrainsBundle.message("settings.toolchain.local-selector.state.invalid")
|
||||||
dialog.setOkActionEnabled(false)
|
dialog.setOkActionEnabled(false)
|
||||||
} else {
|
} else {
|
||||||
val existingToolchain = ZigToolchainListService
|
val existingToolchain = zigToolchainList
|
||||||
.getInstance()
|
|
||||||
.toolchains
|
|
||||||
.mapNotNull { it.second as? LocalZigToolchain }
|
.mapNotNull { it.second as? LocalZigToolchain }
|
||||||
.firstOrNull { it.location == tc.location }
|
.firstOrNull { it.location == tc.location }
|
||||||
if (existingToolchain != null) {
|
if (existingToolchain != null) {
|
||||||
|
@ -95,7 +95,7 @@ object LocalSelector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tc != null) {
|
if (tc != null) {
|
||||||
tc = ZigToolchainListService.getInstance().withUniqueName(tc)
|
tc = zigToolchainList.withUniqueName(tc)
|
||||||
}
|
}
|
||||||
val prevNameDefault = name.emptyText.text.trim() == name.text.trim() || name.text.isBlank()
|
val prevNameDefault = name.emptyText.text.trim() == name.text.trim() || name.text.isBlank()
|
||||||
name.emptyText.text = tc?.name ?: ""
|
name.emptyText.text = tc?.name ?: ""
|
||||||
|
|
|
@ -28,13 +28,23 @@ import com.intellij.execution.ExecutionException
|
||||||
import com.intellij.execution.configurations.GeneralCommandLine
|
import com.intellij.execution.configurations.GeneralCommandLine
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.project.guessProjectDir
|
import com.intellij.openapi.project.guessProjectDir
|
||||||
|
import com.intellij.openapi.util.Key
|
||||||
import com.intellij.openapi.util.SystemInfo
|
import com.intellij.openapi.util.SystemInfo
|
||||||
import com.intellij.openapi.util.io.toNioPathOrNull
|
import com.intellij.openapi.util.io.toNioPathOrNull
|
||||||
import com.intellij.openapi.vfs.toNioPathOrNull
|
import com.intellij.openapi.vfs.toNioPathOrNull
|
||||||
|
import com.intellij.util.keyFMap.KeyFMap
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
@JvmRecord
|
@JvmRecord
|
||||||
data class LocalZigToolchain(val location: Path, val std: Path? = null, override val name: String? = null): ZigToolchain {
|
data class LocalZigToolchain(val location: Path, val std: Path? = null, override val name: String? = null, private val userData: KeyFMap = KeyFMap.EMPTY_MAP): ZigToolchain {
|
||||||
|
override fun <T> getUserData(key: Key<T>): T? {
|
||||||
|
return userData.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T> withUserData(key: Key<T>, value: T?): LocalZigToolchain {
|
||||||
|
return copy(userData = if (value == null) userData.minus(key) else userData.plus(key, value))
|
||||||
|
}
|
||||||
|
|
||||||
override fun workingDirectory(project: Project?): Path? {
|
override fun workingDirectory(project: Project?): Path? {
|
||||||
return project?.guessProjectDir()?.toNioPathOrNull()
|
return project?.guessProjectDir()?.toNioPathOrNull()
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,8 @@ package com.falsepattern.zigbrains.project.toolchain.ui
|
||||||
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainListService
|
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainListService
|
||||||
import com.falsepattern.zigbrains.project.toolchain.downloader.Downloader
|
import com.falsepattern.zigbrains.project.toolchain.downloader.Downloader
|
||||||
import com.falsepattern.zigbrains.project.toolchain.downloader.LocalSelector
|
import com.falsepattern.zigbrains.project.toolchain.downloader.LocalSelector
|
||||||
|
import com.falsepattern.zigbrains.project.toolchain.zigToolchainList
|
||||||
|
import com.falsepattern.zigbrains.shared.withUniqueName
|
||||||
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
|
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
|
||||||
import java.awt.Component
|
import java.awt.Component
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
@ -32,8 +34,8 @@ import java.util.UUID
|
||||||
internal object ZigToolchainComboBoxHandler {
|
internal object ZigToolchainComboBoxHandler {
|
||||||
@RequiresBackgroundThread
|
@RequiresBackgroundThread
|
||||||
suspend fun onItemSelected(context: Component, elem: TCListElem.Pseudo): UUID? = when(elem) {
|
suspend fun onItemSelected(context: Component, elem: TCListElem.Pseudo): UUID? = when(elem) {
|
||||||
is TCListElem.Toolchain.Suggested -> ZigToolchainListService.getInstance().withUniqueName(elem.toolchain)
|
is TCListElem.Toolchain.Suggested -> zigToolchainList.withUniqueName(elem.toolchain)
|
||||||
is TCListElem.Download -> Downloader.downloadToolchain(context)
|
is TCListElem.Download -> Downloader.downloadToolchain(context)
|
||||||
is TCListElem.FromDisk -> LocalSelector.browseFromDisk(context)
|
is TCListElem.FromDisk -> LocalSelector.browseFromDisk(context)
|
||||||
}?.let { ZigToolchainListService.getInstance().registerNewToolchain(it) }
|
}?.let { zigToolchainList.registerNew(it) }
|
||||||
}
|
}
|
|
@ -23,14 +23,13 @@
|
||||||
package com.falsepattern.zigbrains.project.toolchain.ui
|
package com.falsepattern.zigbrains.project.toolchain.ui
|
||||||
|
|
||||||
import com.falsepattern.zigbrains.ZigBrainsBundle
|
import com.falsepattern.zigbrains.ZigBrainsBundle
|
||||||
import com.falsepattern.zigbrains.direnv.DirenvService
|
|
||||||
import com.falsepattern.zigbrains.direnv.DirenvState
|
|
||||||
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
|
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
|
||||||
import com.falsepattern.zigbrains.project.toolchain.ToolchainListChangeListener
|
|
||||||
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainListService
|
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainListService
|
||||||
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainService
|
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainService
|
||||||
import com.falsepattern.zigbrains.project.toolchain.base.createNamedConfigurable
|
import com.falsepattern.zigbrains.project.toolchain.base.createNamedConfigurable
|
||||||
import com.falsepattern.zigbrains.project.toolchain.base.suggestZigToolchains
|
import com.falsepattern.zigbrains.project.toolchain.base.suggestZigToolchains
|
||||||
|
import com.falsepattern.zigbrains.project.toolchain.zigToolchainList
|
||||||
|
import com.falsepattern.zigbrains.shared.StorageChangeListener
|
||||||
import com.falsepattern.zigbrains.shared.SubConfigurable
|
import com.falsepattern.zigbrains.shared.SubConfigurable
|
||||||
import com.falsepattern.zigbrains.shared.coroutine.asContextElement
|
import com.falsepattern.zigbrains.shared.coroutine.asContextElement
|
||||||
import com.falsepattern.zigbrains.shared.coroutine.launchWithEDT
|
import com.falsepattern.zigbrains.shared.coroutine.launchWithEDT
|
||||||
|
@ -48,6 +47,7 @@ import com.intellij.ui.dsl.builder.AlignX
|
||||||
import com.intellij.ui.dsl.builder.Panel
|
import com.intellij.ui.dsl.builder.Panel
|
||||||
import com.intellij.util.concurrency.annotations.RequiresEdt
|
import com.intellij.util.concurrency.annotations.RequiresEdt
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.awt.event.ItemEvent
|
import java.awt.event.ItemEvent
|
||||||
|
@ -55,16 +55,17 @@ import java.util.UUID
|
||||||
import javax.swing.JButton
|
import javax.swing.JButton
|
||||||
import kotlin.collections.addAll
|
import kotlin.collections.addAll
|
||||||
|
|
||||||
class ZigToolchainEditor(private var project: Project?, private val sharedState: ZigProjectConfigurationProvider.IUserDataBridge): SubConfigurable<Project>, ToolchainListChangeListener, ZigProjectConfigurationProvider.UserDataListener {
|
class ZigToolchainEditor(private var project: Project?, private val sharedState: ZigProjectConfigurationProvider.IUserDataBridge): SubConfigurable<Project>, ZigProjectConfigurationProvider.UserDataListener {
|
||||||
private val toolchainBox: TCComboBox
|
private val toolchainBox: TCComboBox
|
||||||
private var selectOnNextReload: UUID? = null
|
private var selectOnNextReload: UUID? = null
|
||||||
private val model: TCModel
|
private val model: TCModel
|
||||||
private var editButton: JButton? = null
|
private var editButton: JButton? = null
|
||||||
|
private val changeListener: StorageChangeListener = { this@ZigToolchainEditor.toolchainListChanged() }
|
||||||
init {
|
init {
|
||||||
model = TCModel(getModelList(project, sharedState))
|
model = TCModel(getModelList(project, sharedState))
|
||||||
toolchainBox = TCComboBox(model)
|
toolchainBox = TCComboBox(model)
|
||||||
toolchainBox.addItemListener(::itemStateChanged)
|
toolchainBox.addItemListener(::itemStateChanged)
|
||||||
ZigToolchainListService.getInstance().addChangeListener(this)
|
zigToolchainList.addChangeListener(changeListener)
|
||||||
sharedState.addUserDataChangeListener(this)
|
sharedState.addUserDataChangeListener(this)
|
||||||
model.whenListChanged {
|
model.whenListChanged {
|
||||||
if (toolchainBox.isPopupVisible) {
|
if (toolchainBox.isPopupVisible) {
|
||||||
|
@ -89,13 +90,14 @@ class ZigToolchainEditor(private var project: Project?, private val sharedState:
|
||||||
return
|
return
|
||||||
zigCoroutineScope.launch(toolchainBox.asContextElement()) {
|
zigCoroutineScope.launch(toolchainBox.asContextElement()) {
|
||||||
val uuid = runCatching { ZigToolchainComboBoxHandler.onItemSelected(toolchainBox, item) }.getOrNull()
|
val uuid = runCatching { ZigToolchainComboBoxHandler.onItemSelected(toolchainBox, item) }.getOrNull()
|
||||||
|
delay(100)
|
||||||
withEDTContext(toolchainBox.asContextElement()) {
|
withEDTContext(toolchainBox.asContextElement()) {
|
||||||
applyUUIDNowOrOnReload(uuid)
|
applyUUIDNowOrOnReload(uuid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun toolchainListChanged() {
|
private suspend fun toolchainListChanged() {
|
||||||
withContext(Dispatchers.EDT + toolchainBox.asContextElement()) {
|
withContext(Dispatchers.EDT + toolchainBox.asContextElement()) {
|
||||||
val list = getModelList(project, sharedState)
|
val list = getModelList(project, sharedState)
|
||||||
model.updateContents(list)
|
model.updateContents(list)
|
||||||
|
@ -143,7 +145,7 @@ class ZigToolchainEditor(private var project: Project?, private val sharedState:
|
||||||
button(ZigBrainsBundle.message("settings.toolchain.editor.toolchain.edit-button.name")) { e ->
|
button(ZigBrainsBundle.message("settings.toolchain.editor.toolchain.edit-button.name")) { e ->
|
||||||
zigCoroutineScope.launchWithEDT(toolchainBox.asContextElement()) {
|
zigCoroutineScope.launchWithEDT(toolchainBox.asContextElement()) {
|
||||||
var selectedUUID = toolchainBox.selectedToolchain ?: return@launchWithEDT
|
var selectedUUID = toolchainBox.selectedToolchain ?: return@launchWithEDT
|
||||||
val toolchain = ZigToolchainListService.getInstance().getToolchain(selectedUUID) ?: return@launchWithEDT
|
val toolchain = zigToolchainList[selectedUUID] ?: return@launchWithEDT
|
||||||
val config = toolchain.createNamedConfigurable(selectedUUID)
|
val config = toolchain.createNamedConfigurable(selectedUUID)
|
||||||
val apply = ShowSettingsUtil.getInstance().editConfigurable(DialogWrapper.findInstance(toolchainBox)?.contentPane, config)
|
val apply = ShowSettingsUtil.getInstance().editConfigurable(DialogWrapper.findInstance(toolchainBox)?.contentPane, config)
|
||||||
if (apply) {
|
if (apply) {
|
||||||
|
@ -182,7 +184,7 @@ class ZigToolchainEditor(private var project: Project?, private val sharedState:
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
ZigToolchainListService.getInstance().removeChangeListener(this)
|
zigToolchainList.removeChangeListener(changeListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val newProjectBeforeInitSelector get() = true
|
override val newProjectBeforeInitSelector get() = true
|
||||||
|
@ -199,7 +201,7 @@ class ZigToolchainEditor(private var project: Project?, private val sharedState:
|
||||||
private fun getModelList(project: Project?, data: UserDataHolder): List<TCListElemIn> {
|
private fun getModelList(project: Project?, data: UserDataHolder): List<TCListElemIn> {
|
||||||
val modelList = ArrayList<TCListElemIn>()
|
val modelList = ArrayList<TCListElemIn>()
|
||||||
modelList.add(TCListElem.None)
|
modelList.add(TCListElem.None)
|
||||||
modelList.addAll(ZigToolchainListService.getInstance().toolchains.map { it.asActual() }.sortedBy { it.toolchain.name })
|
modelList.addAll(zigToolchainList.map { it.asActual() }.sortedBy { it.toolchain.name })
|
||||||
modelList.add(Separator("", true))
|
modelList.add(Separator("", true))
|
||||||
modelList.addAll(TCListElem.fetchGroup)
|
modelList.addAll(TCListElem.fetchGroup)
|
||||||
modelList.add(Separator(ZigBrainsBundle.message("settings.toolchain.model.detected.separator"), true))
|
modelList.add(Separator(ZigBrainsBundle.message("settings.toolchain.model.detected.separator"), true))
|
||||||
|
|
|
@ -23,11 +23,12 @@
|
||||||
package com.falsepattern.zigbrains.project.toolchain.ui
|
package com.falsepattern.zigbrains.project.toolchain.ui
|
||||||
|
|
||||||
import com.falsepattern.zigbrains.ZigBrainsBundle
|
import com.falsepattern.zigbrains.ZigBrainsBundle
|
||||||
import com.falsepattern.zigbrains.project.toolchain.ToolchainListChangeListener
|
|
||||||
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainListService
|
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainListService
|
||||||
import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
|
import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
|
||||||
import com.falsepattern.zigbrains.project.toolchain.base.createNamedConfigurable
|
import com.falsepattern.zigbrains.project.toolchain.base.createNamedConfigurable
|
||||||
import com.falsepattern.zigbrains.project.toolchain.base.suggestZigToolchains
|
import com.falsepattern.zigbrains.project.toolchain.base.suggestZigToolchains
|
||||||
|
import com.falsepattern.zigbrains.project.toolchain.zigToolchainList
|
||||||
|
import com.falsepattern.zigbrains.shared.StorageChangeListener
|
||||||
import com.falsepattern.zigbrains.shared.coroutine.asContextElement
|
import com.falsepattern.zigbrains.shared.coroutine.asContextElement
|
||||||
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
|
||||||
|
@ -39,14 +40,17 @@ import com.intellij.openapi.project.DumbAwareAction
|
||||||
import com.intellij.openapi.ui.MasterDetailsComponent
|
import com.intellij.openapi.ui.MasterDetailsComponent
|
||||||
import com.intellij.util.IconUtil
|
import com.intellij.util.IconUtil
|
||||||
import com.intellij.util.asSafely
|
import com.intellij.util.asSafely
|
||||||
|
import com.intellij.util.concurrency.annotations.RequiresEdt
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.swing.JComponent
|
import javax.swing.JComponent
|
||||||
import javax.swing.tree.DefaultTreeModel
|
import javax.swing.tree.DefaultTreeModel
|
||||||
|
|
||||||
class ZigToolchainListEditor : MasterDetailsComponent(), ToolchainListChangeListener {
|
class ZigToolchainListEditor : MasterDetailsComponent() {
|
||||||
private var isTreeInitialized = false
|
private var isTreeInitialized = false
|
||||||
private var registered: Boolean = false
|
private var registered: Boolean = false
|
||||||
|
private var selectOnNextReload: UUID? = null
|
||||||
|
private val changeListener: StorageChangeListener = { this@ZigToolchainListEditor.toolchainListChanged() }
|
||||||
|
|
||||||
override fun createComponent(): JComponent {
|
override fun createComponent(): JComponent {
|
||||||
if (!isTreeInitialized) {
|
if (!isTreeInitialized) {
|
||||||
|
@ -54,7 +58,7 @@ class ZigToolchainListEditor : MasterDetailsComponent(), ToolchainListChangeList
|
||||||
isTreeInitialized = true
|
isTreeInitialized = true
|
||||||
}
|
}
|
||||||
if (!registered) {
|
if (!registered) {
|
||||||
ZigToolchainListService.getInstance().addChangeListener(this)
|
zigToolchainList.addChangeListener(changeListener)
|
||||||
registered = true
|
registered = true
|
||||||
}
|
}
|
||||||
return super.createComponent()
|
return super.createComponent()
|
||||||
|
@ -81,7 +85,7 @@ class ZigToolchainListEditor : MasterDetailsComponent(), ToolchainListChangeList
|
||||||
|
|
||||||
override fun onItemDeleted(item: Any?) {
|
override fun onItemDeleted(item: Any?) {
|
||||||
if (item is UUID) {
|
if (item is UUID) {
|
||||||
ZigToolchainListService.getInstance().removeToolchain(item)
|
zigToolchainList.remove(item)
|
||||||
}
|
}
|
||||||
super.onItemDeleted(item)
|
super.onItemDeleted(item)
|
||||||
}
|
}
|
||||||
|
@ -93,7 +97,7 @@ class ZigToolchainListEditor : MasterDetailsComponent(), ToolchainListChangeList
|
||||||
val uuid = ZigToolchainComboBoxHandler.onItemSelected(myWholePanel, elem)
|
val uuid = ZigToolchainComboBoxHandler.onItemSelected(myWholePanel, elem)
|
||||||
if (uuid != null) {
|
if (uuid != null) {
|
||||||
withEDTContext(myWholePanel.asContextElement()) {
|
withEDTContext(myWholePanel.asContextElement()) {
|
||||||
selectNodeInTree(uuid)
|
applyUUIDNowOrOnReload(uuid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,6 +112,13 @@ class ZigToolchainListEditor : MasterDetailsComponent(), ToolchainListChangeList
|
||||||
|
|
||||||
override fun getDisplayName() = ZigBrainsBundle.message("settings.toolchain.list.title")
|
override fun getDisplayName() = ZigBrainsBundle.message("settings.toolchain.list.title")
|
||||||
|
|
||||||
|
override fun disposeUIResources() {
|
||||||
|
super.disposeUIResources()
|
||||||
|
if (registered) {
|
||||||
|
zigToolchainList.removeChangeListener(changeListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun addToolchain(uuid: UUID, toolchain: ZigToolchain) {
|
private fun addToolchain(uuid: UUID, toolchain: ZigToolchain) {
|
||||||
val node = MyNode(toolchain.createNamedConfigurable(uuid))
|
val node = MyNode(toolchain.createNamedConfigurable(uuid))
|
||||||
addNode(node, myRoot)
|
addNode(node, myRoot)
|
||||||
|
@ -116,23 +127,37 @@ class ZigToolchainListEditor : MasterDetailsComponent(), ToolchainListChangeList
|
||||||
private fun reloadTree() {
|
private fun reloadTree() {
|
||||||
val currentSelection = selectedObject?.asSafely<UUID>()
|
val currentSelection = selectedObject?.asSafely<UUID>()
|
||||||
myRoot.removeAllChildren()
|
myRoot.removeAllChildren()
|
||||||
ZigToolchainListService.getInstance().toolchains.forEach { (uuid, toolchain) ->
|
val onReload = selectOnNextReload
|
||||||
|
selectOnNextReload = null
|
||||||
|
var hasOnReload = false
|
||||||
|
zigToolchainList.forEach { (uuid, toolchain) ->
|
||||||
addToolchain(uuid, toolchain)
|
addToolchain(uuid, toolchain)
|
||||||
|
if (uuid == onReload) {
|
||||||
|
hasOnReload = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
(myTree.model as DefaultTreeModel).reload()
|
(myTree.model as DefaultTreeModel).reload()
|
||||||
|
if (hasOnReload) {
|
||||||
|
selectNodeInTree(onReload)
|
||||||
|
return
|
||||||
|
}
|
||||||
currentSelection?.let {
|
currentSelection?.let {
|
||||||
selectNodeInTree(it)
|
selectNodeInTree(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun disposeUIResources() {
|
@RequiresEdt
|
||||||
super.disposeUIResources()
|
private fun applyUUIDNowOrOnReload(uuid: UUID?) {
|
||||||
if (registered) {
|
selectNodeInTree(uuid)
|
||||||
ZigToolchainListService.getInstance().removeChangeListener(this)
|
val currentSelection = selectedObject?.asSafely<UUID>()
|
||||||
|
if (uuid != null && uuid != currentSelection) {
|
||||||
|
selectOnNextReload = uuid
|
||||||
|
} else {
|
||||||
|
selectOnNextReload = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun toolchainListChanged() {
|
private suspend fun toolchainListChanged() {
|
||||||
withEDTContext(myWholePanel.asContextElement()) {
|
withEDTContext(myWholePanel.asContextElement()) {
|
||||||
reloadTree()
|
reloadTree()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* 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.shared
|
||||||
|
|
||||||
|
interface NamedObject<T: NamedObject<T>> {
|
||||||
|
val name: String?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returned object must be the exact same class as the called one.
|
||||||
|
*/
|
||||||
|
fun withName(newName: String?): T
|
||||||
|
}
|
|
@ -0,0 +1,192 @@
|
||||||
|
/*
|
||||||
|
* 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.shared
|
||||||
|
|
||||||
|
import com.intellij.openapi.components.SerializablePersistentStateComponent
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
import java.util.UUID
|
||||||
|
import kotlin.collections.any
|
||||||
|
|
||||||
|
typealias UUIDStorage<T> = Map<String, T>
|
||||||
|
|
||||||
|
abstract class UUIDMapSerializable<T, S: Any>(init: S): SerializablePersistentStateComponent<S>(init), ChangeTrackingStorage {
|
||||||
|
private val changeListeners = ArrayList<WeakReference<StorageChangeListener>>()
|
||||||
|
|
||||||
|
protected abstract fun getStorage(state: S): UUIDStorage<T>
|
||||||
|
|
||||||
|
protected abstract fun updateStorage(state: S, storage: UUIDStorage<T>): S
|
||||||
|
|
||||||
|
override fun addChangeListener(listener: StorageChangeListener) {
|
||||||
|
synchronized(changeListeners) {
|
||||||
|
changeListeners.add(WeakReference(listener))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeChangeListener(listener: StorageChangeListener) {
|
||||||
|
synchronized(changeListeners) {
|
||||||
|
changeListeners.removeIf {
|
||||||
|
val v = it.get()
|
||||||
|
v == null || v === listener
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun registerNewUUID(value: T): UUID {
|
||||||
|
var uuid = UUID.randomUUID()
|
||||||
|
updateState {
|
||||||
|
val newMap = HashMap<String, T>()
|
||||||
|
newMap.putAll(getStorage(it))
|
||||||
|
var uuidStr = uuid.toString()
|
||||||
|
while (newMap.containsKey(uuidStr)) {
|
||||||
|
uuid = UUID.randomUUID()
|
||||||
|
uuidStr = uuid.toString()
|
||||||
|
}
|
||||||
|
newMap[uuidStr] = value
|
||||||
|
updateStorage(it, newMap)
|
||||||
|
}
|
||||||
|
notifyChanged()
|
||||||
|
return uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun setStateUUID(uuid: UUID, value: T) {
|
||||||
|
val str = uuid.toString()
|
||||||
|
updateState {
|
||||||
|
val newMap = HashMap<String, T>()
|
||||||
|
newMap.putAll(getStorage(it))
|
||||||
|
newMap[str] = value
|
||||||
|
updateStorage(it, newMap)
|
||||||
|
}
|
||||||
|
notifyChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun getStateUUID(uuid: UUID): T? {
|
||||||
|
return getStorage(state)[uuid.toString()]
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun hasStateUUID(uuid: UUID): Boolean {
|
||||||
|
return getStorage(state).containsKey(uuid.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun removeStateUUID(uuid: UUID) {
|
||||||
|
val str = uuid.toString()
|
||||||
|
updateState {
|
||||||
|
updateStorage(state, getStorage(state).filter { it.key != str })
|
||||||
|
}
|
||||||
|
notifyChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun notifyChanged() {
|
||||||
|
synchronized(changeListeners) {
|
||||||
|
var i = 0
|
||||||
|
while (i < changeListeners.size) {
|
||||||
|
val v = changeListeners[i].get()
|
||||||
|
if (v == null) {
|
||||||
|
changeListeners.removeAt(i)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
zigCoroutineScope.launch {
|
||||||
|
v()
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Converting<R, T, S: Any>(init: S):
|
||||||
|
UUIDMapSerializable<T, S>(init),
|
||||||
|
AccessibleStorage<R>,
|
||||||
|
IterableStorage<R>
|
||||||
|
{
|
||||||
|
protected abstract fun serialize(value: R): T
|
||||||
|
protected abstract fun deserialize(value: T): R?
|
||||||
|
override fun registerNew(value: R): UUID {
|
||||||
|
val ser = serialize(value)
|
||||||
|
return registerNewUUID(ser)
|
||||||
|
}
|
||||||
|
override operator fun set(uuid: UUID, value: R) {
|
||||||
|
val ser = serialize(value)
|
||||||
|
setStateUUID(uuid, ser)
|
||||||
|
}
|
||||||
|
override operator fun get(uuid: UUID): R? {
|
||||||
|
return getStateUUID(uuid)?.let { deserialize(it) }
|
||||||
|
}
|
||||||
|
override operator fun contains(uuid: UUID): Boolean {
|
||||||
|
return hasStateUUID(uuid)
|
||||||
|
}
|
||||||
|
override fun remove(uuid: UUID) {
|
||||||
|
removeStateUUID(uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun iterator(): Iterator<Pair<UUID, R>> {
|
||||||
|
return getStorage(state)
|
||||||
|
.asSequence()
|
||||||
|
.mapNotNull {
|
||||||
|
val uuid = UUID.fromString(it.key) ?: return@mapNotNull null
|
||||||
|
val tc = deserialize(it.value) ?: return@mapNotNull null
|
||||||
|
uuid to tc
|
||||||
|
}.iterator()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Direct<T, S: Any>(init: S): Converting<T, T, S>(init) {
|
||||||
|
override fun serialize(value: T): T {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserialize(value: T): T? {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias StorageChangeListener = suspend CoroutineScope.() -> Unit
|
||||||
|
|
||||||
|
interface ChangeTrackingStorage {
|
||||||
|
fun addChangeListener(listener: StorageChangeListener)
|
||||||
|
fun removeChangeListener(listener: StorageChangeListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AccessibleStorage<R> {
|
||||||
|
fun registerNew(value: R): UUID
|
||||||
|
operator fun set(uuid: UUID, value: R)
|
||||||
|
operator fun get(uuid: UUID): R?
|
||||||
|
operator fun contains(uuid: UUID): Boolean
|
||||||
|
fun remove(uuid: UUID)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IterableStorage<R>: Iterable<Pair<UUID, R>>
|
||||||
|
|
||||||
|
fun <R: NamedObject<R>, T: R> IterableStorage<R>.withUniqueName(value: T): T {
|
||||||
|
val baseName = value.name ?: ""
|
||||||
|
var index = 0
|
||||||
|
var currentName = baseName
|
||||||
|
val names = this.map { (_, existing) -> existing.name }
|
||||||
|
while (names.any { it == currentName }) {
|
||||||
|
index++
|
||||||
|
currentName = "$baseName ($index)"
|
||||||
|
}
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return value.withName(currentName) as T
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue