This commit is contained in:
agra
2026-02-14 19:33:33 +02:00
parent d61c6488f3
commit 0e777e9d2e
7 changed files with 957 additions and 72 deletions

View File

@@ -493,12 +493,75 @@ pub const Server = struct {
}
}
}
} else {
// Bare dot (no prefix) — check if we're inside a match expression
// and offer the subject's enum variants (e.g. case .quit)
try self.collectMatchEnumCompletions(&items, doc, cursor_offset);
}
const items_json = try lsp.completionListJson(self.allocator, items.items);
try self.sendResponse(id_json, items_json);
}
fn collectMatchEnumCompletions(self: *Server, items: *std.ArrayList(lsp.CompletionItem), doc: *const Document, cursor_offset: u32) !void {
const root = doc.root orelse return;
const sema = doc.sema orelse return;
// Find enclosing match expression's subject
const subject = sx.sema.findEnclosingMatchSubject(root, cursor_offset) orelse return;
// Resolve the subject to an enum type name
const enum_name: ?[]const u8 = switch (subject.data) {
.identifier => |id| blk: {
// Look up variable type, then check if it's an enum
for (sema.symbols) |sym| {
if (!std.mem.eql(u8, sym.name, id.name)) continue;
if (sym.kind != .variable and sym.kind != .param) continue;
const ty = sym.ty orelse break;
break :blk switch (ty) {
.enum_type => |n| n,
.union_type => |n| n,
else => null,
};
}
break :blk null;
},
.field_access => |fa| blk: {
// e.g. e.key — resolve the field's type
if (fa.object.data == .identifier) {
const var_name = fa.object.data.identifier.name;
// Find variable's struct type, then look up the field type
for (sema.symbols) |sym| {
if (!std.mem.eql(u8, sym.name, var_name)) continue;
const ty = sym.ty orelse break;
const struct_name = switch (ty) {
.struct_type => |n| n,
else => break,
};
// Look up the struct's field type
if (sema.struct_types.get(struct_name)) |info| {
for (info.field_names, 0..) |fname, fi| {
if (std.mem.eql(u8, fname, fa.field) and fi < info.field_types.len) {
break :blk switch (info.field_types[fi]) {
.enum_type => |n| n,
.union_type => |n| n,
else => null,
};
}
}
}
break;
}
}
break :blk null;
},
else => null,
};
const name = enum_name orelse return;
try self.collectMemberCompletions(items, sema, root, name);
}
fn collectDeclCompletions(allocator: std.mem.Allocator, items: *std.ArrayList(lsp.CompletionItem), decls: []const *sx.ast.Node) !void {
for (decls) |decl| {
switch (decl.data) {
@@ -1643,6 +1706,33 @@ pub const Server = struct {
if (bt.data == .type_expr) {
try buf.appendSlice(allocator, bt.data.type_expr.name);
try buf.appendSlice(allocator, " ");
} else if (bt.data == .struct_decl) {
const sd = bt.data.struct_decl;
try buf.appendSlice(allocator, "struct { ");
for (sd.field_names, 0..) |fn_, fi| {
if (fi > 0) try buf.appendSlice(allocator, "; ");
try buf.appendSlice(allocator, fn_);
try buf.appendSlice(allocator, ": ");
if (fi < sd.field_types.len) {
if (sd.field_types[fi].data == .type_expr) {
try buf.appendSlice(allocator, sd.field_types[fi].data.type_expr.name);
} else if (sd.field_types[fi].data == .array_type_expr) {
const ate = sd.field_types[fi].data.array_type_expr;
try buf.append(allocator, '[');
if (ate.length.data == .int_literal) {
const val = ate.length.data.int_literal.value;
var num_buf: [20]u8 = undefined;
const num_str = std.fmt.bufPrint(&num_buf, "{d}", .{val}) catch "?";
try buf.appendSlice(allocator, num_str);
}
try buf.append(allocator, ']');
if (ate.element_type.data == .type_expr) {
try buf.appendSlice(allocator, ate.element_type.data.type_expr.name);
}
}
}
}
try buf.appendSlice(allocator, " } ");
}
}
try buf.appendSlice(allocator, "{ ");