zx: make ZLS resolve build-graph modules in .zx files zxls forwards .zx files to ZLS under their real .zx URI (the *content* is transpiled to Zig, but the URI keeps the .zx extension). This patches enables .zx files in ZLS Apply from the ZLS package root: git apply patchs/zls.patch (or) patch -p1 < patchs/zls.patch Generated against ZLS 0.16.0 @ 494486203c3a48927f2383aa3d5ce5fca112186d --- a/src/DocumentStore.zig +++ b/src/DocumentStore.zig @@ -51,6 +51,16 @@ }, }; +// zx: zxls forwards `.zx` files to ZLS under their real `.zx` URI (zxls +// transpiles the *content* to Zig but keeps the URI). For cross-file imports to +// resolve, ZLS must treat `.zx` as a Zig file import just like `.zig`. + +/// Whether `import_string` refers to a Zig source file (`.zig` or `.zx`). +fn isZigFileImport(import_string: []const u8) bool { + return std.mem.endsWith(u8, import_string, ".zig") or + std.mem.endsWith(u8, import_string, ".zx"); +} + /// Represents a `build.zig` pub const BuildFile = struct { uri: Uri, @@ -115,6 +125,12 @@ var module_root_source_file_paths: std.ArrayList([]const u8) = .empty; + // zx: `.zx` page files are routed by the zx framework rather than reached + // via `@import`, so they never appear in the import graph below. As a + // fallback, associate any `.zx` file located under the build file's + // directory with the first compilation's root module. + var zx_fallback_root: ?[]const u8 = null; + { const build_config = build_file.tryLockConfig(io) orelse return .unknown; defer build_file.unlockConfig(io); @@ -124,6 +140,13 @@ try module_root_source_file_paths.ensureUnusedCapacity(arena, module_paths.len); for (module_paths) |module_path| { module_root_source_file_paths.appendAssumeCapacity(try arena.dupe(u8, module_path)); + } + + if (std.mem.endsWith(u8, uri.raw, ".zx") and build_config.compilations.len != 0) { + const build_dir = std.Io.Dir.path.dirname(build_file.uri.raw) orelse build_file.uri.raw; + if (std.mem.startsWith(u8, uri.raw, build_dir)) { + zx_fallback_root = try arena.dupe(u8, build_config.compilations[0].root_module); + } } } @@ -148,6 +171,8 @@ } } + if (zx_fallback_root) |root| return .{ .yes = try allocator.dupe(u8, root) }; + return .no; } @@ -516,7 +541,7 @@ var import_string = offsets.tokenToSlice(tree, tree.nodeMainToken(params[0])); import_string = import_string[1 .. import_string.len - 1]; - if (!std.mem.endsWith(u8, import_string, ".zig")) continue; + if (!isZigFileImport(import_string)) continue; const import_uri = try Uri.resolveImport(allocator, uri, parsed_uri, import_string); file_imports.appendAssumeCapacity(import_uri); @@ -1984,7 +2009,7 @@ const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); - if (std.mem.endsWith(u8, import_str, ".zig") or std.mem.endsWith(u8, import_str, ".zon")) { + if (isZigFileImport(import_str) or std.mem.endsWith(u8, import_str, ".zon")) { const parsed_uri = handle.uri.toStdUri(); return .{ .one = try Uri.resolveImport(allocator, handle.uri, parsed_uri, import_str) }; }