Skip to content
Merged
534 changes: 354 additions & 180 deletions src/DocumentStore.zig

Large diffs are not rendered by default.

60 changes: 38 additions & 22 deletions src/analysis.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2289,23 +2289,16 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) error
const import_param = params[0];
if (tree.nodeTag(import_param) != .string_literal) return null;

const import_str = tree.tokenSlice(tree.nodeMainToken(import_param));
const import_uri = (try analyser.store.uriFromImportStr(
analyser.arena,
handle,
import_str[1 .. import_str.len - 1],
)) orelse (try analyser.store.uriFromImportStr(
analyser.arena,
analyser.root_handle orelse return null,
import_str[1 .. import_str.len - 1],
)) orelse return null;

const new_handle = analyser.store.getOrLoadHandle(import_uri) orelse return null;
const string_literal = tree.tokenSlice(tree.nodeMainToken(import_param));
const import_string = string_literal[1 .. string_literal.len - 1];
if (std.mem.endsWith(u8, import_string, ".zon")) {
// TODO
return null;
}

return .{
.data = .{ .container = .root(new_handle) },
.is_type_val = true,
};
if (try analyser.resolveImportString(handle, import_string)) |ty| return ty;
if (try analyser.resolveImportString(analyser.root_handle orelse return null, import_string)) |ty| return ty;
return null;
},
.c_import => {
if (!DocumentStore.supports_build_system) return null;
Expand Down Expand Up @@ -4439,6 +4432,34 @@ pub const ScopeWithHandle = struct {
}
};

pub fn resolveImportString(analyser: *Analyser, handle: *DocumentStore.Handle, import_string: []const u8) error{OutOfMemory}!?Type {
const result = try analyser.store.uriFromImportStr(analyser.arena, handle, import_string);
switch (result) {
.none => return null,
.one => |uri| {
const node_handle = analyser.store.getOrLoadHandle(uri) orelse return null;
return .{
.data = .{ .container = .root(node_handle) },
.is_type_val = true,
};
},
.many => |uris| {
var entries: std.ArrayList(Type.Data.EitherEntry) = try .initCapacity(analyser.arena, uris.len);
for (uris) |uri| {
const node_handle = analyser.store.getOrLoadHandle(uri) orelse continue;
entries.appendAssumeCapacity(.{
.type_data = .{ .container = .root(node_handle) },
.descriptor = "",
});
}
return .{
.data = .{ .either = entries.items },
.is_type_val = true,
};
},
}
}

/// Look up `type_name` in 'zig_lib_dir/std/builtin.zig' and return it as an instance
/// Useful for functionality related to builtin fns
pub fn instanceStdBuiltinType(analyser: *Analyser, type_name: []const u8) error{OutOfMemory}!?Type {
Expand Down Expand Up @@ -4713,12 +4734,7 @@ pub fn getFieldAccessType(
.start = import_str_tok.loc.start + 1,
.end = import_str_tok.loc.end - 1,
});
const import_uri = try analyser.store.uriFromImportStr(analyser.arena, handle, import_str) orelse return null;
const node_handle = analyser.store.getOrLoadHandle(import_uri) orelse return null;
current_type = .{
.data = .{ .container = .root(node_handle) },
.is_type_val = true,
};
current_type = try analyser.resolveImportString(handle, import_str) orelse return null;
_ = tokenizer.next(); // eat the .r_paren
continue; // Outermost `while`
}
Expand Down
648 changes: 323 additions & 325 deletions src/build_runner/build_runner.zig

Large diffs are not rendered by default.

31 changes: 21 additions & 10 deletions src/build_runner/shared.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,33 @@

const std = @import("std");
const builtin = @import("builtin");
const native_endian = builtin.target.cpu.arch.endian();
const need_bswap = native_endian != .little;

