feat: Background toolchain autodetection

This commit is contained in:
FalsePattern 2024-11-11 12:15:28 +01:00
parent 0ed6adff8d
commit 27160d6778
Signed by: falsepattern
GPG key ID: E930CDEC50C50E23
9 changed files with 80 additions and 35 deletions

View file

@ -26,6 +26,7 @@ Changelog structure reference:
- Project - Project
- Direnv now only runs automatically in trusted projects - Direnv now only runs automatically in trusted projects
- Toolchain autodetection is now done in the background on project load
### Fixed ### Fixed

View file

@ -22,6 +22,13 @@
package com.falsepattern.zigbrains package com.falsepattern.zigbrains
import com.falsepattern.zigbrains.direnv.DirenvCmd
import com.falsepattern.zigbrains.direnv.emptyEnv
import com.falsepattern.zigbrains.direnv.getDirenv
import com.falsepattern.zigbrains.lsp.settings.zlsSettings
import com.falsepattern.zigbrains.project.settings.zigProjectSettings
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainProvider
import com.intellij.ide.BrowserUtil import com.intellij.ide.BrowserUtil
import com.intellij.ide.plugins.PluginManager import com.intellij.ide.plugins.PluginManager
import com.intellij.notification.Notification import com.intellij.notification.Notification
@ -33,8 +40,10 @@ import com.intellij.openapi.options.Configurable
import com.intellij.openapi.options.ShowSettingsUtil import com.intellij.openapi.options.ShowSettingsUtil
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.ProjectActivity import com.intellij.openapi.startup.ProjectActivity
import com.intellij.openapi.util.UserDataHolderBase
import java.lang.reflect.Constructor import java.lang.reflect.Constructor
import java.lang.reflect.Method import java.lang.reflect.Method
import kotlin.io.path.pathString
class ZBStartup: ProjectActivity { class ZBStartup: ProjectActivity {
var firstInit = true var firstInit = true
@ -67,6 +76,30 @@ class ZBStartup: ProjectActivity {
notif.notify(null) notif.notify(null)
} }
} }
//Autodetection
val zigProjectState = project.zigProjectSettings.state
if (zigProjectState.toolchainPath.isNullOrBlank()) {
val data = UserDataHolderBase()
data.putUserData(LocalZigToolchain.DIRENV_KEY,
DirenvCmd.direnvInstalled() && !project.isDefault && zigProjectState.direnv
)
val tc = ZigToolchainProvider.suggestToolchain(project, data) ?: return
if (tc is LocalZigToolchain) {
zigProjectState.toolchainPath = tc.location.pathString
project.zigProjectSettings.state = zigProjectState
}
}
val zlsState = project.zlsSettings.state
if (zlsState.zlsPath.isBlank()) {
val env = if (DirenvCmd.direnvInstalled() && !project.isDefault && zlsState.direnv)
project.getDirenv()
else
emptyEnv
env.findExecutableOnPATH("zls")?.let {
zlsState.zlsPath = it.pathString
project.zlsSettings.state = zlsState
}
}
} }
} }

View file

