diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a8f3ed9..a612e68e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,11 @@ Changelog structure reference: ## [Unreleased] +### Added + +- Project + - Support running file main/tests with hotkey (default: ctrl+shift+f10) + ### Changed - Direnv diff --git a/core/src/main/kotlin/com/falsepattern/zigbrains/project/actions/ZigRunFileAction.kt b/core/src/main/kotlin/com/falsepattern/zigbrains/project/actions/ZigRunFileAction.kt new file mode 100644 index 00000000..27aaa33b --- /dev/null +++ b/core/src/main/kotlin/com/falsepattern/zigbrains/project/actions/ZigRunFileAction.kt @@ -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 . + */ + +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 + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/com/falsepattern/zigbrains/project/execution/base/ZigConfigProducer.kt b/core/src/main/kotlin/com/falsepattern/zigbrains/project/execution/base/ZigConfigProducer.kt index 8f9c50df..8d4c1ab6 100644 --- a/core/src/main/kotlin/com/falsepattern/zigbrains/project/execution/base/ZigConfigProducer.kt +++ b/core/src/main/kotlin/com/falsepattern/zigbrains/project/execution/base/ZigConfigProducer.kt @@ -41,7 +41,7 @@ abstract class ZigConfigProducer>: LazyRunConfigurationProdu val psiFile = element.containingFile as? ZigFile ?: return false val theFile = psiFile.virtualFile ?: 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 { @@ -49,7 +49,7 @@ abstract class ZigConfigProducer>: LazyRunConfigurationProdu val psiFile = element.containingFile as? ZigFile ?: return false val theFile = psiFile.virtualFile ?: 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>: LazyRunConfigurationProdu } */ - protected abstract fun setupConfigurationFromContext(configuration: T, element: PsiElement, filePath: Path, theFile: VirtualFile): Boolean - protected abstract fun isConfigurationFromContext(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, psiFile: ZigFile, filePath: Path, theFile: VirtualFile): Boolean abstract override fun shouldReplace(self: ConfigurationFromContext, other: ConfigurationFromContext): Boolean } \ No newline at end of file diff --git a/core/src/main/kotlin/com/falsepattern/zigbrains/project/execution/build/ZigConfigProducerBuild.kt b/core/src/main/kotlin/com/falsepattern/zigbrains/project/execution/build/ZigConfigProducerBuild.kt index 2d027c88..db3675f3 100644 --- a/core/src/main/kotlin/com/falsepattern/zigbrains/project/execution/build/ZigConfigProducerBuild.kt +++ b/core/src/main/kotlin/com/falsepattern/zigbrains/project/execution/build/ZigConfigProducerBuild.kt @@ -25,6 +25,7 @@ package com.falsepattern.zigbrains.project.execution.build import com.falsepattern.zigbrains.ZigBrainsBundle import com.falsepattern.zigbrains.project.execution.base.ZigConfigProducer import com.falsepattern.zigbrains.project.execution.firstConfigFactory +import com.falsepattern.zigbrains.zig.psi.ZigFile import com.intellij.execution.actions.ConfigurationFromContext import com.intellij.execution.configurations.ConfigurationFactory import com.intellij.openapi.vfs.VirtualFile @@ -36,21 +37,18 @@ class ZigConfigProducerBuild: ZigConfigProducer() { return firstConfigFactory() } - override fun setupConfigurationFromContext(configuration: ZigExecConfigBuild, element: PsiElement, filePath: Path, theFile: VirtualFile): Boolean { - if (LINE_MARKER.elementMatches(element)) { - configuration.name = ZigBrainsBundle.message("configuration.build.marker-name") - return true - } - return false + override fun setupConfigurationFromContext(configuration: ZigExecConfigBuild, element: PsiElement, psiFile: ZigFile, filePath: Path, theFile: VirtualFile): Boolean { + if (theFile.name != "build.zig") + return false + configuration.name = ZigBrainsBundle.message("configuration.build.marker-name") + return true } - 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) } override fun shouldReplace(self: ConfigurationFromContext, other: ConfigurationFromContext): Boolean { return self.configurationType is ZigConfigTypeBuild } -} - -private val LINE_MARKER = ZigLineMarkerBuild() \ No newline at end of file +} \ No newline at end of file diff --git a/core/src/main/kotlin/com/falsepattern/zigbrains/project/execution/run/ZigConfigProducerRun.kt b/core/src/main/kotlin/com/falsepattern/zigbrains/project/execution/run/ZigConfigProducerRun.kt index 65c98081..684058d4 100644 --- a/core/src/main/kotlin/com/falsepattern/zigbrains/project/execution/run/ZigConfigProducerRun.kt +++ b/core/src/main/kotlin/com/falsepattern/zigbrains/project/execution/run/ZigConfigProducerRun.kt @@ -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.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.configurations.ConfigurationFactory import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiElement +import com.intellij.psi.util.childrenOfType import java.nio.file.Path class ZigConfigProducerRun: ZigConfigProducer() { @@ -35,16 +38,17 @@ class ZigConfigProducerRun: ZigConfigProducer() { return firstConfigFactory() } - override fun setupConfigurationFromContext(configuration: ZigExecConfigRun, element: PsiElement, filePath: Path, theFile: VirtualFile): Boolean { - if (LINE_MARKER.elementMatches(element)) { - configuration.filePath.path = filePath - configuration.name = theFile.presentableName - return true + override fun setupConfigurationFromContext(configuration: ZigExecConfigRun, element: PsiElement, psiFile: ZigFile, filePath: Path, theFile: VirtualFile): Boolean { + val members = psiFile.childrenOfType().firstOrNull() ?: return false + if (members.containerDeclarationList.none { it.decl?.fnProto?.identifier?.textMatches("main") == true }) { + return false } - 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 } diff --git a/core/src/main/kotlin/com/falsepattern/zigbrains/project/execution/test/ZigConfigProducerTest.kt b/core/src/main/kotlin/com/falsepattern/zigbrains/project/execution/test/ZigConfigProducerTest.kt index 73d01aad..e215785e 100644 --- a/core/src/main/kotlin/com/falsepattern/zigbrains/project/execution/test/ZigConfigProducerTest.kt +++ b/core/src/main/kotlin/com/falsepattern/zigbrains/project/execution/test/ZigConfigProducerTest.kt @@ -25,10 +25,13 @@ package com.falsepattern.zigbrains.project.execution.test import com.falsepattern.zigbrains.ZigBrainsBundle import com.falsepattern.zigbrains.project.execution.base.ZigConfigProducer 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.configurations.ConfigurationFactory import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiElement +import com.intellij.psi.util.childrenOfType import java.nio.file.Path class ZigConfigProducerTest: ZigConfigProducer() { @@ -36,22 +39,21 @@ class ZigConfigProducerTest: ZigConfigProducer() { return firstConfigFactory() } - override fun setupConfigurationFromContext(configuration: ZigExecConfigTest, element: PsiElement, filePath: Path, theFile: VirtualFile): Boolean { - if (LINE_MARKER.elementMatches(element)) { - configuration.filePath.path = filePath - configuration.name = ZigBrainsBundle.message("configuration.test.marker-name", theFile.presentableName) - return true + override fun setupConfigurationFromContext(configuration: ZigExecConfigTest, element: PsiElement, psiFile: ZigFile, filePath: Path, theFile: VirtualFile): Boolean { + val members = psiFile.childrenOfType().firstOrNull() ?: return false + if (members.containerDeclarationList.none { it.testDecl != null }) { + return false } - 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 } override fun shouldReplace(self: ConfigurationFromContext, other: ConfigurationFromContext): Boolean { return self.configurationType is ZigConfigTypeTest } -} - -private val LINE_MARKER = ZigLineMarkerTest() \ No newline at end of file +} \ No newline at end of file diff --git a/core/src/main/resources/META-INF/zigbrains-core.xml b/core/src/main/resources/META-INF/zigbrains-core.xml index 5c6a7f8b..45220600 100644 --- a/core/src/main/resources/META-INF/zigbrains-core.xml +++ b/core/src/main/resources/META-INF/zigbrains-core.xml @@ -193,10 +193,14 @@ + icon="com.falsepattern.zigbrains.Icons.Zig"> + +