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">
+
+