local zig toolchain name, edit button state, sorted recommends

This commit is contained in:
FalsePattern 2025-04-09 18:29:06 +02:00
parent a8f97172d6
commit 54cd514249
Signed by: falsepattern
GPG key ID: E930CDEC50C50E23
7 changed files with 79 additions and 42 deletions

View file

@ -25,9 +25,9 @@ package com.falsepattern.zigbrains.project.toolchain.base
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainListService
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 com.intellij.util.text.SemVer
import java.util.UUID
private val EXTENSION_POINT_NAME = ExtensionPointName.create<ZigToolchainProvider>("com.falsepattern.zigbrains.toolchainProvider")
@ -41,7 +41,7 @@ internal interface ZigToolchainProvider {
fun serialize(toolchain: ZigToolchain): Map<String, String>
fun matchesSuggestion(toolchain: ZigToolchain, suggestion: ZigToolchain): Boolean
fun createConfigurable(uuid: UUID, toolchain: ZigToolchain): ZigToolchainConfigurable<*>
fun suggestToolchains(): List<ZigToolchain>
fun suggestToolchains(): List<Pair<SemVer?, ZigToolchain>>
fun render(toolchain: ZigToolchain, component: SimpleColoredComponent, isSuggestion: Boolean)
}
@ -71,8 +71,8 @@ fun suggestZigToolchains(): List<ZigToolchain> {
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) } }
}
suggestions.filter { suggestion -> compatibleExisting.none { existing -> ext.matchesSuggestion(existing, suggestion.second) } }
}.sortedByDescending { it.first }.map { it.second }
}
fun ZigToolchain.render(component: SimpleColoredComponent, isSuggestion: Boolean) {

View file

@ -74,7 +74,7 @@ object Downloader {
) {
version.downloadAndUnpack(downloadPath)
}
return LocalZigToolchain.tryFromPath(downloadPath)
return LocalZigToolchain.tryFromPath(downloadPath)?.second
}
@RequiresEdt

View file

