backport: 23.1.0

This commit is contained in:
FalsePattern 2025-03-23 13:59:05 +01:00
parent 11fe03fe49
commit 83d7c9d932
Signed by: falsepattern
GPG key ID: E930CDEC50C50E23
26 changed files with 206 additions and 90 deletions

View file

@ -17,6 +17,18 @@ Changelog structure reference:
## [Unreleased] ## [Unreleased]
## [23.1.0]
### Added
- Project
- Support running file main/tests with hotkey (default: ctrl+shift+f10)
### Changed
- Direnv
- Centralized all direnv toggling into a single project-level option
## [23.0.2] ## [23.0.2]
### Fixed ### Fixed

View file

@ -205,6 +205,7 @@ publishVersions.forEach {
archiveFile = distFile(it) archiveFile = distFile(it)
token = providers.environmentVariable("IJ_PUBLISH_TOKEN") token = providers.environmentVariable("IJ_PUBLISH_TOKEN")
channels = if (pluginVersion.contains("-")) listOf("nightly") else listOf("default") channels = if (pluginVersion.contains("-")) listOf("nightly") else listOf("default")
setDependsOn(dependsOn.filter { if (it is TaskProvider<*>) it.name != "signPlugin" && it.name != "buildPlugin" else true })
} }
tasks.named("publish").configure { tasks.named("publish").configure {
dependsOn("jbpublish-$it") dependsOn("jbpublish-$it")

View file

@ -0,0 +1,90 @@
/*
* 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.actions
import com.falsepattern.zigbrains.project.execution.base.ZigExecConfig
import com.falsepattern.zigbrains.project.execution.run.ZigConfigProducerRun
import com.falsepattern.zigbrains.project.execution.run.ZigExecConfigRun
import com.falsepattern.zigbrains.project.execution.test.ZigConfigProducerTest
import com.falsepattern.zigbrains.project.execution.test.ZigExecConfigTest
import com.intellij.execution.ExecutionManager
import com.intellij.execution.actions.ConfigurationContext
import com.intellij.execution.actions.RunConfigurationProducer
import com.intellij.execution.executors.DefaultRunExecutor
import com.intellij.execution.runners.ExecutionEnvironmentBuilder
import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.project.DumbAwareAction
class ZigRunFileAction: DumbAwareAction() {
override fun actionPerformed(e: AnActionEvent) {
val file = e.getData(CommonDataKeys.PSI_FILE) ?: return
val config = getConfig(e) ?: return
val project = file.project
val builder = ExecutionEnvironmentBuilder.createOrNull(DefaultRunExecutor.getRunExecutorInstance(), config) ?: return
ExecutionManager.getInstance(project).restartRunProfile(builder.build())
}
private fun getConfig(e: AnActionEvent): ZigExecConfig<*>? {
val context = ConfigurationContext.getFromContext(e.dataContext, e.place)
return getRunConfiguration(context) ?: getTestConfiguration(context)
}
override fun update(e: AnActionEvent) {
e.presentation.isEnabledAndVisible = getConfig(e) != null
}
private fun getRunConfiguration(context: ConfigurationContext): ZigExecConfigRun? {
try {
val configProducer = RunConfigurationProducer.getInstance(ZigConfigProducerRun::class.java)
val settings = configProducer.findExistingConfiguration(context)
if (settings != null) {
return settings.configuration as? ZigExecConfigRun
}
val fromContext = configProducer.createConfigurationFromContext(context)
if (fromContext != null) {
return fromContext.configuration as? ZigExecConfigRun
}
} catch (_: NullPointerException) {}
return null
}
private fun getTestConfiguration(context: ConfigurationContext): ZigExecConfigTest? {
try {
val configProducer = RunConfigurationProducer.getInstance(ZigConfigProducerTest::class.java)
val settings = configProducer.findExistingConfiguration(context)
if (settings != null) {
return settings.configuration as? ZigExecConfigTest
}
val fromContext = configProducer.createConfigurationFromContext(context)
if (fromContext != null) {
return fromContext.configuration as? ZigExecConfigTest
}
} catch (_: NullPointerException) {}
return null
}
override fun getActionUpdateThread(): ActionUpdateThread {
return ActionUpdateThread.BGT
}
}

View file

@ -280,12 +280,6 @@ class ColoredConfigurable(serializedName: String): CheckboxConfigurable(serializ
} }
} }
class DirenvConfigurable(serializedName: String, project: Project): CheckboxConfigurable(serializedName, ZigBrainsBundle.message("exec.option.label.direnv"), project.zigProjectSettings.state.direnv) {
override fun clone(): DirenvConfigurable {
return super.clone() as DirenvConfigurable
}
}
class OptimizationConfigurable( class OptimizationConfigurable(
@Transient private val serializedName: String, @Transient private val serializedName: String,
var level: OptimizationLevel = OptimizationLevel.Debug, var level: OptimizationLevel = OptimizationLevel.Debug,

View file

@ -41,7 +41,7 @@ abstract class ZigConfigProducer<T: ZigExecConfig<T>>: LazyRunConfigurationProdu
val psiFile = element.containingFile as? ZigFile ?: return false val psiFile = element.containingFile as? ZigFile ?: return false
val theFile = psiFile.virtualFile ?: return false val theFile = psiFile.virtualFile ?: return false
val filePath = theFile.toNioPathOrNull() ?: return false val filePath = theFile.toNioPathOrNull() ?: return false
return setupConfigurationFromContext(configuration, element, filePath, theFile) return setupConfigurationFromContext(configuration, element, psiFile, filePath, theFile)
} }
override fun isConfigurationFromContext(configuration: T, context: ConfigurationContext): Boolean { override fun isConfigurationFromContext(configuration: T, context: ConfigurationContext): Boolean {
@ -49,7 +49,7 @@ abstract class ZigConfigProducer<T: ZigExecConfig<T>>: LazyRunConfigurationProdu
val psiFile = element.containingFile as? ZigFile ?: return false val psiFile = element.containingFile as? ZigFile ?: return false
val theFile = psiFile.virtualFile ?: return false val theFile = psiFile.virtualFile ?: return false
val filePath = theFile.toNioPathOrNull() ?: return false val filePath = theFile.toNioPathOrNull() ?: return false
return isConfigurationFromContext(configuration, element, filePath, theFile) return isConfigurationFromContext(configuration, element, psiFile, filePath, theFile)
} }
/* /*
@ -78,7 +78,7 @@ abstract class ZigConfigProducer<T: ZigExecConfig<T>>: LazyRunConfigurationProdu
} }
*/ */
protected abstract fun setupConfigurationFromContext(configuration: T, element: PsiElement, filePath: Path, theFile: VirtualFile): Boolean protected abstract fun setupConfigurationFromContext(configuration: T, element: PsiElement, psiFile: ZigFile, filePath: Path, theFile: VirtualFile): Boolean
protected abstract fun isConfigurationFromContext(configuration: T, element: PsiElement, filePath: Path, theFile: VirtualFile): Boolean protected abstract fun isConfigurationFromContext(configuration: T, element: PsiElement, psiFile: ZigFile, filePath: Path, theFile: VirtualFile): Boolean
abstract override fun shouldReplace(self: ConfigurationFromContext, other: ConfigurationFromContext): Boolean abstract override fun shouldReplace(self: ConfigurationFromContext, other: ConfigurationFromContext): Boolean
} }

