...
This commit is contained in:
@@ -3017,5 +3017,33 @@ END;
|
|||||||
print("SM2: {} {}\n", p.a, p.b);
|
print("SM2: {} {}\n", p.a, p.b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// OPTIONAL IF-ELSE COERCION
|
||||||
|
// ============================================================
|
||||||
|
{
|
||||||
|
print("--- optional if-else coercion ---\n");
|
||||||
|
OptF :: struct { width: ?f32; }
|
||||||
|
x :f32: 10.0;
|
||||||
|
|
||||||
|
// null in then branch
|
||||||
|
f1 := OptF.{ width = if true then null else x };
|
||||||
|
print("opt-if1: {}\n", f1.width ?? 99.0);
|
||||||
|
|
||||||
|
// value in then branch, null in else
|
||||||
|
f2 := OptF.{ width = if true then x else null };
|
||||||
|
print("opt-if2: {}\n", f2.width ?? 99.0);
|
||||||
|
|
||||||
|
// both branches are values
|
||||||
|
f3 := OptF.{ width = if false then 5.0 else x };
|
||||||
|
print("opt-if3: {}\n", f3.width ?? 99.0);
|
||||||
|
|
||||||
|
// standalone optional variable
|
||||||
|
val: ?f32 = if true then null else 42.0;
|
||||||
|
print("opt-if4: {}\n", val ?? 0.0);
|
||||||
|
|
||||||
|
val2: ?f32 = if false then null else 42.0;
|
||||||
|
print("opt-if5: {}\n", val2 ?? 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
print("=== DONE ===\n");
|
print("=== DONE ===\n");
|
||||||
}
|
}
|
||||||
|
|||||||
7
examples/modules/stb_truetype.sx
Normal file
7
examples/modules/stb_truetype.sx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#import c {
|
||||||
|
#include "vendors/stb_truetype/stb_truetype.h";
|
||||||
|
#source "vendors/stb_truetype/stb_truetype_impl.c";
|
||||||
|
|
||||||
|
#include "vendors/file_utils/file_utils.h";
|
||||||
|
#source "vendors/file_utils/file_utils.c";
|
||||||
|
};
|
||||||
@@ -6183,6 +6183,41 @@ pub const CodeGen = struct {
|
|||||||
if (node.data == .null_literal) {
|
if (node.data == .null_literal) {
|
||||||
return self.makeNullOptional(target_ty);
|
return self.makeNullOptional(target_ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if_expr in optional context: propagate optional type to each branch
|
||||||
|
// so that `null` → makeNullOptional and values → wrapOptional individually
|
||||||
|
if (node.data == .if_expr) {
|
||||||
|
const ie = node.data.if_expr;
|
||||||
|
if (ie.binding_name == null and ie.else_branch != null) {
|
||||||
|
const cond_val = self.valueToBool(try self.genExpr(ie.condition));
|
||||||
|
|
||||||
|
var then_bb = self.appendBB("opt_then");
|
||||||
|
var else_bb = self.appendBB("opt_else");
|
||||||
|
const merge_bb = self.appendBB("opt_merge");
|
||||||
|
|
||||||
|
self.condBr(cond_val, then_bb, else_bb);
|
||||||
|
|
||||||
|
self.positionAt(then_bb);
|
||||||
|
const then_val = try self.genExprAsType(ie.then_branch, target_ty);
|
||||||
|
then_bb = self.getCurrentBlock();
|
||||||
|
self.br(merge_bb);
|
||||||
|
|
||||||
|
self.positionAt(else_bb);
|
||||||
|
const else_val = try self.genExprAsType(ie.else_branch.?, target_ty);
|
||||||
|
else_bb = self.getCurrentBlock();
|
||||||
|
self.br(merge_bb);
|
||||||
|
|
||||||
|
self.positionAt(merge_bb);
|
||||||
|
|
||||||
|
const llvm_ty = self.typeToLLVM(target_ty);
|
||||||
|
const phi = c.LLVMBuildPhi(self.builder, llvm_ty, "opt_iftmp");
|
||||||
|
var vals = [2]c.LLVMValueRef{ then_val, else_val };
|
||||||
|
var blocks = [2]c.LLVMBasicBlockRef{ then_bb, else_bb };
|
||||||
|
c.LLVMAddIncoming(phi, &vals, &blocks, 2);
|
||||||
|
return phi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If source expression already produces the same optional type, pass through
|
// If source expression already produces the same optional type, pass through
|
||||||
const src_ty = self.inferType(node);
|
const src_ty = self.inferType(node);
|
||||||
if (src_ty.eql(target_ty)) {
|
if (src_ty.eql(target_ty)) {
|
||||||
|
|||||||
@@ -230,6 +230,26 @@ pub const Server = struct {
|
|||||||
if (try self.sendSymbolLocationWithOrigin(id_json, doc, sym, qn_origin)) return;
|
if (try self.sendSymbolLocationWithOrigin(id_json, doc, sym, qn_origin)) return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Struct method/field: obj.method or Type.method or obj.field
|
||||||
|
if (try self.resolveStructMemberDef(id_json, sema, doc, qn.ns, qn.member, qn_origin)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1b. Dot-shorthand: .method(args) — identifier preceded by dot with no qualifier
|
||||||
|
if (extractIdentAtOffset(doc.source, offset)) |name| {
|
||||||
|
const name_start = @as(u32, @intCast(@intFromPtr(name.ptr) - @intFromPtr(doc.source.ptr)));
|
||||||
|
if (name_start > 0 and doc.source[name_start - 1] == '.' and
|
||||||
|
(name_start < 2 or !isIdentChar(doc.source[name_start - 2])))
|
||||||
|
{
|
||||||
|
const name_end = name_start + @as(u32, @intCast(name.len));
|
||||||
|
const origin = sx.ast.Span{ .start = name_start, .end = name_end };
|
||||||
|
// Search all type declarations for a matching method/variant/field
|
||||||
|
for (sema.symbols) |sym| {
|
||||||
|
if (sym.kind != .struct_type and sym.kind != .enum_type) continue;
|
||||||
|
const lookup = self.findTypeDeclNode(sema, doc, sym.name) orelse continue;
|
||||||
|
if (try self.sendMemberLocation(id_json, lookup, name, doc, origin)) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Reference at offset → jump to definition
|
// 2. Reference at offset → jump to definition
|
||||||
@@ -525,12 +545,12 @@ pub const Server = struct {
|
|||||||
}
|
}
|
||||||
} else if (doc.root) |root| {
|
} else if (doc.root) |root| {
|
||||||
// Try as type name directly (e.g. Vec2., Color.)
|
// Try as type name directly (e.g. Vec2., Color.)
|
||||||
try self.collectMemberCompletions(&items, sema, root, prefix);
|
try self.collectMemberCompletions(&items, sema, doc, prefix);
|
||||||
|
|
||||||
// Try as variable name — resolve to type and offer fields + UFCS methods
|
// Try as variable name — resolve to type and offer fields + UFCS methods
|
||||||
if (items.items.len == 0) {
|
if (items.items.len == 0) {
|
||||||
if (resolveVariableType(sema, prefix)) |type_name| {
|
if (resolveVariableType(sema, prefix)) |type_name| {
|
||||||
try self.collectMemberCompletions(&items, sema, root, type_name);
|
try self.collectMemberCompletions(&items, sema, doc, type_name);
|
||||||
try self.collectUfcsCompletions(&items, root, type_name);
|
try self.collectUfcsCompletions(&items, root, type_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -664,7 +684,8 @@ pub const Server = struct {
|
|||||||
const payload_type_name = if (vt.data == .type_expr) vt.data.type_expr.name else break;
|
const payload_type_name = if (vt.data == .type_expr) vt.data.type_expr.name else break;
|
||||||
// Now offer that struct's fields
|
// Now offer that struct's fields
|
||||||
const payload_sema = if (lookup_doc) |ld| ld.sema orelse sema else sema;
|
const payload_sema = if (lookup_doc) |ld| ld.sema orelse sema else sema;
|
||||||
try self.collectMemberCompletions(items, payload_sema, root, payload_type_name);
|
const payload_doc = if (lookup_doc) |ld| ld else doc;
|
||||||
|
try self.collectMemberCompletions(items, payload_sema, payload_doc, payload_type_name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -858,88 +879,69 @@ pub const Server = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collectMemberCompletions(self: *Server, items: *std.ArrayList(lsp.CompletionItem), sema: SemaResult, root: *sx.ast.Node, name: []const u8) !void {
|
fn collectMemberCompletions(self: *Server, items: *std.ArrayList(lsp.CompletionItem), sema: SemaResult, doc: *const Document, name: []const u8) !void {
|
||||||
for (sema.symbols) |sym| {
|
const lookup = self.findTypeDeclNode(sema, doc, name) orelse return;
|
||||||
if (!std.mem.eql(u8, sym.name, name)) continue;
|
const node = lookup.node;
|
||||||
|
|
||||||
if (sym.kind == .struct_type) {
|
if (node.data == .struct_decl) {
|
||||||
// For imported symbols, look up the source doc's AST
|
const sd = node.data.struct_decl;
|
||||||
const lookup_root = if (sym.origin) |origin_path|
|
for (sd.field_names, 0..) |field_name, fi| {
|
||||||
if (self.documents.get(origin_path)) |od| od.root orelse root else root
|
const detail: ?[]const u8 = if (fi < sd.field_types.len) blk: {
|
||||||
else
|
const ft = sd.field_types[fi];
|
||||||
root;
|
break :blk if (ft.data == .type_expr) ft.data.type_expr.name else null;
|
||||||
if (sx.sema.findNodeAtOffset(lookup_root, sym.def_span.start)) |node| {
|
} else null;
|
||||||
if (node.data == .struct_decl) {
|
|
||||||
const sd = node.data.struct_decl;
|
|
||||||
for (sd.field_names, 0..) |field_name, fi| {
|
|
||||||
const detail: ?[]const u8 = if (fi < sd.field_types.len) blk: {
|
|
||||||
const ft = sd.field_types[fi];
|
|
||||||
break :blk if (ft.data == .type_expr) ft.data.type_expr.name else null;
|
|
||||||
} else null;
|
|
||||||
|
|
||||||
try items.append(self.allocator, .{
|
try items.append(self.allocator, .{
|
||||||
.label = field_name,
|
.label = field_name,
|
||||||
.kind = @intFromEnum(lsp.CompletionItemKind.Field),
|
.kind = @intFromEnum(lsp.CompletionItemKind.Field),
|
||||||
.detail = detail,
|
.detail = detail,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
for (sd.methods) |method_node| {
|
||||||
}
|
if (method_node.data == .fn_decl) {
|
||||||
} else if (sym.kind == .enum_type) {
|
try items.append(self.allocator, .{
|
||||||
const lookup_root = if (sym.origin) |origin_path|
|
.label = method_node.data.fn_decl.name,
|
||||||
if (self.documents.get(origin_path)) |od| od.root orelse root else root
|
.kind = @intFromEnum(lsp.CompletionItemKind.Method),
|
||||||
else
|
});
|
||||||
root;
|
|
||||||
if (sx.sema.findNodeAtOffset(lookup_root, sym.def_span.start)) |node| {
|
|
||||||
if (node.data == .enum_decl) {
|
|
||||||
const ed = node.data.enum_decl;
|
|
||||||
for (ed.variant_names) |variant| {
|
|
||||||
try items.append(self.allocator, .{
|
|
||||||
.label = variant,
|
|
||||||
.kind = @intFromEnum(lsp.CompletionItemKind.EnumMember),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (sym.kind == .protocol_type) {
|
|
||||||
const lookup_root = if (sym.origin) |origin_path|
|
|
||||||
if (self.documents.get(origin_path)) |od| od.root orelse root else root
|
|
||||||
else
|
|
||||||
root;
|
|
||||||
if (sx.sema.findNodeAtOffset(lookup_root, sym.def_span.start)) |node| {
|
|
||||||
if (node.data == .protocol_decl) {
|
|
||||||
const pd = node.data.protocol_decl;
|
|
||||||
for (pd.methods) |method| {
|
|
||||||
// Build detail string: (params) -> ret
|
|
||||||
var detail_buf = std.ArrayList(u8).empty;
|
|
||||||
try detail_buf.append(self.allocator, '(');
|
|
||||||
for (method.param_names, 0..) |pname, pi| {
|
|
||||||
if (pi > 0) try detail_buf.appendSlice(self.allocator, ", ");
|
|
||||||
try detail_buf.appendSlice(self.allocator, pname);
|
|
||||||
if (pi < method.params.len) {
|
|
||||||
try detail_buf.appendSlice(self.allocator, ": ");
|
|
||||||
if (method.params[pi].data == .type_expr) {
|
|
||||||
try detail_buf.appendSlice(self.allocator, method.params[pi].data.type_expr.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try detail_buf.append(self.allocator, ')');
|
|
||||||
if (method.return_type) |rt| {
|
|
||||||
try detail_buf.appendSlice(self.allocator, " -> ");
|
|
||||||
if (rt.data == .type_expr) {
|
|
||||||
try detail_buf.appendSlice(self.allocator, rt.data.type_expr.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try items.append(self.allocator, .{
|
|
||||||
.label = method.name,
|
|
||||||
.kind = @intFromEnum(lsp.CompletionItemKind.Method),
|
|
||||||
.detail = detail_buf.items,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
} else if (node.data == .enum_decl) {
|
||||||
|
const ed = node.data.enum_decl;
|
||||||
|
for (ed.variant_names) |variant| {
|
||||||
|
try items.append(self.allocator, .{
|
||||||
|
.label = variant,
|
||||||
|
.kind = @intFromEnum(lsp.CompletionItemKind.EnumMember),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (node.data == .protocol_decl) {
|
||||||
|
const pd = node.data.protocol_decl;
|
||||||
|
for (pd.methods) |method| {
|
||||||
|
// Build detail string: (params) -> ret
|
||||||
|
var detail_buf = std.ArrayList(u8).empty;
|
||||||
|
try detail_buf.append(self.allocator, '(');
|
||||||
|
for (method.param_names, 0..) |pname, pi| {
|
||||||
|
if (pi > 0) try detail_buf.appendSlice(self.allocator, ", ");
|
||||||
|
try detail_buf.appendSlice(self.allocator, pname);
|
||||||
|
if (pi < method.params.len) {
|
||||||
|
try detail_buf.appendSlice(self.allocator, ": ");
|
||||||
|
if (method.params[pi].data == .type_expr) {
|
||||||
|
try detail_buf.appendSlice(self.allocator, method.params[pi].data.type_expr.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try detail_buf.append(self.allocator, ')');
|
||||||
|
if (method.return_type) |rt| {
|
||||||
|
try detail_buf.appendSlice(self.allocator, " -> ");
|
||||||
|
if (rt.data == .type_expr) {
|
||||||
|
try detail_buf.appendSlice(self.allocator, rt.data.type_expr.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try items.append(self.allocator, .{
|
||||||
|
.label = method.name,
|
||||||
|
.kind = @intFromEnum(lsp.CompletionItemKind.Method),
|
||||||
|
.detail = detail_buf.items,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1806,6 +1808,89 @@ pub const Server = struct {
|
|||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TypeDeclLookup = struct { node: *sx.ast.Node, doc: *const Document };
|
||||||
|
|
||||||
|
/// Given a type name, find its AST declaration node and the document it lives in.
|
||||||
|
/// Works for struct_decl, enum_decl, union_decl, and protocol_decl across files.
|
||||||
|
fn findTypeDeclNode(self: *Server, sema: SemaResult, doc: *const Document, type_name: []const u8) ?TypeDeclLookup {
|
||||||
|
for (sema.symbols) |sym| {
|
||||||
|
if (!std.mem.eql(u8, sym.name, type_name)) continue;
|
||||||
|
if (sym.kind != .struct_type and sym.kind != .enum_type and sym.kind != .protocol_type) continue;
|
||||||
|
|
||||||
|
const lookup_doc = self.resolveSymbolDoc(doc, sym);
|
||||||
|
const lookup_root = lookup_doc.root orelse return null;
|
||||||
|
|
||||||
|
if (sx.sema.findNodeAtOffset(lookup_root, sym.def_span.start)) |node| {
|
||||||
|
return .{ .node = node, .doc = lookup_doc };
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a go-to-definition response for a struct method, field, or enum variant.
|
||||||
|
fn sendMemberLocation(self: *Server, id_json: []const u8, lookup: TypeDeclLookup, member_name: []const u8, origin_doc: *const Document, origin: sx.ast.Span) !bool {
|
||||||
|
if (lookup.node.data == .struct_decl) {
|
||||||
|
const sd = lookup.node.data.struct_decl;
|
||||||
|
// Check methods
|
||||||
|
for (sd.methods) |method_node| {
|
||||||
|
if (method_node.data == .fn_decl) {
|
||||||
|
if (std.mem.eql(u8, method_node.data.fn_decl.name, member_name)) {
|
||||||
|
return self.sendSpanLocation(id_json, lookup.doc, method_node.span, origin_doc, origin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check fields — use field type span as approximate location
|
||||||
|
for (sd.field_names, 0..) |fname, fi| {
|
||||||
|
if (std.mem.eql(u8, fname, member_name)) {
|
||||||
|
if (fi < sd.field_types.len) {
|
||||||
|
return self.sendSpanLocation(id_json, lookup.doc, sd.field_types[fi].span, origin_doc, origin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (lookup.node.data == .enum_decl) {
|
||||||
|
const ed = lookup.node.data.enum_decl;
|
||||||
|
for (ed.variant_names) |v| {
|
||||||
|
if (std.mem.eql(u8, v, member_name)) {
|
||||||
|
return self.sendSpanLocation(id_json, lookup.doc, lookup.node.span, origin_doc, origin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (lookup.node.data == .union_decl) {
|
||||||
|
const ud = lookup.node.data.union_decl;
|
||||||
|
for (ud.field_names) |fname| {
|
||||||
|
if (std.mem.eql(u8, fname, member_name)) {
|
||||||
|
return self.sendSpanLocation(id_json, lookup.doc, lookup.node.span, origin_doc, origin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve qualified name as struct/union method or field, send definition location.
|
||||||
|
fn resolveStructMemberDef(self: *Server, id_json: []const u8, sema: SemaResult, doc: *const Document, ns: []const u8, member: []const u8, origin: sx.ast.Span) !bool {
|
||||||
|
// Try ns as a type name directly
|
||||||
|
if (self.findTypeDeclNode(sema, doc, ns)) |lookup| {
|
||||||
|
if (try self.sendMemberLocation(id_json, lookup, member, doc, origin)) return true;
|
||||||
|
}
|
||||||
|
// Try ns as a variable name → resolve to struct type
|
||||||
|
if (resolveStructTypeName(sema, doc, ns)) |type_name| {
|
||||||
|
if (self.findTypeDeclNode(sema, doc, type_name)) |lookup| {
|
||||||
|
if (try self.sendMemberLocation(id_json, lookup, member, doc, origin)) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a go-to-definition response pointing to a span in a document.
|
||||||
|
fn sendSpanLocation(self: *Server, id_json: []const u8, target_doc: *const Document, target_span: sx.ast.Span, origin_doc: *const Document, origin_span: sx.ast.Span) !bool {
|
||||||
|
const target_range = spanToRange(target_doc.source, target_span);
|
||||||
|
const target_uri = try std.fmt.allocPrint(self.allocator, "file://{s}", .{target_doc.path});
|
||||||
|
const src_range = spanToRange(origin_doc.source, origin_span);
|
||||||
|
const loc_json = try lsp.locationLinkJson(self.allocator, target_uri, target_range, src_range);
|
||||||
|
try self.sendResponse(id_json, loc_json);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Find an import by namespace name (falls back to last good imports).
|
/// Find an import by namespace name (falls back to last good imports).
|
||||||
fn findImportByNs(_: *Server, doc: *const Document, ns_name: []const u8) ?doc_mod.Import {
|
fn findImportByNs(_: *Server, doc: *const Document, ns_name: []const u8) ?doc_mod.Import {
|
||||||
const imports_lists = [_][]const doc_mod.Import{ doc.imports, doc.last_good_imports };
|
const imports_lists = [_][]const doc_mod.Import{ doc.imports, doc.last_good_imports };
|
||||||
@@ -1958,48 +2043,40 @@ pub const Server = struct {
|
|||||||
const sema = doc.sema orelse return null;
|
const sema = doc.sema orelse return null;
|
||||||
const struct_name = resolveStructTypeName(sema, doc, obj_name) orelse return null;
|
const struct_name = resolveStructTypeName(sema, doc, obj_name) orelse return null;
|
||||||
|
|
||||||
for (sema.symbols) |sym| {
|
const lookup = self.findTypeDeclNode(sema, doc, struct_name) orelse return null;
|
||||||
if (sym.kind != .struct_type or !std.mem.eql(u8, sym.name, struct_name)) continue;
|
if (lookup.node.data != .struct_decl) return null;
|
||||||
|
const sd = lookup.node.data.struct_decl;
|
||||||
|
const lookup_doc = lookup.doc;
|
||||||
|
|
||||||
const lookup_doc = self.resolveSymbolDoc(doc, sym);
|
for (sd.field_names, 0..) |fn_, fi| {
|
||||||
const lookup_root = lookup_doc.root orelse return null;
|
if (!std.mem.eql(u8, fn_, field_name)) continue;
|
||||||
|
|
||||||
if (sx.sema.findNodeAtOffset(lookup_root, sym.def_span.start)) |node| {
|
var buf = std.ArrayList(u8).empty;
|
||||||
if (node.data == .struct_decl) {
|
|
||||||
const sd = node.data.struct_decl;
|
|
||||||
for (sd.field_names, 0..) |fn_, fi| {
|
|
||||||
if (!std.mem.eql(u8, fn_, field_name)) continue;
|
|
||||||
|
|
||||||
var buf = std.ArrayList(u8).empty;
|
// Doc comment above field
|
||||||
|
const fn_addr = @intFromPtr(fn_.ptr);
|
||||||
// Doc comment above field
|
const src_addr = @intFromPtr(lookup_doc.source.ptr);
|
||||||
const fn_addr = @intFromPtr(fn_.ptr);
|
const src_end = src_addr + lookup_doc.source.len;
|
||||||
const src_addr = @intFromPtr(lookup_doc.source.ptr);
|
if (fn_addr >= src_addr and fn_addr < src_end) {
|
||||||
const src_end = src_addr + lookup_doc.source.len;
|
const field_offset = @as(u32, @intCast(fn_addr - src_addr));
|
||||||
if (fn_addr >= src_addr and fn_addr < src_end) {
|
if (extractDocComment(lookup_doc.source, field_offset)) |comment| {
|
||||||
const field_offset = @as(u32, @intCast(fn_addr - src_addr));
|
try buf.appendSlice(self.allocator, comment);
|
||||||
if (extractDocComment(lookup_doc.source, field_offset)) |comment| {
|
try buf.appendSlice(self.allocator, "\n\n");
|
||||||
try buf.appendSlice(self.allocator, comment);
|
|
||||||
try buf.appendSlice(self.allocator, "\n\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try buf.appendSlice(self.allocator, "```sx\n");
|
|
||||||
try buf.appendSlice(self.allocator, struct_name);
|
|
||||||
try buf.append(self.allocator, '.');
|
|
||||||
try buf.appendSlice(self.allocator, field_name);
|
|
||||||
if (fi < sd.field_types.len) {
|
|
||||||
if (sd.field_types[fi].data == .type_expr) {
|
|
||||||
try buf.appendSlice(self.allocator, " : ");
|
|
||||||
try buf.appendSlice(self.allocator, sd.field_types[fi].data.type_expr.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try buf.appendSlice(self.allocator, "\n```");
|
|
||||||
return buf.items;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
try buf.appendSlice(self.allocator, "```sx\n");
|
||||||
|
try buf.appendSlice(self.allocator, struct_name);
|
||||||
|
try buf.append(self.allocator, '.');
|
||||||
|
try buf.appendSlice(self.allocator, field_name);
|
||||||
|
if (fi < sd.field_types.len) {
|
||||||
|
if (sd.field_types[fi].data == .type_expr) {
|
||||||
|
try buf.appendSlice(self.allocator, " : ");
|
||||||
|
try buf.appendSlice(self.allocator, sd.field_types[fi].data.type_expr.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try buf.appendSlice(self.allocator, "\n```");
|
||||||
|
return buf.items;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -2007,39 +2084,37 @@ pub const Server = struct {
|
|||||||
fn formatEnumVariantHover(self: *Server, doc: *const Document, variant_name: []const u8) !?[]const u8 {
|
fn formatEnumVariantHover(self: *Server, doc: *const Document, variant_name: []const u8) !?[]const u8 {
|
||||||
const sema = doc.sema orelse return null;
|
const sema = doc.sema orelse return null;
|
||||||
|
|
||||||
|
// Search all enum types for the variant
|
||||||
for (sema.symbols) |sym| {
|
for (sema.symbols) |sym| {
|
||||||
if (sym.kind != .enum_type) continue;
|
if (sym.kind != .enum_type) continue;
|
||||||
|
|
||||||
const lookup_doc = self.resolveSymbolDoc(doc, sym);
|
const lookup = self.findTypeDeclNode(sema, doc, sym.name) orelse continue;
|
||||||
const lookup_root = lookup_doc.root orelse continue;
|
if (lookup.node.data != .enum_decl) continue;
|
||||||
|
const ed = lookup.node.data.enum_decl;
|
||||||
|
const lookup_doc = lookup.doc;
|
||||||
|
|
||||||
if (sx.sema.findNodeAtOffset(lookup_root, sym.def_span.start)) |node| {
|
for (ed.variant_names) |v| {
|
||||||
if (node.data == .enum_decl) {
|
if (!std.mem.eql(u8, v, variant_name)) continue;
|
||||||
const ed = node.data.enum_decl;
|
|
||||||
for (ed.variant_names) |v| {
|
|
||||||
if (!std.mem.eql(u8, v, variant_name)) continue;
|
|
||||||
|
|
||||||
var buf = std.ArrayList(u8).empty;
|
var buf = std.ArrayList(u8).empty;
|
||||||
|
|
||||||
const v_addr = @intFromPtr(v.ptr);
|
const v_addr = @intFromPtr(v.ptr);
|
||||||
const src_addr2 = @intFromPtr(lookup_doc.source.ptr);
|
const src_addr2 = @intFromPtr(lookup_doc.source.ptr);
|
||||||
const src_end2 = src_addr2 + lookup_doc.source.len;
|
const src_end2 = src_addr2 + lookup_doc.source.len;
|
||||||
if (v_addr >= src_addr2 and v_addr < src_end2) {
|
if (v_addr >= src_addr2 and v_addr < src_end2) {
|
||||||
const variant_offset = @as(u32, @intCast(v_addr - src_addr2));
|
const variant_offset = @as(u32, @intCast(v_addr - src_addr2));
|
||||||
if (extractDocComment(lookup_doc.source, variant_offset)) |comment| {
|
if (extractDocComment(lookup_doc.source, variant_offset)) |comment| {
|
||||||
try buf.appendSlice(self.allocator, comment);
|
try buf.appendSlice(self.allocator, comment);
|
||||||
try buf.appendSlice(self.allocator, "\n\n");
|
try buf.appendSlice(self.allocator, "\n\n");
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try buf.appendSlice(self.allocator, "```sx\n");
|
|
||||||
try buf.appendSlice(self.allocator, sym.name);
|
|
||||||
try buf.append(self.allocator, '.');
|
|
||||||
try buf.appendSlice(self.allocator, variant_name);
|
|
||||||
try buf.appendSlice(self.allocator, "\n```");
|
|
||||||
return buf.items;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try buf.appendSlice(self.allocator, "```sx\n");
|
||||||
|
try buf.appendSlice(self.allocator, sym.name);
|
||||||
|
try buf.append(self.allocator, '.');
|
||||||
|
try buf.appendSlice(self.allocator, variant_name);
|
||||||
|
try buf.appendSlice(self.allocator, "\n```");
|
||||||
|
return buf.items;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -1268,7 +1268,6 @@ pub fn findNodeAtOffset(node: *Node, offset: u32) ?*Node {
|
|||||||
.library_decl,
|
.library_decl,
|
||||||
.function_type_expr,
|
.function_type_expr,
|
||||||
.enum_decl,
|
.enum_decl,
|
||||||
.struct_decl,
|
|
||||||
.union_decl,
|
.union_decl,
|
||||||
.import_decl,
|
.import_decl,
|
||||||
.c_import_decl,
|
.c_import_decl,
|
||||||
@@ -1286,6 +1285,11 @@ pub fn findNodeAtOffset(node: *Node, offset: u32) ?*Node {
|
|||||||
.ufcs_alias,
|
.ufcs_alias,
|
||||||
.closure_type_expr,
|
.closure_type_expr,
|
||||||
=> {},
|
=> {},
|
||||||
|
.struct_decl => |sd| {
|
||||||
|
for (sd.methods) |method_node| {
|
||||||
|
if (findNodeAtOffset(method_node, offset)) |found| return found;
|
||||||
|
}
|
||||||
|
},
|
||||||
.protocol_decl => |pd| {
|
.protocol_decl => |pd| {
|
||||||
for (pd.methods) |method| {
|
for (pd.methods) |method| {
|
||||||
if (method.default_body) |body| {
|
if (method.default_body) |body| {
|
||||||
|
|||||||
@@ -586,4 +586,10 @@ IB5: 52
|
|||||||
SM1: 16.000000 8.000000
|
SM1: 16.000000 8.000000
|
||||||
SM1: 5.000000 5.000000
|
SM1: 5.000000 5.000000
|
||||||
SM2: 10 20
|
SM2: 10 20
|
||||||
|
--- optional if-else coercion ---
|
||||||
|
opt-if1: 99.000000
|
||||||
|
opt-if2: 10.000000
|
||||||
|
opt-if3: 10.000000
|
||||||
|
opt-if4: 0.000000
|
||||||
|
opt-if5: 42.000000
|
||||||
=== DONE ===
|
=== DONE ===
|
||||||
|
|||||||
Reference in New Issue
Block a user