lsp import
This commit is contained in:
@@ -17,6 +17,31 @@ pub fn dirName(path: []const u8) []const u8 {
|
|||||||
return if (found) path[0..last_sep] else ".";
|
return if (found) path[0..last_sep] else ".";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolve an import path: try relative to base_dir first, fall back to cwd-relative.
|
||||||
|
/// If root_path is provided, CWD-relative fallback paths are made absolute.
|
||||||
|
/// Shared between compiler (resolveImports) and LSP (analyzeDocument).
|
||||||
|
pub fn resolveImportPath(allocator: std.mem.Allocator, io: std.Io, base_dir: []const u8, raw_path: []const u8, root_path: ?[]const u8) ![]const u8 {
|
||||||
|
if (!std.mem.eql(u8, base_dir, ".")) {
|
||||||
|
const rel_path = try std.fmt.allocPrint(allocator, "{s}/{s}", .{ base_dir, raw_path });
|
||||||
|
// Check if it exists as file relative to base_dir
|
||||||
|
if (std.Io.Dir.readFileAlloc(.cwd(), io, rel_path, allocator, .limited(10 * 1024 * 1024))) |_| {
|
||||||
|
return rel_path;
|
||||||
|
} else |_| {}
|
||||||
|
// Check if it exists as directory relative to base_dir
|
||||||
|
if (std.Io.Dir.openDir(.cwd(), io, rel_path, .{})) |dir| {
|
||||||
|
dir.close(io);
|
||||||
|
return rel_path;
|
||||||
|
} else |_| {}
|
||||||
|
}
|
||||||
|
// Fall back to raw path (cwd-relative); absolutify if root_path is known
|
||||||
|
if (root_path) |rp| {
|
||||||
|
if (rp.len > 0 and raw_path.len > 0 and raw_path[0] != '/') {
|
||||||
|
return std.fmt.allocPrint(allocator, "{s}/{s}", .{ rp, raw_path });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return raw_path;
|
||||||
|
}
|
||||||
|
|
||||||
/// A resolved module: the fully-resolved declarations of a single .sx file,
|
/// A resolved module: the fully-resolved declarations of a single .sx file,
|
||||||
/// with its own scope tracking which names are defined.
|
/// with its own scope tracking which names are defined.
|
||||||
pub const ResolvedModule = struct {
|
pub const ResolvedModule = struct {
|
||||||
@@ -142,23 +167,7 @@ pub fn resolveImports(
|
|||||||
}
|
}
|
||||||
const imp = decl.data.import_decl;
|
const imp = decl.data.import_decl;
|
||||||
|
|
||||||
// Resolve path: try relative to file dir first, then fall back to cwd-relative
|
const resolved_path = try resolveImportPath(allocator, io, base_dir, imp.path, null);
|
||||||
const resolved_path = resolvePath: {
|
|
||||||
if (!std.mem.eql(u8, base_dir, ".")) {
|
|
||||||
const rel_path = try std.fmt.allocPrint(allocator, "{s}/{s}", .{ base_dir, imp.path });
|
|
||||||
// Check if it exists as file or directory relative to base_dir
|
|
||||||
if (std.Io.Dir.readFileAlloc(.cwd(), io, rel_path, allocator, .limited(10 * 1024 * 1024))) |_| {
|
|
||||||
break :resolvePath rel_path;
|
|
||||||
} else |_| {}
|
|
||||||
// Try as directory
|
|
||||||
if (std.Io.Dir.openDir(.cwd(), io, rel_path, .{})) |dir| {
|
|
||||||
dir.close(io);
|
|
||||||
break :resolvePath rel_path;
|
|
||||||
} else |_| {}
|
|
||||||
}
|
|
||||||
// Fall back to raw path (cwd-relative)
|
|
||||||
break :resolvePath imp.path;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Circular import check — only along the current chain
|
// Circular import check — only along the current chain
|
||||||
if (chain.contains(resolved_path)) continue;
|
if (chain.contains(resolved_path)) continue;
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ pub const Document = struct {
|
|||||||
pub const DocumentStore = struct {
|
pub const DocumentStore = struct {
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
io: std.Io,
|
io: std.Io,
|
||||||
|
/// Workspace root path (from initialize). Used to absolutify CWD-relative import paths.
|
||||||
|
root_path: []const u8 = "",
|
||||||
/// All loaded documents keyed by resolved file path.
|
/// All loaded documents keyed by resolved file path.
|
||||||
by_path: std.StringHashMap(*Document),
|
by_path: std.StringHashMap(*Document),
|
||||||
|
|
||||||
@@ -56,6 +58,10 @@ pub const DocumentStore = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn rootPathOpt(self: *const DocumentStore) ?[]const u8 {
|
||||||
|
return if (self.root_path.len > 0) self.root_path else null;
|
||||||
|
}
|
||||||
|
|
||||||
/// Get or create a document for the given file path. Reads from disk if not yet loaded.
|
/// Get or create a document for the given file path. Reads from disk if not yet loaded.
|
||||||
pub fn getOrLoad(self: *DocumentStore, path: []const u8) !*Document {
|
pub fn getOrLoad(self: *DocumentStore, path: []const u8) !*Document {
|
||||||
if (self.by_path.get(path)) |doc| return doc;
|
if (self.by_path.get(path)) |doc| return doc;
|
||||||
@@ -171,17 +177,14 @@ pub const DocumentStore = struct {
|
|||||||
|
|
||||||
const root = doc.root orelse return;
|
const root = doc.root orelse return;
|
||||||
|
|
||||||
// Extract imports from AST
|
// Extract imports from AST — uses shared resolution logic from imports.zig
|
||||||
var import_list = std.ArrayList(Import).empty;
|
var import_list = std.ArrayList(Import).empty;
|
||||||
const base_dir = sx.imports.dirName(doc.path);
|
const base_dir = sx.imports.dirName(doc.path);
|
||||||
if (root.data == .root) {
|
if (root.data == .root) {
|
||||||
for (root.data.root.decls) |decl| {
|
for (root.data.root.decls) |decl| {
|
||||||
if (decl.data != .import_decl) continue;
|
if (decl.data != .import_decl) continue;
|
||||||
const imp = decl.data.import_decl;
|
const imp = decl.data.import_decl;
|
||||||
const resolved_path = if (std.mem.eql(u8, base_dir, "."))
|
const resolved_path = try sx.imports.resolveImportPath(self.allocator, self.io, base_dir, imp.path, self.rootPathOpt());
|
||||||
imp.path
|
|
||||||
else
|
|
||||||
try std.fmt.allocPrint(self.allocator, "{s}/{s}", .{ base_dir, imp.path });
|
|
||||||
try import_list.append(self.allocator, .{
|
try import_list.append(self.allocator, .{
|
||||||
.ns = imp.name,
|
.ns = imp.name,
|
||||||
.path = resolved_path,
|
.path = resolved_path,
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ pub const Server = struct {
|
|||||||
if (!std.mem.startsWith(u8, root_uri, prefix)) break :chdir;
|
if (!std.mem.startsWith(u8, root_uri, prefix)) break :chdir;
|
||||||
const root_path = root_uri[prefix.len..];
|
const root_path = root_uri[prefix.len..];
|
||||||
self.root_path = self.allocator.dupe(u8, root_path) catch break :chdir;
|
self.root_path = self.allocator.dupe(u8, root_path) catch break :chdir;
|
||||||
|
self.documents.root_path = self.root_path;
|
||||||
const path_z = self.allocator.dupeZ(u8, root_path) catch break :chdir;
|
const path_z = self.allocator.dupeZ(u8, root_path) catch break :chdir;
|
||||||
_ = std.c.chdir(path_z.ptr);
|
_ = std.c.chdir(path_z.ptr);
|
||||||
}
|
}
|
||||||
@@ -249,13 +250,11 @@ pub const Server = struct {
|
|||||||
// 4. #import "path" string → open the file (or directory)
|
// 4. #import "path" string → open the file (or directory)
|
||||||
if (findImportPathAtOffset(doc.source, offset)) |import_path| {
|
if (findImportPathAtOffset(doc.source, offset)) |import_path| {
|
||||||
const base_dir = sx.imports.dirName(file_path);
|
const base_dir = sx.imports.dirName(file_path);
|
||||||
const resolved = if (std.mem.eql(u8, base_dir, "."))
|
const rp: ?[]const u8 = if (self.root_path.len > 0) self.root_path else null;
|
||||||
import_path
|
const resolved = try sx.imports.resolveImportPath(self.allocator, self.io, base_dir, import_path, rp);
|
||||||
else
|
|
||||||
try std.fmt.allocPrint(self.allocator, "{s}/{s}", .{ base_dir, import_path });
|
|
||||||
|
|
||||||
// For directory imports, try to read as file first
|
// For directory imports, try to read as file first
|
||||||
if (std.Io.Dir.readFileAlloc(.cwd(), self.io, resolved, self.allocator, .limited(1))) |_| {
|
if (std.Io.Dir.readFileAlloc(.cwd(), self.io, resolved, self.allocator, .limited(10 * 1024 * 1024))) |_| {
|
||||||
// It's a file — navigate to it
|
// It's a file — navigate to it
|
||||||
const target_uri = try std.fmt.allocPrint(self.allocator, "file://{s}", .{resolved});
|
const target_uri = try std.fmt.allocPrint(self.allocator, "file://{s}", .{resolved});
|
||||||
const range = lsp.Range{
|
const range = lsp.Range{
|
||||||
|
|||||||
@@ -561,4 +561,6 @@ P6.5: 20
|
|||||||
P7.1: 30
|
P7.1: 30
|
||||||
P7.2: 10 300
|
P7.2: 10 300
|
||||||
P2.6: 5 10
|
P2.6: 5 10
|
||||||
|
P2.7: 15
|
||||||
|
P3.3: 102
|
||||||
=== DONE ===
|
=== DONE ===
|
||||||
|
|||||||
Reference in New Issue
Block a user