View file

@ -24,6 +24,7 @@ package com.falsepattern.zigbrains.project.execution.base
import com.falsepattern.zigbrains.ZigBrainsBundle import com.falsepattern.zigbrains.ZigBrainsBundle
import com.falsepattern.zigbrains.direnv.DirenvCmd import com.falsepattern.zigbrains.direnv.DirenvCmd
import com.falsepattern.zigbrains.project.settings.zigProjectSettings
import com.intellij.execution.ExecutionException import com.intellij.execution.ExecutionException
import com.intellij.execution.Executor import com.intellij.execution.Executor
import com.intellij.execution.configurations.ConfigurationFactory import com.intellij.execution.configurations.ConfigurationFactory
@ -44,8 +45,6 @@ abstract class ZigExecConfig<T: ZigExecConfig<T>>(project: Project, factory: Con
private set private set
var pty = CheckboxConfigurable("pty", ZigBrainsBundle.message("exec.option.label.emulate-terminal"), false) var pty = CheckboxConfigurable("pty", ZigBrainsBundle.message("exec.option.label.emulate-terminal"), false)
private set private set
var direnv = DirenvConfigurable("direnv", project)
private set
abstract val suggestedName: @ActionText String abstract val suggestedName: @ActionText String
@Throws(ExecutionException::class) @Throws(ExecutionException::class)
@ -68,7 +67,7 @@ abstract class ZigExecConfig<T: ZigExecConfig<T>>(project: Project, factory: Con
suspend fun patchCommandLine(commandLine: GeneralCommandLine): GeneralCommandLine { suspend fun patchCommandLine(commandLine: GeneralCommandLine): GeneralCommandLine {
if (direnv.value) { if (project.zigProjectSettings.state.direnv) {
commandLine.withEnvironment(DirenvCmd.importDirenv(project).env) commandLine.withEnvironment(DirenvCmd.importDirenv(project).env)
} }
return commandLine return commandLine
@ -82,10 +81,9 @@ abstract class ZigExecConfig<T: ZigExecConfig<T>>(project: Project, factory: Con
val myClone = super.clone() as ZigExecConfig<*> val myClone = super.clone() as ZigExecConfig<*>
myClone.workingDirectory = workingDirectory.clone() myClone.workingDirectory = workingDirectory.clone()
myClone.pty = pty.clone() myClone.pty = pty.clone()
myClone.direnv = direnv.clone()
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
return myClone as T return myClone as T
} }
open fun getConfigurables(): List<ZigConfigurable<*>> = listOf(workingDirectory, pty, direnv) open fun getConfigurables(): List<ZigConfigurable<*>> = listOf(workingDirectory, pty)
} }

View file

@ -25,6 +25,7 @@ package com.falsepattern.zigbrains.project.execution.build
import com.falsepattern.zigbrains.ZigBrainsBundle import com.falsepattern.zigbrains.ZigBrainsBundle
import com.falsepattern.zigbrains.project.execution.base.ZigConfigProducer import com.falsepattern.zigbrains.project.execution.base.ZigConfigProducer
import com.falsepattern.zigbrains.project.execution.firstConfigFactory import com.falsepattern.zigbrains.project.execution.firstConfigFactory
import com.falsepattern.zigbrains.zig.psi.ZigFile
import com.intellij.execution.actions.ConfigurationFromContext import com.intellij.execution.actions.ConfigurationFromContext
import com.intellij.execution.configurations.ConfigurationFactory import com.intellij.execution.configurations.ConfigurationFactory
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
@ -36,21 +37,18 @@ class ZigConfigProducerBuild: ZigConfigProducer<ZigExecConfigBuild>() {
return firstConfigFactory<ZigConfigTypeBuild>() return firstConfigFactory<ZigConfigTypeBuild>()
} }
override fun setupConfigurationFromContext(configuration: ZigExecConfigBuild, element: PsiElement, filePath: Path, theFile: VirtualFile): Boolean { override fun setupConfigurationFromContext(configuration: ZigExecConfigBuild, element: PsiElement, psiFile: ZigFile, filePath: Path, theFile: VirtualFile): Boolean {
if (LINE_MARKER.elementMatches(element)) { if (theFile.name != "build.zig")
configuration.name = ZigBrainsBundle.message("configuration.build.marker-name") return false
return true configuration.name = ZigBrainsBundle.message("configuration.build.marker-name")
} return true
return false
} }
override fun isConfigurationFromContext(configuration: ZigExecConfigBuild, element: PsiElement, filePath: Path, theFile: VirtualFile): Boolean { override fun isConfigurationFromContext(configuration: ZigExecConfigBuild, element: PsiElement, psiFile: ZigFile, filePath: Path, theFile: VirtualFile): Boolean {
return filePath.parent == (configuration.workingDirectory.path ?: return false) return filePath.parent == (configuration.workingDirectory.path ?: return false)
} }
override fun shouldReplace(self: ConfigurationFromContext, other: ConfigurationFromContext): Boolean { override fun shouldReplace(self: ConfigurationFromContext, other: ConfigurationFromContext): Boolean {
return self.configurationType is ZigConfigTypeBuild return self.configurationType is ZigConfigTypeBuild
} }
} }
private val LINE_MARKER = ZigLineMarkerBuild()