pub const BuildConfig = struct {
deps_build_roots: []DepsBuildRoots,
packages: []Package,
include_dirs: []const []const u8,
/// The `dependencies` in `build.zig.zon`.
dependencies: std.json.ArrayHashMap([]const u8),
/// The key is the `root_source_file`.
/// All modules with the same root source file are merged. This limitation may be lifted in the future.
modules: std.json.ArrayHashMap(Module),
/// List of all compilations units.
compilations: []const Compile,
/// The names of all top level steps.
top_level_steps: []const []const u8,
available_options: std.json.ArrayHashMap(AvailableOption),
c_macros: []const []const u8 = &.{},

pub const DepsBuildRoots = Package;
pub const Package = struct {
name: []const u8,
path: []const u8,
pub const Module = struct {
import_table: std.json.ArrayHashMap([]const u8),
c_macros: []const []const u8,
include_dirs: []const []const u8,
};

pub const Compile = struct {
/// Key in `BuildConfig.modules`.
root_module: []const u8,

// may contain additional information in the future like `target` or `link_libc`.
};

/// Equivalent to `std.Build.AvailableOption` which is not accessible because it non-pub.
pub const AvailableOption = @FieldType(@FieldType(std.Build, "available_options_map").KV, "value");
};

Expand Down
173 changes: 94 additions & 79 deletions src/features/completions.zig
Original file line number Diff line number Diff line change
Expand Up @@ -570,10 +570,6 @@ fn completeFieldAccess(builder: *Builder, loc: offsets.Loc) error{OutOfMemory}!v

fn kindToSortScore(kind: types.completion.Item.Kind) []const u8 {
return switch (kind) {
.Module => "1", // used for packages
.Folder => "2",
.File => "3",

.Operator => "1",
.Field, .EnumMember => "2",
.Method => "3",
Expand All @@ -588,6 +584,11 @@ fn kindToSortScore(kind: types.completion.Item.Kind) []const u8 {
.Snippet => "6",
.Keyword => "7",

// Used for `@import` completions. Must be set in `completeFileSystemStringLiteral`.
.Module => unreachable,
.Folder => unreachable,
.File => unreachable,

else => unreachable,
};
}
Expand Down Expand Up @@ -687,7 +688,6 @@ fn completeDot(builder: *Builder, loc: offsets.Loc) error{OutOfMemory}!void {
fn completeFileSystemStringLiteral(builder: *Builder, pos_context: Analyser.PositionContext) error{OutOfMemory}!void {
const io = builder.server.io;

var completions: CompletionSet = .empty;
const store = &builder.server.document_store;
const source = builder.orig_handle.tree.source;

Expand All @@ -713,6 +713,89 @@ fn completeFileSystemStringLiteral(builder: *Builder, pos_context: Analyser.Posi

const completing = offsets.locToSlice(source, .{ .start = string_content_loc.start, .end = previous_separator_index orelse string_content_loc.start });

const after_separator_index = if (previous_separator_index) |index| index + 1 else string_content_loc.start;
const insert_loc: offsets.Loc = .{ .start = after_separator_index, .end = builder.source_index };
const replace_loc: offsets.Loc = .{ .start = after_separator_index, .end = next_separator_index orelse string_content_loc.end };

const insert_range = offsets.locToRange(source, insert_loc, builder.server.offset_encoding);
const replace_range = offsets.locToRange(source, replace_loc, builder.server.offset_encoding);

if (pos_context == .import_string_literal) {
try builder.completions.ensureUnusedCapacity(builder.arena, 2);
if (store.config.zig_lib_dir) |zig_lib_dir| {
builder.completions.appendAssumeCapacity(.{
.label = "std",
.kind = .Module,
.detail = zig_lib_dir.path,
.sortText = "1",
});
}
if (store.config.builtin_path) |builtin_path| {
builder.completions.appendAssumeCapacity(.{
.label = "builtin",
.kind = .Module,
.detail = builtin_path,
.sortText = "2",
});
}

if (!DocumentStore.supports_build_system) {
// no build system modules
} else if (DocumentStore.isBuildFile(builder.orig_handle.uri)) blk: {
const build_file = store.getBuildFile(builder.orig_handle.uri) orelse break :blk;
const build_config = build_file.tryLockConfig(store.io) orelse break :blk;
defer build_file.unlockConfig(store.io);

try builder.completions.ensureUnusedCapacity(builder.arena, build_config.dependencies.map.count());
for (build_config.dependencies.map.keys(), build_config.dependencies.map.values()) |name, path| {
try builder.completions.append(builder.arena, .{
.label = name,
.kind = .Module,
.detail = path,
.sortText = "4",
});
}
} else switch (try builder.orig_handle.getAssociatedBuildFile(store)) {
.none, .unresolved => {},
.resolved => |resolved| blk: {
const build_config = resolved.build_file.tryLockConfig(store.io) orelse break :blk;
defer resolved.build_file.unlockConfig(store.io);

const module = build_config.modules.map.get(resolved.root_source_file) orelse break :blk;

try builder.completions.ensureUnusedCapacity(builder.arena, 1 + module.import_table.map.count());

builder.completions.appendAssumeCapacity(.{
.label = "root",
.kind = .Module,
.detail = try builder.arena.dupe(u8, resolved.root_source_file),
.sortText = "3",
});

for (module.import_table.map.keys(), module.import_table.map.values()) |name, root_source_file| {
builder.completions.appendAssumeCapacity(.{
.label = try builder.arena.dupe(u8, name),
.kind = .Module,
.detail = try builder.arena.dupe(u8, root_source_file),
.sortText = "4",
});
}
},
}

const string_content_range = offsets.locToRange(source, string_content_loc, builder.server.offset_encoding);

// completions on module replace the entire string literal
for (builder.completions.items) |*item| {
if (item.kind == .Module and item.textEdit == null) {
item.textEdit = if (builder.server.client_capabilities.supports_completion_insert_replace_support)
.{ .insert_replace_edit = .{ .newText = item.label, .insert = insert_range, .replace = string_content_range } }
else
.{ .text_edit = .{ .newText = item.label, .range = insert_range } };
}
}
}

var search_paths: std.ArrayList([]const u8) = .empty;
if (std.fs.path.isAbsolute(completing) and pos_context != .import_string_literal) {
try search_paths.append(builder.arena, completing);
Expand All @@ -730,13 +813,6 @@ fn completeFileSystemStringLiteral(builder: *Builder, pos_context: Analyser.Posi
try search_paths.append(builder.arena, std.fs.path.dirname(document_path).?);
}

const after_separator_index = if (previous_separator_index) |index| index + 1 else string_content_loc.start;
const insert_loc: offsets.Loc = .{ .start = after_separator_index, .end = builder.source_index };
const replace_loc: offsets.Loc = .{ .start = after_separator_index, .end = next_separator_index orelse string_content_loc.end };

const insert_range = offsets.locToRange(source, insert_loc, builder.server.offset_encoding);
const replace_range = offsets.locToRange(source, replace_loc, builder.server.offset_encoding);

for (search_paths.items) |path| {
if (!std.fs.path.isAbsolute(path)) continue;
const dir_path = if (std.fs.path.isAbsolute(completing)) path else try std.fs.path.join(builder.arena, &.{ path, completing });
Expand Down Expand Up @@ -768,81 +844,18 @@ fn completeFileSystemStringLiteral(builder: *Builder, pos_context: Analyser.Posi
else
label;

_ = try completions.getOrPut(builder.arena, .{
try builder.completions.append(builder.arena, .{
.label = label,
.kind = if (entry.kind == .file) .File else .Folder,
.detail = if (pos_context == .cinclude_string_literal) path else null,
.textEdit = if (builder.server.client_capabilities.supports_completion_insert_replace_support)
.{ .insert_replace_edit = .{ .newText = insert_text, .insert = insert_range, .replace = replace_range } }
else
.{ .text_edit = .{ .newText = insert_text, .range = insert_range } },
.sortText = if (entry.kind == .file) "6" else "5",
});
}
}

if (completing.len == 0 and pos_context == .import_string_literal) {
no_modules: {
if (!DocumentStore.supports_build_system) break :no_modules;

if (DocumentStore.isBuildFile(builder.orig_handle.uri)) {
const build_file = store.getBuildFile(builder.orig_handle.uri) orelse break :no_modules;
const build_config = build_file.tryLockConfig(store.io) orelse break :no_modules;
defer build_file.unlockConfig(store.io);

try completions.ensureUnusedCapacity(builder.arena, build_config.deps_build_roots.len);
for (build_config.deps_build_roots) |dbr| {
completions.putAssumeCapacity(.{
.label = dbr.name,
.kind = .Module,
.detail = dbr.path,
}, {});
}
} else if (try builder.orig_handle.getAssociatedBuildFileUri(store)) |uri| {
const build_file = store.getBuildFile(uri).?;
const build_config = build_file.tryLockConfig(store.io) orelse break :no_modules;
defer build_file.unlockConfig(store.io);

try completions.ensureUnusedCapacity(builder.arena, build_config.packages.len);
for (build_config.packages) |pkg| {
completions.putAssumeCapacity(.{
.label = pkg.name,
.kind = .Module,
.detail = pkg.path,
}, {});
}
}
}

try completions.ensureUnusedCapacity(builder.arena, 2);
if (store.config.zig_lib_dir) |zig_lib_dir| {
completions.putAssumeCapacity(.{
.label = "std",
.kind = .Module,
.detail = zig_lib_dir.path,
}, {});
}
if (store.config.builtin_path) |builtin_path| {
completions.putAssumeCapacity(.{
.label = "builtin",
.kind = .Module,
.detail = builtin_path,
}, {});
}

const string_content_range = offsets.locToRange(source, string_content_loc, builder.server.offset_encoding);

// completions on module replace the entire string literal
for (completions.keys()) |*item| {
if (item.kind == .Module and item.textEdit == null) {
item.textEdit = if (builder.server.client_capabilities.supports_completion_insert_replace_support)
.{ .insert_replace_edit = .{ .newText = item.label, .insert = insert_range, .replace = string_content_range } }
else
.{ .text_edit = .{ .newText = item.label, .range = insert_range } };
}
}
}

try builder.completions.appendSlice(builder.arena, completions.keys());
}

pub fn completionAtIndex(
Expand Down Expand Up @@ -921,8 +934,10 @@ pub fn completionAtIndex(
}
}

const score = kindToSortScore(item.kind.?);
item.sortText = try std.fmt.allocPrint(arena, "{s}_{s}", .{ score, item.label });
if (item.sortText == null) {
const score = kindToSortScore(item.kind.?);
item.sortText = try std.fmt.allocPrint(arena, "{s}_{s}", .{ score, item.label });
}
}

return .{ .isIncomplete = false, .items = completions };
Expand Down
Loading