direnv decoupling
This commit is contained in:
parent
f7ea73ae45
commit
68b60e2c77
7 changed files with 79 additions and 47 deletions
|
@ -33,6 +33,7 @@ import com.intellij.openapi.components.*
|
|||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.guessProjectDir
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.UserDataHolder
|
||||
import com.intellij.openapi.vfs.toNioPathOrNull
|
||||
import com.intellij.platform.util.progress.withProgressText
|
||||
import com.intellij.util.io.awaitExit
|
||||
|
@ -140,20 +141,14 @@ class DirenvService(val project: Project): SerializablePersistentStateComponent<
|
|||
private const val GROUP_DISPLAY_ID = "zigbrains-direnv"
|
||||
fun getInstance(project: Project): IDirenvService = project.service<DirenvService>()
|
||||
|
||||
val STATE_KEY = Key.create<DirenvState>("DIRENV_STATE")
|
||||
}
|
||||
}
|
||||
private val STATE_KEY = Key.create<DirenvState>("DIRENV_STATE")
|
||||
|
||||
enum class DirenvState {
|
||||
Auto,
|
||||
Enabled,
|
||||
Disabled;
|
||||
fun getStateFor(data: UserDataHolder, project: Project?): DirenvState {
|
||||
return data.getUserData(STATE_KEY) ?: project?.let { getInstance(project).isEnabled } ?: DirenvState.Disabled
|
||||
}
|
||||
|
||||
fun isEnabled(project: Project?): Boolean {
|
||||
return when(this) {
|
||||
Enabled -> true
|
||||
Disabled -> false
|
||||
Auto -> project?.service<DirenvService>()?.hasDotEnv() == true
|
||||
fun setStateFor(data: UserDataHolder, state: DirenvState) {
|
||||
data.putUserData(STATE_KEY, state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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.direnv
|
||||
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.Project
|
||||
|
||||
enum class DirenvState {
|
||||
Auto,
|
||||
Enabled,
|
||||
Disabled;
|
||||
|
||||
fun isEnabled(project: Project?): Boolean {
|
||||
return when(this) {
|
||||
Enabled -> true
|
||||
Disabled -> false
|
||||
Auto -> project?.service<DirenvService>()?.hasDotEnv() == true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -39,10 +39,12 @@ abstract class DirenvEditor<T>(private val sharedState: ZigProjectConfigurationP
|
|||
row(ZigBrainsBundle.message("settings.direnv.enable.label")) {
|
||||
comboBox(DirenvState.entries).component.let {
|
||||
cb = it
|
||||
it.addItemListener { e ->
|
||||
if (e.stateChange != ItemEvent.SELECTED)
|
||||
return@addItemListener
|
||||
sharedState
|
||||
if (sharedState != null) {
|
||||
it.addItemListener { e ->
|
||||
if (e.stateChange != ItemEvent.SELECTED)
|
||||
return@addItemListener
|
||||
DirenvService.setStateFor(sharedState, DirenvState.Auto)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +74,7 @@ abstract class DirenvEditor<T>(private val sharedState: ZigProjectConfigurationP
|
|||
|
||||
class ForProject(sharedState: ZigProjectConfigurationProvider.IUserDataBridge) : DirenvEditor<Project>(sharedState) {
|
||||
override fun isEnabled(context: Project): DirenvState {
|
||||
return context.service<DirenvService>().isEnabledRaw
|
||||
return DirenvService.getInstance(context).isEnabled
|
||||
}
|
||||
|
||||
override fun setEnabled(context: Project, value: DirenvState) {
|
||||
|
@ -85,7 +87,7 @@ abstract class DirenvEditor<T>(private val sharedState: ZigProjectConfigurationP
|
|||
if (project?.isDefault != false) {
|
||||
return null
|
||||
}
|
||||
sharedState.putUserData(DirenvService.STATE_KEY, DirenvState.Auto)
|
||||
DirenvService.setStateFor(sharedState, DirenvState.Auto)
|
||||
return ForProject(sharedState)
|
||||
}
|
||||
|
||||
|
|
|
@ -24,24 +24,18 @@ package com.falsepattern.zigbrains.project.toolchain.base
|
|||
|
||||
import com.falsepattern.zigbrains.direnv.DirenvState
|
||||
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainListService
|
||||
import com.falsepattern.zigbrains.shared.zigCoroutineScope
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.UserDataHolder
|
||||
import com.intellij.ui.SimpleColoredComponent
|
||||
import com.intellij.util.text.SemVer
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.asFlow
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.flatMap
|
||||
import kotlinx.coroutines.flow.flatMapConcat
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import java.util.UUID
|
||||
|
||||
private val EXTENSION_POINT_NAME = ExtensionPointName.create<ZigToolchainProvider>("com.falsepattern.zigbrains.toolchainProvider")
|
||||
|
@ -53,7 +47,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<*>
|
||||
suspend fun suggestToolchains(project: Project?, direnv: DirenvState): Flow<ZigToolchain>
|
||||
suspend fun suggestToolchains(project: Project?, data: UserDataHolder): Flow<ZigToolchain>
|
||||
fun render(toolchain: ZigToolchain, component: SimpleColoredComponent, isSuggestion: Boolean, isSelected: Boolean)
|
||||
}
|
||||
|
||||
|
@ -75,11 +69,11 @@ fun ZigToolchain.createNamedConfigurable(uuid: UUID): ZigToolchainConfigurable<*
|
|||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun suggestZigToolchains(project: Project? = null, direnv: DirenvState = DirenvState.Disabled): Flow<ZigToolchain> {
|
||||
fun suggestZigToolchains(project: Project? = null, data: UserDataHolder = emptyData): Flow<ZigToolchain> {
|
||||
val existing = ZigToolchainListService.getInstance().toolchains.map { (_, tc) -> tc }.toList()
|
||||
return EXTENSION_POINT_NAME.extensionList.asFlow().flatMapConcat { ext ->
|
||||
val compatibleExisting = existing.filter { ext.isCompatible(it) }
|
||||
val suggestions = ext.suggestToolchains(project, direnv)
|
||||
val suggestions = ext.suggestToolchains(project, data)
|
||||
suggestions.filter { suggestion ->
|
||||
compatibleExisting.none { existing -> ext.matchesSuggestion(existing, suggestion) }
|
||||
}
|
||||
|
@ -89,4 +83,15 @@ fun suggestZigToolchains(project: Project? = null, direnv: DirenvState = DirenvS
|
|||
fun ZigToolchain.render(component: SimpleColoredComponent, isSuggestion: Boolean, isSelected: Boolean) {
|
||||
val provider = EXTENSION_POINT_NAME.extensionList.find { it.isCompatible(this) } ?: throw IllegalStateException()
|
||||
return provider.render(this, component, isSuggestion, isSelected)
|
||||
}
|
||||
|
||||
private val emptyData = object: UserDataHolder {
|
||||
override fun <T : Any?> getUserData(key: Key<T?>): T? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun <T : Any?> putUserData(key: Key<T?>, value: T?) {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -40,7 +40,7 @@ data class LocalZigToolchain(val location: Path, val std: Path? = null, override
|
|||
}
|
||||
|
||||
override suspend fun patchCommandLine(commandLine: GeneralCommandLine, project: Project?): GeneralCommandLine {
|
||||
if (project != null && (commandLine.getUserData(DirenvService.STATE_KEY) ?: DirenvService.getInstance(project).isEnabled).isEnabled(project)) {
|
||||
if (project != null && DirenvService.getStateFor(commandLine, project).isEnabled(project)) {
|
||||
commandLine.withEnvironment(DirenvService.getInstance(project).import().env)
|
||||
}
|
||||
return commandLine
|
||||
|
|
|
@ -28,7 +28,6 @@ import com.falsepattern.zigbrains.direnv.Env
|
|||
import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
|
||||
import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchainConfigurable
|
||||
import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchainProvider
|
||||
import com.falsepattern.zigbrains.shared.zigCoroutineScope
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.UserDataHolder
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
|
@ -36,19 +35,15 @@ import com.intellij.openapi.util.io.toNioPathOrNull
|
|||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.ui.SimpleColoredComponent
|
||||
import com.intellij.ui.SimpleTextAttributes
|
||||
import com.intellij.util.EnvironmentUtil
|
||||
import com.intellij.util.system.OS
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.asFlow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flatMapConcat
|
||||
import kotlinx.coroutines.flow.flattenConcat
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.util.UUID
|
||||
|
@ -97,16 +92,14 @@ class LocalZigToolchainProvider: ZigToolchainProvider {
|
|||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override suspend fun suggestToolchains(project: Project?, direnv: DirenvState): Flow<ZigToolchain> {
|
||||
val env = if (project != null && direnv.isEnabled(project)) {
|
||||
override suspend fun suggestToolchains(project: Project?, data: UserDataHolder): Flow<ZigToolchain> {
|
||||
val env = if (project != null && DirenvService.getStateFor(data, project).isEnabled(project)) {
|
||||
DirenvService.getInstance(project).import()
|
||||
} else {
|
||||
Env.empty
|
||||
}
|
||||
val pathToolchains = env.findAllExecutablesOnPATH("zig").mapNotNull { it.parent }
|
||||
val wellKnown = getWellKnown().asFlow().flatMapConcat { dir ->
|
||||
if (!dir.isDirectory())
|
||||
return@flatMapConcat emptyFlow<Path>()
|
||||
runCatching {
|
||||
Files.newDirectoryStream(dir).use { stream ->
|
||||
stream.toList().filterNotNull().asFlow()
|
||||
|
|
|
@ -43,6 +43,7 @@ import com.intellij.openapi.project.Project
|
|||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.ui.DialogWrapper
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.UserDataHolder
|
||||
import com.intellij.ui.dsl.builder.AlignX
|
||||
import com.intellij.ui.dsl.builder.Panel
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt
|
||||
|
@ -60,8 +61,7 @@ class ZigToolchainEditor(private var project: Project?, private val sharedState:
|
|||
private val model: TCModel
|
||||
private var editButton: JButton? = null
|
||||
init {
|
||||
val direnv = sharedState.getUserData(DirenvService.STATE_KEY) ?: project?.let { DirenvService.getInstance(it).isEnabled } ?: DirenvState.Disabled
|
||||
model = TCModel(getModelList(project, direnv))
|
||||
model = TCModel(getModelList(project, sharedState))
|
||||
toolchainBox = TCComboBox(model)
|
||||
toolchainBox.addItemListener(::itemStateChanged)
|
||||
ZigToolchainListService.getInstance().addChangeListener(this)
|
||||
|
@ -97,8 +97,7 @@ class ZigToolchainEditor(private var project: Project?, private val sharedState:
|
|||
|
||||
override suspend fun toolchainListChanged() {
|
||||
withContext(Dispatchers.EDT + toolchainBox.asContextElement()) {
|
||||
val direnv = sharedState.getUserData(DirenvService.STATE_KEY) ?: project?.let { DirenvService.getInstance(it).isEnabled } ?: DirenvState.Disabled
|
||||
val list = getModelList(project, direnv)
|
||||
val list = getModelList(project, sharedState)
|
||||
model.updateContents(list)
|
||||
val onReload = selectOnNextReload
|
||||
selectOnNextReload = null
|
||||
|
@ -129,9 +128,7 @@ class ZigToolchainEditor(private var project: Project?, private val sharedState:
|
|||
}
|
||||
|
||||
override fun onUserDataChanged(key: Key<*>) {
|
||||
if (key == DirenvService.STATE_KEY) {
|
||||
zigCoroutineScope.launch { toolchainListChanged() }
|
||||
}
|
||||
zigCoroutineScope.launch { toolchainListChanged() }
|
||||
}
|
||||
|
||||
|
||||
|
@ -199,13 +196,13 @@ class ZigToolchainEditor(private var project: Project?, private val sharedState:
|
|||
}
|
||||
|
||||
|
||||
private fun getModelList(project: Project?, direnv: DirenvState): List<TCListElemIn> {
|
||||
private fun getModelList(project: Project?, data: UserDataHolder): List<TCListElemIn> {
|
||||
val modelList = ArrayList<TCListElemIn>()
|
||||
modelList.add(TCListElem.None)
|
||||
modelList.addAll(ZigToolchainListService.getInstance().toolchains.map { it.asActual() }.sortedBy { it.toolchain.name })
|
||||
modelList.add(Separator("", true))
|
||||
modelList.addAll(TCListElem.fetchGroup)
|
||||
modelList.add(Separator(ZigBrainsBundle.message("settings.toolchain.model.detected.separator"), true))
|
||||
modelList.add(suggestZigToolchains(project, direnv).asPending())
|
||||
modelList.add(suggestZigToolchains(project, data).asPending())
|
||||
return modelList
|
||||
}
|
Loading…
Add table
Reference in a new issue