This commit is contained in:
agra
2026-02-20 18:22:42 +02:00
parent 6f927361aa
commit 2f95810f9d
10 changed files with 510 additions and 77 deletions

View File

@@ -174,6 +174,7 @@ pub const Server = struct {
// 1. Qualified name (e.g. "std.print" or UFCS "list.append")
if (extractQualifiedName(doc.source, offset)) |qn| {
const qn_origin = sx.ast.Span{ .start = qn.full_start, .end = qn.full_end };
// Namespace import member
if (self.findImportByNs(doc, qn.ns)) |imp| {
if (self.documents.get(imp.path)) |imp_doc| {
@@ -181,7 +182,7 @@ pub const Server = struct {
if (imp_doc.sema) |imp_sema| {
if (findSymbolByName(imp_sema.symbols, qn.member)) |si| {
const sym = imp_sema.symbols[si];
if (try self.sendSymbolLocation(id_json, imp_doc, sym)) return;
if (try self.sendSymbolLocationWithOrigin(id_json, imp_doc, sym, qn_origin)) return;
}
}
} else {
@@ -194,7 +195,7 @@ pub const Server = struct {
if (dir_doc.sema) |dir_sema| {
if (findSymbolByName(dir_sema.symbols, qn.member)) |si| {
const sym = dir_sema.symbols[si];
if (try self.sendSymbolLocation(id_json, dir_doc, sym)) return;
if (try self.sendSymbolLocationWithOrigin(id_json, dir_doc, sym, qn_origin)) return;
}
}
}
@@ -205,7 +206,7 @@ pub const Server = struct {
if (findSymbolByName(sema.symbols, qn.member)) |si| {
const sym = sema.symbols[si];
if (sym.kind == .function) {
if (try self.sendSymbolLocation(id_json, doc, sym)) return;
if (try self.sendSymbolLocationWithOrigin(id_json, doc, sym, qn_origin)) return;
}
}
}
@@ -215,14 +216,14 @@ pub const Server = struct {
const ref = sema.references[ref_idx];
if (ref.symbol_index < sema.symbols.len) {
const sym = sema.symbols[ref.symbol_index];
if (try self.sendSymbolLocation(id_json, doc, sym)) return;
if (try self.sendSymbolLocationWithOrigin(id_json, doc, sym, ref.span)) return;
}
}
// 3. Symbol definition name at offset
if (findSymbolNameAtOffset(sema.symbols, doc.source, offset)) |sym_idx| {
const sym = sema.symbols[sym_idx];
if (try self.sendSymbolLocation(id_json, doc, sym)) return;
if (try self.sendSymbolLocationWithOrigin(id_json, doc, sym, sym.def_span)) return;
}
// 4. #import "path" string → open the file (or directory)
@@ -256,7 +257,9 @@ pub const Server = struct {
if (!is_qualified) {
if (findSymbolByName(sema.symbols, name)) |si| {
const sym = sema.symbols[si];
if (try self.sendSymbolLocation(id_json, doc, sym)) return;
const name_end = name_start + @as(u32, @intCast(name.len));
const origin = sx.ast.Span{ .start = name_start, .end = name_end };
if (try self.sendSymbolLocationWithOrigin(id_json, doc, sym, origin)) return;
}
}
}
@@ -1112,6 +1115,11 @@ pub const Server = struct {
return ST.type_;
}
// Uppercase identifiers are conventionally types
if (name.len > 0 and name[0] >= 'A' and name[0] <= 'Z') {
return ST.type_;
}
return null;
}
@@ -1270,20 +1278,34 @@ pub const Server = struct {
/// Send a Location response for a symbol, resolving to the correct file via origin.
fn sendSymbolLocation(self: *Server, id_json: []const u8, doc: *const Document, sym: sx.sema.Symbol) !bool {
return self.sendSymbolLocationWithOrigin(id_json, doc, sym, null);
}
fn sendSymbolLocationWithOrigin(self: *Server, id_json: []const u8, doc: *const Document, sym: sx.sema.Symbol, origin_span: ?sx.ast.Span) !bool {
if (sym.origin) |origin_path| {
// Symbol is from an imported file
const origin_doc = self.documents.get(origin_path) orelse return false;
const range = spanToRange(origin_doc.source, sym.def_span);
const target_range = spanToRange(origin_doc.source, sym.def_span);
const target_uri = try std.fmt.allocPrint(self.allocator, "file://{s}", .{origin_path});
const loc_json = try lsp.locationJson(self.allocator, target_uri, range);
try self.sendResponse(id_json, loc_json);
if (origin_span) |os| {
const src_range = spanToRange(doc.source, os);
const loc_json = try lsp.locationLinkJson(self.allocator, target_uri, target_range, src_range);
try self.sendResponse(id_json, loc_json);
} else {
const loc_json = try lsp.locationJson(self.allocator, target_uri, target_range);
try self.sendResponse(id_json, loc_json);
}
return true;
} else {
// Symbol is local
const range = spanToRange(doc.source, sym.def_span);
const target_range = spanToRange(doc.source, sym.def_span);
const target_uri = try std.fmt.allocPrint(self.allocator, "file://{s}", .{doc.path});
const loc_json = try lsp.locationJson(self.allocator, target_uri, range);
try self.sendResponse(id_json, loc_json);
if (origin_span) |os| {
const src_range = spanToRange(doc.source, os);
const loc_json = try lsp.locationLinkJson(self.allocator, target_uri, target_range, src_range);
try self.sendResponse(id_json, loc_json);
} else {
const loc_json = try lsp.locationJson(self.allocator, target_uri, target_range);
try self.sendResponse(id_json, loc_json);
}
return true;
}
}
@@ -1775,7 +1797,7 @@ pub const Server = struct {
return source[qstart + 1 .. qend];
}
pub fn extractQualifiedName(source: []const u8, offset: u32) ?struct { ns: []const u8, member: []const u8 } {
pub fn extractQualifiedName(source: []const u8, offset: u32) ?struct { ns: []const u8, member: []const u8, full_start: u32, full_end: u32 } {
if (offset >= source.len) return null;
var end: u32 = offset;
@@ -1792,6 +1814,8 @@ pub const Server = struct {
return .{
.ns = source[ns_start .. start - 1],
.member = source[start..end],
.full_start = ns_start,
.full_end = end,
};
}
}
@@ -1803,6 +1827,8 @@ pub const Server = struct {
return .{
.ns = source[start..end],
.member = source[end + 1 .. member_end],
.full_start = start,
.full_end = member_end,
};
}
}