Skip to content

Commit 8dab9d0

Browse files
authored
Merge pull request #2328 from zigtools/techatrix/wasi
various wasi related improvements
2 parents f85781b + bf5b846 commit 8dab9d0

File tree

8 files changed

+97
-64
lines changed

8 files changed

+97
-64
lines changed

src/DocumentStore.zig

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ config: Config,
2323
lock: std.Thread.RwLock = .{},
2424
thread_pool: if (builtin.single_threaded) void else *std.Thread.Pool,
2525
handles: std.StringArrayHashMapUnmanaged(*Handle) = .empty,
26-
build_files: std.StringArrayHashMapUnmanaged(*BuildFile) = .empty,
27-
cimports: std.AutoArrayHashMapUnmanaged(Hash, translate_c.Result) = .empty,
26+
build_files: if (supports_build_system) std.StringArrayHashMapUnmanaged(*BuildFile) else void = if (supports_build_system) .empty else {},
27+
cimports: if (supports_build_system) std.AutoArrayHashMapUnmanaged(Hash, translate_c.Result) else void = if (supports_build_system) .empty else {},
2828
diagnostics_collection: *DiagnosticsCollection,
2929
builds_in_progress: std.atomic.Value(i32) = .init(0),
3030
transport: ?lsp.AnyTransport = null,
@@ -41,6 +41,8 @@ pub const Hash = [Hasher.mac_length]u8;
4141

4242
pub const max_document_size = std.math.maxInt(u32);
4343

