1
Fork 0

Merge branch 'main' into parser-tests-with-pytest

This commit is contained in:
automaticp 2023-11-01 21:50:00 +06:00 committed by GitHub
commit add32471d8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 338 additions and 12 deletions

View file

@ -156,14 +156,16 @@ fn builtinCompletions(arena: std.mem.Allocator, spec: *const Spec) ![]lsp.Comple
}
for (spec.variables) |variable| {
var signature = std.ArrayList(u8).init(arena);
try signature.writer().print("{}", .{variable.modifiers});
try signature.appendSlice(" ");
try signature.appendSlice(variable.type);
var anonymous_signature = std.ArrayList(u8).init(arena);
try writeVariableSignature(variable, anonymous_signature.writer(), .{ .names = false });
var named_signature = std.ArrayList(u8).init(arena);
try writeVariableSignature(variable, named_signature.writer(), .{ .names = true });
try completions.append(.{
.label = variable.name,
.labelDetails = .{ .detail = signature.items },
.labelDetails = .{ .detail = anonymous_signature.items },
.detail = named_signature.items,
.kind = .variable,
.documentation = try itemDocumentation(arena, variable),
});
@ -207,6 +209,31 @@ fn itemDocumentation(arena: std.mem.Allocator, item: anytype) !lsp.MarkupContent
return .{ .kind = .markdown, .value = try documentation.toOwnedSlice() };
}
fn writeVariableSignature(
variable: Spec.Variable,
writer: anytype,
options: struct { names: bool },
) !void {
if (!std.meta.eql(variable.modifiers, .{ .in = true })) {
try writer.print("{}", .{variable.modifiers});
try writer.writeAll(" ");
}
try writer.writeAll(variable.type);
if (options.names) {
try writer.writeAll(" ");
try writer.writeAll(variable.name);
if (variable.default_value) |value| {
try writer.writeAll(" = ");
try writer.writeAll(value);
}
try writer.writeAll(";");
}
}
fn writeFunctionSignature(
function: Spec.Function,
writer: anytype,

View file

@ -314,6 +314,7 @@ fn formatNode(tree: parse.Tree, current: usize, writer: anytype) !void {
.array,
.array_specifier,
.selection,
.parenthized,
=> {
const children = tree.children(current);
for (children.start..children.end) |child| {
@ -321,6 +322,14 @@ fn formatNode(tree: parse.Tree, current: usize, writer: anytype) !void {
}
},
.conditional => {
const children = tree.children(current);
for (children.start..children.end) |child| {
if (child != children.start) writer.writeSpace();
try formatNode(tree, child, writer);
}
},
// emit tokens separated by spaces
inline else => |tag| {
const operators = comptime parse.assignment_operators.unionWith(parse.infix_operators);
@ -362,7 +371,6 @@ fn needsLeadingSpace(tag: parse.Tag) bool {
.@",",
.parameter_list,
.array,
.array_specifier,
=> false,
else => true,
};
@ -573,6 +581,42 @@ test "format field selection" {
);
}
test "format ternary condition" {
try expectIsFormatted(
\\void main() {
\\ int foo = a ? b : c;
\\}
\\
);
}
test "format parenthized" {
try expectIsFormatted(
\\void main() {
\\ int foo = (1 + 1);
\\}
\\
);
}
test "format assign array" {
try expectIsFormatted(
\\void main() {
\\ int foo = bar[123];
\\}
\\
);
}
test "format array declaration" {
try expectIsFormatted(
\\void main() {
\\ int[2] foo[123];
\\}
\\
);
}
fn expectIsFormatted(source: []const u8) !void {
try expectFormat(source, source);
}

View file

@ -18,6 +18,8 @@
**Requirements:** `python 3.10` and `requirements.txt`.
*The `glsl_analyzer` version you want to test must be available in your PATH.*
You can run the tests by directly invoking `pytest`:
```sh

View file

@ -0,0 +1,77 @@
#version 430 core
// Simple name resolution for global and local scopes.
layout(std140, binding = 0) uniform UBuffer0 {
mat4 field0;
};
layout(std140, binding = 1) uniform UBuffer1 {
vec4 field0;
} ubuffer1;
layout(std430, binding = 0) buffer SSBuffer0 {
mat3 field1[];
};
layout(std430, binding = 1) buffer SSBuffer1 {
mat2 field1[];
} ssbuffer1;
layout(std430, binding = 2) buffer SSBuffer2 {
vec3 field0[];
} ssbuffer2;
// Nested aggregates.
struct AAA {
int field0;
};
layout(std430, binding = 3) buffer SSBuffer3 {
AAA aaa[][3];
} ssbuffer3;
// Nested with repeating names.
struct BBB {
uint ssbuffer4;
};
struct CCC {
BBB ssbuffer4;
};
layout(std430, binding = 4) buffer SSBuffer4 {
CCC ssbuffer4;
} ssbuffer4;
// Arrays of uniform buffers.
layout(std140, binding = 2) uniform UBuffer2 {
mat2 field0;
} ubuffer2[3];
// Qualifiers.
layout(std430, binding = 5) restrict writeonly coherent buffer SSBuffer5 {
bool field0;
} ssbuffer5;
void main() {
field0; // <mat4>
field1; // <mat3[]>
// <layout(std140, binding = 1) uniform UBuffer1 { vec4 field0; }> . <vec4>
ubuffer1.field0;
// <layout(std430, binding = 1) buffer SSBuffer1 { mat2 field1[]; }> . <mat2[]>
ssbuffer1.field1;
// <layout(std430, binding = 2) buffer SSBuffer2 { vec3 field0[]; }> . <vec3[]>
ssbuffer2.field0;
// <layout(std430, binding = 3) buffer SSBuffer3 { AAA aaa[][3]; }> . <AAA[][3]> . <int>
ssbuffer3.aaa[0][0].field0;
// <layout(std430, binding = 4) buffer SSBuffer4 { CCC ssbuffer4; }> . <CCC> . <BBB> . <uint>
ssbuffer4.ssbuffer4.ssbuffer4.ssbuffer4;
// <layout(std140, binding = 2) uniform UBuffer2 { mat2 field0; }[3]> . <mat2>
ubuffer2[0].field0;
// <layout(std430, binding = 5) restrict writeonly coherent buffer SSBuffer5 { bool field0; }> . <bool>
ssbuffer5.field0;
}

View file

@ -0,0 +1,23 @@
#version 430 core
int value = 0;
const int value_ = 1;
void foo() {
// Past-the-end hovers.
// Should only trigger when cursor is adjacent to the token.
value;
value ;
value
;
value/* w */;
value//
;
value;value_;
}

View file

@ -0,0 +1,29 @@
#version 430 core
uniform layout(rgba8, binding = 0) image2D image2d_0;
uniform layout(rgba16i, binding = 1) iimageCubeArray iimagecubearray_1;
uniform layout(rgba16_snorm, binding = 2) restrict writeonly coherent image2DMSArray image2dmsarray_2;
uniform sampler2D sampler2d_0;
uniform isamplerCubeArray isamplercubearray_1;
uniform sampler2DMSArray sampler2dmsarray_2;
void main() {
// <uniform layout(rgba8, binding = 0) image2D>
image2d_0;
// <uniform layout(rgba16i, binding = 1) iimageCubeArray>
iimagecubearray_1;
// <uniform layout(rgba16_snorm, binding = 2) restrict writeonly coherent image2DMSArray>
image2dmsarray_2;
// <unifrom sampler2D>
sampler2d_0;
// <uniform isamplerCubeArray>
isamplercubearray_1;
// <uniform sampler2DMSArray>
sampler2dmsarray_2;
}

View file

@ -0,0 +1,40 @@
#version 430 core
int iarr[3] = { 1, 2, 3 };
int iarr_implicit[];
int iarr_nested[2][3];
const int ciarr[3] = { 1, 2, 3 };
const int ciarr_nested_implicit[][3] = { { 1, 2, 3 }, { 4, 5, 6 } };
struct Elem {
int field1;
};
const Elem elements[] = { {1}, {2}, {3} };
void main() {
ciarr;
const int ciarr[] = { 1, 2, 3 };
ciarr;
iarr_implicit;
iarr_nested[0] = ciarr;
ciarr_nested_implicit;
}
void foo(const Elem elements[3]) {
elements;
// Arrays have no fields.
// v
elements. ;
elements[0].field1;
}

View file

@ -13,7 +13,6 @@ base_directory = pathlib.Path(__file__).parent.resolve()
files_to_test = [
# TODO: Arrays, UBOs and SSBOs, images/samplers.
FileToTest(
path="glsl-samples/well-formed/basic.vert",
hover_test_args=(
@ -188,5 +187,93 @@ files_to_test = [
(29, 8, None),
)
),
FileToTest(
path="hover-and-completion/hover_positions.frag",
hover_test_args=(
# Past-the-end hover should only trigger when cursor is adjacent to the token.
( 9, 10, "int"),
( 9, 11, None),
# Past-the-end break by space and newline.
(11, 11, None),
(13, 1, None),
# Past-the-end break by comments.
(15, 17, None),
(17, 1, None),
# Nearby identifiers.
(19, 10, "int"),
(19, 11, "const int"),
)
),
FileToTest(
path="hover-and-completion/static_arrays.frag",
hover_test_args=(
# Shadowing.
(18, 5, "const int[3]"),
(20, 5, "const int[]"),
# Implicit size.
(22, 5, "int[]"),
# Nested arrays.
(24, 5, "int[2][3]"),
# Nested with partially implicit size.
(26, 5, "const int[][3]"),
# Arrays of structs.
(32, 5, "const Elem[3]"),
(38, 5, "const Elem[3]"),
(38, 17, "int"),
),
completion_test_args=(
# Arrays of structs.
ExpectFail(36, 14, None, \
reason="Bug. Arrays have no fields."),
(38, 17, ("field1",)),
),
),
FileToTest(
path="hover-and-completion/buffers.frag",
hover_test_args=(
# Global buffer fields.
(60, 5, "mat4"),
(61, 5, "mat3[]"),
# Scoped buffer fields.
(63, 5, "layout(std140, binding = 1) uniform UBuffer1 { vec4 field0; }"),
(63, 14, "vec4"),
(65, 5, "layout(std430, binding = 1) buffer SSBuffer1 { mat2 field1[]; }"),
(65, 15, "mat2[]"),
(67, 5, "layout(std430, binding = 2) buffer SSBuffer2 { vec3 field0[]; }"),
(67, 15, "vec3[]"),
# Nested fields.
(69, 5, "layout(std430, binding = 3) buffer SSBuffer3 { AAA aaa[][3]; }"),
(69, 15, "AAA[][3]"),
(69, 25, "int"),
# Nested structs with repeating names.
(71, 5, "layout(std430, binding = 4) buffer SSBuffer4 { CCC ssbuffer4; }"),
(71, 15, "CCC"),
(71, 25, "BBB"),
(71, 35, "uint"),
# Array of uniform buffers.
(73, 5, "layout(std140, binding = 2) uniform UBuffer2 { mat2 field0; }[3]"),
(73, 17, "mat2"),
# Qualifiers.
(75, 5, "layout(std430, binding = 5) restrict writeonly coherent buffer SSBuffer5 { bool field0; }"),
(75, 15, "bool"),
),
completion_test_args=(
# Nested structs with repeating names.
(71, 15, ("ssbuffer4",)),
(71, 25, ("ssbuffer4",)),
(71, 35, ("ssbuffer4",)),
),
),
FileToTest(
path="hover-and-completion/images_and_samplers.frag",
hover_test_args=(
(15, 5, "uniform layout(rgba8, binding = 0) image2D"),
(17, 5, "uniform layout(rgba16i, binding = 1) iimageCubeArray"),
(19, 5, "uniform layout(rgba16_snorm, binding = 2) restrict writeonly coherent image2DMSArray"),
(22, 5, "uniform sampler2D"),
(24, 5, "uniform isamplerCubeArray"),
(26, 5, "uniform sampler2DMSArray"),
),
),
]

View file

@ -149,11 +149,8 @@ async def test_hover(
def expect_function(expected: str|set[str], result: lspt.Hover):
def strip_md(mdtext: lspt.MarkupContent) -> list[str]:
text = mdtext.value
entries = text.split(sep="---")
for i, entry in enumerate(entries):
entries[i] = strip_until_naked(entry)
text = strip_until_naked(mdtext.value)
entries = text.split(sep="\n")
return entries