Compare commits

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

2 commits

65 changed files with 3138 additions and 58 deletions

View file

@ -17,6 +17,9 @@ Changelog structure reference:
## [Unreleased] ## [Unreleased]
- LSP
- LSP4IJ is no longer a hard dependency, ZLS support is now an optional feature.
## [20.3.0] ## [20.3.0]
- Zig - Zig

View file

@ -105,11 +105,12 @@ dependencies {
pluginVerifier() pluginVerifier()
zipSigner() zipSigner()
plugin(lsp4ijPluginString) // plugin(lsp4ijPluginString)
} }
runtimeOnly(project(":core")) runtimeOnly(project(":core"))
runtimeOnly(project(":cidr")) runtimeOnly(project(":cidr"))
runtimeOnly(project(":lsp"))
} }
intellijPlatform { intellijPlatform {

View file

@ -7,8 +7,6 @@ plugins {
kotlin("plugin.serialization") kotlin("plugin.serialization")
} }
val lsp4ijVersion: String by project
val lsp4jVersion: String by project
val ideaCommunityVersion: String by project val ideaCommunityVersion: String by project
val useInstaller = property("useInstaller").toString().toBoolean() val useInstaller = property("useInstaller").toString().toBoolean()
@ -16,8 +14,6 @@ dependencies {
intellijPlatform { intellijPlatform {
create(IntelliJPlatformType.IntellijIdeaCommunity, ideaCommunityVersion, useInstaller = useInstaller) create(IntelliJPlatformType.IntellijIdeaCommunity, ideaCommunityVersion, useInstaller = useInstaller)
} }
compileOnly("com.redhat.devtools.intellij:lsp4ij:$lsp4ijVersion")
compileOnly("org.eclipse.lsp4j:org.eclipse.lsp4j:$lsp4jVersion")
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3") compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3")
} }

View file

@ -175,6 +175,17 @@
//Mixins //Mixins
mixin("StringLiteral")="com.falsepattern.zigbrains.zig.psi.impl.mixins.ZigStringLiteralMixinImpl" mixin("StringLiteral")="com.falsepattern.zigbrains.zig.psi.impl.mixins.ZigStringLiteralMixinImpl"
implements("StringLiteral")="com.falsepattern.zigbrains.zig.psi.mixins.ZigStringLiteralMixin" implements("StringLiteral")="com.falsepattern.zigbrains.zig.psi.mixins.ZigStringLiteralMixin"
mixin("PrimaryTypeExpr")="com.falsepattern.zigbrains.zig.psi.impl.mixins.ZigPrimaryTypeExprMixinImpl"
implements("PrimaryTypeExpr")="com.falsepattern.zigbrains.zig.psi.mixins.ZigPrimaryTypeExprMixin"
implements("Expr")="com.falsepattern.zigbrains.zig.psi.mixins.ZigExprMixin"
mixin("FnDeclProto")="com.falsepattern.zigbrains.zig.psi.impl.mixins.ZigFnDeclProtoMixinImpl"
implements("FnDeclProto")="com.falsepattern.zigbrains.zig.psi.mixins.ZigFnDeclProtoMixin"
mixin("VarDeclProto")="com.falsepattern.zigbrains.zig.psi.impl.mixins.ZigVarDeclProtoMixinImpl"
implements("VarDeclProto")="com.falsepattern.zigbrains.zig.psi.mixins.ZigVarDeclProtoMixin"
mixin("ContainerMembers")="com.falsepattern.zigbrains.zig.psi.impl.mixins.ZigContainerMembersMixinImpl"
implements("ContainerMembers")="com.falsepattern.zigbrains.zig.psi.mixins.ZigContainerMembersMixin"
mixin("ParamDecl")="com.falsepattern.zigbrains.zig.psi.impl.mixins.ZigParamDeclMixinImpl"
implements("ParamDecl")="com.falsepattern.zigbrains.zig.psi.mixins.ZigParamDeclMixin"
} }
Root ::= CONTAINER_DOC_COMMENT? ContainerMembers? Root ::= CONTAINER_DOC_COMMENT? ContainerMembers?
@ -189,11 +200,13 @@ TestDecl ::= KEYWORD_TEST (STRING_LITERAL_SINGLE | IDENTIFIER)? Block {pin=1}
ComptimeDecl ::= KEYWORD_COMPTIME Block ComptimeDecl ::= KEYWORD_COMPTIME Block
Decl Decl
::= (KEYWORD_EXPORT | KEYWORD_EXTERN STRING_LITERAL_SINGLE? | KEYWORD_INLINE | KEYWORD_NOINLINE)? FnProto (SEMICOLON | Block) ::= (KEYWORD_EXPORT | KEYWORD_EXTERN STRING_LITERAL_SINGLE? | KEYWORD_INLINE | KEYWORD_NOINLINE)? FnDeclProto (SEMICOLON | Block)
| (KEYWORD_EXPORT | KEYWORD_EXTERN STRING_LITERAL_SINGLE?)? KEYWORD_THREADLOCAL? GlobalVarDecl | (KEYWORD_EXPORT | KEYWORD_EXTERN STRING_LITERAL_SINGLE?)? KEYWORD_THREADLOCAL? GlobalVarDecl
| KEYWORD_USINGNAMESPACE Expr SEMICOLON | KEYWORD_USINGNAMESPACE Expr SEMICOLON
FnProto ::= KEYWORD_FN IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr {pin=1} FnDeclProto ::= KEYWORD_FN IDENTIFIER LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr {pin=1}
FnTypeProto ::= KEYWORD_FN LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr {pin=1}
VarDeclProto ::= (KEYWORD_CONST | KEYWORD_VAR) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection? {pin=1} VarDeclProto ::= (KEYWORD_CONST | KEYWORD_VAR) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection? {pin=1}
@ -337,7 +350,7 @@ PrimaryTypeExpr
| DOT InitList | DOT InitList
| ErrorSetDecl | ErrorSetDecl
| FLOAT | FLOAT
| FnProto | FnTypeProto
| GroupedExpr | GroupedExpr
| LabeledTypeExpr | LabeledTypeExpr
| IDENTIFIER | IDENTIFIER

View file