View file

@ -24,10 +24,13 @@ package com.falsepattern.zigbrains.project.execution.run
import com.falsepattern.zigbrains.project.execution.base.ZigConfigProducer import com.falsepattern.zigbrains.project.execution.base.ZigConfigProducer
import com.falsepattern.zigbrains.project.execution.firstConfigFactory import com.falsepattern.zigbrains.project.execution.firstConfigFactory
import com.falsepattern.zigbrains.zig.psi.ZigContainerMembers
import com.falsepattern.zigbrains.zig.psi.ZigFile
import com.intellij.execution.actions.ConfigurationFromContext import com.intellij.execution.actions.ConfigurationFromContext
import com.intellij.execution.configurations.ConfigurationFactory import com.intellij.execution.configurations.ConfigurationFactory
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.util.childrenOfType
import java.nio.file.Path import java.nio.file.Path
class ZigConfigProducerRun: ZigConfigProducer<ZigExecConfigRun>() { class ZigConfigProducerRun: ZigConfigProducer<ZigExecConfigRun>() {
@ -35,16 +38,17 @@ class ZigConfigProducerRun: ZigConfigProducer<ZigExecConfigRun>() {
return firstConfigFactory<ZigConfigTypeRun>() return firstConfigFactory<ZigConfigTypeRun>()
} }
override fun setupConfigurationFromContext(configuration: ZigExecConfigRun, element: PsiElement, filePath: Path, theFile: VirtualFile): Boolean { override fun setupConfigurationFromContext(configuration: ZigExecConfigRun, element: PsiElement, psiFile: ZigFile, filePath: Path, theFile: VirtualFile): Boolean {
if (LINE_MARKER.elementMatches(element)) { val members = psiFile.childrenOfType<ZigContainerMembers>().firstOrNull() ?: return false
configuration.filePath.path = filePath if (members.containerDeclarationList.none { it.decl?.fnProto?.identifier?.textMatches("main") == true }) {
configuration.name = theFile.presentableName return false
return true
} }
return false configuration.filePath.path = filePath
configuration.name = theFile.presentableName
return true
} }
override fun isConfigurationFromContext(configuration: ZigExecConfigRun, element: PsiElement, filePath: Path, theFile: VirtualFile): Boolean { override fun isConfigurationFromContext(configuration: ZigExecConfigRun, element: PsiElement, psiFile: ZigFile, filePath: Path, theFile: VirtualFile): Boolean {
return filePath == configuration.filePath.path return filePath == configuration.filePath.path
} }

View file

@ -25,10 +25,13 @@ package com.falsepattern.zigbrains.project.execution.test
import com.falsepattern.zigbrains.ZigBrainsBundle import com.falsepattern.zigbrains.ZigBrainsBundle
import com.falsepattern.zigbrains.project.execution.base.ZigConfigProducer import com.falsepattern.zigbrains.project.execution.base.ZigConfigProducer
import com.falsepattern.zigbrains.project.execution.firstConfigFactory import com.falsepattern.zigbrains.project.execution.firstConfigFactory
import com.falsepattern.zigbrains.zig.psi.ZigContainerMembers
import com.falsepattern.zigbrains.zig.psi.ZigFile
import com.intellij.execution.actions.ConfigurationFromContext import com.intellij.execution.actions.ConfigurationFromContext
import com.intellij.execution.configurations.ConfigurationFactory import com.intellij.execution.configurations.ConfigurationFactory
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.util.childrenOfType
import java.nio.file.Path import java.nio.file.Path
class ZigConfigProducerTest: ZigConfigProducer<ZigExecConfigTest>() { class ZigConfigProducerTest: ZigConfigProducer<ZigExecConfigTest>() {
@ -36,22 +39,21 @@ class ZigConfigProducerTest: ZigConfigProducer<ZigExecConfigTest>() {
return firstConfigFactory<ZigConfigTypeTest>() return firstConfigFactory<ZigConfigTypeTest>()
} }
override fun setupConfigurationFromContext(configuration: ZigExecConfigTest, element: PsiElement, filePath: Path, theFile: VirtualFile): Boolean { override fun setupConfigurationFromContext(configuration: ZigExecConfigTest, element: PsiElement, psiFile: ZigFile, filePath: Path, theFile: VirtualFile): Boolean {
if (LINE_MARKER.elementMatches(element)) { val members = psiFile.childrenOfType<ZigContainerMembers>().firstOrNull() ?: return false
configuration.filePath.path = filePath if (members.containerDeclarationList.none { it.testDecl != null }) {
configuration.name = ZigBrainsBundle.message("configuration.test.marker-name", theFile.presentableName) return false
return true
} }
return false configuration.filePath.path = filePath
configuration.name = ZigBrainsBundle.message("configuration.test.marker-name", theFile.presentableName)
return true
} }
override fun isConfigurationFromContext(configuration: ZigExecConfigTest, element: PsiElement, filePath: Path, theFile: VirtualFile): Boolean { override fun isConfigurationFromContext(configuration: ZigExecConfigTest, element: PsiElement, psiFile: ZigFile, filePath: Path, theFile: VirtualFile): Boolean {
return filePath == configuration.filePath.path return filePath == configuration.filePath.path
} }
override fun shouldReplace(self: ConfigurationFromContext, other: ConfigurationFromContext): Boolean { override fun shouldReplace(self: ConfigurationFromContext, other: ConfigurationFromContext): Boolean {
return self.configurationType is ZigConfigTypeTest return self.configurationType is ZigConfigTypeTest
} }
} }
private val LINE_MARKER = ZigLineMarkerTest()