@ -25,7 +25,7 @@ package com.falsepattern.zigbrains.lsp.settings
import org.jetbrains.annotations.NonNls import org.jetbrains.annotations.NonNls
data class ZLSSettings( data class ZLSSettings(
var direnv: Boolean = true, var direnv: Boolean = false,
var zlsPath: @NonNls String = "", var zlsPath: @NonNls String = "",
var zlsConfigPath: @NonNls String = "", var zlsConfigPath: @NonNls String = "",
var debug: Boolean = false, var debug: Boolean = false,

View file

@ -62,22 +62,17 @@ class ZLSSettingsPanel(private val project: Project?) : Disposable {
private val messageTrace = JBCheckBox() private val messageTrace = JBCheckBox()
private val debug = JBCheckBox() private val debug = JBCheckBox()
private val direnv = JBCheckBox(ZLSBundle.message("settings.zls-path.use-direnv.label")) private val direnv = JBCheckBox(ZLSBundle.message("settings.zls-path.use-direnv.label")).apply { addActionListener {
dispatchAutodetect(true)
} }
fun attach(panel: Panel) = with(panel) { fun attach(panel: Panel) = with(panel) {
group(ZLSBundle.message("settings.group.title")) { group(ZLSBundle.message("settings.group.title")) {
row(ZLSBundle.message("settings.zls-path.label")) { row(ZLSBundle.message("settings.zls-path.label")) {
cell(zlsPath).resizableColumn().align(AlignX.FILL) cell(zlsPath).resizableColumn().align(AlignX.FILL)
if (DirenvCmd.direnvInstalled() && project != null && !project.isDefault) { if (DirenvCmd.direnvInstalled() && project?.isDefault == false) {
cell(direnv) cell(direnv)
} }
button(ZLSBundle.message("settings.zls-path.autodetect.label")) {
project.zigCoroutineScope.launchWithEDT {
withModalProgress(ModalTaskOwner.component(zlsPath), "Detecting ZLS...", TaskCancellation.cancellable()) {
autodetect()
}
}
}
} }
row(ZLSBundle.message("settings.zls-config-path.label")) { cell(zlsConfigPath).align(AlignX.FILL) } row(ZLSBundle.message("settings.zls-config-path.label")) { cell(zlsConfigPath).align(AlignX.FILL) }
row(ZLSBundle.message("settings.inlay-hints.label")) { cell(inlayHints) } row(ZLSBundle.message("settings.inlay-hints.label")) { cell(inlayHints) }
@ -91,6 +86,7 @@ class ZLSSettingsPanel(private val project: Project?) : Disposable {
row(ZLSBundle.message("dev-settings.debug.label")) { cell(debug) } row(ZLSBundle.message("dev-settings.debug.label")) { cell(debug) }
row(ZLSBundle.message("dev-settings.message-trace.label")) { cell(messageTrace) } row(ZLSBundle.message("dev-settings.message-trace.label")) { cell(messageTrace) }
} }
dispatchAutodetect(false)
} }
var data var data
@ -121,16 +117,30 @@ class ZLSSettingsPanel(private val project: Project?) : Disposable {
inlayHintsCompact.isSelected = value.inlayHintsCompact inlayHintsCompact.isSelected = value.inlayHintsCompact
} }
suspend fun autodetect() { private fun dispatchAutodetect(force: Boolean) {
getDirenv().findExecutableOnPATH("zls")?.let { zlsPath.text = it.pathString } project.zigCoroutineScope.launchWithEDT {
withModalProgress(ModalTaskOwner.component(zlsPath), "Detecting ZLS...", TaskCancellation.cancellable()) {
autodetect(force)
}
}
}
suspend fun autodetect(force: Boolean) {
if (force || zlsPath.text.isBlank()) {
getDirenv().findExecutableOnPATH("zls")?.let {
if (force || zlsPath.text.isBlank()) {
zlsPath.text = it.pathString
}
}
}
} }
override fun dispose() { override fun dispose() {
} }
private suspend fun getDirenv(): Env { private suspend fun getDirenv(): Env {
if (!direnv.isSelected) if (DirenvCmd.direnvInstalled() && project?.isDefault == false && direnv.isSelected)
return emptyEnv return project.getDirenv()
return project.getDirenv() return emptyEnv
} }
} }

View file

@ -89,11 +89,6 @@ class ZigNewProjectPanel(private var handleGit: Boolean): Disposable {
zlsConf.attach(p) zlsConf.attach(p)
} }
suspend fun autodetect() {
projConf.autodetect()
zlsConf.autodetect()
}
override fun dispose() { override fun dispose() {
} }
} }

View file