@ -0,0 +1,31 @@
/*
* 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.zig.psi;
import com.falsepattern.zigbrains.zig.references.ZigType;
public interface ZigTypedElement {
default ZigType getType() {
return ZigType.Generic;
}
}

View file

@ -25,7 +25,6 @@ package com.falsepattern.zigbrains
import com.falsepattern.zigbrains.direnv.DirenvCmd import com.falsepattern.zigbrains.direnv.DirenvCmd
import com.falsepattern.zigbrains.direnv.emptyEnv import com.falsepattern.zigbrains.direnv.emptyEnv
import com.falsepattern.zigbrains.direnv.getDirenv import com.falsepattern.zigbrains.direnv.getDirenv
import com.falsepattern.zigbrains.lsp.settings.zlsSettings
import com.falsepattern.zigbrains.project.settings.zigProjectSettings import com.falsepattern.zigbrains.project.settings.zigProjectSettings
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainProvider import com.falsepattern.zigbrains.project.toolchain.ZigToolchainProvider
@ -89,17 +88,6 @@ class ZBStartup: ProjectActivity {
project.zigProjectSettings.state = zigProjectState project.zigProjectSettings.state = zigProjectState
} }
} }
val zlsState = project.zlsSettings.state
if (zlsState.zlsPath.isBlank()) {
val env = if (DirenvCmd.direnvInstalled() && !project.isDefault && zlsState.direnv)
project.getDirenv()
else
emptyEnv
env.findExecutableOnPATH("zls")?.let {
zlsState.zlsPath = it.pathString
project.zlsSettings.state = zlsState
}
}
} }
} }

View file

@ -38,7 +38,7 @@ class ZigLineMarkerBuild: ZigTopLevelLineMarker() {
return null return null
val parent = element.parent ?: return null val parent = element.parent ?: return null
if (parent.elementType != ZigTypes.FN_PROTO) if (parent.elementType != ZigTypes.FN_DECL_PROTO)
return null return null
val file = element.containingFile ?: return null val file = element.containingFile ?: return null

View file

@ -38,7 +38,7 @@ class ZigLineMarkerRun: ZigTopLevelLineMarker() {
return null return null
val parent = element.parent val parent = element.parent
if (parent.elementType != ZigTypes.FN_PROTO) if (parent.elementType != ZigTypes.FN_DECL_PROTO)
return null return null
return parent.parent return parent.parent

View file

@ -22,8 +22,7 @@
package com.falsepattern.zigbrains.project.newproject package com.falsepattern.zigbrains.project.newproject
import com.falsepattern.zigbrains.lsp.settings.ZLSSettingsPanel import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
import com.falsepattern.zigbrains.project.settings.ZigProjectSettingsPanel
import com.falsepattern.zigbrains.project.template.ZigExecutableTemplate import com.falsepattern.zigbrains.project.template.ZigExecutableTemplate
import com.falsepattern.zigbrains.project.template.ZigInitTemplate import com.falsepattern.zigbrains.project.template.ZigInitTemplate
import com.falsepattern.zigbrains.project.template.ZigLibraryTemplate import com.falsepattern.zigbrains.project.template.ZigLibraryTemplate
@ -44,8 +43,7 @@ import javax.swing.ListSelectionModel
class ZigNewProjectPanel(private var handleGit: Boolean): Disposable { class ZigNewProjectPanel(private var handleGit: Boolean): Disposable {
private val git = JBCheckBox() private val git = JBCheckBox()
private val projConf = ZigProjectSettingsPanel(null).also { Disposer.register(this, it) } private val panels = ZigProjectConfigurationProvider.createNewProjectSettingsPanels().onEach { Disposer.register(this, it) }
private val zlsConf = ZLSSettingsPanel(null).also { Disposer.register(this, it) }
private val templateList = JBList(JBList.createDefaultListModel(defaultTemplates)).apply { private val templateList = JBList(JBList.createDefaultListModel(defaultTemplates)).apply {
selectionMode = ListSelectionModel.SINGLE_SELECTION selectionMode = ListSelectionModel.SINGLE_SELECTION
selectedIndex = 0 selectedIndex = 0
@ -68,7 +66,7 @@ class ZigNewProjectPanel(private var handleGit: Boolean): Disposable {
fun getData(): ZigProjectConfigurationData { fun getData(): ZigProjectConfigurationData {
val selectedTemplate = templateList.selectedValue val selectedTemplate = templateList.selectedValue
return ZigProjectConfigurationData(handleGit && git.isSelected, projConf.data, zlsConf.data, selectedTemplate) return ZigProjectConfigurationData(handleGit && git.isSelected, panels.map { it.data }, selectedTemplate)
} }
fun attach(p: Panel): Unit = with(p) { fun attach(p: Panel): Unit = with(p) {
@ -85,8 +83,7 @@ class ZigNewProjectPanel(private var handleGit: Boolean): Disposable {
.align(AlignY.FILL) .align(AlignY.FILL)
} }
} }
projConf.attach(p) panels.forEach { it.attach(p) }
zlsConf.attach(p)
} }
override fun dispose() { override fun dispose() {

View file

@ -22,10 +22,7 @@
package com.falsepattern.zigbrains.project.newproject package com.falsepattern.zigbrains.project.newproject
import com.falsepattern.zigbrains.lsp.settings.ZLSSettings import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
import com.falsepattern.zigbrains.lsp.settings.zlsSettings
import com.falsepattern.zigbrains.project.settings.ZigProjectSettings
import com.falsepattern.zigbrains.project.settings.zigProjectSettings
import com.falsepattern.zigbrains.project.template.ZigInitTemplate import com.falsepattern.zigbrains.project.template.ZigInitTemplate
import com.falsepattern.zigbrains.project.template.ZigProjectTemplate import com.falsepattern.zigbrains.project.template.ZigProjectTemplate
import com.falsepattern.zigbrains.shared.zigCoroutineScope import com.falsepattern.zigbrains.shared.zigCoroutineScope
@ -45,21 +42,21 @@ import kotlinx.coroutines.launch
@JvmRecord @JvmRecord
data class ZigProjectConfigurationData( data class ZigProjectConfigurationData(
val git: Boolean, val git: Boolean,
val projConf: ZigProjectSettings, val conf: List<ZigProjectConfigurationProvider.Settings>,
val zlsConf: ZLSSettings,
val selectedTemplate: ZigProjectTemplate val selectedTemplate: ZigProjectTemplate
) { ) {
@RequiresBackgroundThread @RequiresBackgroundThread
suspend fun generateProject(requestor: Any, project: Project, baseDir: VirtualFile, forceGitignore: Boolean): Boolean { suspend fun generateProject(requestor: Any, project: Project, baseDir: VirtualFile, forceGitignore: Boolean): Boolean {
return reportProgress { reporter -> return reportProgress { reporter ->
project.zigProjectSettings.loadState(projConf) conf.forEach { it.apply(project) }
project.zlsSettings.loadState(zlsConf)
val template = selectedTemplate val template = selectedTemplate
if (!reporter.indeterminateStep("Initializing project") { if (!reporter.indeterminateStep("Initializing project") {
if (template is ZigInitTemplate) { if (template is ZigInitTemplate) {
val toolchain = projConf.toolchain ?: run { val toolchain = conf
.mapNotNull { it as? ZigProjectConfigurationProvider.ToolchainProvider }
.firstNotNullOfOrNull { it.toolchain } ?: run {
Notification( Notification(
"zigbrains", "zigbrains",
"Tried to generate project with zig init, but zig toolchain is invalid", "Tried to generate project with zig init, but zig toolchain is invalid",

View file

@ -22,11 +22,10 @@
package com.falsepattern.zigbrains.project.settings package com.falsepattern.zigbrains.project.settings
import com.falsepattern.zigbrains.lsp.settings.ZLSSettingsConfigurable
import com.falsepattern.zigbrains.shared.MultiConfigurable import com.falsepattern.zigbrains.shared.MultiConfigurable
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
class ZigConfigurable(project: Project): MultiConfigurable(ZigProjectConfigurable(project), ZLSSettingsConfigurable(project)) { class ZigConfigurable(project: Project): MultiConfigurable(ZigProjectConfigurationProvider.createConfigurables(project)) {
override fun getDisplayName(): String { override fun getDisplayName(): String {
return "Zig" return "Zig"
} }

View file

@ -0,0 +1,42 @@
/*
* 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.settings
import com.falsepattern.zigbrains.shared.SubConfigurable
import com.intellij.openapi.project.Project
class ZigCoreProjectConfigurationProvider: ZigProjectConfigurationProvider {
override fun handleMainConfigChanged(project: Project) {
}
override fun createConfigurable(project: Project): SubConfigurable {
return ZigProjectConfigurable(project)
}
override fun createNewProjectSettingsPanel(): ZigProjectConfigurationProvider.SettingsPanel {
return ZigProjectSettingsPanel(null)
}
override val priority: Int
get() = 0
}

View file

@ -22,7 +22,6 @@
package com.falsepattern.zigbrains.project.settings package com.falsepattern.zigbrains.project.settings
import com.falsepattern.zigbrains.lsp.startLSP
import com.falsepattern.zigbrains.shared.SubConfigurable import com.falsepattern.zigbrains.shared.SubConfigurable
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Disposer
@ -45,7 +44,7 @@ class ZigProjectConfigurable(private val project: Project): SubConfigurable {
val modified = service.isModified(data) val modified = service.isModified(data)
service.state = data service.state = data
if (modified) { if (modified) {
startLSP(project, true) ZigProjectConfigurationProvider.mainConfigChanged(project)
} }
} }

View file

@ -0,0 +1,59 @@
/*
* 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.settings
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain
import com.falsepattern.zigbrains.shared.SubConfigurable
import com.intellij.openapi.Disposable
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.project.Project
import com.intellij.ui.dsl.builder.Panel
interface ZigProjectConfigurationProvider {
fun handleMainConfigChanged(project: Project)
fun createConfigurable(project: Project): SubConfigurable
fun createNewProjectSettingsPanel(): SettingsPanel
val priority: Int
companion object {
private val EXTENSION_POINT_NAME = ExtensionPointName.create<ZigProjectConfigurationProvider>("com.falsepattern.zigbrains.projectConfigProvider")
fun mainConfigChanged(project: Project) {
EXTENSION_POINT_NAME.extensionList.forEach { it.handleMainConfigChanged(project) }
}
fun createConfigurables(project: Project): List<SubConfigurable> {
return EXTENSION_POINT_NAME.extensionList.sortedBy { it.priority }.map { it.createConfigurable(project) }
}
fun createNewProjectSettingsPanels(): List<SettingsPanel> {
return EXTENSION_POINT_NAME.extensionList.sortedBy { it.priority }.map { it.createNewProjectSettingsPanel() }
}
}
interface SettingsPanel: Disposable {
val data: Settings
fun attach(p: Panel)
}
interface Settings {
fun apply(project: Project)
}
interface ToolchainProvider {
val toolchain: AbstractZigToolchain?
}
}

View file

@ -23,6 +23,7 @@
package com.falsepattern.zigbrains.project.settings package com.falsepattern.zigbrains.project.settings
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.io.toNioPathOrNull import com.intellij.openapi.util.io.toNioPathOrNull
import com.intellij.util.xmlb.annotations.Transient import com.intellij.util.xmlb.annotations.Transient
import kotlin.io.path.pathString import kotlin.io.path.pathString
@ -32,10 +33,14 @@ data class ZigProjectSettings(
var overrideStdPath: Boolean = false, var overrideStdPath: Boolean = false,
var explicitPathToStd: String? = null, var explicitPathToStd: String? = null,
var toolchainPath: String? = null var toolchainPath: String? = null
) { ): ZigProjectConfigurationProvider.Settings, ZigProjectConfigurationProvider.ToolchainProvider {
override fun apply(project: Project) {
project.zigProjectSettings.loadState(this)
}
@get:Transient @get:Transient
@set:Transient @set:Transient
var toolchain: LocalZigToolchain? override var toolchain: LocalZigToolchain?
get() = toolchainPath?.toNioPathOrNull()?.let { LocalZigToolchain(it) } get() = toolchainPath?.toNioPathOrNull()?.let { LocalZigToolchain(it) }
set(value) { set(value) {
toolchainPath = value?.location?.pathString toolchainPath = value?.location?.pathString

View file

@ -52,7 +52,7 @@ import kotlinx.coroutines.launch
import javax.swing.event.DocumentEvent import javax.swing.event.DocumentEvent
import kotlin.io.path.pathString import kotlin.io.path.pathString
class ZigProjectSettingsPanel(private val project: Project?) : Disposable { class ZigProjectSettingsPanel(private val project: Project?) : ZigProjectConfigurationProvider.SettingsPanel {
private val direnv = JBCheckBox(ZigBrainsBundle.message("settings.project.label.direnv")).apply { addActionListener { private val direnv = JBCheckBox(ZigBrainsBundle.message("settings.project.label.direnv")).apply { addActionListener {
dispatchAutodetect(true) dispatchAutodetect(true)
} } } }
@ -107,7 +107,7 @@ class ZigProjectSettingsPanel(private val project: Project?) : Disposable {
} }
} }
var data override var data
get() = ZigProjectSettings( get() = ZigProjectSettings(
direnv.isSelected, direnv.isSelected,
stdFieldOverride.isSelected, stdFieldOverride.isSelected,
@ -123,7 +123,7 @@ class ZigProjectSettingsPanel(private val project: Project?) : Disposable {
dispatchUpdateUI() dispatchUpdateUI()
} }
fun attach(p: Panel): Unit = with(p) { override fun attach(p: Panel): Unit = with(p) {
val project = project ?: ProjectManager.getInstance().defaultProject val project = project ?: ProjectManager.getInstance().defaultProject
data = project.zigProjectSettings.state data = project.zigProjectSettings.state
group(ZigBrainsBundle.message("settings.project.group.title")) { group(ZigBrainsBundle.message("settings.project.group.title")) {

View file

@ -27,7 +27,7 @@ import com.intellij.openapi.util.Disposer
import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.panel
import javax.swing.JComponent import javax.swing.JComponent
abstract class MultiConfigurable(private vararg val configurables: SubConfigurable): Configurable { abstract class MultiConfigurable(private val configurables: List<SubConfigurable>): Configurable {
override fun createComponent(): JComponent? { override fun createComponent(): JComponent? {
return panel { return panel {
for (configurable in configurables) { for (configurable in configurables) {

View file

@ -87,7 +87,7 @@ private fun getIndentBasedOnParentType(
//Function declaration parameters //Function declaration parameters
if (parentType == PARAM_DECL_LIST || if (parentType == PARAM_DECL_LIST ||
parentType == FN_PROTO && childType === PLACEHOLDER parentType == FN_DECL_PROTO && childType === PLACEHOLDER
) )
return normalIndent return normalIndent

View file

@ -0,0 +1,119 @@
/*
* 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.zig.highlighter
import com.falsepattern.zigbrains.zig.psi.ZigNamedElement
import com.falsepattern.zigbrains.zig.psi.ZigPrimaryTypeExpr
import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.lang.annotation.Annotator
import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.psi.PsiElement
class ZigSemanticHighlighter: Annotator {
override fun annotate(element: PsiElement, holder: AnnotationHolder) {
when(element) {
is ZigPrimaryTypeExpr -> {
val id = element.identifier ?: return
if (primitiveHighlight(id, holder)) {
return
}
if (referenceHighlight(element, holder)) {
return
}
}
is ZigNamedElement -> {
val id = element.identifyingElement ?: return
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(id.textRange)
.textAttributes(element.declarationAttribute)
.create()
}
}
}
private fun referenceHighlight(element: PsiElement, holder: AnnotationHolder): Boolean {
val ref = element.reference ?: return false
val resolved = ref.resolve() ?: return false
if (resolved is ZigNamedElement) {
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(ref.absoluteRange)
.textAttributes(resolved.referenceAttribute)
.create()
return true
}
return false
}
private fun primitiveHighlight(element: PsiElement, holder: AnnotationHolder): Boolean {
val name = element.text
if (name in primitives) {
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(element.textRange)
.textAttributes(ZigSyntaxHighlighter.TYPE_REF)
.create()
return true
}
if (name.startsWith('i') || name.startsWith('u')) {
val numeric = name.substring(1)
val num = numeric.toIntOrNull() ?: return false
if (num < 0 || num > 65535) {
return false
}
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(element.textRange)
.textAttributes(ZigSyntaxHighlighter.TYPE_REF)
.create()
return true
}
return false
}
private val primitives = setOf(
"isize",
"usize",
"c_char",
"c_short",
"c_ushort",
"c_int",
"c_uint",
"c_long",
"c_ulong",
"c_longlong",
"c_ulonglong",
"c_longdouble",
"f16",
"f32",
"f64",
"f80",
"f128",
"bool",
"anyopaque",
"void",
"noreturn",
"type",
"anyerror",
"comptime_int",
"comptime_float",
)
}

View file

@ -0,0 +1,31 @@
/*
* 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.zig.psi
import com.intellij.openapi.editor.colors.TextAttributesKey
import com.intellij.psi.PsiNameIdentifierOwner
interface ZigNamedElement: PsiNameIdentifierOwner {
val declarationAttribute: TextAttributesKey
val referenceAttribute: TextAttributesKey
}

View file

@ -0,0 +1,29 @@
/*
* 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.zig.psi
import com.intellij.psi.PsiElement
interface ZigReferenceElement: PsiElement {
fun rename(name: String): ZigReferenceElement?
}

View file

@ -0,0 +1,42 @@
/*
* 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.zig.psi.impl.mixins
import com.falsepattern.zigbrains.zig.psi.ZigContainerField
import com.falsepattern.zigbrains.zig.psi.ZigContainerMembers
import com.intellij.extapi.psi.ASTWrapperPsiElement
import com.intellij.lang.ASTNode
import com.intellij.psi.util.childrenOfType
import com.intellij.util.resettableLazy
abstract class ZigContainerMembersMixinImpl(node: ASTNode): ASTWrapperPsiElement(node), ZigContainerMembers {
private val _isNamespace = resettableLazy {
childrenOfType<ZigContainerField>().isEmpty()
}
override val isNamespace by _isNamespace
override fun subtreeChanged() {
super.subtreeChanged()
_isNamespace.reset()
}
}

View file

@ -0,0 +1,55 @@
/*
* 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.zig.psi.impl.mixins
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter
import com.falsepattern.zigbrains.zig.psi.ZigFnDeclProto
import com.falsepattern.zigbrains.zig.util.ZigElementFactory
import com.intellij.extapi.psi.ASTWrapperPsiElement
import com.intellij.lang.ASTNode
import com.intellij.openapi.editor.colors.TextAttributesKey
import com.intellij.psi.PsiElement
import com.intellij.psi.util.startOffset
abstract class ZigFnDeclProtoMixinImpl(node: ASTNode): ASTWrapperPsiElement(node), ZigFnDeclProto {
override fun getName(): String? {
return identifier?.text
}
override fun setName(name: String): PsiElement? {
identifier?.replace(ZigElementFactory.createZigIdentifier(project, name) ?: return null) ?: return null
return this
}
override fun getNameIdentifier(): PsiElement? {
return identifier
}
override fun getTextOffset(): Int {
return identifier?.startOffset ?: 0
}
override val declarationAttribute get() = ZigSyntaxHighlighter.FUNCTION_DECL
override val referenceAttribute get() = ZigSyntaxHighlighter.FUNCTION_REF
}

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.zig.psi.impl.mixins
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter
import com.falsepattern.zigbrains.zig.psi.ZigParamDecl
import com.falsepattern.zigbrains.zig.util.ZigElementFactory
import com.falsepattern.zigbrains.zig.psi.ZigVarDeclProto
import com.intellij.extapi.psi.ASTWrapperPsiElement
import com.intellij.lang.ASTNode
import com.intellij.openapi.editor.colors.TextAttributesKey
import com.intellij.psi.PsiElement
import com.intellij.psi.util.startOffset
abstract class ZigParamDeclMixinImpl(node: ASTNode): ASTWrapperPsiElement(node), ZigParamDecl {
override fun getName(): String? {
return identifier?.text
}
override fun setName(name: String): PsiElement? {
identifier?.replace(ZigElementFactory.createZigIdentifier(project, name) ?: return null) ?: return null
return this
}
override fun getNameIdentifier(): PsiElement? {
return identifier
}
override fun getTextOffset(): Int {
return identifier?.startOffset ?: 0
}
override val declarationAttribute get() = ZigSyntaxHighlighter.PARAMETER
override val referenceAttribute get() = ZigSyntaxHighlighter.VARIABLE_REF
}

View file

@ -0,0 +1,113 @@
/*
* 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.zig.psi.impl.mixins
import com.falsepattern.zigbrains.project.settings.zigProjectSettings
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainProvider
import com.falsepattern.zigbrains.zig.psi.ZigFnCallArguments
import com.falsepattern.zigbrains.zig.util.ZigElementFactory
import com.falsepattern.zigbrains.zig.psi.ZigPrimaryTypeExpr
import com.falsepattern.zigbrains.zig.references.ZigReference
import com.falsepattern.zigbrains.zig.psi.ZigReferenceElement
import com.falsepattern.zigbrains.zig.psi.ZigTypes
import com.falsepattern.zigbrains.zig.references.ZigType
import com.intellij.extapi.psi.ASTWrapperPsiElement
import com.intellij.lang.ASTNode
import com.intellij.openapi.util.TextRange
import com.intellij.psi.util.elementType
import com.intellij.util.resettableLazy
import kotlinx.coroutines.runBlocking
import kotlin.io.path.exists
abstract class ZigPrimaryTypeExprMixinImpl(node: ASTNode): ASTWrapperPsiElement(node), ZigPrimaryTypeExpr {
private val ref = resettableLazy {
identifier?.let { return@resettableLazy ZigReference(this, it.textRangeInParent) }
//TODO import resolving
// val children = children
// if (children.size == 2) run {
// val a = children[0]
// val b = children[1]
// if (a.elementType != ZigTypes.BUILTINIDENTIFIER || !a.text.equals("@import")) {
// return@run
// }
// if (b !is ZigFnCallArguments) {
// return@run
// }
// val exprList = b.exprList?.exprList ?: return@run
// if (exprList.size != 1)
// return@run
// val expr = exprList[0] as? ZigPrimaryTypeExpr ?: return@run
// val stringLiteral = expr.stringLiteral ?: return@run
// val esc = stringLiteral.createLiteralTextEscaper()
// val sb = StringBuilder()
// esc.decode(TextRange(0, stringLiteral.textLength), sb)
// val str = sb.toString()
// if (str == "std") {
// val tc = project.zigProjectSettings.state.toolchain ?: return@run
// val env = runBlocking {
// tc.zig.getEnv(project)
// }
// val stdPath = env.stdPath(tc, project) ?: return@run
// val stdFile = stdPath.resolve("std.zig")
// if (!stdFile.exists()) {
// return@run
// }
// }
// }
return@resettableLazy null
}
override fun getReference() = ref.value
override fun subtreeChanged() {
super.subtreeChanged()
ref.reset()
}
override fun rename(name: String): ZigReferenceElement? {
identifier?.replace(ZigElementFactory.createZigIdentifier(project, name) ?: return null) ?: return null
return this
}
override fun getType(): ZigType {
containerDecl?.containerDeclAuto?.let { containerDecl ->
val type = containerDecl.containerDeclType
if (type.keywordStruct != null) {
if (containerDecl.containerMembers?.isNamespace == true) {
return ZigType.Namespace
}
return ZigType.Struct
}
if (type.keywordEnum != null) {
return ZigType.EnumType
}
if (type.keywordUnion != null) {
return ZigType.Union
}
}
errorSetDecl?.let {
return ZigType.ErrorType
}
return ZigType.Generic
}
}

View file

@ -0,0 +1,82 @@
/*
* 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.zig.psi.impl.mixins
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter
import com.falsepattern.zigbrains.zig.psi.ZigGlobalVarDecl
import com.falsepattern.zigbrains.zig.psi.ZigTypedElement
import com.falsepattern.zigbrains.zig.psi.ZigVarDeclExprStatement
import com.falsepattern.zigbrains.zig.util.ZigElementFactory
import com.falsepattern.zigbrains.zig.psi.ZigVarDeclProto
import com.falsepattern.zigbrains.zig.references.ZigType
import com.intellij.extapi.psi.ASTWrapperPsiElement
import com.intellij.lang.ASTNode
import com.intellij.openapi.editor.colors.TextAttributesKey
import com.intellij.psi.PsiElement
import com.intellij.psi.util.startOffset
abstract class ZigVarDeclProtoMixinImpl(node: ASTNode): ASTWrapperPsiElement(node), ZigVarDeclProto {
override fun getName(): String? {
return identifier?.text
}
override fun setName(name: String): PsiElement? {
identifier?.replace(ZigElementFactory.createZigIdentifier(project, name) ?: return null) ?: return null
return this
}
override fun getNameIdentifier(): PsiElement? {
return identifier
}
override fun getTextOffset(): Int {
return identifier?.startOffset ?: 0
}
private fun resolveType(): ZigType {
val parent = parent
when(parent) {
is ZigGlobalVarDecl -> {
parent.expr?.type?.let { return it }
}
is ZigVarDeclExprStatement -> run {
val varDeclList = parent.varDeclProtoList
if (varDeclList.size != 1)
return@run
val varDecl = varDeclList[0]
if (varDecl != this)
return@run
val exprList = parent.exprList
if (exprList.size != 1)
return@run
val expr = exprList[0]
return expr.type
}
}
return ZigType.Generic
}
override val declarationAttribute get() = resolveType().declaration
override val referenceAttribute get() = resolveType().reference
}

View file

@ -0,0 +1,29 @@
/*
* 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.zig.psi.mixins
import com.intellij.psi.PsiElement
interface ZigContainerMembersMixin: PsiElement {
val isNamespace: Boolean
}

View file

@ -0,0 +1,29 @@
/*
* 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.zig.psi.mixins
import com.falsepattern.zigbrains.zig.psi.ZigTypedElement
import com.intellij.psi.PsiElement
interface ZigExprMixin: PsiElement, ZigTypedElement {
}

View file

@ -0,0 +1,29 @@
/*
* 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.zig.psi.mixins
import com.falsepattern.zigbrains.zig.psi.ZigNamedElement
interface ZigFnDeclProtoMixin: ZigNamedElement {
}

View file

@ -0,0 +1,29 @@
/*
* 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.zig.psi.mixins
import com.falsepattern.zigbrains.zig.psi.ZigNamedElement
interface ZigParamDeclMixin: ZigNamedElement {
}

View file

@ -0,0 +1,30 @@
/*
* 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.zig.psi.mixins
import com.falsepattern.zigbrains.zig.psi.ZigReferenceElement
import com.falsepattern.zigbrains.zig.psi.ZigTypedElement
interface ZigPrimaryTypeExprMixin: ZigReferenceElement {
}

View file

@ -0,0 +1,29 @@
/*
* 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.zig.psi.mixins
import com.falsepattern.zigbrains.zig.psi.ZigNamedElement
interface ZigVarDeclProtoMixin: ZigNamedElement {
}

View file

@ -0,0 +1,129 @@
/*
* 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.zig.references
import com.falsepattern.zigbrains.zig.psi.ZigBlock
import com.falsepattern.zigbrains.zig.psi.ZigContainerDeclaration
import com.falsepattern.zigbrains.zig.psi.ZigContainerMembers
import com.falsepattern.zigbrains.zig.psi.ZigDecl
import com.falsepattern.zigbrains.zig.psi.ZigFnDeclProto
import com.falsepattern.zigbrains.zig.psi.ZigParamDecl
import com.falsepattern.zigbrains.zig.psi.ZigReferenceElement
import com.falsepattern.zigbrains.zig.psi.ZigStatement
import com.falsepattern.zigbrains.zig.psi.ZigVarDeclProto
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementResolveResult
import com.intellij.psi.PsiNamedElement
import com.intellij.psi.PsiPolyVariantReferenceBase
import com.intellij.psi.ResolveResult
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.childrenOfType
class ZigReference<T: ZigReferenceElement>(element: T, textRange: TextRange): PsiPolyVariantReferenceBase<T>(element, textRange) {
override fun multiResolve(incompleteCode: Boolean): Array<out ResolveResult> {
element.putUserData(NAME_RANGE_KEY, rangeInElement)
return resolveOrEmpty(element)
}
companion object {
private val NAME_RANGE_KEY = Key.create<TextRange>("ZIG_REF_NAME_RANGE")
private fun resolveOrEmpty(element: PsiElement): Array<out ResolveResult> {
return CachedValuesManager.getCachedValue(element) {
val range = element.getUserData(NAME_RANGE_KEY)
if (range == null)
return@getCachedValue null
val name = element.text.substring(range.startOffset, range.endOffset)
resolveCached(element, name)
} ?: emptyArray()
}
private fun resolveCached(element: PsiElement, name: String): CachedValueProvider.Result<Array<PsiElementResolveResult>>? {
val results = ArrayList<PsiNamedElement>()
val dependencies = ArrayList<Any>()
var prevContainer: PsiElement = element
var container: PsiElement? = prevContainer.parent
while (container != null) {
when (container) {
is ZigBlock -> {
var stmt: PsiElement? = prevContainer
while (stmt != null) {
(stmt as? ZigStatement)?.varDeclExprStatement?.varDeclProtoList?.forEach { varDeclProto ->
if (validate(varDeclProto, name)) {
results.add(varDeclProto)
dependencies.add(varDeclProto)
}
}
stmt = stmt.prevSibling
}
}
is ZigDecl -> {
container.fnDeclProto?.paramDeclList?.paramDeclList?.forEach { paramDecl ->
if (validate(paramDecl, name)) {
results.add(paramDecl)
dependencies.add(paramDecl)
return@forEach
}
}
}
is ZigContainerMembers -> {
container.childrenOfType<ZigContainerDeclaration>().forEach {
val decl = it.decl ?: return@forEach
decl.fnDeclProto?.let { fnProto ->
if (validate(fnProto, name)) {
results.add(fnProto)
dependencies.add(fnProto)
return@forEach
}
}
decl.globalVarDecl?.varDeclProto?.let { varDeclProto ->
if (validate(varDeclProto, name)) {
results.add(varDeclProto)
dependencies.add(varDeclProto)
return@forEach
}
}
}
}
}
prevContainer = container
container = container.parent
}
if (results.isEmpty())
return null
val res = results.map { PsiElementResolveResult(it) }.toTypedArray()
return CachedValueProvider.Result(res, *dependencies.toArray())
}
private fun validate(paramDecl: ZigParamDecl, name: String) = paramDecl.identifier?.text?.equals(name) == true
private fun validate(fnProto: ZigFnDeclProto, name: String) = fnProto.identifier?.text?.equals(name) == true
private fun validate(varDeclProto: ZigVarDeclProto, name: String) = varDeclProto.identifier?.text?.equals(name) == true
}
override fun handleElementRename(newElementName: String): PsiElement? {
return element.rename(newElementName)
}
}

View file

@ -0,0 +1,37 @@
/*
* 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.zig.references
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter
import com.intellij.openapi.editor.colors.TextAttributesKey
enum class ZigType(val declaration: TextAttributesKey, val reference: TextAttributesKey) {
Namespace(ZigSyntaxHighlighter.NAMESPACE_DECL, ZigSyntaxHighlighter.NAMESPACE_REF),
ErrorType(ZigSyntaxHighlighter.TYPE_DECL, ZigSyntaxHighlighter.TYPE_REF),
ErrorTag(ZigSyntaxHighlighter.ERROR_TAG_DECL, ZigSyntaxHighlighter.ERROR_TAG_REF),
EnumType(ZigSyntaxHighlighter.ENUM_DECL, ZigSyntaxHighlighter.ENUM_REF),
EnumMember(ZigSyntaxHighlighter.ENUM_MEMBER_DECL, ZigSyntaxHighlighter.ENUM_MEMBER_REF),
Struct(ZigSyntaxHighlighter.STRUCT_DECL, ZigSyntaxHighlighter.STRUCT_REF),
Union(ZigSyntaxHighlighter.STRUCT_DECL, ZigSyntaxHighlighter.STRUCT_REF),
Generic(ZigSyntaxHighlighter.VARIABLE_DECL, ZigSyntaxHighlighter.VARIABLE_REF),
}

View file

@ -0,0 +1,54 @@
/*
* 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.zig.renaming
import com.falsepattern.zigbrains.zig.psi.ZigFnDeclProto
import com.falsepattern.zigbrains.zig.psi.ZigGlobalVarDecl
import com.falsepattern.zigbrains.zig.psi.ZigVarDeclExprStatement
import com.falsepattern.zigbrains.zig.psi.ZigVarDeclProto
import com.intellij.psi.ElementDescriptionLocation
import com.intellij.psi.ElementDescriptionProvider
import com.intellij.psi.PsiElement
import com.intellij.usageView.UsageViewTypeLocation
class ZigElementDescriptionProvider: ElementDescriptionProvider {
override fun getElementDescription(
element: PsiElement,
location: ElementDescriptionLocation
): String? {
if (location != UsageViewTypeLocation.INSTANCE)
return null
return when(element) {
is ZigVarDeclProto -> {
val type = if (element.keywordConst != null) "constant" else "variable"
when(element.parent) {
is ZigGlobalVarDecl -> "global $type"
is ZigVarDeclExprStatement -> "local $type"
else -> type
}
}
is ZigFnDeclProto -> "function"
else -> null
}
}
}

View file

@ -0,0 +1,48 @@
/*
* 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.zig.renaming
import com.falsepattern.zigbrains.zig.lexer.ZigLexerAdapter
import com.falsepattern.zigbrains.zig.psi.ZigTypes
import com.intellij.lang.refactoring.NamesValidator
import com.intellij.openapi.project.Project
import com.intellij.psi.tree.IElementType
class ZigNamesValidator: NamesValidator {
override fun isKeyword(name: String, project: Project?) = matches(name) { token ->
keywords.contains(token)
}
override fun isIdentifier(name: String, project: Project?) = matches(name) { token ->
token == ZigTypes.IDENTIFIER
}
private inline fun matches(name: String, matcher: (IElementType) -> Boolean): Boolean {
val lexer = ZigLexerAdapter()
lexer.start(name)
val token = lexer.tokenType
return token != null && lexer.tokenEnd == lexer.bufferEnd && matcher(token)
}
val keywords = ZigTypes::class.java.declaredFields.mapNotNullTo(HashSet<IElementType>()) { if (it.name.startsWith("KEYWORD_")) it.get(null) as IElementType else null }
}

View file

@ -0,0 +1,36 @@
/*
* 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.zig.renaming
import com.falsepattern.zigbrains.zig.psi.ZigFnDeclProto
import com.falsepattern.zigbrains.zig.psi.ZigVarDeclProto
import com.intellij.psi.PsiElement
import com.intellij.refactoring.rename.RenamePsiElementProcessorBase
class ZigRenamePsiElementProcessor: RenamePsiElementProcessorBase() {
override fun canProcessElement(element: PsiElement) = when(element) {
is ZigVarDeclProto -> true
is ZigFnDeclProto -> true
else -> false
}
}

View file

@ -0,0 +1,46 @@
/*
* 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.zig.util
import com.falsepattern.zigbrains.zig.ZigFileType
import com.falsepattern.zigbrains.zig.psi.ZigTypes
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiFileFactory
import com.intellij.psi.impl.source.tree.LeafElement
object ZigElementFactory {
private val LOG = Logger.getInstance(ZigElementFactory::class.java)
fun createZigFile(project: Project, text: CharSequence): PsiFile {
return PsiFileFactory.getInstance(project).createFileFromText("a.zig", ZigFileType, text)
}
fun createZigIdentifier(project: Project, name: String): PsiElement? {
val file = createZigFile(project, "const $name = undefined;")
val identifier = file.findElementAt("const ".length) ?: return null
LOG.assertTrue(identifier is LeafElement && identifier.elementType == ZigTypes.IDENTIFIER, name)
return identifier
}
}

View file

@ -2,6 +2,7 @@
<resource-bundle>zigbrains.Bundle</resource-bundle> <resource-bundle>zigbrains.Bundle</resource-bundle>
<!-- region Zig --> <!-- region Zig -->
<extensions defaultExtensionNs="com.intellij"> <extensions defaultExtensionNs="com.intellij">
<!-- core -->
<colorSettingsPage <colorSettingsPage
implementation="com.falsepattern.zigbrains.zig.highlighter.ZigColorSettingsPage"/> implementation="com.falsepattern.zigbrains.zig.highlighter.ZigColorSettingsPage"/>
<fileType <fileType
@ -28,6 +29,14 @@
<lang.syntaxHighlighterFactory <lang.syntaxHighlighterFactory
language="Zig" language="Zig"
implementationClass="com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighterFactory"/> implementationClass="com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighterFactory"/>
<lang.namesValidator
language="Zig"
implementationClass="com.falsepattern.zigbrains.zig.renaming.ZigNamesValidator"/>
<elementDescriptionProvider
implementation="com.falsepattern.zigbrains.zig.renaming.ZigElementDescriptionProvider"/>
<annotator
language="Zig"
implementationClass="com.falsepattern.zigbrains.zig.highlighter.ZigSemanticHighlighter"/>
<!-- String manipulation --> <!-- String manipulation -->
<enterHandlerDelegate <enterHandlerDelegate
@ -184,13 +193,12 @@
</extensions> </extensions>
<extensions defaultExtensionNs="com.falsepattern.zigbrains"> <extensions defaultExtensionNs="com.falsepattern.zigbrains">
<!--suppress PluginXmlValidity -->
<zlsConfigProvider
implementation="com.falsepattern.zigbrains.project.toolchain.ToolchainZLSConfigProvider"
/>
<toolchainProvider <toolchainProvider
implementation="com.falsepattern.zigbrains.project.toolchain.LocalZigToolchainProvider" implementation="com.falsepattern.zigbrains.project.toolchain.LocalZigToolchainProvider"
/> />
<projectConfigProvider
implementation="com.falsepattern.zigbrains.project.settings.ZigCoreProjectConfigurationProvider"
/>
</extensions> </extensions>
<actions resource-bundle="zigbrains.ActionsBundle"> <actions resource-bundle="zigbrains.ActionsBundle">

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,241 @@
//! Contains shared code between ZLS and it's custom build runner
const std = @import("std");
const builtin = @import("builtin");
const native_endian = builtin.target.cpu.arch.endian();
const need_bswap = native_endian != .little;
pub const BuildConfig = struct {
deps_build_roots: []DepsBuildRoots,
packages: []Package,
include_dirs: []const []const u8,
top_level_steps: []const []const u8,
available_options: std.json.ArrayHashMap(AvailableOption),
c_macros: []const []const u8 = &.{},
pub const DepsBuildRoots = Package;
pub const Package = struct {
name: []const u8,
path: []const u8,
};
pub const AvailableOption = std.meta.FieldType(std.meta.FieldType(std.Build, .available_options_map).KV, .value);
};
pub const Transport = struct {
in: std.fs.File,
out: std.fs.File,
poller: std.io.Poller(StreamEnum),
const StreamEnum = enum { in };
pub const Header = extern struct {
tag: u32,
/// Size of the body only; does not include this Header.
bytes_len: u32,
};
pub const Options = struct {
gpa: std.mem.Allocator,
in: std.fs.File,
out: std.fs.File,
};
pub fn init(options: Options) Transport {
return .{
.in = options.in,
.out = options.out,
.poller = std.io.poll(options.gpa, StreamEnum, .{ .in = options.in }),
};
}
pub fn deinit(transport: *Transport) void {
transport.poller.deinit();
transport.* = undefined;
}
pub fn receiveMessage(transport: *Transport, timeout_ns: ?u64) !Header {
const fifo = transport.poller.fifo(.in);
poll: while (true) {
while (fifo.readableLength() < @sizeOf(Header)) {
if (!(if (timeout_ns) |timeout| try transport.poller.pollTimeout(timeout) else try transport.poller.poll())) break :poll;
}
const header = fifo.reader().readStructEndian(Header, .little) catch unreachable;
while (fifo.readableLength() < header.bytes_len) {
if (!(if (timeout_ns) |timeout| try transport.poller.pollTimeout(timeout) else try transport.poller.poll())) break :poll;
}
return header;
}
return error.EndOfStream;
}
pub fn reader(transport: *Transport) std.io.PollFifo.Reader {
return transport.poller.fifo(.in).reader();
}
pub fn discard(transport: *Transport, bytes: usize) void {
transport.poller.fifo(.in).discard(bytes);
}
pub fn receiveBytes(
transport: *Transport,
allocator: std.mem.Allocator,
len: usize,
) (std.mem.Allocator.Error || std.fs.File.ReadError || error{EndOfStream})![]u8 {
return try transport.receiveSlice(allocator, u8, len);
}
pub fn receiveSlice(
transport: *Transport,
allocator: std.mem.Allocator,
comptime T: type,
len: usize,
) (std.mem.Allocator.Error || std.fs.File.ReadError || error{EndOfStream})![]T {
const bytes = try allocator.alignedAlloc(u8, @alignOf(T), len * @sizeOf(T));
errdefer allocator.free(bytes);
const amt = try transport.reader().readAll(bytes);
if (amt != len * @sizeOf(T)) return error.EndOfStream;
const result = std.mem.bytesAsSlice(T, bytes);
std.debug.assert(result.len == len);
if (need_bswap) {
for (result) |*item| {
item.* = @byteSwap(item.*);
}
}
return result;
}
pub fn serveMessage(
client: *const Transport,
header: Header,
bufs: []const []const u8,
) std.fs.File.WriteError!void {
std.debug.assert(bufs.len < 10);
var iovecs: [10]std.posix.iovec_const = undefined;
var header_le = header;
if (need_bswap) std.mem.byteSwapAllFields(Header, &header_le);
const header_bytes = std.mem.asBytes(&header_le);
iovecs[0] = .{ .base = header_bytes.ptr, .len = header_bytes.len };
for (bufs, iovecs[1 .. bufs.len + 1]) |buf, *iovec| {
iovec.* = .{
.base = buf.ptr,
.len = buf.len,
};
}
try client.out.writevAll(iovecs[0 .. bufs.len + 1]);
}
};
pub const ServerToClient = struct {
pub const Tag = enum(u32) {
/// Body is an ErrorBundle.
watch_error_bundle,
_,
};
/// Trailing:
/// * extra: [extra_len]u32,
/// * string_bytes: [string_bytes_len]u8,
/// See `std.zig.ErrorBundle`.
pub const ErrorBundle = extern struct {
step_id: u32,
cycle: u32,
extra_len: u32,
string_bytes_len: u32,
};
};
pub const BuildOnSaveSupport = union(enum) {
supported,
invalid_linux_kernel_version: if (builtin.os.tag == .linux) std.meta.FieldType(std.posix.utsname, .release) else noreturn,
unsupported_linux_kernel_version: if (builtin.os.tag == .linux) std.SemanticVersion else noreturn,
unsupported_zig_version: if (@TypeOf(minimum_zig_version) != void) void else noreturn,
unsupported_os: if (@TypeOf(minimum_zig_version) == void) void else noreturn,
const linux_support_version = std.SemanticVersion.parse("0.14.0-dev.283+1d20ff11d") catch unreachable;
const windows_support_version = std.SemanticVersion.parse("0.14.0-dev.625+2de0e2eca") catch unreachable;
const kqueue_support_version = std.SemanticVersion.parse("0.14.0-dev.2046+b8795b4d0") catch unreachable;
// We can't rely on `std.Build.Watch.have_impl` because we need to
// check the runtime Zig version instead of Zig version that ZLS
// has been built with.
pub const minimum_zig_version = switch (builtin.os.tag) {
.linux => linux_support_version,
.windows => windows_support_version,
.dragonfly,
.freebsd,
.netbsd,
.openbsd,
.ios,
.macos,
.tvos,
.visionos,
.watchos,
.haiku,
=> kqueue_support_version,
else => {},
};
/// std.build.Watch requires `AT_HANDLE_FID` which is Linux 6.5+
/// https://github.com/ziglang/zig/issues/20720
pub const minimum_linux_version: std.SemanticVersion = .{ .major = 6, .minor = 5, .patch = 0 };
/// Returns true if is comptime known that build on save is supported.
pub inline fn isSupportedComptime() bool {
if (!std.process.can_spawn) return false;
if (builtin.single_threaded) return false;
return true;
}
pub fn isSupportedRuntime(runtime_zig_version: std.SemanticVersion) BuildOnSaveSupport {
comptime std.debug.assert(isSupportedComptime());
if (builtin.os.tag == .linux) blk: {
const utsname = std.posix.uname();
const unparsed_version = std.mem.sliceTo(&utsname.release, 0);
const version = parseUnameKernelVersion(unparsed_version) catch
return .{ .invalid_linux_kernel_version = utsname.release };
if (version.order(minimum_linux_version) != .lt) break :blk;
std.debug.assert(version.build == null and version.pre == null); // Otherwise, returning the `std.SemanticVersion` would be unsafe
return .{
.unsupported_linux_kernel_version = version,
};
}
if (@TypeOf(minimum_zig_version) == void) {
return .unsupported_os;
}
if (runtime_zig_version.order(minimum_zig_version) == .lt) {
return .unsupported_zig_version;
}
return .supported;
}
};
/// Parses a Linux Kernel Version. The result will ignore pre-release and build metadata.
fn parseUnameKernelVersion(kernel_version: []const u8) !std.SemanticVersion {
const extra_index = std.mem.indexOfAny(u8, kernel_version, "-+");
const required = kernel_version[0..(extra_index orelse kernel_version.len)];
var it = std.mem.splitScalar(u8, required, '.');
return .{
.major = try std.fmt.parseUnsigned(usize, it.next() orelse return error.InvalidVersion, 10),
.minor = try std.fmt.parseUnsigned(usize, it.next() orelse return error.InvalidVersion, 10),
.patch = try std.fmt.parseUnsigned(usize, it.next() orelse return error.InvalidVersion, 10),
};
}
test parseUnameKernelVersion {
try std.testing.expectFmt("5.17.0", "{}", .{try parseUnameKernelVersion("5.17.0")});
try std.testing.expectFmt("6.12.9", "{}", .{try parseUnameKernelVersion("6.12.9-rc7")});
try std.testing.expectFmt("6.6.71", "{}", .{try parseUnameKernelVersion("6.6.71-42-generic")});
try std.testing.expectFmt("5.15.167", "{}", .{try parseUnameKernelVersion("5.15.167.4-microsoft-standard-WSL2")}); // WSL2
try std.testing.expectFmt("4.4.0", "{}", .{try parseUnameKernelVersion("4.4.0-20241-Microsoft")}); // WSL1
try std.testing.expectError(error.InvalidCharacter, parseUnameKernelVersion(""));
try std.testing.expectError(error.InvalidVersion, parseUnameKernelVersion("5"));
try std.testing.expectError(error.InvalidVersion, parseUnameKernelVersion("5.5"));
}

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) ZLS contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,5 @@
This folder contains build runners taken from the Zig Language Server.
The build runners are based on the following ZLS commits
0.14.0: https://github.com/zigtools/zls/tree/23f57730a20f7eec5a36a848bedb226ace2b56c6/src/build_runner

17
lsp/build.gradle.kts Normal file
View file

@ -0,0 +1,17 @@
import org.jetbrains.grammarkit.tasks.GenerateLexerTask
import org.jetbrains.grammarkit.tasks.GenerateParserTask
import org.jetbrains.intellij.platform.gradle.IntelliJPlatformType
val lsp4ijVersion: String by project
val lsp4jVersion: String by project
val ideaCommunityVersion: String by project
val useInstaller = property("useInstaller").toString().toBoolean()
dependencies {
intellijPlatform {
create(IntelliJPlatformType.IntellijIdeaCommunity, ideaCommunityVersion, useInstaller = useInstaller)
}
compileOnly("com.redhat.devtools.intellij:lsp4ij:$lsp4ijVersion")
compileOnly("org.eclipse.lsp4j:org.eclipse.lsp4j:$lsp4jVersion")
implementation(project(":core"))
}

View file

@ -0,0 +1,46 @@
/*
* 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.lsp
import com.falsepattern.zigbrains.lsp.settings.ZLSSettingsConfigurable
import com.falsepattern.zigbrains.lsp.settings.ZLSSettingsPanel
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
import com.falsepattern.zigbrains.shared.SubConfigurable
import com.intellij.openapi.project.Project
class ZLSProjectConfigurationProvider: ZigProjectConfigurationProvider {
override fun handleMainConfigChanged(project: Project) {
startLSP(project, true)
}
override fun createConfigurable(project: Project): SubConfigurable {
return ZLSSettingsConfigurable(project)
}
override fun createNewProjectSettingsPanel(): ZigProjectConfigurationProvider.SettingsPanel {
return ZLSSettingsPanel(null)
}
override val priority: Int
get() = 1000
}

View file

@ -0,0 +1,47 @@
/*
* 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.lsp
import com.falsepattern.zigbrains.direnv.DirenvCmd
import com.falsepattern.zigbrains.direnv.emptyEnv
import com.falsepattern.zigbrains.direnv.getDirenv
import com.falsepattern.zigbrains.lsp.settings.zlsSettings
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.ProjectActivity
import kotlin.io.path.pathString
class ZLSStartup: ProjectActivity {
override suspend fun execute(project: Project) {
val zlsState = project.zlsSettings.state
if (zlsState.zlsPath.isBlank()) {
val env = if (DirenvCmd.direnvInstalled() && !project.isDefault && zlsState.direnv)
project.getDirenv()
else
emptyEnv
env.findExecutableOnPATH("zls")?.let {
zlsState.zlsPath = it.pathString
project.zlsSettings.state = zlsState
}
}
}
}

View file

@ -22,6 +22,8 @@
package com.falsepattern.zigbrains.lsp.settings package com.falsepattern.zigbrains.lsp.settings
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
import com.intellij.openapi.project.Project
import org.jetbrains.annotations.NonNls import org.jetbrains.annotations.NonNls
data class ZLSSettings( data class ZLSSettings(
@ -36,4 +38,8 @@ data class ZLSSettings(
var comptimeInterpreter: Boolean = false, var comptimeInterpreter: Boolean = false,
var inlayHints: Boolean = true, var inlayHints: Boolean = true,
var inlayHintsCompact: Boolean = true var inlayHintsCompact: Boolean = true
) ): ZigProjectConfigurationProvider.Settings {
override fun apply(project: Project) {
project.zlsSettings.loadState(this)
}
}

View file

@ -27,6 +27,7 @@ import com.falsepattern.zigbrains.direnv.Env
import com.falsepattern.zigbrains.direnv.emptyEnv import com.falsepattern.zigbrains.direnv.emptyEnv
import com.falsepattern.zigbrains.direnv.getDirenv import com.falsepattern.zigbrains.direnv.getDirenv
import com.falsepattern.zigbrains.lsp.ZLSBundle import com.falsepattern.zigbrains.lsp.ZLSBundle
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
import com.falsepattern.zigbrains.shared.coroutine.launchWithEDT import com.falsepattern.zigbrains.shared.coroutine.launchWithEDT
import com.falsepattern.zigbrains.shared.zigCoroutineScope import com.falsepattern.zigbrains.shared.zigCoroutineScope
import com.intellij.openapi.Disposable import com.intellij.openapi.Disposable
@ -43,7 +44,7 @@ import com.intellij.ui.dsl.builder.AlignX
import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.Panel
import kotlin.io.path.pathString import kotlin.io.path.pathString
class ZLSSettingsPanel(private val project: Project?) : Disposable { class ZLSSettingsPanel(private val project: Project?) : ZigProjectConfigurationProvider.SettingsPanel {
private val zlsPath = textFieldWithBrowseButton( private val zlsPath = textFieldWithBrowseButton(
project, project,
FileChooserDescriptorFactory.createSingleFileDescriptor().withTitle(ZLSBundle.message("settings.zls-path.browse.title")), FileChooserDescriptorFactory.createSingleFileDescriptor().withTitle(ZLSBundle.message("settings.zls-path.browse.title")),
@ -66,7 +67,7 @@ class ZLSSettingsPanel(private val project: Project?) : Disposable {
dispatchAutodetect(true) dispatchAutodetect(true)
} } } }
fun attach(panel: Panel) = with(panel) { override fun attach(panel: Panel) = with(panel) {
group(ZLSBundle.message("settings.group.title")) { group(ZLSBundle.message("settings.group.title")) {
row(ZLSBundle.message("settings.zls-path.label")) { row(ZLSBundle.message("settings.zls-path.label")) {
cell(zlsPath).resizableColumn().align(AlignX.FILL) cell(zlsPath).resizableColumn().align(AlignX.FILL)
@ -89,7 +90,7 @@ class ZLSSettingsPanel(private val project: Project?) : Disposable {
dispatchAutodetect(false) dispatchAutodetect(false)
} }
var data override var data
get() = ZLSSettings( get() = ZLSSettings(
direnv.isSelected, direnv.isSelected,
zlsPath.text, zlsPath.text,

View file

@ -32,6 +32,7 @@ import com.intellij.openapi.util.UserDataHolderBase
import com.intellij.openapi.util.io.toNioPathOrNull import com.intellij.openapi.util.io.toNioPathOrNull
import kotlin.io.path.pathString import kotlin.io.path.pathString
class ToolchainZLSConfigProvider: SuspendingZLSConfigProvider { class ToolchainZLSConfigProvider: SuspendingZLSConfigProvider {
override suspend fun getEnvironment(project: Project, previous: ZLSConfig): ZLSConfig { override suspend fun getEnvironment(project: Project, previous: ZLSConfig): ZLSConfig {
val svc = project.zigProjectSettings val svc = project.zigProjectSettings

View file

@ -1,3 +1,25 @@
<!--
~ 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/>.
-->
<idea-plugin> <idea-plugin>
<resource-bundle>zigbrains.lsp.Bundle</resource-bundle> <resource-bundle>zigbrains.lsp.Bundle</resource-bundle>
<extensions defaultExtensionNs="com.intellij"> <extensions defaultExtensionNs="com.intellij">
@ -22,12 +44,20 @@
bundle="zigbrains.lsp.Bundle" bundle="zigbrains.lsp.Bundle"
key="notification.group.zigbrains-lsp" key="notification.group.zigbrains-lsp"
/> />
<postStartupActivity
implementation="com.falsepattern.zigbrains.lsp.ZLSStartup"/>
</extensions> </extensions>
<extensions defaultExtensionNs="com.falsepattern.zigbrains"> <extensions defaultExtensionNs="com.falsepattern.zigbrains">
<zlsConfigProvider <zlsConfigProvider
implementation="com.falsepattern.zigbrains.lsp.settings.ZLSSettingsConfigProvider" implementation="com.falsepattern.zigbrains.lsp.settings.ZLSSettingsConfigProvider"
/> />
<zlsConfigProvider
implementation="com.falsepattern.zigbrains.project.toolchain.ToolchainZLSConfigProvider"
/>
<projectConfigProvider
implementation="com.falsepattern.zigbrains.lsp.ZLSProjectConfigurationProvider"
/>
</extensions> </extensions>
<extensions defaultExtensionNs="com.redhat.devtools.lsp4ij"> <extensions defaultExtensionNs="com.redhat.devtools.lsp4ij">

View file

@ -4,4 +4,5 @@ plugins {
rootProject.name = "ZigBrains" rootProject.name = "ZigBrains"
include("core") include("core")
include("lsp")
include("cidr") include("cidr")

View file

@ -4,7 +4,7 @@
<vendor>FalsePattern</vendor> <vendor>FalsePattern</vendor>
<depends config-file="zigbrains-core.xml">com.intellij.modules.platform</depends> <depends config-file="zigbrains-core.xml">com.intellij.modules.platform</depends>
<depends config-file="zigbrains-lsp.xml">com.redhat.devtools.lsp4ij</depends> <depends config-file="zigbrains-lsp.xml" optional="true">com.redhat.devtools.lsp4ij</depends>
<depends config-file="zigbrains-debugger.xml" optional="true">com.intellij.nativeDebug</depends> <depends config-file="zigbrains-debugger.xml" optional="true">com.intellij.nativeDebug</depends>
<depends config-file="zigbrains-cidr.xml" optional="true">com.intellij.cidr.base</depends> <depends config-file="zigbrains-cidr.xml" optional="true">com.intellij.cidr.base</depends>
<depends config-file="zigbrains-clion.xml" optional="true">com.intellij.clion</depends> <depends config-file="zigbrains-clion.xml" optional="true">com.intellij.clion</depends>
@ -17,6 +17,11 @@
dynamic="true" dynamic="true"
name="zlsConfigProvider" name="zlsConfigProvider"
/> />
<extensionPoint
interface="com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider"
dynamic="true"
name="projectConfigProvider"
/>
<extensionPoint <extensionPoint
interface="com.falsepattern.zigbrains.project.toolchain.ZigToolchainProvider" interface="com.falsepattern.zigbrains.project.toolchain.ZigToolchainProvider"
dynamic="true" dynamic="true"