View file

@ -39,9 +39,9 @@ import com.intellij.util.ui.JBUI
import javax.swing.JList import javax.swing.JList
import javax.swing.ListSelectionModel import javax.swing.ListSelectionModel
class ZigNewProjectPanel(private var handleGit: Boolean): Disposable { class ZigNewProjectPanel(private var handleGit: Boolean): Disposable, ZigProjectConfigurationProvider.SettingsPanelHolder {
private val git = JBCheckBox() private val git = JBCheckBox()
private val panels = ZigProjectConfigurationProvider.createNewProjectSettingsPanels().onEach { Disposer.register(this, it) } override val panels = ZigProjectConfigurationProvider.createNewProjectSettingsPanels(this).onEach { Disposer.register(this, it) }
private val templateList = JBList(JBList.createDefaultListModel(defaultTemplates)).apply { private val templateList = JBList(JBList.createDefaultListModel(defaultTemplates)).apply {
selectionMode = ListSelectionModel.SINGLE_SELECTION selectionMode = ListSelectionModel.SINGLE_SELECTION
selectedIndex = 0 selectedIndex = 0

View file

@ -34,8 +34,8 @@ class ZigCoreProjectConfigurationProvider: ZigProjectConfigurationProvider {
return ZigProjectConfigurable(project) return ZigProjectConfigurable(project)
} }
override fun createNewProjectSettingsPanel(): ZigProjectConfigurationProvider.SettingsPanel { override fun createNewProjectSettingsPanel(holder: ZigProjectConfigurationProvider.SettingsPanelHolder): ZigProjectConfigurationProvider.SettingsPanel {
return ZigProjectSettingsPanel(ProjectManager.getInstance().defaultProject) return ZigProjectSettingsPanel(holder, ProjectManager.getInstance().defaultProject)
} }
override val priority: Int override val priority: Int

View file

@ -29,9 +29,11 @@ import com.intellij.ui.dsl.builder.Panel
class ZigProjectConfigurable(private val project: Project): SubConfigurable { class ZigProjectConfigurable(private val project: Project): SubConfigurable {
private var settingsPanel: ZigProjectSettingsPanel? = null private var settingsPanel: ZigProjectSettingsPanel? = null
override fun createComponent(panel: Panel) { override fun createComponent(holder: ZigProjectConfigurationProvider.SettingsPanelHolder, panel: Panel): ZigProjectConfigurationProvider.SettingsPanel {
settingsPanel?.let { Disposer.dispose(it) } settingsPanel?.let { Disposer.dispose(it) }
settingsPanel = ZigProjectSettingsPanel(project).apply { attach(panel) }.also { Disposer.register(this, it) } val sp = ZigProjectSettingsPanel(holder, project).apply { attach(panel) }.also { Disposer.register(this, it) }
settingsPanel = sp
return sp
} }
override fun isModified(): Boolean { override fun isModified(): Boolean {

View file

@ -32,7 +32,7 @@ import com.intellij.ui.dsl.builder.Panel
interface ZigProjectConfigurationProvider { interface ZigProjectConfigurationProvider {
fun handleMainConfigChanged(project: Project) fun handleMainConfigChanged(project: Project)
fun createConfigurable(project: Project): SubConfigurable fun createConfigurable(project: Project): SubConfigurable
fun createNewProjectSettingsPanel(): SettingsPanel? fun createNewProjectSettingsPanel(holder: SettingsPanelHolder): SettingsPanel?
val priority: Int val priority: Int
companion object { companion object {
private val EXTENSION_POINT_NAME = ExtensionPointName.create<ZigProjectConfigurationProvider>("com.falsepattern.zigbrains.projectConfigProvider") private val EXTENSION_POINT_NAME = ExtensionPointName.create<ZigProjectConfigurationProvider>("com.falsepattern.zigbrains.projectConfigProvider")
@ -42,13 +42,17 @@ interface ZigProjectConfigurationProvider {
fun createConfigurables(project: Project): List<SubConfigurable> { fun createConfigurables(project: Project): List<SubConfigurable> {
return EXTENSION_POINT_NAME.extensionList.sortedBy { it.priority }.map { it.createConfigurable(project) } return EXTENSION_POINT_NAME.extensionList.sortedBy { it.priority }.map { it.createConfigurable(project) }
} }
fun createNewProjectSettingsPanels(): List<SettingsPanel> { fun createNewProjectSettingsPanels(holder: SettingsPanelHolder): List<SettingsPanel> {
return EXTENSION_POINT_NAME.extensionList.sortedBy { it.priority }.mapNotNull { it.createNewProjectSettingsPanel() } return EXTENSION_POINT_NAME.extensionList.sortedBy { it.priority }.mapNotNull { it.createNewProjectSettingsPanel(holder) }
} }
} }
interface SettingsPanel: Disposable { interface SettingsPanel: Disposable {
val data: Settings val data: Settings
fun attach(p: Panel) fun attach(p: Panel)
fun direnvChanged(state: Boolean)
}
interface SettingsPanelHolder {
val panels: List<SettingsPanel>
} }
interface Settings { interface Settings {
fun apply(project: Project) fun apply(project: Project)

View file

@ -52,9 +52,9 @@ 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 ZigProjectSettingsPanel(private val project: Project) : ZigProjectConfigurationProvider.SettingsPanel { class ZigProjectSettingsPanel(private val holder: ZigProjectConfigurationProvider.SettingsPanelHolder, private val project: Project) : ZigProjectConfigurationProvider.SettingsPanel {
private val direnv = JBCheckBox(ZigBrainsBundle.message("settings.project.label.direnv")).apply { addActionListener { private val direnv = JBCheckBox(ZigBrainsBundle.message("settings.project.label.direnv")).apply { addActionListener {
dispatchAutodetect(true) dispatchDirenvUpdate()
} } } }
private val pathToToolchain = textFieldWithBrowseButton( private val pathToToolchain = textFieldWithBrowseButton(
project, project,
@ -86,6 +86,16 @@ class ZigProjectSettingsPanel(private val project: Project) : ZigProjectConfigur
).also { Disposer.register(this, it) } ).also { Disposer.register(this, it) }
private var debounce: Job? = null private var debounce: Job? = null
private fun dispatchDirenvUpdate() {
holder.panels.forEach {
it.direnvChanged(direnv.isSelected)
}
}
override fun direnvChanged(state: Boolean) {
dispatchAutodetect(true)
}
private fun dispatchAutodetect(force: Boolean) { private fun dispatchAutodetect(force: Boolean) {
project.zigCoroutineScope.launchWithEDT { project.zigCoroutineScope.launchWithEDT {
withModalProgress(ModalTaskOwner.component(pathToToolchain), "Detecting Zig...", TaskCancellation.cancellable()) { withModalProgress(ModalTaskOwner.component(pathToToolchain), "Detecting Zig...", TaskCancellation.cancellable()) {

View file

@ -22,17 +22,19 @@
package com.falsepattern.zigbrains.shared package com.falsepattern.zigbrains.shared
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
import com.intellij.openapi.options.Configurable import com.intellij.openapi.options.Configurable
import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Disposer
import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.panel
import javax.swing.JComponent import javax.swing.JComponent
abstract class MultiConfigurable(private val configurables: List<SubConfigurable>): Configurable { abstract class MultiConfigurable(val configurables: List<SubConfigurable>): Configurable, ZigProjectConfigurationProvider.SettingsPanelHolder {
final override var panels: List<ZigProjectConfigurationProvider.SettingsPanel> = emptyList()
private set
override fun createComponent(): JComponent? { override fun createComponent(): JComponent? {
return panel { return panel {
for (configurable in configurables) { panels = configurables.map { it.createComponent(this@MultiConfigurable, this@panel) }
configurable.createComponent(this)
}
} }
} }
@ -50,5 +52,6 @@ abstract class MultiConfigurable(private val configurables: List<SubConfigurable
override fun disposeUIResources() { override fun disposeUIResources() {
configurables.forEach { Disposer.dispose(it) } configurables.forEach { Disposer.dispose(it) }
panels = emptyList()
} }
} }

View file

@ -22,12 +22,14 @@
package com.falsepattern.zigbrains.shared package com.falsepattern.zigbrains.shared
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider.SettingsPanelHolder
import com.intellij.openapi.Disposable import com.intellij.openapi.Disposable
import com.intellij.openapi.options.ConfigurationException import com.intellij.openapi.options.ConfigurationException
import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.Panel
interface SubConfigurable: Disposable { interface SubConfigurable: Disposable {
fun createComponent(panel: Panel) fun createComponent(holder: SettingsPanelHolder, panel: Panel): ZigProjectConfigurationProvider.SettingsPanel
fun isModified(): Boolean fun isModified(): Boolean
@Throws(ConfigurationException::class) @Throws(ConfigurationException::class)
fun apply() fun apply()

View file

@ -193,10 +193,14 @@
<action <action
id="zigbrains.file.new" id="zigbrains.file.new"
class="com.falsepattern.zigbrains.project.actions.ZigNewFileAction" class="com.falsepattern.zigbrains.project.actions.ZigNewFileAction"
icon="com.falsepattern.zigbrains.Icons.Zig" icon="com.falsepattern.zigbrains.Icons.Zig">
>
<add-to-group group-id="NewGroup" anchor="after" relative-to-action="FileTemplateSeparatorGroup"/> <add-to-group group-id="NewGroup" anchor="after" relative-to-action="FileTemplateSeparatorGroup"/>
</action> </action>
<!--suppress PluginXmlValidity -->
<action id="zigbrains.file.run"
class="com.falsepattern.zigbrains.project.actions.ZigRunFileAction"
icon="AllIcons.RunConfigurations.TestState.Run"
use-shortcut-of="RunClass"/>
</actions> </actions>
<!-- endregion Project --> <!-- endregion Project -->

View file

@ -53,16 +53,6 @@ zig.color-settings.variable-decl-depr=Variable//Declaration//Deprecated
zig.color-settings.variable-ref=Variable//Reference zig.color-settings.variable-ref=Variable//Reference
zig.color-settings.variable-ref-depr=Variable//Reference//Deprecated zig.color-settings.variable-ref-depr=Variable//Reference//Deprecated
zon.file.description=Zig object notation file zon.file.description=Zig object notation file
configurable.name.zon-color-settings-page=Zon
zon.color-settings.eq=Equals
zon.color-settings.id=Identifier
zon.color-settings.comment=Comment
zon.color-settings.bad_char=Bad value
zon.color-settings.string=String
zon.color-settings.comma=Comma
zon.color-settings.dot=Dot
zon.color-settings.boolean=Boolean
zon.color-settings.brace=Braces
notification.group.zigbrains=ZigBrains notification.group.zigbrains=ZigBrains
notification.title.native-debug=Zig native debugger notification.title.native-debug=Zig native debugger
notification.content.native-debug=You need to install the "Native Debugging Support" plugin for Zig debugging in this IDE! notification.content.native-debug=You need to install the "Native Debugging Support" plugin for Zig debugging in this IDE!

View file

@ -1,7 +1,7 @@
pluginName=ZigBrains pluginName=ZigBrains
pluginRepositoryUrl=https://github.com/FalsePattern/ZigBrains pluginRepositoryUrl=https://github.com/FalsePattern/ZigBrains
pluginVersion=23.0.2 pluginVersion=23.1.0
pluginSinceBuild=242 pluginSinceBuild=242
pluginUntilBuild=242.* pluginUntilBuild=242.*

View file

@ -38,7 +38,7 @@ class ZLSProjectConfigurationProvider: ZigProjectConfigurationProvider {
return ZLSSettingsConfigurable(project) return ZLSSettingsConfigurable(project)
} }
override fun createNewProjectSettingsPanel(): ZigProjectConfigurationProvider.SettingsPanel { override fun createNewProjectSettingsPanel(holder: ZigProjectConfigurationProvider.SettingsPanelHolder): ZigProjectConfigurationProvider.SettingsPanel {
return ZLSSettingsPanel(ProjectManager.getInstance().defaultProject) return ZLSSettingsPanel(ProjectManager.getInstance().defaultProject)
} }

View file

@ -26,6 +26,7 @@ import com.falsepattern.zigbrains.direnv.DirenvCmd
import com.falsepattern.zigbrains.direnv.emptyEnv import com.falsepattern.zigbrains.direnv.emptyEnv
import com.falsepattern.zigbrains.direnv.getDirenv import com.falsepattern.zigbrains.direnv.getDirenv
import com.falsepattern.zigbrains.lsp.settings.zlsSettings import com.falsepattern.zigbrains.lsp.settings.zlsSettings
import com.falsepattern.zigbrains.project.settings.zigProjectSettings
import com.falsepattern.zigbrains.shared.zigCoroutineScope import com.falsepattern.zigbrains.shared.zigCoroutineScope
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.ProjectActivity import com.intellij.openapi.startup.ProjectActivity
@ -38,7 +39,7 @@ class ZLSStartup: ProjectActivity {
override suspend fun execute(project: Project) { override suspend fun execute(project: Project) {
val zlsState = project.zlsSettings.state val zlsState = project.zlsSettings.state
if (zlsState.zlsPath.isBlank()) { if (zlsState.zlsPath.isBlank()) {
val env = if (DirenvCmd.direnvInstalled() && !project.isDefault && zlsState.direnv) val env = if (DirenvCmd.direnvInstalled() && !project.isDefault && project.zigProjectSettings.state.direnv)
project.getDirenv() project.getDirenv()
else else
emptyEnv emptyEnv

View file

@ -26,6 +26,7 @@ import com.falsepattern.zigbrains.direnv.emptyEnv
import com.falsepattern.zigbrains.direnv.getDirenv import com.falsepattern.zigbrains.direnv.getDirenv
import com.falsepattern.zigbrains.lsp.config.ZLSConfigProviderBase import com.falsepattern.zigbrains.lsp.config.ZLSConfigProviderBase
import com.falsepattern.zigbrains.lsp.settings.zlsSettings import com.falsepattern.zigbrains.lsp.settings.zlsSettings
import com.falsepattern.zigbrains.project.settings.zigProjectSettings
import com.intellij.execution.configurations.GeneralCommandLine import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.notification.Notification import com.intellij.notification.Notification
import com.intellij.notification.NotificationType import com.intellij.notification.NotificationType
@ -58,7 +59,7 @@ class ZLSStreamConnectionProvider private constructor(private val project: Proje
val state = svc.state val state = svc.state
val zlsPath: Path = state.zlsPath.let { zlsPath -> val zlsPath: Path = state.zlsPath.let { zlsPath ->
if (zlsPath.isEmpty()) { if (zlsPath.isEmpty()) {
val env = if (state.direnv) project.getDirenv() else emptyEnv val env = if (project.zigProjectSettings.state.direnv) project.getDirenv() else emptyEnv
env.findExecutableOnPATH("zls") ?: run { env.findExecutableOnPATH("zls") ?: run {
Notification( Notification(
"zigbrains-lsp", "zigbrains-lsp",

View file

@ -26,6 +26,7 @@ import com.falsepattern.zigbrains.direnv.emptyEnv
import com.falsepattern.zigbrains.direnv.getDirenv import com.falsepattern.zigbrains.direnv.getDirenv
import com.falsepattern.zigbrains.lsp.ZLSBundle import com.falsepattern.zigbrains.lsp.ZLSBundle
import com.falsepattern.zigbrains.lsp.startLSP import com.falsepattern.zigbrains.lsp.startLSP
import com.falsepattern.zigbrains.project.settings.zigProjectSettings
import com.intellij.openapi.components.* import com.intellij.openapi.components.*
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.io.toNioPathOrNull import com.intellij.openapi.util.io.toNioPathOrNull
@ -96,7 +97,7 @@ class ZLSProjectSettingsService(val project: Project): PersistentStateComponent<
private suspend fun doValidate(project: Project, state: ZLSSettings): Boolean { private suspend fun doValidate(project: Project, state: ZLSSettings): Boolean {
val zlsPath: Path = state.zlsPath.let { zlsPath -> val zlsPath: Path = state.zlsPath.let { zlsPath ->
if (zlsPath.isEmpty()) { if (zlsPath.isEmpty()) {
val env = if (state.direnv) project.getDirenv() else emptyEnv val env = if (project.zigProjectSettings.state.direnv) project.getDirenv() else emptyEnv
env.findExecutableOnPATH("zls") ?: run { env.findExecutableOnPATH("zls") ?: run {
return false return false
} }

View file

@ -29,7 +29,6 @@ import org.jetbrains.annotations.NonNls
@Suppress("PropertyName") @Suppress("PropertyName")
data class ZLSSettings( data class ZLSSettings(
var direnv: Boolean = false,
var zlsPath: @NonNls String = "", var zlsPath: @NonNls String = "",
var zlsConfigPath: @NonNls String = "", var zlsConfigPath: @NonNls String = "",
val inlayHints: Boolean = true, val inlayHints: Boolean = true,

View file

@ -22,6 +22,7 @@
package com.falsepattern.zigbrains.lsp.settings package com.falsepattern.zigbrains.lsp.settings
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
import com.falsepattern.zigbrains.shared.SubConfigurable import com.falsepattern.zigbrains.shared.SubConfigurable
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Disposer
@ -29,8 +30,10 @@ import com.intellij.ui.dsl.builder.Panel
class ZLSSettingsConfigurable(private val project: Project): SubConfigurable { class ZLSSettingsConfigurable(private val project: Project): SubConfigurable {
private var appSettingsComponent: ZLSSettingsPanel? = null private var appSettingsComponent: ZLSSettingsPanel? = null
override fun createComponent(panel: Panel) { override fun createComponent(holder: ZigProjectConfigurationProvider.SettingsPanelHolder, panel: Panel): ZigProjectConfigurationProvider.SettingsPanel {
appSettingsComponent = ZLSSettingsPanel(project).apply { attach(panel) }.also { Disposer.register(this, it) } val settingsPanel = ZLSSettingsPanel(project).apply { attach(panel) }.also { Disposer.register(this, it) }
appSettingsComponent = settingsPanel
return settingsPanel
} }
override fun isModified(): Boolean { override fun isModified(): Boolean {

View file

@ -29,6 +29,7 @@ import com.falsepattern.zigbrains.direnv.getDirenv
import com.falsepattern.zigbrains.lsp.ZLSBundle import com.falsepattern.zigbrains.lsp.ZLSBundle
import com.falsepattern.zigbrains.lsp.config.SemanticTokens import com.falsepattern.zigbrains.lsp.config.SemanticTokens
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
import com.falsepattern.zigbrains.project.settings.zigProjectSettings
import com.falsepattern.zigbrains.shared.cli.call import com.falsepattern.zigbrains.shared.cli.call
import com.falsepattern.zigbrains.shared.cli.createCommandLineSafe import com.falsepattern.zigbrains.shared.cli.createCommandLineSafe
import com.falsepattern.zigbrains.shared.coroutine.launchWithEDT import com.falsepattern.zigbrains.shared.coroutine.launchWithEDT
@ -87,6 +88,8 @@ class ZLSSettingsPanel(private val project: Project) : ZigProjectConfigurationPr
private var debounce: Job? = null private var debounce: Job? = null
private var direnv: Boolean = project.zigProjectSettings.state.direnv
private val inlayHints = JBCheckBox() private val inlayHints = JBCheckBox()
private val enable_snippets = JBCheckBox() private val enable_snippets = JBCheckBox()
private val enable_argument_placeholders = JBCheckBox() private val enable_argument_placeholders = JBCheckBox()
@ -109,12 +112,6 @@ class ZLSSettingsPanel(private val project: Project) : ZigProjectConfigurationPr
private val build_runner_path = ExtendableTextField() private val build_runner_path = ExtendableTextField()
private val global_cache_path = ExtendableTextField() private val global_cache_path = ExtendableTextField()
private val direnv = JBCheckBox(ZLSBundle.message("settings.zls-path.use-direnv.label")).apply {
addActionListener {
dispatchAutodetect(true)
}
}
override fun attach(p: Panel) = with(p) { override fun attach(p: Panel) = with(p) {
if (!project.isDefault) { if (!project.isDefault) {
group(ZLSBundle.message("settings.group.title")) { group(ZLSBundle.message("settings.group.title")) {
@ -123,9 +120,6 @@ class ZLSSettingsPanel(private val project: Project) : ZigProjectConfigurationPr
"settings.zls-path.tooltip" "settings.zls-path.tooltip"
) { ) {
cell(zlsPath).resizableColumn().align(AlignX.FILL) cell(zlsPath).resizableColumn().align(AlignX.FILL)
if (DirenvCmd.direnvInstalled()) {
cell(direnv)
}
} }
row(ZLSBundle.message("settings.zls-version.label")) { row(ZLSBundle.message("settings.zls-version.label")) {
cell(zlsVersion) cell(zlsVersion)
@ -225,9 +219,13 @@ class ZLSSettingsPanel(private val project: Project) : ZigProjectConfigurationPr
dispatchAutodetect(false) dispatchAutodetect(false)
} }
override fun direnvChanged(state: Boolean) {
direnv = state
dispatchAutodetect(true)
}
override var data override var data
get() = ZLSSettings( get() = ZLSSettings(
direnv.isSelected,
zlsPath.text, zlsPath.text,
zlsConfigPath.text, zlsConfigPath.text,
inlayHints.isSelected, inlayHints.isSelected,
@ -253,7 +251,6 @@ class ZLSSettingsPanel(private val project: Project) : ZigProjectConfigurationPr
global_cache_path.text?.ifBlank { null }, global_cache_path.text?.ifBlank { null },
) )
set(value) { set(value) {
direnv.isSelected = value.direnv
zlsPath.text = value.zlsPath zlsPath.text = value.zlsPath
zlsConfigPath.text = value.zlsConfigPath zlsConfigPath.text = value.zlsConfigPath
inlayHints.isSelected = value.inlayHints inlayHints.isSelected = value.inlayHints
@ -305,7 +302,7 @@ class ZLSSettingsPanel(private val project: Project) : ZigProjectConfigurationPr
} }
private suspend fun getDirenv(): Env { private suspend fun getDirenv(): Env {
if (!project.isDefault && DirenvCmd.direnvInstalled() && direnv.isSelected) if (!project.isDefault && DirenvCmd.direnvInstalled() && direnv)
return project.getDirenv() return project.getDirenv()
return emptyEnv return emptyEnv
} }