@ -28,7 +28,7 @@ import com.intellij.util.xmlb.annotations.Transient
import kotlin.io.path.pathString import kotlin.io.path.pathString
data class ZigProjectSettings( data class ZigProjectSettings(
var direnv: Boolean = true, var direnv: Boolean = false,
var overrideStdPath: Boolean = false, var overrideStdPath: Boolean = false,
var explicitPathToStd: String? = null, var explicitPathToStd: String? = null,
var toolchainPath: String? = null var toolchainPath: String? = null

View file

@ -56,7 +56,9 @@ import kotlin.io.path.notExists
import kotlin.io.path.pathString import kotlin.io.path.pathString
class ZigProjectSettingsPanel(private val project: Project?) : Disposable { class ZigProjectSettingsPanel(private val project: Project?) : Disposable {
private val direnv = JBCheckBox(ZigBrainsBundle.message("settings.project.label.direnv")) private val direnv = JBCheckBox(ZigBrainsBundle.message("settings.project.label.direnv")).apply { addActionListener {
dispatchAutodetect(true)
} }
private val pathToToolchain = textFieldWithBrowseButton( private val pathToToolchain = textFieldWithBrowseButton(
project, project,
FileChooserDescriptorFactory.createSingleFolderDescriptor().withTitle(ZigBrainsBundle.message("dialog.title.zig-toolchain")) FileChooserDescriptorFactory.createSingleFolderDescriptor().withTitle(ZigBrainsBundle.message("dialog.title.zig-toolchain"))
@ -85,15 +87,27 @@ class ZigProjectSettingsPanel(private val project: Project?) : Disposable {
).also { Disposer.register(this, it) } ).also { Disposer.register(this, it) }
private var debounce: Job? = null private var debounce: Job? = null
suspend fun autodetect() { private fun dispatchAutodetect(force: Boolean) {
project.zigCoroutineScope.launchWithEDT {
withModalProgress(ModalTaskOwner.component(pathToToolchain), "Detecting Zig...", TaskCancellation.cancellable()) {
autodetect(force)
}
}
}
suspend fun autodetect(force: Boolean) {
if (!force && pathToToolchain.text.isNotBlank())
return
val data = UserDataHolderBase() val data = UserDataHolderBase()
data.putUserData(LocalZigToolchain.DIRENV_KEY, direnv.isSelected) data.putUserData(LocalZigToolchain.DIRENV_KEY, DirenvCmd.direnvInstalled() && project?.isDefault == false && direnv.isSelected)
val tc = ZigToolchainProvider.suggestToolchain(project, data) ?: return val tc = ZigToolchainProvider.suggestToolchain(project, data) ?: return
if (tc !is LocalZigToolchain) { if (tc !is LocalZigToolchain) {
TODO("Implement non-local zig toolchain in config") TODO("Implement non-local zig toolchain in config")
} }
pathToToolchain.text = tc.location.pathString if (force || pathToToolchain.text.isBlank()) {
dispatchUpdateUI() pathToToolchain.text = tc.location.pathString
dispatchUpdateUI()
}
} }
var data var data
@ -121,13 +135,6 @@ class ZigProjectSettingsPanel(private val project: Project?) : Disposable {
if (DirenvCmd.direnvInstalled() && !project.isDefault) { if (DirenvCmd.direnvInstalled() && !project.isDefault) {
cell(direnv) cell(direnv)
} }
button(ZigBrainsBundle.message("settings.project.label.toolchain-autodetect")) {
project.zigCoroutineScope.launchWithEDT {
withModalProgress(ModalTaskOwner.component(pathToToolchain), "Detecting Zig...", TaskCancellation.cancellable()) {
autodetect()
}
}
}
} }
row(ZigBrainsBundle.message("settings.project.label.toolchain-version")) { row(ZigBrainsBundle.message("settings.project.label.toolchain-version")) {
cell(toolchainVersion) cell(toolchainVersion)
@ -137,6 +144,7 @@ class ZigProjectSettingsPanel(private val project: Project?) : Disposable {
cell(stdFieldOverride) cell(stdFieldOverride)
} }
} }
dispatchAutodetect(false)
} }
private fun dispatchUpdateUI() { private fun dispatchUpdateUI() {

View file

@ -105,7 +105,6 @@ configuration.build.marker-name=Build and Run
settings.project.group.title=Zig Settings settings.project.group.title=Zig Settings
settings.project.label.direnv=Use direnv settings.project.label.direnv=Use direnv
settings.project.label.toolchain=Toolchain location settings.project.label.toolchain=Toolchain location
settings.project.label.toolchain-autodetect=Autodetect
settings.project.label.toolchain-version=Toolchain version settings.project.label.toolchain-version=Toolchain version
settings.project.label.override-std=Override standard library settings.project.label.override-std=Override standard library
settings.project.label.std-location=Standard library location settings.project.label.std-location=Standard library location

View file

@ -13,7 +13,6 @@ settings.build-on-save-step.tooltip=Which step should be executed on build-on-sa
settings.global-var-declarations.label=Highlight global variable declarations settings.global-var-declarations.label=Highlight global variable declarations
settings.comptime-interpreter.label=Use the ZLS comptime interpreter (dangerous) settings.comptime-interpreter.label=Use the ZLS comptime interpreter (dangerous)
settings.zls-path.use-direnv.label=Use direnv settings.zls-path.use-direnv.label=Use direnv
settings.zls-path.autodetect.label=Autodetect
dev-settings.group.title=ZLS Developer Settings dev-settings.group.title=ZLS Developer Settings
dev-settings.debug.label=Debug log dev-settings.debug.label=Debug log
dev-settings.message-trace.label=Message trace dev-settings.message-trace.label=Message trace