Added zig_exe_path option, use it to detect lib path if none is provided

This commit is contained in:
Alexandros Naskos 2020-05-30 23:36:18 +03:00
parent c9af33011a
commit 543cb5816c
4 changed files with 45 additions and 19 deletions

View File

@ -55,6 +55,7 @@ The following options are currently available.
| --- | --- | --- | --- |
| `enable_snippets` | `bool` | `false` | Enables snippet completion, set to false for compatibility with language clients that do not support snippets (such as ale). |
| `zig_lib_path` | `?[]const u8` | `null` | zig library path, e.g. `/path/to/zig/lib/zig`, used to analyze std library imports. |
| `zig_exe_path` | `?[]const u8` | `null` | zig executable path, e.g. `/path/to/zig/bin/zig`, used to run the custom build runner. If `null`, zig is looked up in `PATH`. Will be used to infer the zig standard library path if none is provided. |
| `warn_style` | `bool` | `false` | Enables warnings for style *guideline* mismatches |
| `build_runner_path` | `?[]const u8` | `null` | Path to the build_runner.zig file provided by zls. This option must be present in one of the global configuration files to have any effect. `null` is equivalent to `${executable_directory}/build_runner.zig` |

View File

@ -6,6 +6,10 @@ enable_snippets: bool = false,
/// zig library path
zig_lib_path: ?[]const u8 = null,
/// zig executable path used to run the custom build runner.
/// May be used to find a lib path if none is provided.
zig_exe_path: ?[]const u8 = null,
/// Whether to pay attention to style issues. This is opt-in since the style
/// guide explicitly states that the style info provided is a guideline only.
warn_style: bool = false,

View File

