feat: Add support for ctrl+shift+f10

This commit is contained in:
FalsePattern 2025-03-26 15:51:38 +01:00
parent cc062b533e
commit af9ebee500
Signed by: falsepattern
GPG key ID: E930CDEC50C50E23
7 changed files with 136 additions and 33 deletions

View file

@ -17,6 +17,11 @@ Changelog structure reference:
## [Unreleased] ## [Unreleased]
### Added
- Project
- Support running file main/tests with hotkey (default: ctrl+shift+f10)
### Changed ### Changed
- Direnv - Direnv

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

@ -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

@ -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,15 +37,14 @@ 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")
return false
configuration.name = ZigBrainsBundle.message("configuration.build.marker-name") configuration.name = ZigBrainsBundle.message("configuration.build.marker-name")
return true 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)
} }
@ -52,5 +52,3 @@ class ZigConfigProducerBuild: ZigConfigProducer<ZigExecConfigBuild>() {
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
if (members.containerDeclarationList.none { it.decl?.fnProto?.identifier?.textMatches("main") == true }) {
return false
}
configuration.filePath.path = filePath configuration.filePath.path = filePath
configuration.name = theFile.presentableName configuration.name = theFile.presentableName
return true return true
} }
return false
}
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,16 +39,17 @@ 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
if (members.containerDeclarationList.none { it.testDecl != null }) {
return false
}
configuration.filePath.path = filePath configuration.filePath.path = filePath
configuration.name = ZigBrainsBundle.message("configuration.test.marker-name", theFile.presentableName) configuration.name = ZigBrainsBundle.message("configuration.test.marker-name", theFile.presentableName)
return true return true
} }
return false
}
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
} }
@ -53,5 +57,3 @@ class ZigConfigProducerTest: ZigConfigProducer<ZigExecConfigTest>() {
return self.configurationType is ZigConfigTypeTest return self.configurationType is ZigConfigTypeTest
} }
} }
private val LINE_MARKER = ZigLineMarkerTest()

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