Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/features/completions.zig
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,7 @@ fn completeDot(builder: *Builder, loc: offsets.Loc) Analyser.Error!void {
const containers = try collectContainerNodes(builder, builder.orig_handle, dot_context);
for (containers) |container| {
try collectContainerFields(builder, dot_context.likely, container, used_members_set);
try collectErrorSetNames(builder, container, used_members_set, loc);
}
}

Expand All @@ -840,14 +841,15 @@ fn completeError(builder: *Builder, loc: offsets.Loc) Analyser.Error!void {
const used_members_set = try collectUsedMembersSet(builder, dot_context.likely, dot_token_index);
const containers = try collectContainerNodes(builder, builder.orig_handle, dot_context);
for (containers) |container| {
try collectErrorSetNames(builder, container, used_members_set);
try collectErrorSetNames(builder, container, used_members_set, null);
}
}

fn collectErrorSetNames(
builder: *Builder,
container: Analyser.Type,
omit_members: std.BufSet,
dot_token: ?offsets.Loc,
) Analyser.Error!void {
const ip = builder.analyser.ip;
const key = switch (container.data) {
Expand All @@ -860,10 +862,22 @@ fn collectErrorSetNames(
const str = names.at(@intCast(idx), ip);
const name = try builder.arena.print("{f}", .{str.fmt(ip.io, &ip.string_pool)});
if (omit_members.contains(name)) continue;
const textEdit = if (dot_token) |dot| blk: {
const tree = &builder.orig_handle.tree;
const source = tree.source;
const dot_range = offsets.locToRange(source, dot, builder.server.offset_encoding);
break :blk createTextEdit(builder, .{
.newText = try builder.arena.print("error.{s}", .{name}),
.insert = dot_range,
// FIXME:
.replace = dot_range,
});
} else null;
try builder.completions.append(builder.arena, .{
.label = try builder.arena.print("error.{s}", .{name}),
.filterText = name,
.insertText = name,
.textEdit = textEdit,
.kind = .Constant,
});
}
Expand Down
121 changes: 121 additions & 0 deletions tests/lsp_features/completion.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2116,6 +2116,7 @@ test "switch on error set - text edits result" {
.enable_snippets = false,
});
}

test "switch on error set - insert/replace text edits" {
try testCompletionTextEdit(.{
.source =
Expand Down Expand Up @@ -2181,6 +2182,126 @@ test "switch on error set - Works in a function of a container" {
});
}

test "switch on error set (dot completion) - all values are suggested" {
try testCompletion(
\\const err: error{E1, E2} = undefined;
\\switch(err) {
\\ .<cursor>
\\}
, &.{
.{ .label = "error.E1", .kind = .Constant },
.{ .label = "error.E2", .kind = .Constant },
});
}

test "switch on error set (dot completion) - already used values are not suggested" {
try testCompletion(
\\const err: error{E1, E2} = undefined;
\\switch(err) {
\\ error.E1 => {},
\\ .<cursor>
\\}
, &.{
.{ .label = "error.E2", .kind = .Constant },
});
}

test "switch on error set (dot completion) - error unions get all values" {
try testCompletion(
\\const Err2 = error{F1, F2};
\\const Err = error{E1, E2} || Err2;
\\const err: Err = undefined;
\\switch(err) {
\\ .<cursor>
\\}
, &.{
.{ .label = "error.E1", .kind = .Constant },
.{ .label = "error.E2", .kind = .Constant },
.{ .label = "error.F1", .kind = .Constant },
.{ .label = "error.F2", .kind = .Constant },
});
}

test "switch on error set (dot completion) - text edits result" {
try testCompletionTextEdit(.{
.source =
\\const err: error{E1, E2} = undefined;
\\switch(err) {
\\ .<cursor>
\\}
,
.label = "error.E1",
.expected_insert_line = " error.E1",
.expected_replace_line = " error.E1",
.enable_snippets = false,
});
}

test "switch on error set (dot completion) - insert/replace text edits" {
try testCompletionTextEdit(.{
.source =
\\const err: error{Err1, Err2} = undefined;
\\switch(err) {
\\ .E<cursor>0
\\}
,
.label = "error.Err1",
.expected_insert_line = " error.Err10",
.expected_replace_line = " error.Err1",
.enable_snippets = false,
});
}

test "switch on error set (dot completion) - completion inside catch block works" {
try testCompletion(
\\fn idk() error{ E1, E2 }!void {}
\\test {
\\ idk() catch |err| {
\\ switch (err) {
\\ .<cursor>
\\ }
\\ };
\\}
, &.{
.{ .label = "error.E1", .kind = .Constant },
.{ .label = "error.E2", .kind = .Constant },
});
}

test "switch on error set (dot completion) - completion inside catch statement" {
if (true) return error.SkipZigTest; // TODO un-skip after https://github.com/zigtools/zls/issues/2341 and/or https://github.com/zigtools/zls/issues/1112
try testCompletion(
\\fn idk() error{ E1, E2 }!void {}
\\test {
\\ idk() catch |err| switch (err) {
\\ .<cursor>
\\ };
\\}
, &.{
.{ .label = "error.E1", .kind = .Constant },
.{ .label = "error.E2", .kind = .Constant },
});
}

test "switch on error set (dot completion) - Works in a function of a container" {
if (true) return error.SkipZigTest; // TODO un-skip after https://github.com/zigtools/zls/issues/1535
try testCompletion(
\\fn idk() error{ E1, E2 }!void {}
\\pub const Manager = struct {
\\ pub fn testIt() void {
\\ _ = idk() catch |err| {
\\ switch(err) {
\\ .<cursor>
\\ }
\\ };
\\ }
\\};
, &.{
.{ .label = "error.E1", .kind = .Constant },
.{ .label = "error.E2", .kind = .Constant },
});
}

test "error set" {
try testCompletion(
\\const E = error {
Expand Down
Loading