44+
pub const supports_build_system = std.process.can_spawn;
45+
4446
pub fn computeHash(bytes: []const u8) Hash {
4547
var hasher: Hasher = .init(&@splat(0));
4648
hasher.update(bytes);
@@ -316,6 +318,7 @@ pub const Handle = struct {
316318
/// `DocumentStore.build_files` is guaranteed to contain this Uri.
317319
/// Uri memory managed by its build_file
318320
pub fn getAssociatedBuildFileUri(self: *Handle, document_store: *DocumentStore) error{OutOfMemory}!?Uri {
321+
comptime std.debug.assert(supports_build_system);
319322
switch (try self.getAssociatedBuildFileUri2(document_store)) {
320323
.none,
321324
.unresolved,
@@ -336,6 +339,8 @@ pub const Handle = struct {
336339
/// The associated build file (build.zig) has been successfully resolved.
337340
resolved: *BuildFile,
338341
} {
342+
comptime std.debug.assert(supports_build_system);
343+
339344
self.impl.lock.lock();
340345
defer self.impl.lock.unlock();
341346

@@ -634,16 +639,19 @@ pub fn deinit(self: *DocumentStore) void {
634639
}
635640
self.handles.deinit(self.allocator);
636641

637-
for (self.build_files.values()) |build_file| {
638-
build_file.deinit(self.allocator);
639-
self.allocator.destroy(build_file);
640-
}
641-
self.build_files.deinit(self.allocator);
642+
if (supports_build_system) {
643+
for (self.build_files.values()) |build_file| {
644+
build_file.deinit(self.allocator);
645+
self.allocator.destroy(build_file);
646+
}
647+
self.build_files.deinit(self.allocator);
642648

643-
for (self.cimports.values()) |*result| {
644-
result.deinit(self.allocator);
649+
for (self.cimports.values()) |*result| {
650+
result.deinit(self.allocator);
651+
}
652+
self.cimports.deinit(self.allocator);
645653
}
646-
self.cimports.deinit(self.allocator);
654+
647655
self.* = undefined;
648656
}
649657

@@ -718,6 +726,7 @@ pub fn getOrLoadHandle(self: *DocumentStore, uri: Uri) ?*Handle {
718726
/// **Thread safe** takes a shared lock
719727
/// This function does not protect against data races from modifying the BuildFile
720728
pub fn getBuildFile(self: *DocumentStore, uri: Uri) ?*BuildFile {
729+
comptime std.debug.assert(supports_build_system);
721730
self.lock.lockShared();
722731
defer self.lock.unlockShared();
723732
return self.build_files.get(uri);
@@ -727,6 +736,8 @@ pub fn getBuildFile(self: *DocumentStore, uri: Uri) ?*BuildFile {
727736
/// **Thread safe** takes an exclusive lock
728737
/// This function does not protect against data races from modifying the BuildFile
729738
fn getOrLoadBuildFile(self: *DocumentStore, uri: Uri) ?*BuildFile {
739+
comptime std.debug.assert(supports_build_system);
740+
730741
if (self.getBuildFile(uri)) |build_file| return build_file;
731742

732743
const new_build_file: *BuildFile = blk: {
@@ -754,9 +765,7 @@ fn getOrLoadBuildFile(self: *DocumentStore, uri: Uri) ?*BuildFile {
754765

755766
// this code path is only reached when the build file is new
756767

757-
if (std.process.can_spawn) {
758-
self.invalidateBuildFile(new_build_file.uri);
759-
}
768+
self.invalidateBuildFile(new_build_file.uri);
760769

761770
return new_build_file;
762771
}
@@ -817,8 +826,10 @@ pub fn closeDocument(self: *DocumentStore, uri: Uri) void {
817826
defer self.lock.unlock();
818827

819828
self.garbageCollectionImports() catch {};
820-
self.garbageCollectionCImports() catch {};
821-
self.garbageCollectionBuildFiles() catch {};
829+
if (supports_build_system) {
830+
self.garbageCollectionCImports() catch {};
831+
self.garbageCollectionBuildFiles() catch {};
832+
}
822833
}
823834

824835
/// Takes ownership of `new_text` which has to be allocated with this DocumentStore's allocator.
@@ -864,7 +875,7 @@ pub fn refreshDocumentFromFileSystem(self: *DocumentStore, uri: Uri) !bool {
864875
/// Invalidates a build files.
865876
/// **Thread safe** takes a shared lock
866877
pub fn invalidateBuildFile(self: *DocumentStore, build_file_uri: Uri) void {
867-
comptime std.debug.assert(std.process.can_spawn);
878+
comptime std.debug.assert(supports_build_system);
868879

869880
if (self.config.zig_exe_path == null) return;
870881
if (self.config.build_runner_path == null) return;
@@ -1464,7 +1475,9 @@ fn createDocument(self: *DocumentStore, uri: Uri, text: [:0]const u8, open: bool
14641475

14651476
_ = handle.setOpen(open);
14661477

1467-
if (isBuildFile(handle.uri) and !isInStd(handle.uri)) {
1478+
if (!supports_build_system) {
1479+
// nothing to do
1480+
} else if (isBuildFile(handle.uri) and !isInStd(handle.uri)) {
14681481
_ = self.getOrLoadBuildFile(handle.uri);
14691482
} else if (!isBuiltinFile(handle.uri) and !isInStd(handle.uri)) blk: {
14701483
const potential_build_files = self.collectPotentialBuildFiles(uri) catch {
@@ -1614,6 +1627,8 @@ fn collectDependenciesInternal(
16141627
const tracy_zone = tracy.trace(@src());
16151628
defer tracy_zone.end();
16161629

1630+
if (!supports_build_system) return;
1631+
16171632
{
16181633
if (lock) store.lock.lockShared();
16191634
defer if (lock) store.lock.unlockShared();
@@ -1656,6 +1671,8 @@ pub fn collectIncludeDirs(
16561671
handle: *Handle,
16571672
include_dirs: *std.ArrayListUnmanaged([]const u8),
16581673
) !bool {
1674+
comptime std.debug.assert(supports_build_system);
1675+
16591676
var arena_allocator: std.heap.ArenaAllocator = .init(allocator);
16601677
defer arena_allocator.deinit();
16611678

@@ -1695,6 +1712,8 @@ pub fn collectCMacros(
16951712
handle: *Handle,
16961713
c_macros: *std.ArrayListUnmanaged([]const u8),
16971714
) !bool {
1715+
comptime std.debug.assert(supports_build_system);
1716+
16981717
const collected_all = switch (try handle.getAssociatedBuildFileUri2(store)) {
16991718
.none => true,
17001719
.unresolved => false,
@@ -1719,10 +1738,11 @@ pub fn collectCMacros(
17191738
/// returned memory is owned by DocumentStore
17201739
/// **Thread safe** takes an exclusive lock
17211740
pub fn resolveCImport(self: *DocumentStore, handle: *Handle, node: Ast.Node.Index) error{OutOfMemory}!?Uri {
1741+
comptime std.debug.assert(supports_build_system);
1742+
17221743
const tracy_zone = tracy.trace(@src());
17231744
defer tracy_zone.end();
17241745

1725-
if (!std.process.can_spawn) return null;
17261746
if (self.config.zig_exe_path == null) return null;
17271747
if (self.config.zig_lib_dir == null) return null;
17281748
if (self.config.global_cache_dir == null) return null;
@@ -1891,17 +1911,21 @@ pub fn uriFromImportStr(self: *DocumentStore, allocator: std.mem.Allocator, hand
18911911

18921912
return try URI.fromPath(allocator, std_path);
18931913
} else if (std.mem.eql(u8, import_str, "builtin")) {
1894-
if (try handle.getAssociatedBuildFileUri(self)) |build_file_uri| {
1895-
const build_file = self.getBuildFile(build_file_uri).?;
1896-
if (build_file.builtin_uri) |builtin_uri| {
1897-
return try allocator.dupe(u8, builtin_uri);
1914+
if (supports_build_system) {
1915+
if (try handle.getAssociatedBuildFileUri(self)) |build_file_uri| {
1916+
const build_file = self.getBuildFile(build_file_uri).?;
1917+
if (build_file.builtin_uri) |builtin_uri| {
1918+
return try allocator.dupe(u8, builtin_uri);
1919+
}
18981920
}
18991921
}
19001922
if (self.config.builtin_path) |builtin_path| {
19011923
return try URI.fromPath(allocator, builtin_path);
19021924
}
19031925
return null;
19041926
} else if (!std.mem.endsWith(u8, import_str, ".zig")) {
1927+
if (!supports_build_system) return null;
1928+
19051929
if (try handle.getAssociatedBuildFileUri(self)) |build_file_uri| blk: {
19061930
const build_file = self.getBuildFile(build_file_uri).?;
19071931
const build_config = build_file.tryLockConfig() orelse break :blk;

src/Server.zig

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@ fn initializeHandler(server: *Server, arena: std.mem.Allocator, request: types.I
527527
}
528528
}
529529

530+
// TODO Instead of checking `is_test`, possible config paths should be provided by the main function.
530531
if (!zig_builtin.is_test) {
531532
var maybe_config_result = if (server.config_path) |config_path|
532533
configuration.loadFromFile(server.allocator, config_path)
@@ -877,9 +878,12 @@ fn removeWorkspace(server: *Server, uri: types.URI) void {
877878
fn didChangeWatchedFilesHandler(server: *Server, arena: std.mem.Allocator, notification: types.DidChangeWatchedFilesParams) Error!void {
878879
var updated_files: usize = 0;
879880
for (notification.changes) |change| {
880-
const file_path = Uri.parse(arena, change.uri) catch |err| {
881-
log.err("failed to parse URI '{s}': {}", .{ change.uri, err });
882-
continue;
881+
const file_path = Uri.parse(arena, change.uri) catch |err| switch (err) {
882+
error.UnsupportedScheme => continue,
883+
else => {
884+
log.err("failed to parse URI '{s}': {}", .{ change.uri, err });
885+
continue;
886+
},
883887
};
884888
const file_extension = std.fs.path.extension(file_path);
885889
if (!std.mem.eql(u8, file_extension, ".zig") and !std.mem.eql(u8, file_extension, ".zon")) continue;
@@ -1079,7 +1083,7 @@ pub fn updateConfiguration(
10791083
}
10801084
}
10811085

1082-
if (new_zig_exe_path or new_zig_lib_path) {
1086+
if (DocumentStore.supports_build_system and (new_zig_exe_path or new_zig_lib_path)) {
10831087
for (server.document_store.cimports.values()) |*result| {
10841088
result.deinit(server.document_store.allocator);
10851089
}
@@ -1148,14 +1152,6 @@ pub fn updateConfiguration(
11481152
}
11491153
}
11501154

1151-
if (server.config.prefer_ast_check_as_child_process) {
1152-
if (!std.process.can_spawn) {
1153-
log.info("'prefer_ast_check_as_child_process' is ignored because the '{s}' operating system can't spawn child processes", .{@tagName(zig_builtin.target.os.tag)});
1154-
} else if (server.status == .initialized and server.config.zig_exe_path == null) {
1155-
log.warn("'prefer_ast_check_as_child_process' is ignored because Zig could not be found", .{});
1156-
}
1157-
}
1158-
11591155
if (server.config.enable_build_on_save orelse false) {
11601156
if (!BuildOnSaveSupport.isSupportedComptime()) {
11611157
// This message is not very helpful but it relatively uncommon to happen anyway.

src/analysis.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2150,6 +2150,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) error
21502150
}
21512151

21522152
if (std.mem.eql(u8, call_name, "@cImport")) {
2153+
if (!DocumentStore.supports_build_system) return null;
21532154
const cimport_uri = (try analyser.store.resolveCImport(handle, node)) orelse return null;
21542155

21552156
const new_handle = analyser.store.getOrLoadHandle(cimport_uri) orelse return null;

src/configuration.zig

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,21 @@ const Config = @import("Config.zig");
1010

1111
const logger = std.log.scoped(.config);
1212

13-
pub fn getLocalConfigPath(allocator: std.mem.Allocator) known_folders.Error!?[]const u8 {
13+
fn getLocalConfigPath(allocator: std.mem.Allocator) known_folders.Error!?[]const u8 {
1414
const folder_path = try known_folders.getPath(allocator, .local_configuration) orelse return null;
1515
defer allocator.free(folder_path);
1616
return try std.fs.path.join(allocator, &.{ folder_path, "zls.json" });
1717
}
1818

19-
pub fn getGlobalConfigPath(allocator: std.mem.Allocator) known_folders.Error!?[]const u8 {
19+
fn getGlobalConfigPath(allocator: std.mem.Allocator) known_folders.Error!?[]const u8 {
2020
const folder_path = try known_folders.getPath(allocator, .global_configuration) orelse return null;
2121
defer allocator.free(folder_path);
2222
return try std.fs.path.join(allocator, &.{ folder_path, "zls.json" });
2323
}
2424

2525
pub fn load(allocator: std.mem.Allocator) error{OutOfMemory}!LoadConfigResult {
26+
if (builtin.target.os.tag == .wasi) return .not_found;
27+
2628
const local_config_path = getLocalConfigPath(allocator) catch |err| blk: {
2729
logger.warn("failed to resolve local configuration path: {}", .{err});
2830
break :blk null;

src/features/completions.zig

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,7 @@ fn completeFileSystemStringLiteral(builder: *Builder, pos_context: Analyser.Posi
722722
if (std.fs.path.isAbsolute(completing) and pos_context != .import_string_literal) {
723723
try search_paths.append(builder.arena, completing);
724724
} else if (pos_context == .cinclude_string_literal) {
725+
if (!DocumentStore.supports_build_system) return;
725726
_ = store.collectIncludeDirs(builder.arena, builder.orig_handle, &search_paths) catch |err| {
726727
log.err("failed to resolve include paths: {}", .{err});
727728
return;
@@ -782,31 +783,35 @@ fn completeFileSystemStringLiteral(builder: *Builder, pos_context: Analyser.Posi
782783
}
783784

784785
if (completing.len == 0 and pos_context == .import_string_literal) {
785-
if (try builder.orig_handle.getAssociatedBuildFileUri(store)) |uri| blk: {
786-
const build_file = store.getBuildFile(uri).?;
787-
const build_config = build_file.tryLockConfig() orelse break :blk;
788-
defer build_file.unlockConfig();
789-
790-
try completions.ensureUnusedCapacity(builder.arena, build_config.packages.len);
791-
for (build_config.packages) |pkg| {
792-
completions.putAssumeCapacity(.{
793-
.label = pkg.name,
794-
.kind = .Module,
795-
.detail = pkg.path,
796-
}, {});
797-
}
798-
} else if (DocumentStore.isBuildFile(builder.orig_handle.uri)) blk: {
799-
const build_file = store.getBuildFile(builder.orig_handle.uri) orelse break :blk;
800-
const build_config = build_file.tryLockConfig() orelse break :blk;
801-
defer build_file.unlockConfig();
802-
803-
try completions.ensureUnusedCapacity(builder.arena, build_config.deps_build_roots.len);
804-
for (build_config.deps_build_roots) |dbr| {
805-
completions.putAssumeCapacity(.{
806-
.label = dbr.name,
807-
.kind = .Module,
808-
.detail = dbr.path,
809-
}, {});
786+
no_modules: {
787+
if (!DocumentStore.supports_build_system) break :no_modules;
788+
789+
if (try builder.orig_handle.getAssociatedBuildFileUri(store)) |uri| {
790+
const build_file = store.getBuildFile(uri).?;
791+
const build_config = build_file.tryLockConfig() orelse break :no_modules;
792+
defer build_file.unlockConfig();
793+
794+
try completions.ensureUnusedCapacity(builder.arena, build_config.packages.len);
795+
for (build_config.packages) |pkg| {
796+
completions.putAssumeCapacity(.{
797+
.label = pkg.name,
798+
.kind = .Module,
799+
.detail = pkg.path,
800+
}, {});
801+
}
802+
} else if (DocumentStore.isBuildFile(builder.orig_handle.uri)) {
803+
const build_file = store.getBuildFile(builder.orig_handle.uri) orelse break :no_modules;
804+
const build_config = build_file.tryLockConfig() orelse break :no_modules;
805+
defer build_file.unlockConfig();
806+
807+
try completions.ensureUnusedCapacity(builder.arena, build_config.deps_build_roots.len);
808+
for (build_config.deps_build_roots) |dbr| {
809+
completions.putAssumeCapacity(.{
810+
.label = dbr.name,
811+
.kind = .Module,
812+
.detail = dbr.path,
813+
}, {});
814+
}
810815
}
811816
}
812817

src/features/goto.zig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ fn gotoDefinitionBuiltin(
129129
const name_loc = offsets.tokenIndexToLoc(handle.tree.source, loc.start);
130130
const name = offsets.locToSlice(handle.tree.source, name_loc);
131131
if (std.mem.eql(u8, name, "@cImport")) {
132+
if (!DocumentStore.supports_build_system) return null;
133+
132134
const tree = handle.tree;
133135
const index = for (handle.cimports.items(.node), 0..) |cimport_node, index| {
134136
const main_token = tree.nodeMainToken(cimport_node);
@@ -205,6 +207,8 @@ fn gotoDefinitionString(
205207
.cinclude_string_literal => try URI.fromPath(
206208
arena,
207209
blk: {
210+
if (!DocumentStore.supports_build_system) return null;
211+
208212
if (std.fs.path.isAbsolute(import_str)) break :blk import_str;
209213
var include_dirs: std.ArrayListUnmanaged([]const u8) = .empty;
210214
_ = document_store.collectIncludeDirs(arena, handle, &include_dirs) catch |err| {

src/main.zig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ fn logFn(
100100
}
101101

102102
fn defaultLogFilePath(allocator: std.mem.Allocator) std.mem.Allocator.Error!?[]const u8 {
103+
if (zig_builtin.target.os.tag == .wasi) return null;
103104
const cache_path = known_folders.getPath(allocator, .cache) catch |err| switch (err) {
104105
error.OutOfMemory => return error.OutOfMemory,
105106
error.ParseError => return null,
@@ -282,7 +283,7 @@ fn parseArgs(allocator: std.mem.Allocator) ParseArgsError!ParseArgsResult {
282283
}
283284
}
284285

285-
if (std.io.getStdIn().isTty()) {
286+
if (zig_builtin.target.os.tag != .wasi and std.io.getStdIn().isTty()) {
286287
log.warn("ZLS is not a CLI tool, it communicates over the Language Server Protocol.", .{});
287288
log.warn("Did you mean to run 'zls --help'?", .{});
288289
log.warn("", .{});

src/uri.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ test fromPath {
6969

7070
/// Parses a Uri and returns the unescaped path
7171
/// Caller owns the returned memory
72-
pub fn parse(allocator: std.mem.Allocator, str: []const u8) (std.Uri.ParseError || error{OutOfMemory})![]u8 {
72+
pub fn parse(allocator: std.mem.Allocator, str: []const u8) (std.Uri.ParseError || error{ UnsupportedScheme, OutOfMemory })![]u8 {
7373
var uri = try std.Uri.parse(str);
74-
if (!std.mem.eql(u8, uri.scheme, "file")) return error.InvalidFormat;
74+
if (!std.mem.eql(u8, uri.scheme, "file")) return error.UnsupportedScheme;
7575
if (builtin.os.tag == .windows and uri.path.percent_encoded.len != 0 and uri.path.percent_encoded[0] == '/') {
7676
uri.path.percent_encoded = uri.path.percent_encoded[1..];
7777
}

0 commit comments

Comments
 (0)