Compare commits

...
Sign in to create a new pull request.

32 commits
master ... 241

Author SHA1 Message Date
5e1f2e4bb2
backport: 25.2.0 2025-04-20 16:11:03 +02:00
ce9692ecdb
backport: 25.1.0 2025-04-17 13:59:05 +02:00
c726efc858
backport: 25.0.2 2025-04-16 00:44:40 +02:00
150d85f96e
backport: 25.0.1 2025-04-11 18:52:05 +02:00
3dfe8731cc
backport: 25.0.0 2025-04-11 18:29:47 +02:00
f41b0f2e3d
backport: 24.0.1 2025-03-27 23:05:20 +01:00
db85b56084
backport: 24.0.0 2025-03-27 22:07:37 +01:00
3954ff8ff5
backport: 23.1.2 2025-03-27 11:53:48 +01:00
06f933a69c
backport: 23.1.1 2025-03-26 23:34:16 +01:00
dd9c6daae7
backport: 23.1.0 2025-03-26 16:23:57 +01:00
9176cdd439
backport: 23.0.2 2025-03-23 14:42:07 +01:00
1450dd76eb
backport: 23.0.1 2025-03-20 00:20:35 +01:00
8a7a5aa1cb
backport: 23.0.0 2025-03-15 16:52:25 +01:00
0ec03d6030
backport: 22.0.1 2025-03-13 23:08:52 +01:00
add259d506
backport: 22.0.0 2025-03-13 16:41:36 +01:00
c209294479
backport: 21.1.0 2025-03-11 14:21:31 +01:00
0a7d5cb4c6
backport: 21.0.0 2025-03-11 01:57:19 +01:00
b360873857
backport: 20.3.0 2025-02-06 00:59:45 +01:00
808f45bb35
backport: 20.2.2 2025-01-30 13:27:12 +01:00
456425f35a
backport: 20.2.1 2025-01-22 12:29:53 +01:00
b4cb04dcde
backport: 20.2.0 2025-01-21 15:52:32 +01:00
062aeaaa97
backport: 20.1.3 2025-01-15 20:16:29 +01:00
655f05c3b8
backport: 20.1.2 2025-01-12 14:41:58 +01:00
d3755fbc70
backport: 20.1.1 2024-12-24 12:54:41 +01:00
323ba12fbc
backport: 20.1.0 2024-12-22 23:12:04 +01:00
808d82b44f
backport: 20.0.4 2024-12-11 17:15:28 +01:00
e63ad21db7
backport: 20.0.3 2024-11-28 13:29:38 +01:00
90744797b6
backport: 20.0.2 2024-11-11 12:18:11 +01:00
24e886f13c
backport: 20.0.1 2024-11-09 12:02:34 +01:00
72eaae23fa
ci: publishing fixes 2024-11-07 17:50:32 +01:00
fb29d221a9
backport: 20.0.0 2024-11-07 17:22:17 +01:00
c2e20b7f0f
ci: downgrade to 2024.1 2024-11-07 17:07:45 +01:00
280 changed files with 7797 additions and 2716 deletions

2
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1,2 @@
patreon: falsepattern
ko_fi: falsepattern

1
.gitignore vendored
View file

@ -61,3 +61,4 @@ gradle-app.setting
jbr jbr
secrets secrets
!**/src/**/build/ !**/src/**/build/
.kotlin

View file

