feat: very basic reference resolving/highlighting

This commit is contained in:
FalsePattern 2025-02-21 20:06:41 +01:00
parent 14fc1b912a
commit 25e8e04836
Signed by: falsepattern
GPG key ID: E930CDEC50C50E23
30 changed files with 2847 additions and 6 deletions

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

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

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

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