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]
### Added
- Project
- Support running file main/tests with hotkey (default: ctrl+shift+f10)
### Changed
- 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 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<T: ZigExecConfig<T>>: 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<T: ZigExecConfig<T>>: 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
}

View file

@ -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,15 +37,14 @@ class ZigConfigProducerBuild: ZigConfigProducer<ZigExecConfigBuild>() {
return firstConfigFactory<ZigConfigTypeBuild>()
}
override fun setupConfigurationFromContext(configuration: ZigExecConfigBuild, element: PsiElement, filePath: Path, theFile: VirtualFile): Boolean {
if (LINE_MARKER.elementMatches(element)) {
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
}
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)
}
@ -52,5 +52,3 @@ class ZigConfigProducerBuild: ZigConfigProducer<ZigExecConfigBuild>() {
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.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<ZigExecConfigRun>() {
@ -35,16 +38,17 @@ class ZigConfigProducerRun: ZigConfigProducer<ZigExecConfigRun>() {
return firstConfigFactory<ZigConfigTypeRun>()
}
override fun setupConfigurationFromContext(configuration: ZigExecConfigRun, element: PsiElement, filePath: Path, theFile: VirtualFile): Boolean {
if (LINE_MARKER.elementMatches(element)) {
override fun setupConfigurationFromContext(configuration: ZigExecConfigRun, element: PsiElement, psiFile: ZigFile, filePath: Path, theFile: VirtualFile): Boolean {
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.name = theFile.presentableName
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
}

View file

@ -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<ZigExecConfigTest>() {
@ -36,16 +39,17 @@ class ZigConfigProducerTest: ZigConfigProducer<ZigExecConfigTest>() {
return firstConfigFactory<ZigConfigTypeTest>()
}
override fun setupConfigurationFromContext(configuration: ZigExecConfigTest, element: PsiElement, filePath: Path, theFile: VirtualFile): Boolean {
if (LINE_MARKER.elementMatches(element)) {
override fun setupConfigurationFromContext(configuration: ZigExecConfigTest, element: PsiElement, psiFile: ZigFile, filePath: Path, theFile: VirtualFile): Boolean {
val members = psiFile.childrenOfType<ZigContainerMembers>().firstOrNull() ?: return false
if (members.containerDeclarationList.none { it.testDecl != null }) {
return false
}
configuration.filePath.path = filePath
configuration.name = ZigBrainsBundle.message("configuration.test.marker-name", theFile.presentableName)
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
}
@ -53,5 +57,3 @@ class ZigConfigProducerTest: ZigConfigProducer<ZigExecConfigTest>() {
return self.configurationType is ZigConfigTypeTest
}
}
private val LINE_MARKER = ZigLineMarkerTest()

View file

@ -193,10 +193,14 @@
<action
id="zigbrains.file.new"
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"/>
</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>
<!-- endregion Project -->