@ -74,7 +74,7 @@ pub const TagStore = struct {
allocator: *std.mem.Allocator,
handles: std.StringHashMap(*Handle),
has_zig: bool,
zig_exe_path: ?[]const u8,
build_files: std.ArrayListUnmanaged(*BuildFile),
build_runner_path: []const u8,
@ -84,12 +84,12 @@ enum_completions: TagStore,
pub fn init(
self: *DocumentStore,
allocator: *std.mem.Allocator,
has_zig: bool,
zig_exe_path: ?[]const u8,
build_runner_path: []const u8,
) !void {
self.allocator = allocator;
self.handles = std.StringHashMap(*Handle).init(allocator);
self.has_zig = has_zig;
self.zig_exe_path = zig_exe_path;
self.build_files = .{};
self.build_runner_path = build_runner_path;
self.error_completions = TagStore.init(allocator);
@ -100,12 +100,14 @@ const LoadPackagesContext = struct {
build_file: *BuildFile,
allocator: *std.mem.Allocator,
build_runner_path: []const u8,
zig_exe_path: []const u8,
};
fn loadPackages(context: LoadPackagesContext) !void {
const allocator = context.allocator;
const build_file = context.build_file;
const build_runner_path = context.build_runner_path;
const zig_exe_path = context.zig_exe_path;
const directory_path = try URI.parse(allocator, build_file.uri[0 .. build_file.uri.len - "build.zig".len]);
defer allocator.free(directory_path);
@ -134,7 +136,7 @@ fn loadPackages(context: LoadPackagesContext) !void {
const zig_run_result = try std.ChildProcess.exec(.{
.allocator = allocator,
.argv = &[_][]const u8{ "zig", "run", "build_runner.zig" },
.argv = &[_][]const u8{ zig_exe_path, "run", "build_runner.zig" },
.cwd = directory_path,
});
@ -204,7 +206,7 @@ fn newDocument(self: *DocumentStore, uri: []const u8, text: []u8) anyerror!*Hand
// TODO: Better logic for detecting std or subdirectories?
const in_std = std.mem.indexOf(u8, uri, "/std/") != null;
if (self.has_zig and std.mem.endsWith(u8, uri, "/build.zig") and !in_std) {
if (self.zig_exe_path != null and std.mem.endsWith(u8, uri, "/build.zig") and !in_std) {
std.debug.warn("Document is a build file, extracting packages...\n", .{});
// This is a build file.
var build_file = try self.allocator.create(BuildFile);
@ -225,10 +227,11 @@ fn newDocument(self: *DocumentStore, uri: []const u8, text: []u8) anyerror!*Hand
.build_file = build_file,
.allocator = self.allocator,
.build_runner_path = self.build_runner_path,
.zig_exe_path = self.zig_exe_path.?,
}) catch |err| {
std.debug.warn("Failed to load packages of build file {} (error: {})\n", .{ build_file.uri, err });
};
} else if (self.has_zig and !in_std) associate_build_file: {
} else if (self.zig_exe_path != null and !in_std) associate_build_file: {
// Look into build files to see if we already have one that fits
for (self.build_files.items) |build_file| {
const build_file_base_uri = build_file.uri[0 .. std.mem.lastIndexOfScalar(u8, build_file.uri, '/').? + 1];
@ -471,6 +474,7 @@ pub fn applyChanges(
.build_file = build_file,
.allocator = self.allocator,
.build_runner_path = self.build_runner_path,
.zig_exe_path = self.zig_exe_path.?,
}) catch |err| {
std.debug.warn("Failed to load packages of build file {} (error: {})\n", .{ build_file.uri, err });
};

View File

@ -533,9 +533,7 @@ fn documentSymbol(id: i64, handle: *DocumentStore.Handle) !void {
try send(types.Response{
.id = .{ .Integer = id },
.result = .{
.DocumentSymbols = try analysis.getDocumentSymbols(&arena.allocator, handle.tree)
},
.result = .{ .DocumentSymbols = try analysis.getDocumentSymbols(&arena.allocator, handle.tree) },
});
}
@ -760,7 +758,7 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8, config: Config) !v
}),
.var_access, .empty => try completeGlobal(id, pos_index, handle, this_config),
.field_access => |range| try completeFieldAccess(id, handle, pos, range, this_config),
.global_error_set => try send(types.Response{
.global_error_set => try send(types.Response{
.id = .{ .Integer = id },
.result = .{
.CompletionList = .{
@ -769,7 +767,7 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8, config: Config) !v
},
},
}),
.enum_literal => try send(types.Response{
.enum_literal => try send(types.Response{
.id = .{ .Integer = id },
.result = .{
.CompletionList = .{
@ -930,11 +928,19 @@ pub fn main() anyerror!void {
}
// Find the zig executable in PATH
var has_zig = false;
var zig_exe_path: ?[]const u8 = null;
defer if (zig_exe_path) |exe_path| allocator.free(exe_path);
// TODO: Should we just spawn a child process that calls "zig version" or something
// and check that way?
find_zig: {
if (config.zig_exe_path) |exe_path| {
if (std.fs.path.isAbsolute(exe_path)) {
zig_exe_path = try std.mem.dupe(allocator, u8, exe_path);
break :find_zig;
}
std.debug.warn("zig path `{}` is not absolute, will look in path\n", .{exe_path});
}
const env_path = std.process.getEnvVarOwned(allocator, "PATH") catch |err| switch (err) {
error.EnvironmentVariableNotFound => {
std.debug.warn("Could not get PATH.\n", .{});
@ -957,21 +963,32 @@ pub fn main() anyerror!void {
defer allocator.free(full_path);
var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
const zig_path = std.os.realpath(full_path, &buf) catch continue;
std.debug.warn("Found zig in PATH: {}\n", .{zig_path});
has_zig = true;
zig_exe_path = std.os.realpath(full_path, &buf) catch continue;
std.debug.warn("Found zig in PATH: {}\n", .{zig_exe_path});
break :find_zig;
}
}
if (zig_exe_path) |exe_path| {
std.debug.warn("Using zig executable {}\n", .{exe_path});
if (config.zig_lib_path == null) {
// Set the lib path relative to the executable path.
config.zig_lib_path = try std.fs.path.resolve(allocator, &[_][]const u8{
std.fs.path.dirname(exe_path).?, "./lib/zig",
});
std.debug.warn("Resolved standard library from executable: {}\n", .{config.zig_lib_path});
}
}
if (config.build_runner_path) |build_runner_path| {
try document_store.init(allocator, has_zig, try std.mem.dupe(allocator, u8, build_runner_path));
try document_store.init(allocator, zig_exe_path, try std.mem.dupe(allocator, u8, build_runner_path));
} else {
var exe_dir_bytes: [std.fs.MAX_PATH_BYTES]u8 = undefined;
const exe_dir_path = try std.fs.selfExeDirPath(&exe_dir_bytes);
const build_runner_path = try std.fs.path.resolve(allocator, &[_][]const u8{ exe_dir_path, "build_runner.zig" });
try document_store.init(allocator, has_zig, build_runner_path);
try document_store.init(allocator, zig_exe_path, build_runner_path);
}
defer document_store.deinit();