@ -17,6 +17,307 @@ Changelog structure reference:
## [Unreleased] ## [Unreleased]
## [25.2.0]
### Added
- Debugger
- Notify the user if zig run / zig test debugging starts, but a build.zig is present
### Changed
- Project
- Line marker task suggestions for main/test now defer to Zig Build if build.zig file is detected.
### Fixed
- Debugger
- Compilation failures did not open the terminal properly and suppressed the error message
## [25.1.0]
### Added
- IDEA 2025.1 support
- LSP
- Configurable inlay hints file size limit to reduce IDE lag
## [25.0.2]
### Fixed
- Project
- ZLS settings not scrollable in the language server list
## [25.0.1]
### Fixed
- Project
- Zig.iml file created in every project
### Changed
- Project
- BREAKING MAJOR UPDATE: Fully reworked toolchain and language server management
The configuration menu is now very similar to the intellij java toolchain management,
with proper toolchain selection, detection, downloading, etc. This change will require
you to re-configure your toolchains!
- Zig external library root is now no longer shown if zig is not configured
## [24.0.1]
### Added
- Project, Debugging
- TTY support for zig processes
### Removed
- Project
- "Emulate terminal" and "colored output" config options have been removed from zig run/test/build tasks, as they are no longer required for ZigBrains to work.
### Fixed
- Debugger
- Build errors didn't get shown in the console
- Project
- File path browse buttons in zig run configurations didn't work
- Occasional GUI deadlocks
- Zig
- IPC wrapper wasn't passing exit code
## [23.1.2]
### Fixed
- LSP
- IDE warning when renaming symbols
## [23.1.1]
### Fixed
- Project
- New project creation creates a blank ZLS config
## [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]
### Fixed
- Zig
- Documentation comment after regular comment was being highlighted as regular comment
## [23.0.1]
### Fixed
- Project
- mkfifo/bash for zig progress visualization is now detected more reliably (fixes error on macOS)
- Deadlock when launching zig build tasks
## [23.0.0]
### Added
- Project
- Zig std.Progress visualization in the zig tool window (Linux/macOS only)
### Removed
- Project
- Executable / Library new project templates temporarily removed until zig stabilizes
## [22.0.1]
### Fixed
- LSP
- Changing ZLS configs would not restart ZLS
- Project
- Occasional "AWT events are not allowed inside write action" error coming from LSP
## [22.0.0]
### Added
- LSP
- Error/Warning banner at the top of the editor when ZLS is misconfigured/not running
- ZLS version indicator in the zig settings
- Toolchain
- More descriptive error messages when toolchain detection fails
### Changed
- Project
- !!BREAKING CHANGE!! Changed file format of zig tasks to store command line arguments as strings instead of string lists.
This (and newer) versions of the plugin will automatically upgrade tasks from 21.1.0 and before.
### Fixed
- Debugging
- Breakpoints could not be placed inside zig code in Android Studio
- Project
- Zig run/debug configuration command line arguments would lose quotes around arguments
## [21.1.0]
### Added
- Zon
- ZLS integration
### Changed
- Zon
- Fully refactored the parser for parity with the zig parser
## [21.0.0]
### Added
- ZLS
- All of the config options are now exposed in the GUI
### Changed
- Project
- New project panel is now much more compact
### Fixed
- Zig
- `zig env` failure causes an IDE error
- A local toolchain disappearing (std directory or zig exe deleted) is now handled properly
## [20.3.0]
- Zig
- Improved default colors
## [20.2.2]
### Fixed
- Debugging
- `zig build run` would run the process twice, one without, one with debugging
## [20.2.1]
### Fixed
- Zig
- Lexer error when a zig file has a comment or multiline string at the end of file without trailing newline
## [20.2.0]
### Added
- Zig
- Live template support
## [20.1.3]
### Added
- Project
- `.zig-cache` directory added to autogenerated gitignore in the project generator
### Fixed
- Project
- Zig Build tool window crashes when opening remote projects
## [20.1.2]
### Fixed
- Zig
- Source file path highlighter made the terminal lag with some files
- Non-terminating rule in lexer could make the editor hang
## [20.1.1]
### Fixed
- Zig
- Unterminated string at the end of the file soft-locks the editor
- Trailing commas in for loop parameters don't get parsed correctly
## [20.1.0]
### Added
- Zig
- String, character literal, and `@"identifier"` quote matching
### Fixed
- Zon
- Broken string quote handling
## [20.0.4]
### Fixed
- Renamed Zig new file task to "Zig File" and moved to the file creation group
## [20.0.3]
### Fixed
- Project
- "Save all documents" hanging when trying to run a zig file
## [20.0.2]
### Added
- Zig
- Escape sequence highlighting in char literals
### Changed
- Project
- Direnv now only runs automatically in trusted projects
- Toolchain autodetection is now done in the background on project load
### Fixed
- Zig
- Unicode characters in char literals triggered an error
## [20.0.1]
### Fixed
- Project
- IDE freezes when opening a zig project / doing zig init
- Test tasks don't work and try to run the file as an executable
- Zig
- Struct fields being styled as static fields instead of instance fields
## [20.0.0]
### Added ### Added
- Debugging - Debugging
@ -30,6 +331,18 @@ Changelog structure reference:
- Most of the internals have been rewritten to be fully asynchronous, so freezes should happen way less - Most of the internals have been rewritten to be fully asynchronous, so freezes should happen way less
## [19.3.0]
### Added
- Toolchains, Run Configurations
- [Direnv](https://github.com/direnv/direnv) support
### Fixed
- Zig
- Missing description for string conversion intentions
## [19.2.0] ## [19.2.0]
### Added ### Added

View file

@ -1,5 +1,5 @@
ZigBrains ZigBrains
Copyright (C) 2023-2024 FalsePattern Copyright (C) 2023-2025 FalsePattern
All Rights Reserved All Rights Reserved
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
@ -25,6 +25,11 @@ which are the property of the Zig Software Foundation.
(https://github.com/ziglang/logo) (https://github.com/ziglang/logo)
These art assets are licensed under Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0). These art assets are licensed under Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0).
-------------------------------- --------------------------------
The art assets inside src/art/zls, and all copies of them, are derived from the Zig Language Server,
which are the property of the zigtools organization.
(https://github.com/zigtools/zls)
These art assets are licensed under MIT license.
--------------------------------
Parts of the codebase are based on the intellij-zig plugin, Parts of the codebase are based on the intellij-zig plugin,
developed by HTGAzureX1212 (https://github.com/HTGAzureX1212), licensed under the Apache 2.0 license. developed by HTGAzureX1212 (https://github.com/HTGAzureX1212), licensed under the Apache 2.0 license.
-------------------------------- --------------------------------
@ -38,3 +43,4 @@ All of the licenses listed here are available in the following files, bundled wi
- licenses/GPL3.LICENSE - licenses/GPL3.LICENSE
- licenses/INTELLIJ-RUST.LICENSE - licenses/INTELLIJ-RUST.LICENSE
- licenses/LGPL3.LICENSE - licenses/LGPL3.LICENSE
- licenses/ZLS.LICENSE

View file

@ -15,6 +15,7 @@ through the built-in plugin browser:
1. Go to `Settings -> Plugins` 1. Go to `Settings -> Plugins`
2. To the right of the `Installed` button at the top, click on the `...` dropdown menu, then select `Manage Plugin Repositories...` 2. To the right of the `Installed` button at the top, click on the `...` dropdown menu, then select `Manage Plugin Repositories...`
3. Click the add button, and then enter the ZigBrains updater URL, based on your IDE version: 3. Click the add button, and then enter the ZigBrains updater URL, based on your IDE version:
- `2025.1.*` or newer: https://falsepattern.com/zigbrains/updatePlugins-251.xml
- `2024.3.*`: https://falsepattern.com/zigbrains/updatePlugins-243.xml - `2024.3.*`: https://falsepattern.com/zigbrains/updatePlugins-243.xml
- `2024.2.*`: https://falsepattern.com/zigbrains/updatePlugins-242.xml - `2024.2.*`: https://falsepattern.com/zigbrains/updatePlugins-242.xml
- `2024.1.*`: https://falsepattern.com/zigbrains/updatePlugins-241.xml - `2024.1.*`: https://falsepattern.com/zigbrains/updatePlugins-241.xml
@ -38,6 +39,7 @@ and might as well utilize the full semver string for extra information.
## Supporters ## Supporters
- ### [Techatrix](https://github.com/Techatrix) - ### [Techatrix](https://github.com/Techatrix)
- ### [nuxusr](https://github.com/nuxusr)
- gree7 - gree7
- xceno - xceno
- AnErrupTion - AnErrupTion
@ -70,11 +72,7 @@ excellent example on how to write debugger support that doesn't depend on CLion.
<!-- Plugin description --> <!-- Plugin description -->
Adds support for the Zig Language, utilizing the ZLS language server for advanced coding assistance. Adds support for the Zig Language, utilizing the ZLS language server for advanced coding assistance.
## Quick setup guide for Zig and ZLS Before you can properly use the plugin, you need to select or download the Zig toolchain and language server in `Settings` -> `Languages & Frameworks` -> `Zig`.
1. Download the latest version of Zig from https://ziglang.org/download
2. Download and compile the ZLS language server, available at https://github.com/zigtools/zls
3. Go to `Settings` -> `Languages & Frameworks` -> `Zig`, and point the `Toolchain Location` and `ZLS path` to the correct places
## Debugging ## Debugging
@ -88,6 +86,7 @@ Debugging Zig code is supported in any native debugging capable IDE. The followi
- RustRover (including the non-commercial free version too) - RustRover (including the non-commercial free version too)
- GoLand - GoLand
- PyCharm Professional - PyCharm Professional
- Android Studio
Additionally, in CLion, the plugin uses the C++ Toolchains for sourcing the debugger (this can be toggled off in the settings). Additionally, in CLion, the plugin uses the C++ Toolchains for sourcing the debugger (this can be toggled off in the settings).

View file

@ -6,15 +6,15 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
plugins { plugins {
kotlin("jvm") version "1.9.24" apply false kotlin("jvm") version "1.9.22" apply false
kotlin("plugin.serialization") version "1.9.24" apply false kotlin("plugin.serialization") version "1.9.22" apply false
id("org.jetbrains.intellij.platform") version "2.1.0" id("org.jetbrains.intellij.platform") version "2.5.0"
id("org.jetbrains.changelog") version "2.2.1" id("org.jetbrains.changelog") version "2.2.1"
id("org.jetbrains.grammarkit") version "2022.3.2.2" apply false id("org.jetbrains.grammarkit") version "2022.3.2.2" apply false
idea idea
`maven-publish` `maven-publish`
} }
val publishVersions = listOf("241", "242", "243") val publishVersions = listOf("241", "242", "243", "251")
val pluginVersionFull get() = "$pluginVersion-$pluginSinceBuild" val pluginVersionFull get() = "$pluginVersion-$pluginSinceBuild"
val pluginVersion: String by project val pluginVersion: String by project
val pluginSinceBuild: String by project val pluginSinceBuild: String by project
@ -23,7 +23,10 @@ val javaVersion = property("javaVersion").toString().toInt()
val lsp4ijVersion: String by project val lsp4ijVersion: String by project
val runIdeTarget: String by project val runIdeTarget: String by project
val lsp4ijNightly = property("lsp4ijNightly").toString().toBoolean() val lsp4ijNightly = property("lsp4ijNightly").toString().toBoolean()
val useInstaller = property("useInstaller").toString().toBoolean()
val lsp4ijPluginString = "com.redhat.devtools.lsp4ij:$lsp4ijVersion${if (lsp4ijNightly) "@nightly" else ""}" val lsp4ijPluginString = "com.redhat.devtools.lsp4ij:$lsp4ijVersion${if (lsp4ijNightly) "@nightly" else ""}"
val ideaCommunityVersion: String by project
val clionVersion: String by project
group = "com.falsepattern" group = "com.falsepattern"
version = pluginVersionFull version = pluginVersionFull
@ -57,7 +60,6 @@ tasks {
allprojects { allprojects {
idea { idea {
module { module {
isDownloadJavadoc = false
isDownloadSources = true isDownloadSources = true
} }
} }
@ -90,18 +92,13 @@ allprojects {
defaultRepositories() defaultRepositories()
} }
} }
dependencies {
intellijPlatform {
instrumentationTools()
}
}
} }
dependencies { dependencies {
intellijPlatform { intellijPlatform {
when(runIdeTarget) { when(runIdeTarget) {
"ideaCommunity" -> create(IntelliJPlatformType.IntellijIdeaCommunity, providers.gradleProperty("ideaCommunityVersion")) "ideaCommunity" -> create(IntelliJPlatformType.IntellijIdeaCommunity, ideaCommunityVersion, useInstaller = useInstaller)
"clion" -> create(IntelliJPlatformType.CLion, providers.gradleProperty("clionVersion")) "clion" -> create(IntelliJPlatformType.CLion, clionVersion, useInstaller = useInstaller)
} }
pluginVerifier() pluginVerifier()
@ -109,13 +106,14 @@ dependencies {
plugin(lsp4ijPluginString) plugin(lsp4ijPluginString)
} }
implementation(project(":core")) runtimeOnly(project(":core"))
implementation(project(":cidr")) runtimeOnly(project(":cidr"))
runtimeOnly(project(":lsp"))
} }
intellijPlatform { intellijPlatform {
pluginConfiguration { pluginConfiguration {
version = providers.gradleProperty("pluginVersion") version = pluginVersionFull
description = providers.fileContents(layout.projectDirectory.file("README.md")).asText.map { description = providers.fileContents(layout.projectDirectory.file("README.md")).asText.map {
val start = "<!-- Plugin description -->" val start = "<!-- Plugin description -->"
@ -131,7 +129,7 @@ intellijPlatform {
val changelog = project.changelog val changelog = project.changelog
changeNotes = providers.gradleProperty("pluginVersion").map { pluginVersion -> changeNotes = provider { pluginVersion }.map { pluginVersion ->
with(changelog) { with(changelog) {
renderItem( renderItem(
(getOrNull(pluginVersion) ?: getUnreleased()) (getOrNull(pluginVersion) ?: getUnreleased())
@ -146,6 +144,8 @@ intellijPlatform {
sinceBuild = pluginSinceBuild sinceBuild = pluginSinceBuild
if (pluginUntilBuild.isNotBlank()) { if (pluginUntilBuild.isNotBlank()) {
untilBuild = pluginUntilBuild untilBuild = pluginUntilBuild
} else {
untilBuild = provider { null }
} }
} }
} }
@ -156,11 +156,6 @@ intellijPlatform {
password = providers.environmentVariable("PRIVATE_KEY_PASSWORD") password = providers.environmentVariable("PRIVATE_KEY_PASSWORD")
} }
publishing {
token = providers.environmentVariable("PUBLISH_TOKEN")
channels = providers.gradleProperty("pluginVersion").map { listOf(it.substringAfter('-', "").substringBefore('.').ifEmpty { "default" }) }
}
pluginVerification { pluginVerification {
ides { ides {
select { select {
@ -196,6 +191,9 @@ tasks {
inputArchiveFile = signPlugin.map { it.signedArchiveFile }.get() inputArchiveFile = signPlugin.map { it.signedArchiveFile }.get()
dependsOn(signPlugin) dependsOn(signPlugin)
} }
publishPlugin {
enabled = false
}
} }
@ -206,6 +204,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

@ -1,23 +1,29 @@
#!/bin/sh #!/bin/sh
# #
# Copyright 2023-2024 FalsePattern # This file is part of ZigBrains.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Copyright (C) 2023-2025 FalsePattern
# you may not use this file except in compliance with the License. # All Rights Reserved
# You may obtain a copy of the License at
# #
# http://www.apache.org/licenses/LICENSE-2.0 # The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
# #
# Unless required by applicable law or agreed to in writing, software # ZigBrains is free software: you can redistribute it and/or modify
# distributed under the License is distributed on an "AS IS" BASIS, # it under the terms of the GNU Lesser General Public License as published by
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # the Free Software Foundation, only version 3 of the License.
# See the License for the specific language governing permissions and #
# limitations under 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/>.
# #
set -e set -e
declare -a branches=("master" "242" "241") declare -a branches=("master" "243" "242" "241")
DEFAULT_BRANCH="${branches[0]}" DEFAULT_BRANCH="${branches[0]}"

View file

@ -7,12 +7,15 @@ plugins {
} }
val lsp4jVersion: String by project val lsp4jVersion: String by project
val clionVersion: String by project val clionVersion: String by project
val useInstaller = property("useInstaller").toString().toBoolean()
val genOutputDir = layout.buildDirectory.dir("generated-resources") val genOutputDir = layout.buildDirectory.dir("generated-resources")
sourceSets["main"].resources.srcDir(genOutputDir) sourceSets["main"].resources.srcDir(genOutputDir)
tasks { tasks {
register<Download>("downloadProps") { register<Download>("downloadProps") {
onlyIfModified(true)
useETag(true)
src("https://falsepattern.com/zigbrains/msvc.properties") src("https://falsepattern.com/zigbrains/msvc.properties")
dest(genOutputDir.map { it.file("msvc.properties") }) dest(genOutputDir.map { it.file("msvc.properties") })
} }
@ -23,10 +26,12 @@ tasks {
dependencies { dependencies {
intellijPlatform { intellijPlatform {
create(IntelliJPlatformType.CLion, providers.gradleProperty("clionVersion")) create(IntelliJPlatformType.CLion, clionVersion, useInstaller = useInstaller)
bundledPlugins("com.intellij.clion", "com.intellij.cidr.base", "com.intellij.nativeDebug") bundledPlugins("com.intellij.clion", "com.intellij.cidr.base", "com.intellij.nativeDebug")
} }
implementation(project(":core")) implementation(project(":core")) {
isTransitive = false
}
implementation("org.eclipse.lsp4j:org.eclipse.lsp4j.debug:$lsp4jVersion") { implementation("org.eclipse.lsp4j:org.eclipse.lsp4j.debug:$lsp4jVersion") {
exclude("org.eclipse.lsp4j", "org.eclipse.lsp4j") exclude("org.eclipse.lsp4j", "org.eclipse.lsp4j")
exclude("org.eclipse.lsp4j", "org.eclipse.lsp4j.jsonrpc") exclude("org.eclipse.lsp4j", "org.eclipse.lsp4j.jsonrpc")

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -24,7 +24,6 @@ package com.falsepattern.zigbrains.clion
import com.falsepattern.zigbrains.debugger.ZigDebuggerDriverConfigurationProvider import com.falsepattern.zigbrains.debugger.ZigDebuggerDriverConfigurationProvider
import com.falsepattern.zigbrains.debugger.settings.ZigDebuggerSettings import com.falsepattern.zigbrains.debugger.settings.ZigDebuggerSettings
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.util.SystemInfo
@ -58,8 +57,8 @@ class ZigClionDebuggerDriverConfigurationProvider: ZigDebuggerDriverConfiguratio
} }
return when(toolchain.debuggerKind) { return when(toolchain.debuggerKind) {
CPPDebugger.Kind.BUNDLED_GDB, CPPDebugger.Kind.BUNDLED_GDB,
CPPDebugger.Kind.CUSTOM_GDB -> CLionGDBDriverConfiguration(project, toolchain) CPPDebugger.Kind.CUSTOM_GDB -> CLionGDBDriverConfiguration(project, toolchain, isEmulateTerminal = emulateTerminal)
CPPDebugger.Kind.BUNDLED_LLDB -> CLionLLDBDriverConfiguration(project, toolchain) CPPDebugger.Kind.BUNDLED_LLDB -> CLionLLDBDriverConfiguration(project, toolchain, isEmulateTerminal = emulateTerminal)
} }
} }
} }

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -27,6 +27,7 @@ import com.falsepattern.zigbrains.debugger.toolchain.*
import com.falsepattern.zigbrains.debugger.win.MSVCDriverConfiguration import com.falsepattern.zigbrains.debugger.win.MSVCDriverConfiguration
import com.falsepattern.zigbrains.shared.coroutine.withEDTContext import com.falsepattern.zigbrains.shared.coroutine.withEDTContext
import com.falsepattern.zigbrains.zig.ZigLanguage import com.falsepattern.zigbrains.zig.ZigLanguage
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.DoNotAskOption import com.intellij.openapi.ui.DoNotAskOption
import com.intellij.openapi.ui.MessageDialogBuilder import com.intellij.openapi.ui.MessageDialogBuilder
@ -85,7 +86,7 @@ private suspend fun availabilityCheck(project: Project, kind: DebuggerKind): Boo
} }
if (downloadDebugger) { if (downloadDebugger) {
val result = withEDTContext { val result = withEDTContext(ModalityState.any()) {
service.downloadDebugger(project, kind) service.downloadDebugger(project, kind)
} }
if (result is ZigDebuggerToolchainService.DownloadResult.Ok) { if (result is ZigDebuggerToolchainService.DownloadResult.Ok) {
@ -104,7 +105,7 @@ private suspend fun showDialog(project: Project, message: String, action: String
} }
} }
return withEDTContext { return withEDTContext(ModalityState.any()) {
MessageDialogBuilder MessageDialogBuilder
.okCancel(ZigDebugBundle.message("debugger.run.unavailable"), message) .okCancel(ZigDebugBundle.message("debugger.run.unavailable"), message)
.yesText(action) .yesText(action)

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -22,9 +22,10 @@
package com.falsepattern.zigbrains.debugger package com.falsepattern.zigbrains.debugger
import com.intellij.execution.filters.Filter
import com.intellij.execution.filters.TextConsoleBuilder import com.intellij.execution.filters.TextConsoleBuilder
import com.intellij.xdebugger.XDebugSession import com.intellij.xdebugger.XDebugSession
import com.jetbrains.cidr.execution.RunParameters import com.jetbrains.cidr.execution.RunParameters
import com.jetbrains.cidr.execution.debugger.CidrLocalDebugProcess import com.jetbrains.cidr.execution.debugger.CidrLocalDebugProcess
class ZigLocalDebugProcess(parameters: RunParameters, session: XDebugSession, consoleBuilder: TextConsoleBuilder) : CidrLocalDebugProcess(parameters, session, consoleBuilder) class ZigLocalDebugProcess(parameters: RunParameters, session: XDebugSession, consoleBuilder: TextConsoleBuilder) : CidrLocalDebugProcess(parameters, session, consoleBuilder, { Filter.EMPTY_ARRAY }, true)

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -26,11 +26,7 @@ import com.falsepattern.zigbrains.project.run.ZigProcessHandler
import com.falsepattern.zigbrains.shared.zigCoroutineScope import com.falsepattern.zigbrains.shared.zigCoroutineScope
import com.intellij.execution.ExecutionException import com.intellij.execution.ExecutionException
import com.intellij.execution.configurations.PtyCommandLine import com.intellij.execution.configurations.PtyCommandLine
import com.intellij.execution.process.BaseProcessHandler import com.intellij.execution.process.*
import com.intellij.execution.process.ProcessAdapter
import com.intellij.execution.process.ProcessEvent
import com.intellij.execution.process.ProcessListener
import com.intellij.execution.process.ProcessOutputType
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
import com.intellij.openapi.util.KeyWithDefaultValue import com.intellij.openapi.util.KeyWithDefaultValue
import com.intellij.openapi.util.io.toNioPathOrNull import com.intellij.openapi.util.io.toNioPathOrNull
@ -45,9 +41,12 @@ import com.jetbrains.cidr.execution.debugger.memory.AddressRange
import com.jetbrains.cidr.system.HostMachine import com.jetbrains.cidr.system.HostMachine
import com.jetbrains.cidr.system.LocalHost import com.jetbrains.cidr.system.LocalHost
import io.ktor.util.* import io.ktor.util.*
import kotlinx.coroutines.* import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.future.asCompletableFuture import kotlinx.coroutines.future.asCompletableFuture
import kotlinx.coroutines.future.asDeferred import kotlinx.coroutines.future.asDeferred
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.eclipse.lsp4j.debug.* import org.eclipse.lsp4j.debug.*
import org.eclipse.lsp4j.debug.services.IDebugProtocolClient import org.eclipse.lsp4j.debug.services.IDebugProtocolClient
import org.eclipse.lsp4j.debug.services.IDebugProtocolServer import org.eclipse.lsp4j.debug.services.IDebugProtocolServer
@ -58,8 +57,7 @@ import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
import java.io.OutputStream import java.io.OutputStream
import java.io.PipedOutputStream import java.io.PipedOutputStream
import java.lang.Exception import java.util.*
import java.util.TreeMap
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
import java.util.concurrent.Executors import java.util.concurrent.Executors
import kotlin.math.min import kotlin.math.min
@ -953,10 +951,10 @@ abstract class DAPDriver<Server : IDebugProtocolServer, Client : IDebugProtocolC
fun runInTerminalAsync(args: RunInTerminalRequestArguments): RunInTerminalResponse { fun runInTerminalAsync(args: RunInTerminalRequestArguments): RunInTerminalResponse {
val cli = PtyCommandLine(args.args.toList()) val cli = PtyCommandLine(args.args.toList())
cli.charset = Charsets.UTF_8 cli.withCharset(Charsets.UTF_8)
val cwd = args.cwd?.ifBlank { null }?.toNioPathOrNull() val cwd = args.cwd?.ifBlank { null }?.toNioPathOrNull()
if (cwd != null) { if (cwd != null) {
cli.withWorkingDirectory(cwd) cli.withWorkDirectory(cwd.toFile())
} }
val childProcess = ZigProcessHandler(cli) val childProcess = ZigProcessHandler(cli)
this@DAPDriver.childProcess = childProcess this@DAPDriver.childProcess = childProcess

View file

@ -1,17 +1,23 @@
/* /*
* Copyright 2023-2024 FalsePattern * This file is part of ZigBrains.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Copyright (C) 2023-2025 FalsePattern
* you may not use this file except in compliance with the License. * All Rights Reserved
* You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
* *
* Unless required by applicable law or agreed to in writing, software * ZigBrains is free software: you can redistribute it and/or modify
* distributed under the License is distributed on an "AS IS" BASIS, * it under the terms of the GNU Lesser General Public License as published by
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * the Free Software Foundation, only version 3 of the License.
* See the License for the specific language governing permissions and *
* limitations under 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.debugger.dap package com.falsepattern.zigbrains.debugger.dap

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -33,7 +33,7 @@ class ZigConfigTypeBinary: ConfigurationTypeBase(
IDENTIFIER, IDENTIFIER,
ZigDebugBundle.message("configuration.binary.name"), ZigDebugBundle.message("configuration.binary.name"),
ZigDebugBundle.message("configuration.binary.description"), ZigDebugBundle.message("configuration.binary.description"),
Icons.ZIG Icons.Zig
) { ) {
init { init {
addFactory(ConfigFactoryBinary(this)) addFactory(ConfigFactoryBinary(this))

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -39,7 +39,7 @@ class ZigExecConfigBinary(project: Project, factory: ConfigurationFactory) : Zig
get() = ZigDebugBundle.message("configuration.binary.suggested-name") get() = ZigDebugBundle.message("configuration.binary.suggested-name")
override suspend fun buildCommandLineArgs(debug: Boolean): List<String> { override suspend fun buildCommandLineArgs(debug: Boolean): List<String> {
return args.args return args.argsSplit()
} }
override fun getConfigurables(): List<ZigConfigurable<*>> { override fun getConfigurables(): List<ZigConfigurable<*>> {

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -24,17 +24,17 @@ package com.falsepattern.zigbrains.debugger.execution.binary
import com.falsepattern.zigbrains.debugger.ZigDebugBundle import com.falsepattern.zigbrains.debugger.ZigDebugBundle
import com.falsepattern.zigbrains.project.execution.base.ZigProfileState import com.falsepattern.zigbrains.project.execution.base.ZigProfileState
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
import com.intellij.execution.ExecutionException import com.intellij.execution.ExecutionException
import com.intellij.execution.configurations.GeneralCommandLine import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.execution.runners.ExecutionEnvironment import com.intellij.execution.runners.ExecutionEnvironment
import kotlin.io.path.pathString import kotlin.io.path.pathString
class ZigProfileStateBinary(environment: ExecutionEnvironment, configuration: ZigExecConfigBinary) : ZigProfileState<ZigExecConfigBinary>(environment, configuration) { class ZigProfileStateBinary(environment: ExecutionEnvironment, configuration: ZigExecConfigBinary) : ZigProfileState<ZigExecConfigBinary>(environment, configuration) {
override suspend fun getCommandLine(toolchain: AbstractZigToolchain, debug: Boolean): GeneralCommandLine { override suspend fun getCommandLine(toolchain: ZigToolchain, debug: Boolean): GeneralCommandLine {
val cli = GeneralCommandLine() val cli = GeneralCommandLine()
val cfg = configuration val cfg = configuration
cfg.workingDirectory.path?.let { cli.withWorkingDirectory(it) } cfg.workingDirectory.path?.let { cli.withWorkDirectory(it.toFile()) }
cli.withExePath(cfg.exePath.path?.pathString ?: throw ExecutionException(ZigDebugBundle.message("exception.missing-exe-path"))) cli.withExePath(cfg.exePath.path?.pathString ?: throw ExecutionException(ZigDebugBundle.message("exception.missing-exe-path")))
cli.withCharset(Charsets.UTF_8) cli.withCharset(Charsets.UTF_8)
cli.addParameters(cfg.args.args) cli.addParameters(cfg.args.args)

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -23,6 +23,7 @@
package com.falsepattern.zigbrains.debugger.runner.base package com.falsepattern.zigbrains.debugger.runner.base
import com.falsepattern.zigbrains.project.run.ZigProcessHandler import com.falsepattern.zigbrains.project.run.ZigProcessHandler
import com.falsepattern.zigbrains.shared.cli.startIPCAwareProcess
import com.intellij.execution.ExecutionException import com.intellij.execution.ExecutionException
import com.intellij.execution.configurations.GeneralCommandLine import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.execution.process.ProcessEvent import com.intellij.execution.process.ProcessEvent
@ -30,6 +31,7 @@ import com.intellij.execution.process.ProcessHandler
import com.intellij.execution.process.ProcessListener import com.intellij.execution.process.ProcessListener
import com.intellij.execution.ui.ConsoleView import com.intellij.execution.ui.ConsoleView
import com.intellij.execution.ui.ConsoleViewContentType import com.intellij.execution.ui.ConsoleViewContentType
import com.intellij.openapi.project.Project
import com.intellij.platform.util.progress.withProgressText import com.intellij.platform.util.progress.withProgressText
import com.intellij.util.io.awaitExit import com.intellij.util.io.awaitExit
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -39,17 +41,17 @@ import kotlinx.coroutines.withContext
class PreLaunchProcessListener(val console: ConsoleView) : ProcessListener { class PreLaunchProcessListener(val console: ConsoleView) : ProcessListener {
var isBuildFailed: Boolean = false var isBuildFailed: Boolean = false
private set private set
lateinit var processHandler: ProcessHandler lateinit var processHandler: ZigProcessHandler.IPCAware
private set private set
@Throws(ExecutionException::class) @Throws(ExecutionException::class)
suspend fun executeCommandLineWithHook(commandLine: GeneralCommandLine): Boolean { suspend fun executeCommandLineWithHook(project: Project, commandLine: GeneralCommandLine): Boolean {
return withProgressText(commandLine.commandLineString) { return withProgressText(commandLine.commandLineString) {
val processHandler = ZigProcessHandler(commandLine) val processHandler = commandLine.startIPCAwareProcess(project)
this@PreLaunchProcessListener.processHandler = processHandler this@PreLaunchProcessListener.processHandler = processHandler
hook(processHandler) hook(processHandler)
processHandler.startNotify() processHandler.startNotify()
withContext(Dispatchers.Default) { withContext(Dispatchers.IO) {
processHandler.process.awaitExit() processHandler.process.awaitExit()
} }
runInterruptible { runInterruptible {
@ -66,10 +68,6 @@ class PreLaunchProcessListener(val console: ConsoleView) : ProcessListener {
override fun processTerminated(event: ProcessEvent) { override fun processTerminated(event: ProcessEvent) {
if (event.exitCode != 0) { if (event.exitCode != 0) {
console.print(
"Process finished with exit code " + event.exitCode,
ConsoleViewContentType.NORMAL_OUTPUT
)
isBuildFailed = true isBuildFailed = true
} else { } else {
isBuildFailed = false isBuildFailed = false

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -23,9 +23,10 @@
package com.falsepattern.zigbrains.debugger.runner.base package com.falsepattern.zigbrains.debugger.runner.base
import com.falsepattern.zigbrains.project.execution.base.ZigProfileState import com.falsepattern.zigbrains.project.execution.base.ZigProfileState
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
import com.falsepattern.zigbrains.shared.zigCoroutineScope import com.falsepattern.zigbrains.shared.zigCoroutineScope
import com.intellij.execution.configurations.GeneralCommandLine import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.execution.configurations.PtyCommandLine
import com.jetbrains.cidr.execution.Installer import com.jetbrains.cidr.execution.Installer
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.future.asCompletableFuture import kotlinx.coroutines.future.asCompletableFuture
@ -33,14 +34,14 @@ import java.io.File
class ZigDebugEmitBinaryInstaller<ProfileState: ZigProfileState<*>>( class ZigDebugEmitBinaryInstaller<ProfileState: ZigProfileState<*>>(
private val profileState: ProfileState, private val profileState: ProfileState,
private val toolchain: AbstractZigToolchain, private val toolchain: ZigToolchain,
private val executableFile: File, private val executableFile: File,
private val exeArgs: List<String> private val exeArgs: List<String>
): Installer { ): Installer {
override fun install(): GeneralCommandLine { override fun install(): GeneralCommandLine {
val cfg = profileState.configuration val cfg = profileState.configuration
val cli = GeneralCommandLine().withExePath(executableFile.absolutePath) val cli = PtyCommandLine().withConsoleMode(false).withExePath(executableFile.absolutePath)
cfg.workingDirectory.path?.let { x -> cli.withWorkingDirectory(x) } cfg.workingDirectory.path?.let { x -> cli.withWorkDirectory(x.toFile()) }
cli.addParameters(exeArgs) cli.addParameters(exeArgs)
cli.withCharset(Charsets.UTF_8) cli.withCharset(Charsets.UTF_8)
cli.withRedirectErrorStream(true) cli.withRedirectErrorStream(true)

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -23,7 +23,7 @@
package com.falsepattern.zigbrains.debugger.runner.base package com.falsepattern.zigbrains.debugger.runner.base
import com.falsepattern.zigbrains.project.execution.base.ZigProfileState import com.falsepattern.zigbrains.project.execution.base.ZigProfileState
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
import com.intellij.util.system.CpuArch import com.intellij.util.system.CpuArch
import com.jetbrains.cidr.ArchitectureType import com.jetbrains.cidr.ArchitectureType
import com.jetbrains.cidr.execution.RunParameters import com.jetbrains.cidr.execution.RunParameters
@ -31,7 +31,7 @@ import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration
abstract class ZigDebugParametersBase<ProfileState: ZigProfileState<*>>( abstract class ZigDebugParametersBase<ProfileState: ZigProfileState<*>>(
private val driverConfiguration: DebuggerDriverConfiguration, private val driverConfiguration: DebuggerDriverConfiguration,
protected val toolchain: AbstractZigToolchain, protected val toolchain: ZigToolchain,
protected val profileState: ProfileState protected val profileState: ProfileState
): RunParameters() { ): RunParameters() {
override fun getDebuggerDriverConfiguration(): DebuggerDriverConfiguration { override fun getDebuggerDriverConfiguration(): DebuggerDriverConfiguration {

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -24,9 +24,10 @@ package com.falsepattern.zigbrains.debugger.runner.base
import com.falsepattern.zigbrains.debugger.ZigDebugBundle import com.falsepattern.zigbrains.debugger.ZigDebugBundle
import com.falsepattern.zigbrains.project.execution.base.ZigProfileState import com.falsepattern.zigbrains.project.execution.base.ZigProfileState
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
import com.intellij.execution.ExecutionException import com.intellij.execution.ExecutionException
import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.io.toNioPathOrNull
import com.intellij.platform.util.progress.withProgressText import com.intellij.platform.util.progress.withProgressText
import com.intellij.util.containers.orNull import com.intellij.util.containers.orNull
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration
@ -36,10 +37,11 @@ import java.io.File
import java.nio.file.Files import java.nio.file.Files
import kotlin.io.path.absolutePathString import kotlin.io.path.absolutePathString
import kotlin.io.path.isExecutable import kotlin.io.path.isExecutable
import kotlin.io.path.pathString
abstract class ZigDebugParametersEmitBinaryBase<ProfileState: ZigProfileState<*>>( abstract class ZigDebugParametersEmitBinaryBase<ProfileState: ZigProfileState<*>>(
driverConfiguration: DebuggerDriverConfiguration, driverConfiguration: DebuggerDriverConfiguration,
toolchain: AbstractZigToolchain, toolchain: ZigToolchain,
profileState: ProfileState, profileState: ProfileState,
) : ZigDebugParametersBase<ProfileState>(driverConfiguration, toolchain, profileState), PreLaunchAware { ) : ZigDebugParametersBase<ProfileState>(driverConfiguration, toolchain, profileState), PreLaunchAware {
@Volatile @Volatile
@ -49,13 +51,14 @@ abstract class ZigDebugParametersEmitBinaryBase<ProfileState: ZigProfileState<*>
@Throws(ExecutionException::class) @Throws(ExecutionException::class)
private suspend fun compileExe(listener: PreLaunchProcessListener): File { private suspend fun compileExe(listener: PreLaunchProcessListener): File {
val commandLine = profileState.getCommandLine(toolchain, true) val commandLine = profileState.getCommandLine(toolchain, true)
val cliString = commandLine.getCommandLineString(commandLine.exePath.toNioPathOrNull()?.fileName?.pathString)
val tmpDir = FileUtil.createTempDirectory("zigbrains_debug", "", true).toPath() val tmpDir = FileUtil.createTempDirectory("zigbrains_debug", "", true).toPath()
val exe = tmpDir.resolve("executable") val exe = tmpDir.resolve("executable")
commandLine.addParameters("-femit-bin=${exe.absolutePathString()}") commandLine.addParameters("-femit-bin=${exe.absolutePathString()}")
if (listener.executeCommandLineWithHook(commandLine)) if (listener.executeCommandLineWithHook(profileState.environment.project, commandLine))
throw ExecutionException(ZigDebugBundle.message("debug.base.compile.failed.generic")) throw ExecutionException(ZigDebugBundle.message("debug.base.compile.failed.generic", cliString))
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
Files.list(tmpDir).use { stream -> Files.list(tmpDir).use { stream ->

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -24,9 +24,10 @@ package com.falsepattern.zigbrains.debugger.runner.base
import com.falsepattern.zigbrains.debugbridge.ZigDebuggerDriverConfigurationProviderBase import com.falsepattern.zigbrains.debugbridge.ZigDebuggerDriverConfigurationProviderBase
import com.falsepattern.zigbrains.debugger.ZigLocalDebugProcess import com.falsepattern.zigbrains.debugger.ZigLocalDebugProcess
import com.falsepattern.zigbrains.debugger.runner.build.ZigDebugRunnerBuild
import com.falsepattern.zigbrains.project.execution.base.ZigProfileState import com.falsepattern.zigbrains.project.execution.base.ZigProfileState
import com.falsepattern.zigbrains.project.run.ZigProgramRunner import com.falsepattern.zigbrains.project.run.ZigProgramRunner
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
import com.falsepattern.zigbrains.shared.coroutine.runInterruptibleEDT import com.falsepattern.zigbrains.shared.coroutine.runInterruptibleEDT
import com.falsepattern.zigbrains.shared.coroutine.withEDTContext import com.falsepattern.zigbrains.shared.coroutine.withEDTContext
import com.intellij.execution.DefaultExecutionResult import com.intellij.execution.DefaultExecutionResult
@ -40,6 +41,8 @@ import com.intellij.execution.runners.RunContentBuilder
import com.intellij.execution.ui.ConsoleView import com.intellij.execution.ui.ConsoleView
import com.intellij.execution.ui.ConsoleViewContentType import com.intellij.execution.ui.ConsoleViewContentType
import com.intellij.execution.ui.RunContentDescriptor import com.intellij.execution.ui.RunContentDescriptor
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.project.guessProjectDir
import com.intellij.platform.util.progress.reportProgress import com.intellij.platform.util.progress.reportProgress
import com.intellij.xdebugger.XDebugProcess import com.intellij.xdebugger.XDebugProcess
import com.intellij.xdebugger.XDebugProcessStarter import com.intellij.xdebugger.XDebugProcessStarter
@ -51,13 +54,13 @@ abstract class ZigDebugRunnerBase<ProfileState : ZigProfileState<*>> : ZigProgra
@Throws(ExecutionException::class) @Throws(ExecutionException::class)
override suspend fun execute( override suspend fun execute(
state: ProfileState, state: ProfileState,
toolchain: AbstractZigToolchain, toolchain: ZigToolchain,
environment: ExecutionEnvironment environment: ExecutionEnvironment
): RunContentDescriptor? { ): RunContentDescriptor? {
val project = environment.project val project = environment.project
val driverProviders = ZigDebuggerDriverConfigurationProviderBase.EXTENSION_POINT_NAME.extensionList val driverProviders = ZigDebuggerDriverConfigurationProviderBase.EXTENSION_POINT_NAME.extensionList
for (provider in driverProviders) { for (provider in driverProviders) {
val driver = provider.getDebuggerConfiguration(project, isElevated = false, emulateTerminal = false, DebuggerDriverConfiguration::class.java) ?: continue val driver = provider.getDebuggerConfiguration(project, isElevated = false, emulateTerminal = true, DebuggerDriverConfiguration::class.java) ?: continue
return executeWithDriver(state, toolchain, environment, driver) ?: continue return executeWithDriver(state, toolchain, environment, driver) ?: continue
} }
return null return null
@ -66,7 +69,7 @@ abstract class ZigDebugRunnerBase<ProfileState : ZigProfileState<*>> : ZigProgra
@Throws(ExecutionException::class) @Throws(ExecutionException::class)
private suspend fun executeWithDriver( private suspend fun executeWithDriver(
state: ProfileState, state: ProfileState,
toolchain: AbstractZigToolchain, toolchain: ZigToolchain,
environment: ExecutionEnvironment, environment: ExecutionEnvironment,
debuggerDriver: DebuggerDriverConfiguration debuggerDriver: DebuggerDriverConfiguration
): RunContentDescriptor? { ): RunContentDescriptor? {
@ -81,22 +84,25 @@ abstract class ZigDebugRunnerBase<ProfileState : ZigProfileState<*>> : ZigProgra
} }
} catch (e: ExecutionException) { } catch (e: ExecutionException) {
console.print("\n", ConsoleViewContentType.ERROR_OUTPUT) console.print("\n", ConsoleViewContentType.ERROR_OUTPUT)
e.message?.let { listener.console.print(it, ConsoleViewContentType.SYSTEM_OUTPUT) } e.message?.let { listener.console.print(it, ConsoleViewContentType.ERROR_OUTPUT) }
if (this !is ZigDebugRunnerBuild && environment.project.guessProjectDir()?.children?.any { it.name == "build.zig" } == true) {
console.print("\n Warning: build.zig file detected in project.\n Did you want to use a Zig Build task instead?", ConsoleViewContentType.ERROR_OUTPUT)
}
} }
if (listener.isBuildFailed) { if (listener.isBuildFailed) {
val executionResult = DefaultExecutionResult(console, listener.processHandler) val executionResult = DefaultExecutionResult(console, listener.processHandler.unwrap())
return@reportProgress withEDTContext { return@reportProgress withEDTContext(ModalityState.any()) {
val runContentBuilder = RunContentBuilder(executionResult, environment) val runContentBuilder = RunContentBuilder(executionResult, environment)
runContentBuilder.showRunContent(null) runContentBuilder.showRunContent(null)
} }
} }
} }
return@reportProgress runInterruptibleEDT { return@reportProgress runInterruptibleEDT(ModalityState.any()) {
val debuggerManager = XDebuggerManager.getInstance(environment.project) val debuggerManager = XDebuggerManager.getInstance(environment.project)
debuggerManager.startSession(environment, object: XDebugProcessStarter() { debuggerManager.startSession(environment, object: XDebugProcessStarter() {
override fun start(session: XDebugSession): XDebugProcess { override fun start(session: XDebugSession): XDebugProcess {
val project = session.project val project = session.project
val textConsoleBuilder = SharedConsoleBuilder(console) val textConsoleBuilder = state.consoleBuilder
val debugProcess = ZigLocalDebugProcess(runParameters, session, textConsoleBuilder) val debugProcess = ZigLocalDebugProcess(runParameters, session, textConsoleBuilder)
ProcessTerminatedListener.attach(debugProcess.processHandler, project) ProcessTerminatedListener.attach(debugProcess.processHandler, project)
debugProcess.start() debugProcess.start()
@ -111,7 +117,7 @@ abstract class ZigDebugRunnerBase<ProfileState : ZigProfileState<*>> : ZigProgra
protected abstract fun getDebugParameters( protected abstract fun getDebugParameters(
state: ProfileState, state: ProfileState,
debuggerDriver: DebuggerDriverConfiguration, debuggerDriver: DebuggerDriverConfiguration,
toolchain: AbstractZigToolchain toolchain: ZigToolchain
): ZigDebugParametersBase<ProfileState> ): ZigDebugParametersBase<ProfileState>
private class SharedConsoleBuilder(private val console: ConsoleView) : TextConsoleBuilder() { private class SharedConsoleBuilder(private val console: ConsoleView) : TextConsoleBuilder() {

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -26,16 +26,16 @@ import com.falsepattern.zigbrains.debugger.ZigDebugBundle
import com.falsepattern.zigbrains.debugger.execution.binary.ZigProfileStateBinary import com.falsepattern.zigbrains.debugger.execution.binary.ZigProfileStateBinary
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugEmitBinaryInstaller import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugEmitBinaryInstaller
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
import com.intellij.execution.ExecutionException import com.intellij.execution.ExecutionException
import com.jetbrains.cidr.execution.Installer import com.jetbrains.cidr.execution.Installer
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration
class ZigDebugParametersBinary @Throws(ExecutionException::class) constructor(driverConfiguration: DebuggerDriverConfiguration, toolchain: AbstractZigToolchain, profileState: ZigProfileStateBinary) : class ZigDebugParametersBinary @Throws(ExecutionException::class) constructor(driverConfiguration: DebuggerDriverConfiguration, toolchain: ZigToolchain, profileState: ZigProfileStateBinary) :
ZigDebugParametersBase<ZigProfileStateBinary>(driverConfiguration, toolchain, profileState) { ZigDebugParametersBase<ZigProfileStateBinary>(driverConfiguration, toolchain, profileState) {
private val executableFile = profileState.configuration.exePath.path?.toFile() ?: throw ExecutionException(ZigDebugBundle.message("exception.missing-exe-path")) private val executableFile = profileState.configuration.exePath.path?.toFile() ?: throw ExecutionException(ZigDebugBundle.message("exception.missing-exe-path"))
override fun getInstaller(): Installer { override fun getInstaller(): Installer {
return ZigDebugEmitBinaryInstaller(profileState, toolchain, executableFile, profileState.configuration.args.args) return ZigDebugEmitBinaryInstaller(profileState, toolchain, executableFile, profileState.configuration.args.argsSplit())
} }
} }

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -27,8 +27,8 @@ import com.falsepattern.zigbrains.debugger.execution.binary.ZigProfileStateBinar
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugRunnerBase import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugRunnerBase
import com.falsepattern.zigbrains.project.execution.base.ZigProfileState import com.falsepattern.zigbrains.project.execution.base.ZigProfileState
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain import com.falsepattern.zigbrains.project.toolchain.local.LocalZigToolchain
import com.intellij.execution.configurations.RunProfile import com.intellij.execution.configurations.RunProfile
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration
@ -36,7 +36,7 @@ class ZigDebugRunnerBinary: ZigDebugRunnerBase<ZigProfileStateBinary>() {
override fun getDebugParameters( override fun getDebugParameters(
state: ZigProfileStateBinary, state: ZigProfileStateBinary,
debuggerDriver: DebuggerDriverConfiguration, debuggerDriver: DebuggerDriverConfiguration,
toolchain: AbstractZigToolchain toolchain: ZigToolchain
): ZigDebugParametersBase<ZigProfileStateBinary> { ): ZigDebugParametersBase<ZigProfileStateBinary> {
return ZigDebugParametersBinary(debuggerDriver, LocalZigToolchain.ensureLocal(toolchain), state) return ZigDebugParametersBinary(debuggerDriver, LocalZigToolchain.ensureLocal(toolchain), state)
} }

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -28,7 +28,7 @@ import com.falsepattern.zigbrains.debugger.runner.base.PreLaunchProcessListener
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugEmitBinaryInstaller import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugEmitBinaryInstaller
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase
import com.falsepattern.zigbrains.project.execution.build.ZigProfileStateBuild import com.falsepattern.zigbrains.project.execution.build.ZigProfileStateBuild
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
import com.intellij.execution.ExecutionException import com.intellij.execution.ExecutionException
import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.util.SystemInfo
import com.intellij.platform.util.progress.withProgressText import com.intellij.platform.util.progress.withProgressText
@ -43,18 +43,17 @@ import java.nio.file.Path
import java.util.stream.Stream import java.util.stream.Stream
import kotlin.io.path.isExecutable import kotlin.io.path.isExecutable
import kotlin.io.path.isRegularFile import kotlin.io.path.isRegularFile
import kotlin.io.path.notExists
class ZigDebugParametersBuild( class ZigDebugParametersBuild(
driverConfiguration: DebuggerDriverConfiguration, driverConfiguration: DebuggerDriverConfiguration,
toolchain: AbstractZigToolchain, toolchain: ZigToolchain,
profileState: ZigProfileStateBuild profileState: ZigProfileStateBuild
) : ZigDebugParametersBase<ZigProfileStateBuild>(driverConfiguration, toolchain, profileState), PreLaunchAware { ) : ZigDebugParametersBase<ZigProfileStateBuild>(driverConfiguration, toolchain, profileState), PreLaunchAware {
@Volatile @Volatile
private lateinit var executableFile: File private lateinit var executableFile: File
override fun getInstaller(): Installer { override fun getInstaller(): Installer {
return ZigDebugEmitBinaryInstaller(profileState, toolchain, executableFile, profileState.configuration.exeArgs.args) return ZigDebugEmitBinaryInstaller(profileState, toolchain, executableFile, profileState.configuration.exeArgs.argsSplit())
} }
@Throws(ExecutionException::class) @Throws(ExecutionException::class)
@ -62,8 +61,8 @@ class ZigDebugParametersBuild(
withProgressText("Building zig project") { withProgressText("Building zig project") {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val commandLine = profileState.getCommandLine(toolchain, true) val commandLine = profileState.getCommandLine(toolchain, true)
if (listener.executeCommandLineWithHook(commandLine)) if (listener.executeCommandLineWithHook(profileState.environment.project, commandLine))
throw ExecutionException(ZigDebugBundle.message("debug.build.compile.failed.generic")) return@withContext
val cfg = profileState.configuration val cfg = profileState.configuration
val workingDir = cfg.workingDirectory.path val workingDir = cfg.workingDirectory.path
val exe = profileState.configuration.exePath.path ?: run { val exe = profileState.configuration.exePath.path ?: run {
@ -73,7 +72,7 @@ class ZigDebugParametersBuild(
fail("debug.build.compile.failed.no-workdir") fail("debug.build.compile.failed.no-workdir")
} }
val expectedOutputDir = workingDir.resolve(Path.of("zig-out", "bin")) val expectedOutputDir = workingDir.resolve(Path.of("zig-out", "bin"))
if (expectedOutputDir.notExists()) { if (!expectedOutputDir.toFile().exists()) {
fail("debug.build.compile.failed.autodetect") fail("debug.build.compile.failed.autodetect")
} }
@ -82,7 +81,7 @@ class ZigDebugParametersBuild(
} }
} }
if (exe.notExists()) if (!exe.toFile().exists())
fail("debug.build.compile.failed.no-file", exe) fail("debug.build.compile.failed.no-file", exe)
else if (!exe.isExecutable()) else if (!exe.isExecutable())
fail("debug.build.compile.failed.non-exec-file", exe) fail("debug.build.compile.failed.non-exec-file", exe)

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -27,8 +27,8 @@ import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugRunnerBase
import com.falsepattern.zigbrains.project.execution.base.ZigProfileState import com.falsepattern.zigbrains.project.execution.base.ZigProfileState
import com.falsepattern.zigbrains.project.execution.build.ZigExecConfigBuild import com.falsepattern.zigbrains.project.execution.build.ZigExecConfigBuild
import com.falsepattern.zigbrains.project.execution.build.ZigProfileStateBuild import com.falsepattern.zigbrains.project.execution.build.ZigProfileStateBuild
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain import com.falsepattern.zigbrains.project.toolchain.local.LocalZigToolchain
import com.intellij.execution.configurations.RunProfile import com.intellij.execution.configurations.RunProfile
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration
@ -36,7 +36,7 @@ class ZigDebugRunnerBuild: ZigDebugRunnerBase<ZigProfileStateBuild>() {
override fun getDebugParameters( override fun getDebugParameters(
state: ZigProfileStateBuild, state: ZigProfileStateBuild,
debuggerDriver: DebuggerDriverConfiguration, debuggerDriver: DebuggerDriverConfiguration,
toolchain: AbstractZigToolchain toolchain: ZigToolchain
): ZigDebugParametersBase<ZigProfileStateBuild> { ): ZigDebugParametersBase<ZigProfileStateBuild> {
return ZigDebugParametersBuild(debuggerDriver, LocalZigToolchain.ensureLocal(toolchain), state) return ZigDebugParametersBuild(debuggerDriver, LocalZigToolchain.ensureLocal(toolchain), state)
} }

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -25,13 +25,13 @@ package com.falsepattern.zigbrains.debugger.runner.run
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugEmitBinaryInstaller import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugEmitBinaryInstaller
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersEmitBinaryBase import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersEmitBinaryBase
import com.falsepattern.zigbrains.project.execution.run.ZigProfileStateRun import com.falsepattern.zigbrains.project.execution.run.ZigProfileStateRun
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
import com.jetbrains.cidr.execution.Installer import com.jetbrains.cidr.execution.Installer
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration
class ZigDebugParametersRun(driverConfiguration: DebuggerDriverConfiguration, toolchain: AbstractZigToolchain, profileState: ZigProfileStateRun) : class ZigDebugParametersRun(driverConfiguration: DebuggerDriverConfiguration, toolchain: ZigToolchain, profileState: ZigProfileStateRun) :
ZigDebugParametersEmitBinaryBase<ZigProfileStateRun>(driverConfiguration, toolchain, profileState) { ZigDebugParametersEmitBinaryBase<ZigProfileStateRun>(driverConfiguration, toolchain, profileState) {
override fun getInstaller(): Installer { override fun getInstaller(): Installer {
return ZigDebugEmitBinaryInstaller(profileState, toolchain, executableFile, profileState.configuration.exeArgs.args) return ZigDebugEmitBinaryInstaller(profileState, toolchain, executableFile, profileState.configuration.exeArgs.argsSplit())
} }
} }

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -27,8 +27,8 @@ import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugRunnerBase
import com.falsepattern.zigbrains.project.execution.base.ZigProfileState import com.falsepattern.zigbrains.project.execution.base.ZigProfileState
import com.falsepattern.zigbrains.project.execution.run.ZigExecConfigRun import com.falsepattern.zigbrains.project.execution.run.ZigExecConfigRun
import com.falsepattern.zigbrains.project.execution.run.ZigProfileStateRun import com.falsepattern.zigbrains.project.execution.run.ZigProfileStateRun
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain import com.falsepattern.zigbrains.project.toolchain.local.LocalZigToolchain
import com.intellij.execution.configurations.RunProfile import com.intellij.execution.configurations.RunProfile
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration
@ -36,7 +36,7 @@ class ZigDebugRunnerRun: ZigDebugRunnerBase<ZigProfileStateRun>() {
override fun getDebugParameters( override fun getDebugParameters(
state: ZigProfileStateRun, state: ZigProfileStateRun,
debuggerDriver: DebuggerDriverConfiguration, debuggerDriver: DebuggerDriverConfiguration,
toolchain: AbstractZigToolchain toolchain: ZigToolchain
): ZigDebugParametersBase<ZigProfileStateRun> { ): ZigDebugParametersBase<ZigProfileStateRun> {
return ZigDebugParametersRun(debuggerDriver, LocalZigToolchain.ensureLocal(toolchain), state) return ZigDebugParametersRun(debuggerDriver, LocalZigToolchain.ensureLocal(toolchain), state)
} }

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -25,11 +25,11 @@ package com.falsepattern.zigbrains.debugger.runner.test
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugEmitBinaryInstaller import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugEmitBinaryInstaller
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersEmitBinaryBase import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersEmitBinaryBase
import com.falsepattern.zigbrains.project.execution.test.ZigProfileStateTest import com.falsepattern.zigbrains.project.execution.test.ZigProfileStateTest
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
import com.jetbrains.cidr.execution.Installer import com.jetbrains.cidr.execution.Installer
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration
class ZigDebugParametersTest(driverConfiguration: DebuggerDriverConfiguration, toolchain: AbstractZigToolchain, profileState: ZigProfileStateTest) : class ZigDebugParametersTest(driverConfiguration: DebuggerDriverConfiguration, toolchain: ZigToolchain, profileState: ZigProfileStateTest) :
ZigDebugParametersEmitBinaryBase<ZigProfileStateTest>(driverConfiguration, toolchain, profileState) { ZigDebugParametersEmitBinaryBase<ZigProfileStateTest>(driverConfiguration, toolchain, profileState) {
override fun getInstaller(): Installer { override fun getInstaller(): Installer {
return ZigDebugEmitBinaryInstaller(profileState, toolchain, executableFile, listOf()) return ZigDebugEmitBinaryInstaller(profileState, toolchain, executableFile, listOf())

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -27,8 +27,8 @@ import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugRunnerBase
import com.falsepattern.zigbrains.project.execution.base.ZigProfileState import com.falsepattern.zigbrains.project.execution.base.ZigProfileState
import com.falsepattern.zigbrains.project.execution.test.ZigExecConfigTest import com.falsepattern.zigbrains.project.execution.test.ZigExecConfigTest
import com.falsepattern.zigbrains.project.execution.test.ZigProfileStateTest import com.falsepattern.zigbrains.project.execution.test.ZigProfileStateTest
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain import com.falsepattern.zigbrains.project.toolchain.local.LocalZigToolchain
import com.intellij.execution.configurations.RunProfile import com.intellij.execution.configurations.RunProfile
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration
@ -36,7 +36,7 @@ class ZigDebugRunnerTest: ZigDebugRunnerBase<ZigProfileStateTest>() {
override fun getDebugParameters( override fun getDebugParameters(
state: ZigProfileStateTest, state: ZigProfileStateTest,
debuggerDriver: DebuggerDriverConfiguration, debuggerDriver: DebuggerDriverConfiguration,
toolchain: AbstractZigToolchain toolchain: ZigToolchain
): ZigDebugParametersBase<ZigProfileStateTest> { ): ZigDebugParametersBase<ZigProfileStateTest> {
return ZigDebugParametersTest(debuggerDriver, LocalZigToolchain.ensureLocal(toolchain), state) return ZigDebugParametersTest(debuggerDriver, LocalZigToolchain.ensureLocal(toolchain), state)
} }

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -31,6 +31,7 @@ import com.falsepattern.zigbrains.shared.coroutine.launchWithEDT
import com.falsepattern.zigbrains.shared.coroutine.runModalOrBlocking import com.falsepattern.zigbrains.shared.coroutine.runModalOrBlocking
import com.falsepattern.zigbrains.shared.zigCoroutineScope import com.falsepattern.zigbrains.shared.zigCoroutineScope
import com.intellij.ide.plugins.PluginManager import com.intellij.ide.plugins.PluginManager
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.extensions.PluginId import com.intellij.openapi.extensions.PluginId
import com.intellij.openapi.observable.util.whenItemSelected import com.intellij.openapi.observable.util.whenItemSelected
import com.intellij.openapi.ui.ComboBox import com.intellij.openapi.ui.ComboBox
@ -88,7 +89,7 @@ class ZigDebuggerToolchainConfigurableUi : ZigDebuggerUiComponent {
row(ZigDebugBundle.message("settings.debugger.toolchain.debugger.label")) { row(ZigDebugBundle.message("settings.debugger.toolchain.debugger.label")) {
comment = cell(debuggerKindComboBox) comment = cell(debuggerKindComboBox)
.comment("", DEFAULT_COMMENT_WIDTH) { .comment("", DEFAULT_COMMENT_WIDTH) {
zigCoroutineScope.launchWithEDT { zigCoroutineScope.launchWithEDT(ModalityState.defaultModalityState()) {
withModalProgress(ModalTaskOwner.component(debuggerKindComboBox), "Downloading debugger", TaskCancellation.cancellable()) { withModalProgress(ModalTaskOwner.component(debuggerKindComboBox), "Downloading debugger", TaskCancellation.cancellable()) {
downloadDebugger() downloadDebugger()
} }
@ -96,7 +97,7 @@ class ZigDebuggerToolchainConfigurableUi : ZigDebuggerUiComponent {
} }
.applyToComponent { .applyToComponent {
whenItemSelected(null) { whenItemSelected(null) {
zigCoroutineScope.launchWithEDT { zigCoroutineScope.launchWithEDT(ModalityState.defaultModalityState()) {
this@ZigDebuggerToolchainConfigurableUi.update() this@ZigDebuggerToolchainConfigurableUi.update()
} }
} }
@ -111,7 +112,7 @@ class ZigDebuggerToolchainConfigurableUi : ZigDebuggerUiComponent {
cell(useClion) cell(useClion)
} }
} }
zigCoroutineScope.launchWithEDT { zigCoroutineScope.launchWithEDT(ModalityState.defaultModalityState()) {
update() update()
} }
} }

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -27,16 +27,14 @@ import com.falsepattern.zigbrains.debugger.settings.MSVCDownloadPermission
import com.falsepattern.zigbrains.debugger.settings.ZigDebuggerSettings import com.falsepattern.zigbrains.debugger.settings.ZigDebuggerSettings
import com.falsepattern.zigbrains.debugger.toolchain.ZigDebuggerToolchainService.Companion.downloadPath import com.falsepattern.zigbrains.debugger.toolchain.ZigDebuggerToolchainService.Companion.downloadPath
import com.falsepattern.zigbrains.shared.coroutine.withCurrentEDTModalityContext import com.falsepattern.zigbrains.shared.coroutine.withCurrentEDTModalityContext
import com.falsepattern.zigbrains.shared.coroutine.withEDTContext
import com.intellij.notification.Notification import com.intellij.notification.Notification
import com.intellij.notification.NotificationType import com.intellij.notification.NotificationType
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.progress.coroutineToIndicator
import com.intellij.openapi.ui.DialogBuilder import com.intellij.openapi.ui.DialogBuilder
import com.intellij.platform.util.progress.withProgressText import com.intellij.platform.util.progress.withProgressText
import com.intellij.ui.components.JBLabel import com.intellij.ui.components.JBLabel
import com.intellij.ui.components.JBPanel import com.intellij.ui.components.JBPanel
import com.intellij.util.download.DownloadableFileService import com.intellij.util.download.DownloadableFileService
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
@ -98,7 +96,7 @@ private suspend fun downloadMSVCProps(): Properties {
val downloader = service.createDownloader(listOf(desc), "Debugger metadata downloading") val downloader = service.createDownloader(listOf(desc), "Debugger metadata downloading")
val downloadDirectory = downloadPath().toFile() val downloadDirectory = downloadPath().toFile()
val prop = Properties() val prop = Properties()
val downloadResults = coroutineToIndicator { val downloadResults = runBlocking {
downloader.download(downloadDirectory) downloader.download(downloadDirectory)
} }
for (result in downloadResults) { for (result in downloadResults) {

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -23,39 +23,37 @@
package com.falsepattern.zigbrains.debugger.toolchain package com.falsepattern.zigbrains.debugger.toolchain
import com.falsepattern.zigbrains.debugger.ZigDebugBundle import com.falsepattern.zigbrains.debugger.ZigDebugBundle
import com.falsepattern.zigbrains.shared.Unarchiver
import com.intellij.notification.Notification import com.intellij.notification.Notification
import com.intellij.notification.NotificationType import com.intellij.notification.NotificationType
import com.intellij.openapi.application.PathManager import com.intellij.openapi.application.PathManager
import com.intellij.openapi.components.Service import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.progress.coroutineToIndicator import com.intellij.openapi.progress.blockingContext
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.DialogBuilder import com.intellij.openapi.ui.DialogBuilder
import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.util.io.toNioPathOrNull import com.intellij.openapi.util.io.toNioPathOrNull
import com.intellij.platform.util.progress.reportSequentialProgress
import com.intellij.ui.BrowserHyperlinkListener import com.intellij.ui.BrowserHyperlinkListener
import com.intellij.ui.HyperlinkLabel import com.intellij.ui.HyperlinkLabel
import com.intellij.ui.components.JBPanel import com.intellij.ui.components.JBPanel
import com.intellij.util.application import com.intellij.util.application
import com.intellij.util.concurrency.annotations.RequiresEdt import com.intellij.util.concurrency.annotations.RequiresEdt
import com.intellij.util.download.DownloadableFileService import com.intellij.util.download.DownloadableFileService
import com.intellij.util.io.Decompressor
import com.intellij.util.system.CpuArch import com.intellij.util.system.CpuArch
import com.intellij.util.system.OS import com.intellij.util.system.OS
import com.jetbrains.cidr.execution.debugger.CidrDebuggerPathManager import com.jetbrains.cidr.execution.debugger.CidrDebuggerPathManager
import com.jetbrains.cidr.execution.debugger.backend.bin.UrlProvider import com.jetbrains.cidr.execution.debugger.backend.bin.UrlProvider
import com.jetbrains.cidr.execution.debugger.backend.lldb.LLDBDriverConfiguration import com.jetbrains.cidr.execution.debugger.backend.lldb.LLDBDriverConfiguration
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runInterruptible
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File
import java.io.IOException import java.io.IOException
import java.net.URL import java.net.URL
import java.nio.file.Path import java.nio.file.Path
import java.util.* import java.util.*
import kotlin.io.path.name import kotlin.io.path.name
import kotlin.io.path.notExists
@Service @Service
class ZigDebuggerToolchainService { class ZigDebuggerToolchainService {
@ -80,7 +78,7 @@ class ZigDebuggerToolchainService {
val lldbPath = lldbPath() val lldbPath = lldbPath()
val frameworkFile = lldbPath.resolve(frameworkPath) val frameworkFile = lldbPath.resolve(frameworkPath)
val frontendFile = lldbPath.resolve(frontendPath) val frontendFile = lldbPath.resolve(frontendPath)
if (frameworkFile.notExists() || frontendFile.notExists()) return DebuggerAvailability.NeedToDownload if (!frameworkFile.toFile().exists() || !frontendFile.toFile().exists()) return DebuggerAvailability.NeedToDownload
val versions = loadDebuggerVersions(DebuggerKind.LLDB) val versions = loadDebuggerVersions(DebuggerKind.LLDB)
val (lldbFrameworkUrl, lldbFrontendUrl) = lldbUrls() ?: return DebuggerAvailability.Unavailable val (lldbFrameworkUrl, lldbFrontendUrl) = lldbUrls() ?: return DebuggerAvailability.Unavailable
@ -105,7 +103,7 @@ class ZigDebuggerToolchainService {
} }
val gdbFile = gdbPath().resolve(gdbBinaryPath) val gdbFile = gdbPath().resolve(gdbBinaryPath)
if (gdbFile.notExists()) return DebuggerAvailability.NeedToDownload if (!gdbFile.toFile().exists()) return DebuggerAvailability.NeedToDownload
val versions = loadDebuggerVersions(DebuggerKind.GDB) val versions = loadDebuggerVersions(DebuggerKind.GDB)
val gdbUrl = gdbUrl() ?: return DebuggerAvailability.Unavailable val gdbUrl = gdbUrl() ?: return DebuggerAvailability.Unavailable
@ -123,7 +121,7 @@ class ZigDebuggerToolchainService {
val msvcBinaryPath = "vsdbg.exe" val msvcBinaryPath = "vsdbg.exe"
val msvcFile = msvcPath().resolve(msvcBinaryPath) val msvcFile = msvcPath().resolve(msvcBinaryPath)
if (msvcFile.notExists()) return DebuggerAvailability.NeedToDownload if (!msvcFile.toFile().exists()) return DebuggerAvailability.NeedToDownload
val msvcUrl = msvcUrl() ?: return DebuggerAvailability.Binaries(MSVCBinaries(msvcFile)) val msvcUrl = msvcUrl() ?: return DebuggerAvailability.Binaries(MSVCBinaries(msvcFile))
@ -170,7 +168,9 @@ class ZigDebuggerToolchainService {
} }
try { try {
withContext(Dispatchers.IO) {
downloadAndUnArchive(baseDir, downloadableBinaries) downloadAndUnArchive(baseDir, downloadableBinaries)
}
return DownloadResult.Ok(baseDir) return DownloadResult.Ok(baseDir)
} catch (e: IOException) { } catch (e: IOException) {
//TODO logging //TODO logging
@ -209,6 +209,7 @@ class ZigDebuggerToolchainService {
@Throws(IOException::class) @Throws(IOException::class)
@RequiresEdt @RequiresEdt
private suspend fun downloadAndUnArchive(baseDir: Path, binariesToDownload: List<DownloadableDebuggerBinary>) { private suspend fun downloadAndUnArchive(baseDir: Path, binariesToDownload: List<DownloadableDebuggerBinary>) {
reportSequentialProgress { reporter ->
val service = DownloadableFileService.getInstance() val service = DownloadableFileService.getInstance()
val downloadDir = baseDir.toFile() val downloadDir = baseDir.toFile()
@ -220,8 +221,8 @@ class ZigDebuggerToolchainService {
val downloader = service.createDownloader(descriptions, "Debugger downloading") val downloader = service.createDownloader(descriptions, "Debugger downloading")
val downloadDirectory = downloadPath().toFile() val downloadDirectory = downloadPath().toFile()
val downloadResults = withContext(Dispatchers.IO) { val downloadResults = reporter.sizedStep(100) {
coroutineToIndicator { blockingContext {
downloader.download(downloadDirectory) downloader.download(downloadDirectory)
} }
} }
@ -231,13 +232,18 @@ class ZigDebuggerToolchainService {
val binaryToDownload = binariesToDownload.first { it.url == downloadUrl } val binaryToDownload = binariesToDownload.first { it.url == downloadUrl }
val propertyName = binaryToDownload.propertyName val propertyName = binaryToDownload.propertyName
val archiveFile = result.first val archiveFile = result.first
Unarchiver.unarchive(archiveFile, downloadDir, binaryToDownload.prefix) reporter.indeterminateStep {
blockingContext {
Unarchiver.unarchive(archiveFile.toPath(), baseDir, binaryToDownload.prefix)
}
}
archiveFile.delete() archiveFile.delete()
versions[propertyName] = binaryToDownload.version versions[propertyName] = binaryToDownload.version
} }
saveVersionsFile(baseDir, versions) saveVersionsFile(baseDir, versions)
} }
}
private fun lldbUrls(): Pair<URL, URL>? { private fun lldbUrls(): Pair<URL, URL>? {
val lldb = UrlProvider.lldb(OS.CURRENT, CpuArch.CURRENT) ?: return null val lldb = UrlProvider.lldb(OS.CURRENT, CpuArch.CURRENT) ?: return null
@ -331,38 +337,6 @@ class ZigDebuggerToolchainService {
} }
} }
private enum class Unarchiver {
ZIP {
override val extension = "zip"
override fun createDecompressor(file: File) = Decompressor.Zip(file)
},
TAR {
override val extension = "tar.gz"
override fun createDecompressor(file: File) = Decompressor.Tar(file)
},
VSIX {
override val extension = "vsix"
override fun createDecompressor(file: File) = Decompressor.Zip(file)
};
protected abstract val extension: String
protected abstract fun createDecompressor(file: File): Decompressor
companion object {
@Throws(IOException::class)
suspend fun unarchive(archivePath: File, dst: File, prefix: String? = null) {
runInterruptible {
val unarchiver = entries.find { archivePath.name.endsWith(it.extension) } ?: error("Unexpected archive type: $archivePath")
val dec = unarchiver.createDecompressor(archivePath)
if (prefix != null) {
dec.removePrefixPath(prefix)
}
dec.extract(dst)
}
}
}
}
sealed class DownloadResult { sealed class DownloadResult {
class Ok(val baseDir: Path): DownloadResult() class Ok(val baseDir: Path): DownloadResult()
data object NoUrls: DownloadResult() data object NoUrls: DownloadResult()

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -40,9 +40,9 @@ abstract class MSVCDriverConfiguration: DAPDebuggerDriverConfiguration() {
override fun createDriverCommandLine(driver: DebuggerDriver, arch: ArchitectureType): GeneralCommandLine { override fun createDriverCommandLine(driver: DebuggerDriver, arch: ArchitectureType): GeneralCommandLine {
val path = debuggerExecutable val path = debuggerExecutable
val cli = GeneralCommandLine() val cli = GeneralCommandLine()
cli.exePath = path.pathString cli.withExePath(path.pathString)
cli.addParameters("--interpreter=vscode", "--extconfigdir=%USERPROFILE%\\.cppvsdbg\\extensions") cli.addParameters("--interpreter=vscode", "--extconfigdir=%USERPROFILE%\\.cppvsdbg\\extensions")
cli.withWorkingDirectory(path.parent) cli.withWorkDirectory(path.parent.toFile())
return cli return cli
} }

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -38,9 +38,9 @@ import org.eclipse.lsp4j.jsonrpc.MessageConsumer
import org.eclipse.lsp4j.jsonrpc.debug.messages.DebugResponseMessage import org.eclipse.lsp4j.jsonrpc.debug.messages.DebugResponseMessage
import org.eclipse.lsp4j.jsonrpc.messages.Message import org.eclipse.lsp4j.jsonrpc.messages.Message
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest import org.eclipse.lsp4j.jsonrpc.services.JsonRequest
import java.lang.RuntimeException import java.io.InputStream
import java.security.MessageDigest import java.security.MessageDigest
import java.util.Base64 import java.util.*
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
import java.util.zip.Inflater import java.util.zip.Inflater
@ -73,7 +73,7 @@ class WinDAPDriver(handler: Handler) : DAPDriver<IDebugProtocolServer, WinDAPDri
handshakeFinished.acquire() handshakeFinished.acquire()
} }
inner class WinDAPDebuggerClient: DAPDriver<IDebugProtocolServer, WinDAPDriver.WinDAPDebuggerClient>.DAPDebuggerClient() { inner class WinDAPDebuggerClient: DAPDriver<IDebugProtocolServer, WinDAPDebuggerClient>.DAPDebuggerClient() {
override fun output(args: OutputEventArguments) { override fun output(args: OutputEventArguments) {
if ("telemetry" == args.category) if ("telemetry" == args.category)
return return
@ -92,7 +92,7 @@ class WinDAPDriver(handler: Handler) : DAPDriver<IDebugProtocolServer, WinDAPDri
val hasher = MessageDigest.getInstance("SHA-256") val hasher = MessageDigest.getInstance("SHA-256")
hasher.update(handshake.value.encodeToByteArray()) hasher.update(handshake.value.encodeToByteArray())
val inflater = Inflater(true) val inflater = Inflater(true)
val coconut = DAPDebuggerClient::class.java.getResourceAsStream("/coconut.jpg").use { it.readAllBytes() } ?: throw RuntimeException("No coconut") val coconut = DAPDebuggerClient::class.java.getResourceAsStream("/coconut.jpg")?.use(InputStream::readAllBytes) ?: throw RuntimeException("No coconut")
inflater.setInput(coconut, coconut.size - 80, 77) inflater.setInput(coconut, coconut.size - 80, 77)
inflater.finished() inflater.finished()
val b = ByteArray(1) val b = ByteArray(1)

View file

@ -1,5 +1,5 @@
<idea-plugin package="com.falsepattern.zigbrains.debugger"> <idea-plugin package="com.falsepattern.zigbrains.debugger">
<depends>com.intellij.nativeDebug</depends> <depends>com.intellij.modules.cidr.debugger</depends>
<extensions defaultExtensionNs="com.intellij"> <extensions defaultExtensionNs="com.intellij">
<configurationType <configurationType

View file

@ -1,13 +1,7 @@
dialog.title.download.debugger=Download Debugger
notification.nativedebug.title=Zig native debugger
notification.nativedebug.text=You need to install the "Native Debugging Support" plugin for Zig debugging in this IDE!
notification.nativedebug.market=Install from Marketplace
notification.nativedebug.browser=Open in Browser
notification.title.debugger=Debugger notification.title.debugger=Debugger
notification.content.debugger.successfully.downloaded=Debugger successfully downloaded notification.content.debugger.successfully.downloaded=Debugger successfully downloaded
notification.content.debugger.downloading.failed=Debugger downloading failed notification.content.debugger.downloading.failed=Debugger downloading failed
notification.content.debugger.metadata.downloading.failed=Debugger metadata downloading failed, switching to fallback notification.content.debugger.metadata.downloading.failed=Debugger metadata downloading failed, switching to fallback
notification.content.debugger.metadata.fallback.fetch.failed=Debugger fallback metadata fetch failed
notification.content.debugger.metadata.fallback.parse.failed=Debugger fallback metadata parse failed notification.content.debugger.metadata.fallback.parse.failed=Debugger fallback metadata parse failed
settings.debugger.toolchain.download.debugger.automatically.checkbox=Download and update the debugger automatically settings.debugger.toolchain.download.debugger.automatically.checkbox=Download and update the debugger automatically
settings.debugger.title=Zig settings.debugger.title=Zig
@ -15,14 +9,13 @@ settings.debugger.toolchain.debugger.label=Debugger:
settings.debugger.toolchain.download.comment=Need to be <a>downloaded</a> settings.debugger.toolchain.download.comment=Need to be <a>downloaded</a>
settings.debugger.toolchain.update.comment=Need to be <a>updated</a> settings.debugger.toolchain.update.comment=Need to be <a>updated</a>
settings.debugger.toolchain.use.clion.toolchains=Use Clion toolchains instead settings.debugger.toolchain.use.clion.toolchains=Use Clion toolchains instead
notification.content.debugger.need.download=You need to download/update the debugger before you can run debugging! (Settings | Build, Execution, Deployment | Debugging -> Zig)
debugger.run.unavailable=Unable to Run Debugger debugger.run.unavailable=Unable to Run Debugger
debugger.run.unavailable.reason.download=Debugger is not downloaded yet debugger.run.unavailable.reason.download=Debugger is not downloaded yet
debugger.run.unavailable.reason.download.button=Download debugger.run.unavailable.reason.download.button=Download
debugger.run.unavailable.reason.update=Debugger is outdated debugger.run.unavailable.reason.update=Debugger is outdated
debugger.run.unavailable.reason.update.button=Update debugger.run.unavailable.reason.update.button=Update
debug.build.compile.failed.boilerplate={0}\nPlease edit this intellij build configuration and specify the path of the executable created by "zig build" directly! debug.build.compile.failed.boilerplate={0}\nPlease edit this intellij build configuration and specify the path of the executable created by "zig build" directly!
debug.base.compile.failed.generic=Failed to compile executable debug.base.compile.failed.generic=Failed to compile executable with command: {0}
debug.base.compile.failed.no-exe=Failed to find compiled binary debug.base.compile.failed.no-exe=Failed to find compiled binary
debug.build.compile.failed.multiple-exe=Multiple compiled binaries found debug.build.compile.failed.multiple-exe=Multiple compiled binaries found
debug.build.compile.failed.no-workdir=Cannot find working directory to run debugged executable debug.build.compile.failed.no-workdir=Cannot find working directory to run debugged executable

View file

@ -7,16 +7,17 @@ plugins {
kotlin("plugin.serialization") kotlin("plugin.serialization")
} }
val lsp4ijVersion: String by project val ideaCommunityVersion: String by project
val lsp4jVersion: String by project val useInstaller = property("useInstaller").toString().toBoolean()
val serializationVersion: String by project
dependencies { dependencies {
intellijPlatform { intellijPlatform {
create(IntelliJPlatformType.IntellijIdeaCommunity, providers.gradleProperty("ideaCommunityVersion")) create(IntelliJPlatformType.IntellijIdeaCommunity, ideaCommunityVersion, useInstaller = useInstaller)
}
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:$serializationVersion") {
isTransitive = false
} }
compileOnly("com.redhat.devtools.intellij:lsp4ij:$lsp4ijVersion")
compileOnly("org.eclipse.lsp4j:org.eclipse.lsp4j:$lsp4jVersion")
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.3")
} }
//region grammars //region grammars

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -165,6 +165,8 @@
STRING_LITERAL_SINGLE='quoted string literal' STRING_LITERAL_SINGLE='quoted string literal'
STRING_LITERAL_MULTI='multiline string literal' STRING_LITERAL_MULTI='multiline string literal'
BAD_SQUOT='unterminated character literal'
BAD_DQUOT='unterminated string'
IDENTIFIER='identifier' IDENTIFIER='identifier'
BUILTINIDENTIFIER='builtin identifier' BUILTINIDENTIFIER='builtin identifier'
@ -178,11 +180,11 @@
Root ::= CONTAINER_DOC_COMMENT? ContainerMembers? Root ::= CONTAINER_DOC_COMMENT? ContainerMembers?
// *** Top level *** // *** Top level ***
ContainerMembers ::= ContainerDeclarations? (ContainerField COMMA)* (ContainerField | ContainerDeclarations)? ContainerMembers ::= ContainerDeclaration* (ContainerField COMMA)* (ContainerField | ContainerDeclaration*)
ContainerDeclarations ::= (TestDecl | ComptimeDecl | DOC_COMMENT? KEYWORD_PUB? Decl)+ ContainerDeclaration ::= TestDecl | ComptimeDecl | DOC_COMMENT? KEYWORD_PUB? Decl
TestDecl ::= DOC_COMMENT? KEYWORD_TEST (STRING_LITERAL_SINGLE | IDENTIFIER)? Block {pin=2} TestDecl ::= KEYWORD_TEST (STRING_LITERAL_SINGLE | IDENTIFIER)? Block {pin=1}
ComptimeDecl ::= KEYWORD_COMPTIME Block ComptimeDecl ::= KEYWORD_COMPTIME Block
@ -203,6 +205,7 @@ ContainerField ::= DOC_COMMENT? KEYWORD_COMPTIME? !KEYWORD_FN (IDENTIFIER COLON)
Statement Statement
::= KEYWORD_COMPTIME ComptimeStatement ::= KEYWORD_COMPTIME ComptimeStatement
| KEYWORD_NOSUSPEND BlockExprStatement | KEYWORD_NOSUSPEND BlockExprStatement
| KEYWORD_SUSPEND BlockExprStatement
| KEYWORD_DEFER BlockExprStatement | KEYWORD_DEFER BlockExprStatement
| KEYWORD_ERRDEFER Payload? BlockExprStatement | KEYWORD_ERRDEFER Payload? BlockExprStatement
| IfStatement | IfStatement
@ -413,14 +416,21 @@ WhilePrefix ::= KEYWORD_WHILE ZB_WhilePrefix_Operand PtrPayload? WhileContinueEx
private ZB_WhilePrefix_Operand ::= LPAREN Expr RPAREN {pin=1} private ZB_WhilePrefix_Operand ::= LPAREN Expr RPAREN {pin=1}
ForRange ::= Expr DOT2 Expr? ForPrefix ::= KEYWORD_FOR LPAREN ZB_ForParams RPAREN ForPayload {pin=1}
ForOperand ::= ForRange | Expr {recoverWhile="ZB_ForOperand_Recover"}
private ZB_ForOperand_Recover ::= !(COMMA | RPAREN) private ZB_ForParams ::= ForInput (COMMA ForInput)* COMMA? {recoverWhile="ZB_ForParams_Recover"}
ForPrefix ::= KEYWORD_FOR ZB_ForPrefix_Operands PtrIndexPayload {pin=1} private ZB_ForParams_Recover ::= !(RPAREN)
private ZB_ForPrefix_Operands ::= LPAREN (ForOperand COMMA)* ForOperand RPAREN {pin=1} ForInput ::= Expr (DOT2 Expr?)? {recoverWhile="ZB_ForInput_Recover"}
private ZB_ForInput_Recover ::= !(COMMA | RPAREN)
ForPayload ::= PIPE ZB_ForPayload_Item (COMMA ZB_ForPayload_Item)* PIPE {pin=1}
private ZB_ForPayload_Item ::= ASTERISK? IDENTIFIER {recoverWhile="ZB_ForPayload_Recover"}
private ZB_ForPayload_Recover ::= !(COMMA | PIPE)
// Payloads // Payloads
Payload ::= PIPE IDENTIFIER PIPE Payload ::= PIPE IDENTIFIER PIPE

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -34,9 +34,12 @@ import static com.falsepattern.zigbrains.zig.psi.ZigTypes.*;
%implements FlexLexer %implements FlexLexer
%function advance %function advance
%type IElementType %type IElementType
%unicode
CRLF=\R WHITE_SPACE=\s+
WHITE_SPACE=[\s]+
// visual studio parity
LF=\r\n?|[\n\u0085\u2028\u2029]
bin=[01] bin=[01]
bin_="_"? {bin} bin_="_"? {bin}
@ -52,55 +55,14 @@ oct_int={oct} {oct_}*
dec_int={dec} {dec_}* dec_int={dec} {dec_}*
hex_int={hex} {hex_}* hex_int={hex} {hex_}*
ox80_oxBF=[\200-\277] char_char= \\ .
oxF4=\364 | [^\'\r\n\u0085\u2028\u2029]
ox80_ox8F=[\200-\217]
oxF1_oxF3=[\361-\363]
oxF0=\360
ox90_0xBF=[\220-\277]
oxEE_oxEF=[\356-\357]
oxED=\355
ox80_ox9F=[\200-\237]
oxE1_oxEC=[\341-\354]
oxE0=\340
oxA0_oxBF=[\240-\277]
oxC2_oxDF=[\302-\337]
// From https://lemire.me/blog/2018/05/09/how-quickly-can-you-check-that-a-string-is-valid-unicode-utf-8/
// First Byte Second Byte Third Byte Fourth Byte
// [0x00,0x7F]
// [0xC2,0xDF] [0x80,0xBF]
// 0xE0 [0xA0,0xBF] [0x80,0xBF]
// [0xE1,0xEC] [0x80,0xBF] [0x80,0xBF]
// 0xED [0x80,0x9F] [0x80,0xBF]
// [0xEE,0xEF] [0x80,0xBF] [0x80,0xBF]
// 0xF0 [0x90,0xBF] [0x80,0xBF] [0x80,0xBF]
// [0xF1,0xF3] [0x80,0xBF] [0x80,0xBF] [0x80,0xBF]
// 0xF4 [0x80,0x8F] [0x80,0xBF] [0x80,0xBF]
mb_utf8_literal= {oxF4} {ox80_ox8F} {ox80_oxBF} {ox80_oxBF}
| {oxF1_oxF3} {ox80_oxBF} {ox80_oxBF} {ox80_oxBF}
| {oxF0} {ox90_0xBF} {ox80_oxBF} {ox80_oxBF}
| {oxEE_oxEF} {ox80_oxBF} {ox80_oxBF}
| {oxED} {ox80_ox9F} {ox80_oxBF}
| {oxE1_oxEC} {ox80_oxBF} {ox80_oxBF}
| {oxE0} {oxA0_oxBF} {ox80_oxBF}
| {oxC2_oxDF} {ox80_oxBF}
ascii_char_not_nl_slash_squote=[\000-\011\013-\046\050-\133\135-\177]
char_escape= "\\x" {hex} {hex}
| "\\u{" {hex}+ "}"
| "\\" [nr\\t'\"]
char_char= {mb_utf8_literal}
| {char_escape}
| {ascii_char_not_nl_slash_squote}
string_char= \\ . string_char= \\ .
| [^\"\n] | [^\"\r\n\u0085\u2028\u2029]
all_nl_wrap=[^\n]* [ \n]* nl_wrap={LF} (\s|{LF})*
all_nl_nowrap=[^\n]* \n all_no_nl=[^\r\n\u0085\u2028\u2029]+
FLOAT= "0x" {hex_int} "." {hex_int} ([pP] [-+]? {dec_int})? FLOAT= "0x" {hex_int} "." {hex_int} ([pP] [-+]? {dec_int})?
@ -121,26 +83,38 @@ BUILTINIDENTIFIER="@"[A-Za-z_][A-Za-z0-9_]*
%state CHAR_LIT %state CHAR_LIT
%state ID_QUOT %state ID_QUOT
%state UNT_QUOT %state UNT_SQUOT
%state UNT_DQUOT
%state CDOC_CMT %state CMT_LINE
%state DOC_CMT %state CMT_DOC
%state LINE_CMT %state CMT_CDOC
%% %%
//Comments //Comments
<YYINITIAL> "//!" { yybegin(CDOC_CMT); } <YYINITIAL> "//!" { yybegin(CMT_CDOC); }
<CDOC_CMT> {all_nl_wrap} "//!" { } <YYINITIAL> "////" { yybegin(CMT_LINE); }
<CDOC_CMT> {all_nl_nowrap} { yybegin(YYINITIAL); return CONTAINER_DOC_COMMENT; } <YYINITIAL> "///" { yybegin(CMT_DOC); }
<YYINITIAL> "//" { yybegin(CMT_LINE); }
<YYINITIAL> "///" { yybegin(DOC_CMT); } <CMT_LINE> {all_no_nl} { }
<DOC_CMT> {all_nl_wrap} "///" { } <CMT_LINE> {nl_wrap} "////" { }
<DOC_CMT> {all_nl_nowrap} { yybegin(YYINITIAL); return DOC_COMMENT; } <CMT_LINE> {nl_wrap} "///" { yypushback(yylength()); yybegin(YYINITIAL); return LINE_COMMENT; }
<CMT_LINE> {nl_wrap} "//" { }
<CMT_LINE> {LF} { yybegin(YYINITIAL); return LINE_COMMENT; }
<CMT_LINE> <<EOF>> { yybegin(YYINITIAL); return LINE_COMMENT; }
<YYINITIAL> "//" { yybegin(LINE_CMT); } <CMT_DOC> {all_no_nl} { }
<LINE_CMT> {all_nl_wrap} "//" { } <CMT_DOC> {nl_wrap} "////" { yypushback(yylength()); yybegin(YYINITIAL); return DOC_COMMENT; }
<LINE_CMT> {all_nl_nowrap} { yybegin(YYINITIAL); return LINE_COMMENT; } <CMT_DOC> {nl_wrap} "///" { }
<CMT_DOC> {LF} { yybegin(YYINITIAL); return DOC_COMMENT; }
<CMT_DOC> <<EOF>> { yybegin(YYINITIAL); return DOC_COMMENT; }
<CMT_CDOC> {all_no_nl} { }
<CMT_CDOC> {nl_wrap} "//!" { }
<CMT_CDOC> {LF} { yybegin(YYINITIAL); return CONTAINER_DOC_COMMENT; }
<CMT_CDOC> <<EOF>> { yybegin(YYINITIAL); return CONTAINER_DOC_COMMENT; }
//Symbols //Symbols
<YYINITIAL> "&" { return AMPERSAND; } <YYINITIAL> "&" { return AMPERSAND; }
@ -260,28 +234,49 @@ BUILTINIDENTIFIER="@"[A-Za-z_][A-Za-z0-9_]*
<YYINITIAL> "volatile" { return KEYWORD_VOLATILE; } <YYINITIAL> "volatile" { return KEYWORD_VOLATILE; }
<YYINITIAL> "while" { return KEYWORD_WHILE; } <YYINITIAL> "while" { return KEYWORD_WHILE; }
//Strings
<YYINITIAL> "'" { yybegin(CHAR_LIT); } <YYINITIAL> "'" { yybegin(CHAR_LIT); }
<CHAR_LIT> {char_char}"'" { yybegin(YYINITIAL); return CHAR_LITERAL; } <CHAR_LIT> {char_char}*"'" { yybegin(YYINITIAL); return CHAR_LITERAL; }
<CHAR_LIT> [^] { yypushback(1); yybegin(UNT_QUOT); } <CHAR_LIT> <<EOF>> { yybegin(YYINITIAL); return BAD_SQUOT; }
<CHAR_LIT> [^] { yypushback(1); yybegin(UNT_SQUOT); }
<YYINITIAL> "\"" { yybegin(STR_LIT); }
<STR_LIT> {string_char}*"\"" { yybegin(YYINITIAL); return STRING_LITERAL_SINGLE; }
<STR_LIT> <<EOF>> { yybegin(YYINITIAL); return BAD_DQUOT; }
<STR_LIT> [^] { yypushback(1); yybegin(UNT_DQUOT); }
<YYINITIAL> "\\\\" { yybegin(STR_MULT_LINE); }
<STR_MULT_LINE> {all_no_nl} { }
<STR_MULT_LINE> {nl_wrap} "\\\\" { }
<STR_MULT_LINE> {LF} { yybegin(YYINITIAL); return STRING_LITERAL_MULTI; }
<STR_MULT_LINE> <<EOF>> { yybegin(YYINITIAL); return STRING_LITERAL_MULTI; }
//Numbers
<YYINITIAL> {FLOAT} { return FLOAT; } <YYINITIAL> {FLOAT} { return FLOAT; }
<YYINITIAL> {INTEGER} { return INTEGER; } <YYINITIAL> {INTEGER} { return INTEGER; }
<YYINITIAL> "\"" { yybegin(STR_LIT); } //Identifiers
<STR_LIT> {string_char}*"\"" { yybegin(YYINITIAL); return STRING_LITERAL_SINGLE; }
<STR_LIT> [^] { yypushback(1); yybegin(UNT_QUOT); }
<YYINITIAL> "\\\\" { yybegin(STR_MULT_LINE); }
<STR_MULT_LINE> {all_nl_wrap} "\\\\" { }
<STR_MULT_LINE> {all_nl_nowrap} { yybegin(YYINITIAL); return STRING_LITERAL_MULTI; }
<YYINITIAL> {IDENTIFIER_PLAIN} { return IDENTIFIER; } <YYINITIAL> {IDENTIFIER_PLAIN} { return IDENTIFIER; }
<YYINITIAL> "@\"" { yybegin(ID_QUOT); } <YYINITIAL> "@\"" { yybegin(ID_QUOT); }
<ID_QUOT> {string_char}*"\"" { yybegin(YYINITIAL); return IDENTIFIER; } <ID_QUOT> {string_char}*"\"" { yybegin(YYINITIAL); return IDENTIFIER; }
<ID_QUOT> [^] { yypushback(1); yybegin(UNT_QUOT); } <ID_QUOT> <<EOF>> { yybegin(YYINITIAL); return BAD_DQUOT; }
<ID_QUOT> [^] { yypushback(1); yybegin(UNT_DQUOT); }
<YYINITIAL> {BUILTINIDENTIFIER} { return BUILTINIDENTIFIER; } <YYINITIAL> {BUILTINIDENTIFIER} { return BUILTINIDENTIFIER; }
<UNT_QUOT> [^\n]*{CRLF} { yybegin(YYINITIAL); return BAD_CHARACTER; } //Error handling
<UNT_SQUOT> <<EOF>> { yybegin(YYINITIAL); return BAD_SQUOT; }
<UNT_SQUOT> {LF} { yybegin(YYINITIAL); return BAD_SQUOT; }
<UNT_SQUOT> {all_no_nl} { }
<UNT_DQUOT> <<EOF>> { yybegin(YYINITIAL); return BAD_DQUOT; }
<UNT_DQUOT> {LF} { yybegin(YYINITIAL); return BAD_DQUOT; }
<UNT_DQUOT> {all_no_nl} { }
//Misc
<YYINITIAL> {WHITE_SPACE} { return WHITE_SPACE; } <YYINITIAL> {WHITE_SPACE} { return WHITE_SPACE; }

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -40,17 +40,21 @@ import static com.intellij.psi.StringEscapesTokenTypes.*;
hex=[0-9a-fA-F] hex=[0-9a-fA-F]
char_escape_unicode= "\\x" {hex} {hex} | "\\u{" {hex}+ "}" char_escape_unicode= "\\x" {hex} {hex} | "\\u{" {hex}+ "}"
char_escape_unicode_invalid= "\\x" | "\\u" char_escape_unicode_invalid= "\\x" .? .? | "\\u" ("{" [^}]* "}"?)?
char_escape_single_valid= "\\" [nr\\t'\"] char_escape_single_valid= "\\" [nr\\t'\"]
char_escape_single_invalid= "\\" [^nr\\t'\"] char_escape_single_invalid= "\\" [^nr\\t'\"]
%state STR %state STR
%state CHAR
%state CHAR_END
%state CHAR_FINISH
%% %%
<YYINITIAL> { <YYINITIAL> {
"\"" { yybegin(STR); return STRING_LITERAL_SINGLE; } "\"" { yybegin(STR); return STRING_LITERAL_SINGLE; }
"'" { yybegin(CHAR); return CHAR_LITERAL; }
[^] { return STRING_LITERAL_SINGLE; } [^] { return STRING_LITERAL_SINGLE; }
} }
@ -61,3 +65,20 @@ char_escape_single_invalid= "\\" [^nr\\t'\"]
{char_escape_single_invalid} { return INVALID_CHARACTER_ESCAPE_TOKEN; } {char_escape_single_invalid} { return INVALID_CHARACTER_ESCAPE_TOKEN; }
[^] { return STRING_LITERAL_SINGLE; } [^] { return STRING_LITERAL_SINGLE; }
} }
<CHAR> {
{char_escape_unicode} { yybegin(CHAR_END); return VALID_STRING_ESCAPE_TOKEN; }
{char_escape_unicode_invalid} { yybegin(CHAR_END); return INVALID_UNICODE_ESCAPE_TOKEN; }
{char_escape_single_valid} { yybegin(CHAR_END); return VALID_STRING_ESCAPE_TOKEN; }
{char_escape_single_invalid} { yybegin(CHAR_END); return INVALID_CHARACTER_ESCAPE_TOKEN; }
[^] { yybegin(CHAR_END); return CHAR_LITERAL; }
}
<CHAR_END> {
"'" { yybegin(CHAR_FINISH); return CHAR_LITERAL; }
[^] { return BAD_CHARACTER; }
}
<CHAR_FINISH> {
[^] { return BAD_CHARACTER; }
}

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -34,46 +34,57 @@
elementTypeClass="com.falsepattern.zigbrains.zon.parser.ZonElementType" elementTypeClass="com.falsepattern.zigbrains.zon.parser.ZonElementType"
tokenTypeClass="com.falsepattern.zigbrains.zon.parser.ZonTokenType" tokenTypeClass="com.falsepattern.zigbrains.zon.parser.ZonTokenType"
tokens=[ tokens=[
LINE_COMMENT='comment'
DOT='.' DOT='.'
EQUAL='='
LBRACE='{' LBRACE='{'
RBRACE='}' RBRACE='}'
EQ='='
COMMA=',' COMMA=','
COMMENT='comment' KEYWORD_FALSE='false'
ID='identifier' KEYWORD_TRUE='true'
STRING_LITERAL_SINGLE='string' KEYWORD_NULL='null'
LINE_STRING='multiline string' NUM_NAN='nan'
BAD_STRING='unterminated string' NUM_INF='inf'
BOOL_TRUE='true' CHAR_LITERAL='char literal'
BOOL_FALSE='false' STRING_LITERAL_SINGLE='string literal'
STRING_LITERAL_MULTI='multiline string literal'
FLOAT='float'
INTEGER='integer'
IDENTIFIER='identifier'
BAD_SQUOT='unterminated quote'
BAD_DQUOT='unterminated double quote'
] ]
//Mixins
mixin("entry")="com.falsepattern.zigbrains.zon.psi.impl.mixins.ZonEntryMixinImpl"
implements("entry")="com.falsepattern.zigbrains.zon.psi.mixins.ZonEntryMixin"
mixin("identifier")="com.falsepattern.zigbrains.zon.psi.impl.mixins.ZonIdentifierMixinImpl"
implements("identifier")="com.falsepattern.zigbrains.zon.psi.mixins.ZonIdentifierMixin"
} }
zonFile ::= entry Root ::= Expr
entry ::= DOT LBRACE (list | struct | ()) RBRACE Expr
::= CHAR_LITERAL
| StringLiteral
| DOT IDENTIFIER
| DOT InitList
| Bool
| Number
| KEYWORD_NULL
struct ::= (property | property_placeholder) (COMMA (property_placeholder? property property_placeholder? | property_placeholder))* COMMA?
list ::= value (COMMA value)* COMMA? InitList
::= LBRACE ZB_InitList_Body RBRACE {pin=1}
property ::= DOT identifier EQ value private ZB_InitList_Body
::= FieldInit (COMMA ZB_InitList_FieldInit)* COMMA?
| Expr (COMMA ZB_InitList_Expr)* COMMA?
| ()
identifier ::= ID private ZB_InitList_FieldInit ::= FieldInit {recoverWhile="ZB_InitList_Recover"}
private ZB_InitList_Expr ::= Expr {recoverWhile="ZB_InitList_Recover"}
property_placeholder ::= DOT? INTELLIJ_COMPLETION_DUMMY private ZB_InitList_Recover ::= !(COMMA | RBRACE)
private value ::= entry | boolean | STRING_LITERAL | value_placeholder FieldInit ::= DOT IDENTIFIER EQUAL Expr
value_placeholder ::= INTELLIJ_COMPLETION_DUMMY Bool ::= KEYWORD_TRUE | KEYWORD_FALSE
boolean ::= BOOL_TRUE | BOOL_FALSE Number ::= FLOAT | INTEGER | NUM_NAN | NUM_INF
STRING_LITERAL ::= STRING_LITERAL_SINGLE | LINE_STRING+ StringLiteral ::= STRING_LITERAL_SINGLE | STRING_LITERAL_MULTI

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -36,54 +36,124 @@ import static com.falsepattern.zigbrains.zon.psi.ZonTypes.*;
%type IElementType %type IElementType
%unicode %unicode
CRLF=\R WHITE_SPACE=\s+
WHITE_SPACE=[\s]+
LINE_COMMENT="//" [^\n]* | "////" [^\n]*
COMMENT="///".*
ID=[A-Za-z_][A-Za-z0-9_]* // visual studio parity
LF=\r\n?|[\n\u0085\u2028\u2029]
bin=[01]
bin_="_"? {bin}
oct=[0-7]
oct_="_"? {oct}
hex=[0-9a-fA-F] hex=[0-9a-fA-F]
char_escape hex_="_"? {hex}
= "\\x" {hex} {hex} dec=[0-9]
| "\\u{" {hex}+ "}" dec_="_"? {dec}
| "\\" [nr\\t'\"]
string_char bin_int={bin} {bin_}*
= {char_escape} oct_int={oct} {oct_}*
| [^\\\"\n] dec_int={dec} {dec_}*
hex_int={hex} {hex_}*
LINE_STRING=("\\\\" [^\n]* [ \n]*)+ char_char= \\ .
| [^\'\r\n\u0085\u2028\u2029]
%state STRING_LITERAL string_char= \\ .
%state ID_STRING | [^\"\r\n\u0085\u2028\u2029]
%state UNCLOSED_STRING
nl_wrap={LF} (\s|{LF})*
all_no_nl=[^\r\n\u0085\u2028\u2029]+
FLOAT= "0x" {hex_int} "." {hex_int} ([pP] [-+]? {dec_int})?
| {dec_int} "." {dec_int} ([eE] [-+]? {dec_int})?
| "0x" {hex_int} [pP] [-+]? {dec_int}
| {dec_int} [eE] [-+]? {dec_int}
INTEGER= "0b" {bin_int}
| "0o" {oct_int}
| "0x" {hex_int}
| {dec_int}
IDENTIFIER_PLAIN=[A-Za-z_][A-Za-z0-9_]*
%state STR_LIT
%state STR_MULT_LINE
%state CHAR_LIT
%state ID_QUOT
%state UNT_SQUOT
%state UNT_DQUOT
%state CMT_LINE
%% %%
//Comments
<YYINITIAL> "//" { yybegin(CMT_LINE); }
<CMT_LINE> {all_no_nl} { }
<CMT_LINE> {nl_wrap} "//" { }
<CMT_LINE> \R { yybegin(YYINITIAL); return LINE_COMMENT; }
<CMT_LINE> <<EOF>> { yybegin(YYINITIAL); return LINE_COMMENT; }
//Symbols
<YYINITIAL> {WHITE_SPACE} { return WHITE_SPACE; }
<YYINITIAL> "." { return DOT; } <YYINITIAL> "." { return DOT; }
<YYINITIAL> "IntellijIdeaRulezzz" { return INTELLIJ_COMPLETION_DUMMY; } <YYINITIAL> "=" { return EQUAL; }
<YYINITIAL> "{" { return LBRACE; } <YYINITIAL> "{" { return LBRACE; }
<YYINITIAL> "}" { return RBRACE; } <YYINITIAL> "}" { return RBRACE; }
<YYINITIAL> "=" { return EQ; }
<YYINITIAL> "," { return COMMA; } <YYINITIAL> "," { return COMMA; }
<YYINITIAL> "true" { return BOOL_TRUE; }
<YYINITIAL> "false" { return BOOL_FALSE; }
<YYINITIAL> {COMMENT} { return COMMENT; }
<YYINITIAL> {LINE_COMMENT} { return COMMENT; }
<YYINITIAL> {ID} { return ID; } //Keywords
<YYINITIAL> "@\"" { yybegin(ID_STRING); }
<ID_STRING> {string_char}*"\"" { yybegin(YYINITIAL); return ID; }
<ID_STRING> [^] { yypushback(1); yybegin(UNCLOSED_STRING); }
<YYINITIAL> "\"" { yybegin(STRING_LITERAL); } <YYINITIAL> "false" { return KEYWORD_FALSE; }
<STRING_LITERAL> {string_char}*"\"" { yybegin(YYINITIAL); return STRING_LITERAL_SINGLE; } <YYINITIAL> "true" { return KEYWORD_TRUE; }
<STRING_LITERAL> [^] { yypushback(1); yybegin(UNCLOSED_STRING); } <YYINITIAL> "null" { return KEYWORD_NULL; }
<YYINITIAL> "nan" { return NUM_NAN; }
<YYINITIAL> "inf" { return NUM_INF; }
<UNCLOSED_STRING>[^\n]*{CRLF} { yybegin(YYINITIAL); return BAD_STRING; } //Strings
<YYINITIAL> {LINE_STRING} { return LINE_STRING; } <YYINITIAL> "'" { yybegin(CHAR_LIT); }
<CHAR_LIT> {char_char}*"'" { yybegin(YYINITIAL); return CHAR_LITERAL; }
<CHAR_LIT> <<EOF>> { yybegin(YYINITIAL); return BAD_SQUOT; }
<CHAR_LIT> [^] { yypushback(1); yybegin(UNT_SQUOT); }
<YYINITIAL> "\"" { yybegin(STR_LIT); }
<STR_LIT> {string_char}*"\"" { yybegin(YYINITIAL); return STRING_LITERAL_SINGLE; }
<STR_LIT> <<EOF>> { yybegin(YYINITIAL); return BAD_DQUOT; }
<STR_LIT> [^] { yypushback(1); yybegin(UNT_DQUOT); }
<YYINITIAL> "\\\\" { yybegin(STR_MULT_LINE); }
<STR_MULT_LINE> {all_no_nl} { }
<STR_MULT_LINE> {nl_wrap} "\\\\" { }
<STR_MULT_LINE> {LF} { yybegin(YYINITIAL); return STRING_LITERAL_MULTI; }
<STR_MULT_LINE> <<EOF>> { yybegin(YYINITIAL); return STRING_LITERAL_MULTI; }
//Numbers
<YYINITIAL> {FLOAT} { return FLOAT; }
<YYINITIAL> {INTEGER} { return INTEGER; }
//Identifiers
<YYINITIAL> {IDENTIFIER_PLAIN} { return IDENTIFIER; }
<YYINITIAL> "@\"" { yybegin(ID_QUOT); }
<ID_QUOT> {string_char}*"\"" { yybegin(YYINITIAL); return IDENTIFIER; }
<ID_QUOT> <<EOF>> { yybegin(YYINITIAL); return BAD_DQUOT; }
<ID_QUOT> [^] { yypushback(1); yybegin(UNT_DQUOT); }
//Error handling
<UNT_SQUOT> <<EOF>> { yybegin(YYINITIAL); return BAD_SQUOT; }
<UNT_SQUOT> {LF} { yybegin(YYINITIAL); return BAD_SQUOT; }
<UNT_SQUOT> {all_no_nl} { }
<UNT_DQUOT> <<EOF>> { yybegin(YYINITIAL); return BAD_DQUOT; }
<UNT_DQUOT> {LF} { yybegin(YYINITIAL); return BAD_DQUOT; }
<UNT_DQUOT> {all_no_nl} { }
//Misc
<YYINITIAL> {WHITE_SPACE} { return WHITE_SPACE; }
[^] { return BAD_CHARACTER; } [^] { return BAD_CHARACTER; }

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -27,6 +27,8 @@ import org.jetbrains.annotations.NonNls
@NonNls @NonNls
object Icons { object Icons {
val ZIG = IconLoader.getIcon("/icons/zig.svg", Icons::class.java) @JvmField
val ZON = IconLoader.getIcon("/icons/zon.svg", Icons::class.java) val Zig = IconLoader.getIcon("/icons/zig.svg", Icons::class.java)
@JvmField
val Zon = IconLoader.getIcon("/icons/zon.svg", Icons::class.java)
} }

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -25,26 +25,57 @@ package com.falsepattern.zigbrains.direnv
import com.falsepattern.zigbrains.ZigBrainsBundle import com.falsepattern.zigbrains.ZigBrainsBundle
import com.intellij.execution.configurations.GeneralCommandLine import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.execution.configurations.PathEnvironmentVariableUtil import com.intellij.execution.configurations.PathEnvironmentVariableUtil
import com.intellij.ide.impl.isTrusted
import com.intellij.notification.Notification import com.intellij.notification.Notification
import com.intellij.notification.NotificationType import com.intellij.notification.NotificationType
import com.intellij.notification.Notifications import com.intellij.notification.Notifications
import com.intellij.openapi.components.*
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.project.guessProjectDir import com.intellij.openapi.project.guessProjectDir
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.UserDataHolder
import com.intellij.openapi.vfs.toNioPathOrNull
import com.intellij.platform.util.progress.withProgressText import com.intellij.platform.util.progress.withProgressText
import com.intellij.util.io.awaitExit import com.intellij.util.io.awaitExit
import com.intellij.util.xmlb.annotations.Attribute
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.nio.file.Path import java.nio.file.Path
import kotlin.io.path.isRegularFile
object DirenvCmd { @Service(Service.Level.PROJECT)
suspend fun importDirenv(project: Project): Env { @State(
if (!direnvInstalled()) name = "Direnv",
return emptyEnv storages = [Storage("zigbrains.xml")]
val workDir = project.guessProjectDir()?.toNioPath() ?: return emptyEnv )
class DirenvService(val project: Project): SerializablePersistentStateComponent<DirenvService.State>(State()), IDirenvService {
private val mutex = Mutex()
val runOutput = run(project, workDir, "export", "json") override val isInstalled: Boolean by lazy {
// Using the builtin stuff here instead of Env because it should only scan for direnv on the process path
PathEnvironmentVariableUtil.findExecutableInPathOnAnyOS("direnv") != null
}
var isEnabledRaw: DirenvState
get() = state.enabled
set(value) {
updateState {
it.copy(enabled = value)
}
}
override val isEnabled: DirenvState
get() = isEnabledRaw
override suspend fun import(): Env {
if (!isInstalled || !project.isTrusted() || project.isDefault)
return Env.empty
val workDir = project.guessProjectDir()?.toNioPath() ?: return Env.empty
val runOutput = run(workDir, "export", "json")
if (runOutput.error) { if (runOutput.error) {
if (runOutput.output.contains("is blocked")) { if (runOutput.output.contains("is blocked")) {
Notifications.Bus.notify(Notification( Notifications.Bus.notify(Notification(
@ -53,7 +84,7 @@ object DirenvCmd {
ZigBrainsBundle.message("notification.content.direnv-blocked"), ZigBrainsBundle.message("notification.content.direnv-blocked"),
NotificationType.ERROR NotificationType.ERROR
)) ))
return emptyEnv return Env.empty
} else { } else {
Notifications.Bus.notify(Notification( Notifications.Bus.notify(Notification(
GROUP_DISPLAY_ID, GROUP_DISPLAY_ID,
@ -61,22 +92,22 @@ object DirenvCmd {
ZigBrainsBundle.message("notification.content.direnv-error", runOutput.output), ZigBrainsBundle.message("notification.content.direnv-error", runOutput.output),
NotificationType.ERROR NotificationType.ERROR
)) ))
return emptyEnv return Env.empty
} }
} }
if (runOutput.output.isBlank()) { return if (runOutput.output.isBlank()) {
return emptyEnv Env.empty
} else { } else {
return Env(Json.decodeFromString<Map<String, String>>(runOutput.output)) Env(Json.decodeFromString<Map<String, String>>(runOutput.output))
} }
} }
private suspend fun run(project: Project, workDir: Path, vararg args: String): DirenvOutput { private suspend fun run(workDir: Path, vararg args: String): DirenvOutput {
val cli = GeneralCommandLine("direnv", *args).withWorkingDirectory(workDir) val cli = GeneralCommandLine("direnv", *args).withWorkDirectory(workDir.toFile())
val (process, exitCode) = withProgressText("Running ${cli.commandLineString}") { val (process, exitCode) = withProgressText("Running ${cli.commandLineString}") {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
project.direnvService.mutex.withLock { mutex.withLock {
val process = cli.createProcess() val process = cli.createProcess()
val exitCode = process.awaitExit() val exitCode = process.awaitExit()
process to exitCode process to exitCode
@ -93,14 +124,39 @@ object DirenvCmd {
return DirenvOutput(stdOut, false) return DirenvOutput(stdOut, false)
} }
fun hasDotEnv(): Boolean {
if (!isInstalled)
return false
val projectDir = project.guessProjectDir()?.toNioPathOrNull() ?: return false
return envFiles.any { projectDir.resolve(it).isRegularFile() }
}
data class State(
@JvmField
@Attribute
var enabled: DirenvState = DirenvState.Auto
)
companion object {
private const val GROUP_DISPLAY_ID = "zigbrains-direnv" private const val GROUP_DISPLAY_ID = "zigbrains-direnv"
fun direnvInstalled() = fun getInstance(project: Project): IDirenvService = project.service<DirenvService>()
// Using the builtin stuff here instead of Env because it should only scan for direnv on the process path
PathEnvironmentVariableUtil.findExecutableInPathOnAnyOS("direnv") != null private val STATE_KEY = Key.create<DirenvState>("DIRENV_STATE")
fun getStateFor(data: UserDataHolder?, project: Project?): DirenvState {
return data?.getUserData(STATE_KEY) ?: project?.let { getInstance(project).isEnabled } ?: DirenvState.Disabled
}
fun setStateFor(data: UserDataHolder, state: DirenvState) {
data.putUserData(STATE_KEY, state)
}
}
} }
suspend fun Project?.getDirenv(): Env { sealed interface IDirenvService {
if (this == null) val isInstalled: Boolean
return emptyEnv val isEnabled: DirenvState
return DirenvCmd.importDirenv(this) suspend fun import(): Env
} }
private val envFiles = listOf(".envrc", ".env")

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -22,14 +22,19 @@
package com.falsepattern.zigbrains.direnv package com.falsepattern.zigbrains.direnv
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import kotlinx.coroutines.sync.Mutex
@Service(Service.Level.PROJECT) enum class DirenvState {
class DirenvProjectService { Auto,
val mutex = Mutex() Enabled,
Disabled;
fun isEnabled(project: Project?): Boolean {
return when(this) {
Enabled -> true
Disabled -> false
Auto -> project?.service<DirenvService>()?.hasDotEnv() == true
}
}
} }
val Project.direnvService get() = service<DirenvProjectService>()

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -25,31 +25,38 @@ package com.falsepattern.zigbrains.direnv
import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.util.io.toNioPathOrNull import com.intellij.openapi.util.io.toNioPathOrNull
import com.intellij.util.EnvironmentUtil import com.intellij.util.EnvironmentUtil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import org.jetbrains.annotations.NonNls import org.jetbrains.annotations.NonNls
import java.io.File import java.io.File
import java.nio.file.Path import kotlin.io.path.absolute
import kotlin.io.path.* import kotlin.io.path.isDirectory
import kotlin.io.path.isExecutable
import kotlin.io.path.isRegularFile
@JvmRecord
data class Env(val env: Map<String, String>) { data class Env(val env: Map<String, String>) {
private val path get() = getVariable("PATH")?.split(File.pathSeparatorChar) private val path get() = getVariable("PATH")?.split(File.pathSeparatorChar)
private fun getVariable(name: @NonNls String) = private fun getVariable(name: @NonNls String) =
env.getOrElse(name) { EnvironmentUtil.getValue(name) } env.getOrElse(name) { EnvironmentUtil.getValue(name) }
fun findExecutableOnPATH(exe: @NonNls String): Path? { fun findAllExecutablesOnPATH(exe: @NonNls String) = flow {
val exeName = if (SystemInfo.isWindows) "$exe.exe" else exe val exeName = if (SystemInfo.isWindows) "$exe.exe" else exe
val paths = path ?: return null val paths = path ?: return@flow
for (dir in paths) { for (dir in paths) {
val path = dir.toNioPathOrNull()?.absolute() ?: continue val path = dir.toNioPathOrNull()?.absolute() ?: continue
if (path.notExists() || !path.isDirectory()) if (!path.toFile().exists() || !path.isDirectory())
continue continue
val exePath = path.resolve(exeName).absolute() val exePath = path.resolve(exeName).absolute()
if (!exePath.isRegularFile() || !exePath.isExecutable()) if (!exePath.isRegularFile() || !exePath.isExecutable())
continue continue
return exePath emit(exePath)
} }
return null }.flowOn(Dispatchers.IO)
companion object {
val empty = Env(emptyMap())
} }
} }
val emptyEnv = Env(emptyMap())

View file

@ -0,0 +1,101 @@
/*
* 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.direnv.ui
import com.falsepattern.zigbrains.ZigBrainsBundle
import com.falsepattern.zigbrains.direnv.DirenvService
import com.falsepattern.zigbrains.direnv.DirenvState
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider.Companion.PROJECT_KEY
import com.falsepattern.zigbrains.shared.SubConfigurable
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.ComboBox
import com.intellij.ui.dsl.builder.Panel
import java.awt.event.ItemEvent
abstract class DirenvEditor<T>(private val sharedState: ZigProjectConfigurationProvider.IUserDataBridge?): SubConfigurable<T> {
private var cb: ComboBox<DirenvState>? = null
override fun attach(panel: Panel): Unit = with(panel) {
row(ZigBrainsBundle.message("settings.direnv.enable.label")) {
comboBox(DirenvState.entries).component.let {
cb = it
if (sharedState != null) {
it.addItemListener { e ->
if (e.stateChange != ItemEvent.SELECTED)
return@addItemListener
val item = e.item
if (item !is DirenvState)
return@addItemListener
DirenvService.setStateFor(sharedState, item)
}
}
}
}
}
override fun isModified(context: T): Boolean {
return isEnabled(context) != cb?.selectedItem as DirenvState
}
override fun apply(context: T) {
setEnabled(context, cb?.selectedItem as DirenvState)
}
override fun reset(context: T?) {
if (context == null) {
cb?.selectedItem = DirenvState.Auto
return
}
cb?.selectedItem = isEnabled(context)
}
override fun dispose() {
}
abstract fun isEnabled(context: T): DirenvState
abstract fun setEnabled(context: T, value: DirenvState)
class ForProject(sharedState: ZigProjectConfigurationProvider.IUserDataBridge) : DirenvEditor<Project>(sharedState) {
override fun isEnabled(context: Project): DirenvState {
return DirenvService.getInstance(context).isEnabled
}
override fun setEnabled(context: Project, value: DirenvState) {
context.service<DirenvService>().isEnabledRaw = value
}
}
class Provider: ZigProjectConfigurationProvider {
override fun create(sharedState: ZigProjectConfigurationProvider.IUserDataBridge): SubConfigurable<Project>? {
if (sharedState.getUserData(PROJECT_KEY)?.isDefault != false) {
return null
}
DirenvService.setStateFor(sharedState, DirenvState.Auto)
return ForProject(sharedState)
}
override val index: Int
get() = 100
}
}

View file

@ -1,76 +0,0 @@
/*
* This file is part of ZigBrains.
*
* Copyright (C) 2023-2024 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.lsp
import com.falsepattern.zigbrains.lsp.settings.zlsSettings
import com.falsepattern.zigbrains.shared.zigCoroutineScope
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.platform.ide.progress.ModalTaskOwner
import com.intellij.platform.ide.progress.runWithModalProgressBlocking
import com.intellij.util.application
import com.redhat.devtools.lsp4ij.LanguageServerEnablementSupport
import com.redhat.devtools.lsp4ij.LanguageServerFactory
import com.redhat.devtools.lsp4ij.LanguageServerManager
import com.redhat.devtools.lsp4ij.ServerStatus
import com.redhat.devtools.lsp4ij.server.StreamConnectionProvider
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
class ZigLanguageServerFactory: LanguageServerFactory, LanguageServerEnablementSupport {
override fun createConnectionProvider(project: Project): StreamConnectionProvider {
return if (application.isDispatchThread) {
runWithModalProgressBlocking(ModalTaskOwner.project(project), ZLSBundle.message("progress.title.create-connection-provider")) {
ZLSStreamConnectionProvider.create(project)
}
} else {
runBlocking {
ZLSStreamConnectionProvider.create(project)
}
}
}
override fun isEnabled(project: Project): Boolean {
return (project.getUserData(ENABLED_KEY) ?: true) && project.zlsSettings.validate()
}
override fun setEnabled(enabled: Boolean, project: Project) {
project.putUserData(ENABLED_KEY, enabled)
}
}
class ZLSStarter: LanguageServerStarter {
override fun startLSP(project: Project, restart: Boolean) {
project.zigCoroutineScope.launch {
val manager = project.service<LanguageServerManager>()
val status = manager.getServerStatus("ZigBrains")
if ((status == ServerStatus.started || status == ServerStatus.starting) && !restart)
return@launch
manager.start("ZigBrains")
}
}
}
private val ENABLED_KEY = Key.create<Boolean>("ZLS_ENABLED")

View file

@ -1,39 +0,0 @@
/*
* This file is part of ZigBrains.
*
* Copyright (C) 2023-2024 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/>.
*/
@file:Suppress("HardCodedStringLiteral")
package com.falsepattern.zigbrains.lsp.config
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import org.jetbrains.annotations.NonNls
@Serializable
data class ZLSConfig(
@SerialName("zig_exe_path") val zigExePath: @NonNls String? = null,
@SerialName("zig_lib_path") val zigLibPath: @NonNls String? = null,
@SerialName("enable_build_on_save") val buildOnSave: Boolean? = null,
@SerialName("build_on_save_step") val buildOnSaveStep: @NonNls String? = null,
@SerialName("dangerous_comptime_experiments_do_not_enable") val comptimeInterpreter: Boolean? = null,
@SerialName("highlight_global_var_declarations") val globalVarDeclarations: Boolean? = null
)

View file

@ -1,120 +0,0 @@
/*
* This file is part of ZigBrains.
*
* Copyright (C) 2023-2024 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.lsp.settings
import com.falsepattern.zigbrains.direnv.emptyEnv
import com.falsepattern.zigbrains.direnv.getDirenv
import com.falsepattern.zigbrains.lsp.ZLSBundle
import com.intellij.openapi.components.*
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.io.toNioPathOrNull
import com.intellij.platform.ide.progress.ModalTaskOwner
import com.intellij.platform.ide.progress.runWithModalProgressBlocking
import com.intellij.util.application
import kotlinx.coroutines.runBlocking
import java.nio.file.Path
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
import kotlin.io.path.isExecutable
import kotlin.io.path.isRegularFile
import kotlin.io.path.notExists
@Service(Service.Level.PROJECT)
@State(
name = "ZLSSettings",
storages = [Storage(value = "zigbrains.xml")]
)
class ZLSProjectSettingsService(val project: Project): PersistentStateComponent<ZLSSettings> {
@Volatile
private var state = ZLSSettings()
@Volatile
private var dirty = true
@Volatile
private var valid = false
private val mutex = ReentrantLock()
override fun getState(): ZLSSettings {
return state.copy()
}
fun setState(value: ZLSSettings) {
mutex.withLock {
this.state = value
dirty = true
}
}
override fun loadState(state: ZLSSettings) {
mutex.withLock {
this.state = state
dirty = true
}
}
fun isModified(otherData: ZLSSettings): Boolean {
return state != otherData
}
fun validate(): Boolean {
mutex.withLock {
if (dirty) {
val state = this.state
valid = if (application.isDispatchThread) {
runWithModalProgressBlocking(ModalTaskOwner.project(project), ZLSBundle.message("progress.title.validate")) {
doValidate(project, state)
}
} else {
runBlocking {
doValidate(project, state)
}
}
dirty = false
}
return valid
}
}
}
private suspend fun doValidate(project: Project, state: ZLSSettings): Boolean {
val zlsPath: Path = state.zlsPath.let { zlsPath ->
if (zlsPath.isEmpty()) {
val env = if (state.direnv) project.getDirenv() else emptyEnv
env.findExecutableOnPATH("zls") ?: run {
return false
}
} else {
zlsPath.toNioPathOrNull() ?: run {
return false
}
}
}
if (zlsPath.notExists()) {
return false
}
if (!zlsPath.isRegularFile() || !zlsPath.isExecutable()) {
return false
}
return true
}
val Project.zlsSettings get() = service<ZLSProjectSettingsService>()

View file

@ -1,59 +0,0 @@
/*
* This file is part of ZigBrains.
*
* Copyright (C) 2023-2024 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.lsp.settings
import com.falsepattern.zigbrains.lsp.startLSP
import com.falsepattern.zigbrains.shared.SubConfigurable
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
import com.intellij.ui.dsl.builder.Panel
class ZLSSettingsConfigurable(private val project: Project): SubConfigurable {
private var appSettingsComponent: ZLSSettingsPanel? = null
override fun createComponent(panel: Panel) {
appSettingsComponent = ZLSSettingsPanel(project).apply { attach(panel) }.also { Disposer.register(this, it) }
}
override fun isModified(): Boolean {
val data = appSettingsComponent?.data ?: return false
return project.zlsSettings.state != data
}
override fun apply() {
val data = appSettingsComponent?.data ?: return
val settings = project.zlsSettings
val reloadZLS = settings.isModified(data)
settings.state = data
if (reloadZLS) {
startLSP(project, true)
}
}
override fun reset() {
appSettingsComponent?.data = project.zlsSettings.state
}
override fun dispose() {
appSettingsComponent = null
}
}

View file

@ -1,138 +0,0 @@
/*
* This file is part of ZigBrains.
*
* Copyright (C) 2023-2024 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.lsp.settings
import com.falsepattern.zigbrains.direnv.DirenvCmd
import com.falsepattern.zigbrains.direnv.Env
import com.falsepattern.zigbrains.direnv.emptyEnv
import com.falsepattern.zigbrains.direnv.getDirenv
import com.falsepattern.zigbrains.lsp.ZLSBundle
import com.falsepattern.zigbrains.shared.coroutine.launchWithEDT
import com.falsepattern.zigbrains.shared.zigCoroutineScope
import com.intellij.openapi.Disposable
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
import com.intellij.platform.ide.progress.ModalTaskOwner
import com.intellij.platform.ide.progress.TaskCancellation
import com.intellij.platform.ide.progress.withModalProgress
import com.intellij.ui.components.JBCheckBox
import com.intellij.ui.components.fields.ExtendableTextField
import com.intellij.ui.components.textFieldWithBrowseButton
import com.intellij.ui.dsl.builder.AlignX
import com.intellij.ui.dsl.builder.Panel
import kotlin.io.path.pathString
class ZLSSettingsPanel(private val project: Project?) : Disposable {
private val zlsPath = textFieldWithBrowseButton(
project,
ZLSBundle.message("settings.zls-path.browse.title"),
FileChooserDescriptorFactory.createSingleFileDescriptor(),
).also { Disposer.register(this, it) }
private val zlsConfigPath = textFieldWithBrowseButton(
project,
ZLSBundle.message("settings.zls-config-path.browse.title"),
FileChooserDescriptorFactory.createSingleFileDescriptor()
).also { Disposer.register(this, it) }
private val buildOnSave = JBCheckBox().apply { toolTipText = ZLSBundle.message("settings.build-on-save.tooltip") }
private val buildOnSaveStep = ExtendableTextField().apply { toolTipText = ZLSBundle.message("settings.build-on-save-step.tooltip") }
private val globalVarDeclarations = JBCheckBox()
private val comptimeInterpreter = JBCheckBox()
private val inlayHints = JBCheckBox()
private val inlayHintsCompact = JBCheckBox().apply { toolTipText = ZLSBundle.message("settings.inlay-hints-compact.tooltip") }
private val messageTrace = JBCheckBox()
private val debug = JBCheckBox()
private val direnv = JBCheckBox(ZLSBundle.message("settings.zls-path.use-direnv.label"))
fun attach(panel: Panel) = with(panel) {
group(ZLSBundle.message("settings.group.title")) {
row(ZLSBundle.message("settings.zls-path.label")) {
cell(zlsPath).resizableColumn().align(AlignX.FILL)
if (DirenvCmd.direnvInstalled() && project != null && !project.isDefault) {
cell(direnv)
}
button(ZLSBundle.message("settings.zls-path.autodetect.label")) {
project.zigCoroutineScope.launchWithEDT {
withModalProgress(ModalTaskOwner.component(zlsPath), "Detecting ZLS...", TaskCancellation.cancellable()) {
autodetect()
}
}
}
}
row(ZLSBundle.message("settings.zls-config-path.label")) { cell(zlsConfigPath).align(AlignX.FILL) }
row(ZLSBundle.message("settings.inlay-hints.label")) { cell(inlayHints) }
row(ZLSBundle.message("settings.inlay-hints-compact.label")) { cell(inlayHintsCompact) }
row(ZLSBundle.message("settings.build-on-save.label")) { cell(buildOnSave) }
row(ZLSBundle.message("settings.build-on-save-step.label")) { cell(buildOnSaveStep).resizableColumn().align(AlignX.FILL) }
row(ZLSBundle.message("settings.global-var-declarations.label")) { cell(globalVarDeclarations) }
row(ZLSBundle.message("settings.comptime-interpreter.label")) { cell(comptimeInterpreter) }
}
group(ZLSBundle.message("dev-settings.group.title")) {
row(ZLSBundle.message("dev-settings.debug.label")) { cell(debug) }
row(ZLSBundle.message("dev-settings.message-trace.label")) { cell(messageTrace) }
}
}
var data
get() = ZLSSettings(
direnv.isSelected,
zlsPath.text,
zlsConfigPath.text,
debug.isSelected,
messageTrace.isSelected,
buildOnSave.isSelected,
buildOnSaveStep.text,
globalVarDeclarations.isSelected,
comptimeInterpreter.isSelected,
inlayHints.isSelected,
inlayHintsCompact.isSelected
)
set(value) {
direnv.isSelected = value.direnv
zlsPath.text = value.zlsPath
zlsConfigPath.text = value.zlsConfigPath
debug.isSelected = value.debug
messageTrace.isSelected = value.messageTrace
buildOnSave.isSelected = value.buildOnSave
buildOnSaveStep.text = value.buildOnSaveStep
globalVarDeclarations.isSelected = value.globalVarDeclarations
comptimeInterpreter.isSelected = value.comptimeInterpreter
inlayHints.isSelected = value.inlayHints
inlayHintsCompact.isSelected = value.inlayHintsCompact
}
suspend fun autodetect() {
getDirenv().findExecutableOnPATH("zls")?.let { zlsPath.text = it.pathString }
}
override fun dispose() {
}
private suspend fun getDirenv(): Env {
if (!direnv.isSelected)
return emptyEnv
return project.getDirenv()
}
}

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -31,7 +31,7 @@ import com.intellij.psi.PsiDirectory
class ZigNewFileAction: CreateFileFromTemplateAction() { class ZigNewFileAction: CreateFileFromTemplateAction() {
override fun buildDialog(project: Project, directory: PsiDirectory, builder: CreateFileFromTemplateDialog.Builder) { override fun buildDialog(project: Project, directory: PsiDirectory, builder: CreateFileFromTemplateDialog.Builder) {
builder.setTitle("Zig File") builder.setTitle("Zig File")
.addKind("Empty file", Icons.ZIG, "blank_zig_file") .addKind("Empty file", Icons.Zig, "blank_zig_file")
} }
override fun getActionName(directory: PsiDirectory?, newName: String, templateName: String?): String { override fun getActionName(directory: PsiDirectory?, newName: String, templateName: String?): String {

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

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -27,25 +27,23 @@ import com.intellij.execution.filters.Filter.ResultItem
import com.intellij.execution.filters.OpenFileHyperlinkInfo import com.intellij.execution.filters.OpenFileHyperlinkInfo
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.project.guessProjectDir import com.intellij.openapi.project.guessProjectDir
import com.intellij.openapi.util.io.toNioPathOrNull
import com.intellij.openapi.vfs.refreshAndFindVirtualFile import com.intellij.openapi.vfs.refreshAndFindVirtualFile
import com.intellij.openapi.vfs.toNioPathOrNull import com.intellij.openapi.vfs.toNioPathOrNull
import java.io.File
import java.nio.file.InvalidPathException import java.nio.file.InvalidPathException
import java.nio.file.Path import java.nio.file.Path
import kotlin.io.path.isRegularFile
import kotlin.io.path.notExists
import kotlin.math.max import kotlin.math.max
class ZigSourceFileFilter(private val project: Project): Filter { class ZigSourceFileFilter(private val project: Project): Filter {
private val projectPath = runCatching { project.guessProjectDir()?.toNioPathOrNull()?.toFile() }.getOrNull()
override fun applyFilter(line: String, entireLength: Int): Filter.Result? { override fun applyFilter(line: String, entireLength: Int): Filter.Result? {
val lineStart = entireLength - line.length val lineStart = entireLength - line.length
val projectPath = project.guessProjectDir()?.toNioPathOrNull()
val results = ArrayList<ResultItem>() val results = ArrayList<ResultItem>()
val matcher = LEN_REGEX.findAll(line) val matcher = LEN_REGEX.findAll(line)
for (match in matcher) { for (match in matcher) {
val start = match.range.first val start = match.range.first
val pair = findLongestParsablePathFromOffset(line, start, projectPath) val pair = findLongestParsablePathFromOffset(line, start)
val path = pair?.first ?: return null val path = pair?.first ?: return null
val file = path.refreshAndFindVirtualFile() ?: return null val file = path.refreshAndFindVirtualFile() ?: return null
val lineNumber = max(match.groups[1]!!.value.toInt() - 1, 0) val lineNumber = max(match.groups[1]!!.value.toInt() - 1, 0)
@ -55,25 +53,29 @@ class ZigSourceFileFilter(private val project: Project): Filter {
return Filter.Result(results) return Filter.Result(results)
} }
private fun findLongestParsablePathFromOffset(line: String, end: Int, projectPath: Path?): Pair<Path, Int>? { private fun findLongestParsablePathFromOffset(line: String, end: Int): Pair<Path, Int>? {
var longestStart = -1 var longestStart = -1
var longest: Path? = null var longest: File? = null
for (i in end - 1 downTo 0) { for (i in end - 1 downTo 0) {
try { try {
val pathStr = line.substring(i, end) val pathStr = line.substring(i, end)
var path: Path = pathStr.toNioPathOrNull() ?: continue var file = File(pathStr)
if ((path.notExists() || !path.isRegularFile()) && projectPath != null) { if (!file.isFile) {
path = projectPath.resolve(pathStr) if (projectPath == null) {
if (path.notExists() || !path.isRegularFile())
continue continue
} }
longest = path file = projectPath.resolve(pathStr)
if (!file.isFile) {
continue
}
}
longest = file
longestStart = i longestStart = i
} catch (ignored: InvalidPathException) { } catch (ignored: InvalidPathException) {
} }
} }
longest ?: return null longest ?: return null
return Pair(longest, longestStart) return Pair(longest.toPath(), longestStart)
} }
} }

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -20,20 +20,18 @@
* along with ZigBrains. If not, see <https://www.gnu.org/licenses/>. * along with ZigBrains. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.falsepattern.zigbrains.lsp.settings package com.falsepattern.zigbrains.project.execution
import org.jetbrains.annotations.NonNls import com.intellij.execution.filters.TextConsoleBuilderImpl
import com.intellij.execution.ui.ConsoleView
import com.intellij.openapi.project.Project
import com.intellij.terminal.TerminalExecutionConsole
data class ZLSSettings( class ZigConsoleBuilder(private val project: Project, private val emulateTerminal: Boolean = false): TextConsoleBuilderImpl(project) {
var direnv: Boolean = true, override fun createConsole(): ConsoleView {
var zlsPath: @NonNls String = "", return if (emulateTerminal)
var zlsConfigPath: @NonNls String = "", TerminalExecutionConsole(project, null)
var debug: Boolean = false, else
var messageTrace: Boolean = false, super.createConsole()
var buildOnSave: Boolean = false, }
var buildOnSaveStep: @NonNls String = "install", }
var globalVarDeclarations: Boolean = false,
var comptimeInterpreter: Boolean = false,
var inlayHints: Boolean = true,
var inlayHintsCompact: Boolean = true
)

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -24,20 +24,17 @@ package com.falsepattern.zigbrains.project.execution.base
import com.falsepattern.zigbrains.ZigBrainsBundle import com.falsepattern.zigbrains.ZigBrainsBundle
import com.falsepattern.zigbrains.project.execution.base.ZigConfigurable.ZigConfigModule import com.falsepattern.zigbrains.project.execution.base.ZigConfigurable.ZigConfigModule
import com.falsepattern.zigbrains.project.settings.zigProjectSettings
import com.falsepattern.zigbrains.shared.cli.translateCommandline import com.falsepattern.zigbrains.shared.cli.translateCommandline
import com.falsepattern.zigbrains.shared.element.* import com.falsepattern.zigbrains.shared.element.*
import com.intellij.openapi.Disposable import com.intellij.openapi.Disposable
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
import com.intellij.openapi.options.SettingsEditor import com.intellij.openapi.options.SettingsEditor
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.ComboBox import com.intellij.openapi.ui.ComboBox
import com.intellij.openapi.ui.TextBrowseFolderListener
import com.intellij.openapi.ui.TextFieldWithBrowseButton
import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.io.toNioPathOrNull import com.intellij.openapi.util.io.toNioPathOrNull
import com.intellij.ui.components.JBCheckBox import com.intellij.ui.components.JBCheckBox
import com.intellij.ui.components.JBTextField import com.intellij.ui.components.JBTextField
import com.intellij.ui.components.textFieldWithBrowseButton
import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.AlignX
import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.Panel
import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.panel
@ -161,12 +158,11 @@ class WorkDirectoryConfigurable(@Transient override val serializedName: String)
} }
class WorkDirectoryConfigModule(private val serializedName: String) : PathConfigModule<WorkDirectoryConfigurable>() { class WorkDirectoryConfigModule(private val serializedName: String) : PathConfigModule<WorkDirectoryConfigurable>() {
private val field = TextFieldWithBrowseButton( private val field = textFieldWithBrowseButton(
TextBrowseFolderListener( null,
ZigBrainsBundle.message("dialog.title.working-directory"),
FileChooserDescriptorFactory.createSingleFolderDescriptor().withTitle(ZigBrainsBundle.message("dialog.title.working-directory")) FileChooserDescriptorFactory.createSingleFolderDescriptor().withTitle(ZigBrainsBundle.message("dialog.title.working-directory"))
), ).also { Disposer.register(this, it) }
this
)
override var stringValue by field::text override var stringValue by field::text
@ -201,9 +197,10 @@ class FilePathConfigurable(
} }
class FilePathConfigModule(private val serializedName: String, @Nls private val label: String) : PathConfigModule<FilePathConfigurable>() { class FilePathConfigModule(private val serializedName: String, @Nls private val label: String) : PathConfigModule<FilePathConfigurable>() {
private val field = TextFieldWithBrowseButton( private val field = textFieldWithBrowseButton(
TextBrowseFolderListener(FileChooserDescriptorFactory.createSingleFileDescriptor()), null,
this null,
FileChooserDescriptorFactory.createSingleFileDescriptor(),
) )
override var stringValue by field::text override var stringValue by field::text
@ -224,7 +221,7 @@ class FilePathConfigurable(
} }
} }
class CheckboxConfigurable( open class CheckboxConfigurable(
@Transient private val serializedName: String, @Transient private val serializedName: String,
@Transient @Nls private val label: String, @Transient @Nls private val label: String,
var value: Boolean var value: Boolean
@ -274,11 +271,6 @@ class CheckboxConfigurable(
} }
} }
fun ColoredConfigurable(serializedName: String) = CheckboxConfigurable(serializedName, ZigBrainsBundle.message("exec.option.label.colored-terminal"), true)
fun DirenvConfigurable(serializedName: String, project: Project) =
CheckboxConfigurable(serializedName, ZigBrainsBundle.message("exec.option.label.direnv"), project.zigProjectSettings.state.direnv)
class OptimizationConfigurable( class OptimizationConfigurable(
@Transient private val serializedName: String, @Transient private val serializedName: String,
var level: OptimizationLevel = OptimizationLevel.Debug, var level: OptimizationLevel = OptimizationLevel.Debug,
@ -340,14 +332,18 @@ class ArgsConfigurable(
@Transient private val serializedName: String, @Transient private val serializedName: String,
@Transient @Nls private val guiName: String @Transient @Nls private val guiName: String
) : ZigConfigurable<ArgsConfigurable>, Cloneable { ) : ZigConfigurable<ArgsConfigurable>, Cloneable {
var args: List<String> = emptyList() var args: String = ""
override fun readExternal(element: Element) { override fun readExternal(element: Element) {
args = element.readStrings(serializedName) ?: return args = element.readString(serializedName) ?: element.readStrings(serializedName)?.joinToString(separator = " ") { if (it.contains(' ')) "\"$it\"" else it } ?: ""
}
fun argsSplit(): List<String> {
return translateCommandline(args)
} }
override fun writeExternal(element: Element) { override fun writeExternal(element: Element) {
element.writeStrings(serializedName, args) element.writeString(serializedName, args)
} }
override fun createEditor(): ZigConfigModule<ArgsConfigurable> { override fun createEditor(): ZigConfigModule<ArgsConfigurable> {
@ -369,12 +365,12 @@ class ArgsConfigurable(
} }
override fun apply(configurable: ArgsConfigurable): Boolean { override fun apply(configurable: ArgsConfigurable): Boolean {
configurable.args = translateCommandline(argsField.text) configurable.args = argsField.text ?: ""
return true return true
} }
override fun reset(configurable: ArgsConfigurable) { override fun reset(configurable: ArgsConfigurable) {
argsField.text = configurable.args.joinToString(separator = " ") argsField.text = configurable.args
} }
override fun construct(p: Panel): Unit = with(p) { override fun construct(p: Panel): Unit = with(p) {

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -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

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -22,9 +22,7 @@
package com.falsepattern.zigbrains.project.execution.base package com.falsepattern.zigbrains.project.execution.base
import com.falsepattern.zigbrains.ZigBrainsBundle import com.falsepattern.zigbrains.direnv.DirenvService
import com.falsepattern.zigbrains.direnv.DirenvCmd
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain
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
@ -43,10 +41,6 @@ import org.jetbrains.annotations.Nls
abstract class ZigExecConfig<T: ZigExecConfig<T>>(project: Project, factory: ConfigurationFactory, @Nls name: String): LocatableConfigurationBase<ZigProfileState<T>>(project, factory, name) { abstract class ZigExecConfig<T: ZigExecConfig<T>>(project: Project, factory: ConfigurationFactory, @Nls name: String): LocatableConfigurationBase<ZigProfileState<T>>(project, factory, name) {
var workingDirectory = WorkDirectoryConfigurable("workingDirectory").apply { path = project.guessProjectDir()?.toNioPathOrNull() } var workingDirectory = WorkDirectoryConfigurable("workingDirectory").apply { path = project.guessProjectDir()?.toNioPathOrNull() }
private set private set
var pty = CheckboxConfigurable("pty", ZigBrainsBundle.message("exec.option.label.emulate-terminal"), false)
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)
@ -69,24 +63,19 @@ 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) { val direnv = DirenvService.getInstance(project)
commandLine.withEnvironment(DirenvCmd.importDirenv(project).env) if (direnv.isEnabled.isEnabled(project)) {
commandLine.withEnvironment(direnv.import().env)
} }
return commandLine return commandLine
} }
fun emulateTerminal(): Boolean {
return pty.value
}
override fun clone(): T { override fun clone(): T {
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.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)
} }

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -23,18 +23,16 @@
package com.falsepattern.zigbrains.project.execution.base package com.falsepattern.zigbrains.project.execution.base
import com.falsepattern.zigbrains.ZigBrainsBundle import com.falsepattern.zigbrains.ZigBrainsBundle
import com.falsepattern.zigbrains.project.run.ZigProcessHandler import com.falsepattern.zigbrains.project.execution.ZigConsoleBuilder
import com.falsepattern.zigbrains.project.settings.zigProjectSettings import com.falsepattern.zigbrains.project.toolchain.ZigToolchainService
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
import com.falsepattern.zigbrains.shared.cli.startIPCAwareProcess
import com.falsepattern.zigbrains.shared.coroutine.runModalOrBlocking import com.falsepattern.zigbrains.shared.coroutine.runModalOrBlocking
import com.intellij.build.BuildTextConsoleView
import com.intellij.execution.DefaultExecutionResult
import com.intellij.execution.ExecutionException import com.intellij.execution.ExecutionException
import com.intellij.execution.configurations.CommandLineState import com.intellij.execution.configurations.CommandLineState
import com.intellij.execution.configurations.GeneralCommandLine import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.execution.configurations.PtyCommandLine import com.intellij.execution.configurations.PtyCommandLine
import com.intellij.execution.process.ProcessHandler import com.intellij.execution.process.ProcessHandler
import com.intellij.execution.process.ProcessTerminatedListener
import com.intellij.execution.runners.ExecutionEnvironment import com.intellij.execution.runners.ExecutionEnvironment
import com.intellij.platform.ide.progress.ModalTaskOwner import com.intellij.platform.ide.progress.ModalTaskOwner
import kotlin.io.path.pathString import kotlin.io.path.pathString
@ -44,6 +42,10 @@ abstract class ZigProfileState<T: ZigExecConfig<T>> (
val configuration: T val configuration: T
): CommandLineState(environment) { ): CommandLineState(environment) {
init {
consoleBuilder = ZigConsoleBuilder(environment.project, true)
}
@Throws(ExecutionException::class) @Throws(ExecutionException::class)
override fun startProcess(): ProcessHandler { override fun startProcess(): ProcessHandler {
return runModalOrBlocking({ModalTaskOwner.project(environment.project)}, {"ZigProfileState.startProcess"}) { return runModalOrBlocking({ModalTaskOwner.project(environment.project)}, {"ZigProfileState.startProcess"}) {
@ -53,37 +55,20 @@ abstract class ZigProfileState<T: ZigExecConfig<T>> (
@Throws(ExecutionException::class) @Throws(ExecutionException::class)
suspend fun startProcessSuspend(): ProcessHandler { suspend fun startProcessSuspend(): ProcessHandler {
val toolchain = environment.project.zigProjectSettings.state.toolchain ?: throw ExecutionException(ZigBrainsBundle.message("exception.zig-profile-state.start-process.no-toolchain")) val toolchain = ZigToolchainService.getInstance(environment.project).toolchain ?: throw ExecutionException(ZigBrainsBundle.message("exception.zig-profile-state.start-process.no-toolchain"))
return ZigProcessHandler(getCommandLine(toolchain, false)) return getCommandLine(toolchain, false).startIPCAwareProcess(environment.project, emulateTerminal = true)
} }
@Throws(ExecutionException::class) @Throws(ExecutionException::class)
open suspend fun getCommandLine(toolchain: AbstractZigToolchain, debug: Boolean): GeneralCommandLine { open suspend fun getCommandLine(toolchain: ZigToolchain, debug: Boolean): GeneralCommandLine {
val workingDir = configuration.workingDirectory val workingDir = configuration.workingDirectory
val zigExePath = toolchain.zig.path() val zigExePath = toolchain.zig.path()
// TODO remove this check once JetBrains implements colored terminal in the debugger val cli = PtyCommandLine().withConsoleMode(false)
// https://youtrack.jetbrains.com/issue/CPP-11622/ANSI-color-codes-not-honored-in-Debug-Run-Configuration-output-window cli.withExePath(zigExePath.pathString)
val cli = if (configuration.emulateTerminal() && !debug) PtyCommandLine() else GeneralCommandLine() workingDir.path?.let { cli.withWorkDirectory(it.toFile()) }
cli.exePath = zigExePath.pathString cli.withCharset(Charsets.UTF_8)
workingDir.path?.let { cli.withWorkingDirectory(it) }
cli.charset = Charsets.UTF_8
cli.addParameters(configuration.buildCommandLineArgs(debug)) cli.addParameters(configuration.buildCommandLineArgs(debug))
return configuration.patchCommandLine(cli) return configuration.patchCommandLine(cli)
} }
} }
@Throws(ExecutionException::class)
fun executeCommandLine(commandLine: GeneralCommandLine, environment: ExecutionEnvironment): DefaultExecutionResult {
val handler = startProcess(commandLine)
val console = BuildTextConsoleView(environment.project, false, emptyList())
console.attachToProcess(handler)
return DefaultExecutionResult(console, handler)
}
@Throws(ExecutionException::class)
fun startProcess(commandLine: GeneralCommandLine): ProcessHandler {
val handler = ZigProcessHandler(commandLine)
ProcessTerminatedListener.attach(handler)
return handler
}

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -34,9 +34,9 @@ abstract class ZigTopLevelLineMarker: RunLineMarkerContributor() {
private fun getParentIfTopLevel(element: PsiElement): PsiElement? { private fun getParentIfTopLevel(element: PsiElement): PsiElement? {
var parent = getDeclaration(element) var parent = getDeclaration(element)
var nestingLevel = 0; var nestingLevel = 0
while (parent != null && parent !is PsiFile) { while (parent != null && parent !is PsiFile) {
if (parent.elementType == ZigTypes.CONTAINER_DECLARATIONS) { if (parent.elementType == ZigTypes.CONTAINER_DECLARATION) {
if (nestingLevel != 0) if (nestingLevel != 0)
return null return null
nestingLevel++ nestingLevel++
@ -54,7 +54,7 @@ abstract class ZigTopLevelLineMarker: RunLineMarkerContributor() {
override fun getInfo(element: PsiElement): Info? { override fun getInfo(element: PsiElement): Info? {
if (!elementMatches(element)) if (!elementMatches(element))
return null; return null
val actions = ExecutorAction.getActions(0) val actions = ExecutorAction.getActions(0)
return Info(getIcon(element), actions, null) return Info(getIcon(element), actions, null)
} }

View file

@ -0,0 +1,56 @@
/*
* 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.execution.base
import com.falsepattern.zigbrains.zig.psi.ZigContainerMembers
import com.falsepattern.zigbrains.zig.psi.ZigFile
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.isFile
import com.intellij.psi.util.childrenOfType
fun ZigFile.hasMainFunction(): Boolean {
val members = childrenOfType<ZigContainerMembers>().firstOrNull() ?: return false
return members.containerDeclarationList.any { it.decl?.fnProto?.identifier?.textMatches("main") == true }
}
fun ZigFile.hasTests(): Boolean {
val members = childrenOfType<ZigContainerMembers>().firstOrNull() ?: return false
return members.containerDeclarationList.any { it.testDecl != null }
}
fun VirtualFile.findBuildZig(): VirtualFile? {
var parent = this.parent
while (parent != null) {
parent.children.forEach {
if (it.isFile && it.name == "build.zig") {
return it
}
}
parent = parent.parent
}
return null
}
fun VirtualFile.isBuildZig(): Boolean {
return name == "build.zig"
}

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -24,11 +24,16 @@ 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.base.findBuildZig
import com.falsepattern.zigbrains.project.execution.base.isBuildZig
import com.falsepattern.zigbrains.project.execution.firstConfigFactory import com.falsepattern.zigbrains.project.execution.firstConfigFactory
import com.falsepattern.zigbrains.zig.psi.ZigFile
import com.falsepattern.zigbrains.zig.psi.ZigTypes
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.elementType
import java.nio.file.Path import java.nio.file.Path
class ZigConfigProducerBuild: ZigConfigProducer<ZigExecConfigBuild>() { class ZigConfigProducerBuild: ZigConfigProducer<ZigExecConfigBuild>() {
@ -36,21 +41,43 @@ 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.isBuildZig()) {
configuration.name = ZigBrainsBundle.message("configuration.build.marker-name") configuration.name = ZigBrainsBundle.message("configuration.build.marker-run")
configuration.buildSteps.args = "run"
configuration.debugBuildSteps.args = "install"
return true
}
val buildZig = theFile.findBuildZig() ?: return false
configuration.workingDirectory.path = buildZig.parent.toNioPath()
if (element.elementType == ZigTypes.KEYWORD_TEST) {
configuration.name = ZigBrainsBundle.message("configuration.build.marker-test")
configuration.buildSteps.args = "test"
configuration.debugBuildSteps.args = "install_test"
return true
} else {
configuration.name = ZigBrainsBundle.message("configuration.build.marker-run")
configuration.buildSteps.args = "run"
configuration.debugBuildSteps.args = "install"
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) val dir = configuration.workingDirectory.path ?: return false
if (theFile.isBuildZig()) {
return filePath.parent == dir
} else {
if (element.elementType == ZigTypes.KEYWORD_TEST) {
if (configuration.buildSteps.args != "test")
return false
}
val buildZig = theFile.findBuildZig() ?: return false
return buildZig.parent.toNioPath() == dir
}
} }
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

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -33,7 +33,7 @@ class ZigConfigTypeBuild : ConfigurationTypeBase(
IDENTIFIER, IDENTIFIER,
ZigBrainsBundle.message("configuration.build.name"), ZigBrainsBundle.message("configuration.build.name"),
ZigBrainsBundle.message("configuration.build.description"), ZigBrainsBundle.message("configuration.build.description"),
Icons.ZIG Icons.Zig
) { ) {
init { init {
addFactory(ConfigFactoryRun(this)) addFactory(ConfigFactoryRun(this))

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -25,7 +25,6 @@ package com.falsepattern.zigbrains.project.execution.build
import com.falsepattern.zigbrains.ZigBrainsBundle import com.falsepattern.zigbrains.ZigBrainsBundle
import com.falsepattern.zigbrains.project.execution.base.* import com.falsepattern.zigbrains.project.execution.base.*
import com.falsepattern.zigbrains.shared.ZBFeatures import com.falsepattern.zigbrains.shared.ZBFeatures
import com.falsepattern.zigbrains.shared.cli.coloredCliFlags
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
@ -37,7 +36,9 @@ class ZigExecConfigBuild(project: Project, factory: ConfigurationFactory): ZigEx
private set private set
var extraArgs = ArgsConfigurable("compilerArgs", ZigBrainsBundle.message("exec.option.label.build.args")) var extraArgs = ArgsConfigurable("compilerArgs", ZigBrainsBundle.message("exec.option.label.build.args"))
private set private set
var colored = ColoredConfigurable("colored") var debugBuildSteps = ArgsConfigurable("debugBuildSteps", ZigBrainsBundle.message("exec.option.label.build.steps-debug"))
private set
var debugExtraArgs = ArgsConfigurable("debugCompilerArgs", ZigBrainsBundle.message("exec.option.label.build.args-debug"))
private set private set
var exePath = FilePathConfigurable("exePath", ZigBrainsBundle.message("exec.option.label.build.exe-path-debug")) var exePath = FilePathConfigurable("exePath", ZigBrainsBundle.message("exec.option.label.build.exe-path-debug"))
private set private set
@ -48,22 +49,9 @@ class ZigExecConfigBuild(project: Project, factory: ConfigurationFactory): ZigEx
override suspend fun buildCommandLineArgs(debug: Boolean): List<String> { override suspend fun buildCommandLineArgs(debug: Boolean): List<String> {
val result = ArrayList<String>() val result = ArrayList<String>()
result.add("build") result.add("build")
val steps = buildSteps.args val steps = if (debug) debugBuildSteps.argsSplit() else buildSteps.argsSplit()
if (debug) {
val truncatedSteps = ArrayList<String>()
for (step in steps) {
if (step == "run")
continue
if (step == "test")
throw ExecutionException(ZigBrainsBundle.message("exception.zig-build.debug.test-not-supported"))
truncatedSteps.add(step)
}
}
result.addAll(steps) result.addAll(steps)
result.addAll(coloredCliFlags(colored.value, debug)) result.addAll(if (debug) debugExtraArgs.argsSplit() else extraArgs.argsSplit())
result.addAll(extraArgs.args)
return result return result
} }
@ -74,18 +62,17 @@ class ZigExecConfigBuild(project: Project, factory: ConfigurationFactory): ZigEx
val clone = super.clone() val clone = super.clone()
clone.buildSteps = buildSteps.clone() clone.buildSteps = buildSteps.clone()
clone.exeArgs = exeArgs.clone() clone.exeArgs = exeArgs.clone()
clone.colored = colored.clone()
clone.exePath = exePath.clone() clone.exePath = exePath.clone()
clone.exeArgs = exeArgs.clone() clone.exeArgs = exeArgs.clone()
return clone return clone
} }
override fun getConfigurables(): List<ZigConfigurable<*>> { override fun getConfigurables(): List<ZigConfigurable<*>> {
val baseCfg = super.getConfigurables() + listOf(buildSteps, extraArgs, colored) val baseCfg = super.getConfigurables() + listOf(buildSteps, extraArgs)
if (ZBFeatures.debug()) { return if (ZBFeatures.debug()) {
return baseCfg + listOf(exePath, exeArgs) baseCfg + listOf(debugBuildSteps, debugExtraArgs, exePath, exeArgs)
} else { } else {
return baseCfg baseCfg
} }
} }

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -25,5 +25,4 @@ package com.falsepattern.zigbrains.project.execution.build
import com.falsepattern.zigbrains.project.execution.base.ZigProfileState import com.falsepattern.zigbrains.project.execution.base.ZigProfileState
import com.intellij.execution.runners.ExecutionEnvironment import com.intellij.execution.runners.ExecutionEnvironment
class ZigProfileStateBuild(environment: ExecutionEnvironment, configuration: ZigExecConfigBuild) : ZigProfileState<ZigExecConfigBuild>(environment, configuration) { class ZigProfileStateBuild(environment: ExecutionEnvironment, configuration: ZigExecConfigBuild) : ZigProfileState<ZigExecConfigBuild>(environment, configuration)
}

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -23,7 +23,10 @@
package com.falsepattern.zigbrains.project.execution.run 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.base.findBuildZig
import com.falsepattern.zigbrains.project.execution.base.hasMainFunction
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
@ -35,16 +38,19 @@ 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)) { if (!psiFile.hasMainFunction()) {
return false
}
if (theFile.findBuildZig() != null) {
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
} }
@ -52,5 +58,3 @@ class ZigConfigProducerRun: ZigConfigProducer<ZigExecConfigRun>() {
return self.configurationType is ZigConfigTypeRun return self.configurationType is ZigConfigTypeRun
} }
} }
private val LINE_MARKER = ZigLineMarkerRun()

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -33,7 +33,7 @@ class ZigConfigTypeRun : ConfigurationTypeBase(
IDENTIFIER, IDENTIFIER,
ZigBrainsBundle.message("configuration.run.name"), ZigBrainsBundle.message("configuration.run.name"),
ZigBrainsBundle.message("configuration.run.description"), ZigBrainsBundle.message("configuration.run.description"),
Icons.ZIG Icons.Zig
) { ) {
init { init {
addFactory(ConfigFactoryRun(this)) addFactory(ConfigFactoryRun(this))

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -24,7 +24,6 @@ package com.falsepattern.zigbrains.project.execution.run
import com.falsepattern.zigbrains.ZigBrainsBundle import com.falsepattern.zigbrains.ZigBrainsBundle
import com.falsepattern.zigbrains.project.execution.base.* import com.falsepattern.zigbrains.project.execution.base.*
import com.falsepattern.zigbrains.shared.cli.coloredCliFlags
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
@ -35,8 +34,6 @@ import kotlin.io.path.pathString
class ZigExecConfigRun(project: Project, factory: ConfigurationFactory): ZigExecConfig<ZigExecConfigRun>(project, factory, ZigBrainsBundle.message("exec.type.run.label")) { class ZigExecConfigRun(project: Project, factory: ConfigurationFactory): ZigExecConfig<ZigExecConfigRun>(project, factory, ZigBrainsBundle.message("exec.type.run.label")) {
var filePath = FilePathConfigurable("filePath", ZigBrainsBundle.message("exec.option.label.file-path")) var filePath = FilePathConfigurable("filePath", ZigBrainsBundle.message("exec.option.label.file-path"))
private set private set
var colored = ColoredConfigurable("colored")
private set
var optimization = OptimizationConfigurable("optimization") var optimization = OptimizationConfigurable("optimization")
private set private set
var compilerArgs = ArgsConfigurable("compilerArgs", ZigBrainsBundle.message("exec.option.label.compiler-args")) var compilerArgs = ArgsConfigurable("compilerArgs", ZigBrainsBundle.message("exec.option.label.compiler-args"))
@ -47,15 +44,14 @@ class ZigExecConfigRun(project: Project, factory: ConfigurationFactory): ZigExec
override suspend fun buildCommandLineArgs(debug: Boolean): List<String> { override suspend fun buildCommandLineArgs(debug: Boolean): List<String> {
val result = ArrayList<String>() val result = ArrayList<String>()
result.add(if (debug) "build-exe" else "run") result.add(if (debug) "build-exe" else "run")
result.addAll(coloredCliFlags(colored.value, debug))
result.add(filePath.path?.pathString ?: throw ExecutionException(ZigBrainsBundle.message("exception.zig.empty-file-path"))) result.add(filePath.path?.pathString ?: throw ExecutionException(ZigBrainsBundle.message("exception.zig.empty-file-path")))
if (!debug || optimization.forced) { if (!debug || optimization.forced) {
result.addAll(listOf("-O", optimization.level.name)) result.addAll(listOf("-O", optimization.level.name))
} }
result.addAll(compilerArgs.args) result.addAll(compilerArgs.argsSplit())
if (!debug) { if (!debug) {
result.add("--") result.add("--")
result.addAll(exeArgs.args) result.addAll(exeArgs.argsSplit())
} }
return result return result
} }
@ -66,7 +62,6 @@ class ZigExecConfigRun(project: Project, factory: ConfigurationFactory): ZigExec
override fun clone(): ZigExecConfigRun { override fun clone(): ZigExecConfigRun {
val clone = super.clone() val clone = super.clone()
clone.filePath = filePath.clone() clone.filePath = filePath.clone()
clone.colored = colored.clone()
clone.compilerArgs = compilerArgs.clone() clone.compilerArgs = compilerArgs.clone()
clone.optimization = optimization.clone() clone.optimization = optimization.clone()
clone.exeArgs = exeArgs.clone() clone.exeArgs = exeArgs.clone()
@ -74,7 +69,7 @@ class ZigExecConfigRun(project: Project, factory: ConfigurationFactory): ZigExec
} }
override fun getConfigurables(): List<ZigConfigurable<*>> { override fun getConfigurables(): List<ZigConfigurable<*>> {
return super.getConfigurables() + listOf(filePath, optimization, colored, compilerArgs, exeArgs) return super.getConfigurables() + listOf(filePath, optimization, compilerArgs, exeArgs)
} }
override fun getState(executor: Executor, environment: ExecutionEnvironment): ZigProfileState<ZigExecConfigRun> { override fun getState(executor: Executor, environment: ExecutionEnvironment): ZigProfileState<ZigExecConfigRun> {

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -25,5 +25,4 @@ package com.falsepattern.zigbrains.project.execution.run
import com.falsepattern.zigbrains.project.execution.base.ZigProfileState import com.falsepattern.zigbrains.project.execution.base.ZigProfileState
import com.intellij.execution.runners.ExecutionEnvironment import com.intellij.execution.runners.ExecutionEnvironment
class ZigProfileStateRun(environment: ExecutionEnvironment, configuration: ZigExecConfigRun) : ZigProfileState<ZigExecConfigRun>(environment, configuration) { class ZigProfileStateRun(environment: ExecutionEnvironment, configuration: ZigExecConfigRun) : ZigProfileState<ZigExecConfigRun>(environment, configuration)
}

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -24,7 +24,10 @@ 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.base.findBuildZig
import com.falsepattern.zigbrains.project.execution.base.hasTests
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,16 +39,19 @@ 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)) { if (!psiFile.hasTests()) {
return false
}
if (theFile.findBuildZig() != 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 +59,3 @@ class ZigConfigProducerTest: ZigConfigProducer<ZigExecConfigTest>() {
return self.configurationType is ZigConfigTypeTest return self.configurationType is ZigConfigTypeTest
} }
} }
private val LINE_MARKER = ZigLineMarkerTest()

View file

@ -1,7 +1,7 @@
/* /*
* This file is part of ZigBrains. * This file is part of ZigBrains.
* *
* Copyright (C) 2023-2024 FalsePattern * Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved * All Rights Reserved
* *
* The above copyright notice and this permission notice shall be included * The above copyright notice and this permission notice shall be included
@ -33,7 +33,7 @@ class ZigConfigTypeTest : ConfigurationTypeBase(
IDENTIFIER, IDENTIFIER,
ZigBrainsBundle.message("configuration.test.name"), ZigBrainsBundle.message("configuration.test.name"),
ZigBrainsBundle.message("configuration.test.description"), ZigBrainsBundle.message("configuration.test.description"),
Icons.ZIG Icons.Zig
) { ) {
init { init {
addFactory(ConfigFactoryRun(this)) addFactory(ConfigFactoryRun(this))

Some files were not shown because too many files have changed in this diff Show more