@ -30,11 +30,13 @@ import com.falsepattern.zigbrains.project.toolchain.local.LocalZigToolchain
import com.falsepattern.zigbrains.shared.coroutine.asContextElement
import com.falsepattern.zigbrains.shared.coroutine.runInterruptibleEDT
import com.intellij.icons.AllIcons
import com.intellij.openapi.fileChooser.FileChooser
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
import com.intellij.openapi.ui.DialogBuilder
import com.intellij.openapi.util.Disposer
import com.intellij.ui.DocumentAdapter
import com.intellij.ui.components.JBLabel
import com.intellij.ui.components.JBTextField
import com.intellij.ui.components.textFieldWithBrowseButton
import com.intellij.ui.dsl.builder.AlignX
import com.intellij.ui.dsl.builder.panel
@ -52,39 +54,50 @@ object LocalSelector {
@RequiresEdt
private fun doBrowseFromDisk(): ZigToolchain? {
val dialog = DialogBuilder()
val name = JBTextField().also { it.columns = 25 }
val path = textFieldWithBrowseButton(
null,
FileChooserDescriptorFactory.createSingleFolderDescriptor()
.withTitle(ZigBrainsBundle.message("settings.toolchain.local-selector.chooser.title"))
)
Disposer.register(dialog, path)
path.textField.columns = 50
lateinit var errorMessageBox: JBLabel
fun verify(path: String) {
val tc = LocalZigToolchain.tryFromPathString(path)?.second
if (tc == null) {
errorMessageBox.icon = AllIcons.General.Error
errorMessageBox.text = ZigBrainsBundle.message("settings.toolchain.local-selector.state.invalid")
dialog.setOkActionEnabled(false)
} else if (ZigToolchainListService
.getInstance()
.toolchains
.mapNotNull { it.second as? LocalZigToolchain }
.any { it.location == tc.location }
) {
errorMessageBox.icon = AllIcons.General.Warning
errorMessageBox.text = tc.name?.let { ZigBrainsBundle.message("settings.toolchain.local-selector.state.already-exists-named", it) }
?: ZigBrainsBundle.message("settings.toolchain.local-selector.state.already-exists-unnamed")
dialog.setOkActionEnabled(true)
} else {
errorMessageBox.icon = AllIcons.General.Information
errorMessageBox.text = ZigBrainsBundle.message("settings.toolchain.local-selector.state.ok")
dialog.setOkActionEnabled(true)
}
val prevNameDefault = name.emptyText.text.trim() == name.text.trim() || name.text.isBlank()
name.emptyText.text = tc?.name ?: ""
if (prevNameDefault) {
name.text = name.emptyText.text
}
}
path.addDocumentListener(object: DocumentAdapter() {
override fun textChanged(e: DocumentEvent) {
val tc = LocalZigToolchain.tryFromPathString(path.text)
if (tc == null) {
errorMessageBox.icon = AllIcons.General.Error
errorMessageBox.text = ZigBrainsBundle.message("settings.toolchain.local-selector.state.invalid")
dialog.setOkActionEnabled(false)
} else if (ZigToolchainListService
.getInstance()
.toolchains
.mapNotNull { it.second as? LocalZigToolchain }
.any { it.location == tc.location }
) {
errorMessageBox.icon = AllIcons.General.Warning
errorMessageBox.text = tc.name?.let { ZigBrainsBundle.message("settings.toolchain.local-selector.state.already-exists-named", it) }
?: ZigBrainsBundle.message("settings.toolchain.local-selector.state.already-exists-unnamed")
dialog.setOkActionEnabled(true)
} else {
errorMessageBox.icon = Icons.Zig
errorMessageBox.text = tc.name ?: ZigBrainsBundle.message("settings.toolchain.local-selector.state.ok")
dialog.setOkActionEnabled(true)
}
verify(path.text)
}
})
val center = panel {
row(ZigBrainsBundle.message("settings.toolchain.local-selector.name.label")) {
cell(name).resizableColumn().align(AlignX.FILL)
}
row(ZigBrainsBundle.message("settings.toolchain.local-selector.path.label")) {
cell(path).resizableColumn().align(AlignX.FILL)
}
@ -97,9 +110,19 @@ object LocalSelector {
dialog.setTitle(ZigBrainsBundle.message("settings.toolchain.local-selector.title"))
dialog.addCancelAction()
dialog.addOkAction().also { it.setText(ZigBrainsBundle.message("settings.toolchain.local-selector.ok-action")) }
val chosenFile = FileChooser.chooseFile(
FileChooserDescriptorFactory.createSingleFolderDescriptor()
.withTitle(ZigBrainsBundle.message("settings.toolchain.local-selector.chooser.title")),
null,
null
)
if (chosenFile != null) {
verify(chosenFile.path)
path.text = chosenFile.path
}
if (!dialog.showAndGet()) {
return null
}
return LocalZigToolchain.tryFromPathString(path.text)
return LocalZigToolchain.tryFromPathString(path.text)?.second?.also { it.copy(name = name.text.ifBlank { null } ?: it.name) }
}
}

View file

@ -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 com.intellij.util.text.SemVer
import java.nio.file.Path
@JvmRecord
@ -66,22 +67,27 @@ data class LocalZigToolchain(val location: Path, val std: Path? = null, override
}
}
fun tryFromPathString(pathStr: String?): LocalZigToolchain? {
fun tryFromPathString(pathStr: String?): Pair<SemVer?, LocalZigToolchain>? {
return pathStr?.ifBlank { null }?.toNioPathOrNull()?.let(::tryFromPath)
}
fun tryFromPath(path: Path): LocalZigToolchain? {
fun tryFromPath(path: Path): Pair<SemVer?, LocalZigToolchain>? {
var tc = LocalZigToolchain(path)
if (!tc.zig.fileValid()) {
return null
}
tc.zig
val versionStr = tc.zig
.getEnvBlocking(null)
.getOrNull()
?.version
?.let { "Zig $it" }
?.let { tc = tc.copy(name = it) }
return tc
val version: SemVer?
if (versionStr != null) {
version = SemVer.parseFromText(versionStr)
tc = tc.copy(name = "Zig $versionStr")
} else {
version = null
}
return version to tc
}
}
}

View file

@ -37,6 +37,7 @@ import com.intellij.ui.SimpleColoredComponent
import com.intellij.ui.SimpleTextAttributes
import com.intellij.util.EnvironmentUtil
import com.intellij.util.system.OS
import com.intellij.util.text.SemVer
import java.io.File
import java.nio.file.Files
import java.nio.file.Path
@ -97,7 +98,7 @@ class LocalZigToolchainProvider: ZigToolchainProvider {
return LocalZigToolchainConfigurable(uuid, toolchain)
}
override fun suggestToolchains(): List<ZigToolchain> {
override fun suggestToolchains(): List<Pair<SemVer?, ZigToolchain>> {
val res = HashSet<String>()
EnvironmentUtil.getValue("PATH")?.split(File.pathSeparatorChar)?.let { res.addAll(it.toList()) }
val wellKnown = getWellKnown()

View file

@ -52,7 +52,7 @@ class ZigToolchainEditor(private val isForDefaultProject: Boolean = false): SubC
private val toolchainBox: TCComboBox
private var selectOnNextReload: UUID? = null
private val model: TCModel
private lateinit var editButton: JButton
private var editButton: JButton? = null
init {
model = TCModel(getModelList())
toolchainBox = TCComboBox(model)
@ -60,15 +60,21 @@ class ZigToolchainEditor(private val isForDefaultProject: Boolean = false): SubC
ZigToolchainListService.getInstance().addChangeListener(this)
}
private fun refreshButtonState(item: Any?) {
editButton?.isEnabled = item is TCListElem.Toolchain.Actual
editButton?.repaint()
}
private fun itemStateChanged(event: ItemEvent) {
if (event.stateChange != ItemEvent.SELECTED) {
return
}
val item = event.item
refreshButtonState(item)
if (item !is TCListElem.Pseudo)
return
zigCoroutineScope.launch(toolchainBox.asContextElement()) {
val uuid = ZigToolchainComboBoxHandler.onItemSelected(toolchainBox, item)
val uuid = runCatching { ZigToolchainComboBoxHandler.onItemSelected(toolchainBox, item) }.getOrNull()
withEDTContext(toolchainBox.asContextElement()) {
applyUUIDNowOrOnReload(uuid)
}
@ -105,10 +111,6 @@ class ZigToolchainEditor(private val isForDefaultProject: Boolean = false): SubC
}
model.selectedItem = TCListElem.None
}
withContext(Dispatchers.EDT + editButton.asContextElement()) {
editButton.isEnabled = model.selectedItem is TCListElem.Toolchain.Actual
editButton.repaint()
}
}
override fun attach(p: Panel): Unit = with(p) {
@ -129,7 +131,10 @@ class ZigToolchainEditor(private val isForDefaultProject: Boolean = false): SubC
applyUUIDNowOrOnReload(selectedUUID)
}
}
}.component.let { editButton = it }
}.component.let {
editButton = it
refreshButtonState(toolchainBox.selectedItem)
}
}
}
@ -154,6 +159,7 @@ class ZigToolchainEditor(private val isForDefaultProject: Boolean = false): SubC
override fun reset(context: Project?) {
val project = context ?: ProjectManager.getInstance().defaultProject
toolchainBox.selectedToolchain = ZigToolchainService.getInstance(project).toolchainUUID
refreshButtonState(toolchainBox.selectedItem)
}
override fun dispose() {

View file

@ -143,10 +143,11 @@ settings.toolchain.downloader.archive-size.text=Archive size: {0}
settings.toolchain.downloader.service.index=Zig version information
settings.toolchain.downloader.service.tarball=Zig archive
settings.toolchain.local-selector.title=Select Zig From Disk
settings.toolchain.local-selector.name.label=Name:
settings.toolchain.local-selector.path.label=Path:
settings.toolchain.local-selector.ok-action=Add
settings.toolchain.local-selector.chooser.title=Existing Zig Install Directory
settings.toolchain.local-selector.state.invalid=Invalid toolchain path
settings.toolchain.local-selector.state.already-exists-unnamed=Toolchain already exists
settings.toolchain.local-selector.state.already-exists-named=Toolchain already exists as "{0}"
settings.toolchain.local-selector.state.ok=OK
settings.toolchain.local-selector.state.ok=Toolchain path OK