From 6f19772c17abeb32a7d6998aa1d98a3253da1eef Mon Sep 17 00:00:00 2001 From: Auguste Rame Date: Fri, 15 Jul 2022 18:06:18 +0200 Subject: [PATCH] Fix config source of truth problems, refactor some more --- build.zig | 2 +- src/Config.zig | 123 +++++++++++++++++++++++++++++++++- src/DocumentStore.zig | 22 +++--- src/analysis.zig | 8 +-- src/main.zig | 145 ++++++---------------------------------- src/offsets.zig | 2 +- src/references.zig | 10 +-- src/rename.zig | 10 +-- src/requests.zig | 2 +- src/semantic_tokens.zig | 8 +-- src/signature_help.zig | 12 ++-- src/unit_tests.zig | 8 +-- 12 files changed, 186 insertions(+), 166 deletions(-) diff --git a/build.zig b/build.zig index ae77cb2..16c9bc0 100644 --- a/build.zig +++ b/build.zig @@ -1,6 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); -const shared = @import("./src/shared.zig"); +const shared = @import("src/shared.zig"); pub fn build(b: *std.build.Builder) !void { const target = b.standardTargetOptions(.{}); diff --git a/src/Config.zig b/src/Config.zig index 641cf37..297c023 100644 --- a/src/Config.zig +++ b/src/Config.zig @@ -1,4 +1,12 @@ -// Configuration options for zls. +//! Configuration options for zls. + +const Config = @This(); + +const std = @import("std"); +const setup = @import("setup.zig"); +const known_folders = @import("known-folders"); + +const logger = std.log.scoped(.config); /// Whether to enable snippet completions enable_snippets: bool = false, @@ -44,3 +52,116 @@ skip_std_references: bool = false, /// Path to "builtin;" useful for debugging, automatically set if let null builtin_path: ?[]const u8 = null, + +pub fn configChanged(config: *Config, allocator: std.mem.Allocator, builtin_creation_dir: ?[]const u8) !void { + // Find the zig executable in PATH + find_zig: { + if (config.zig_exe_path) |exe_path| { + if (std.fs.path.isAbsolute(exe_path)) not_valid: { + std.fs.cwd().access(exe_path, .{}) catch break :not_valid; + break :find_zig; + } + logger.debug("zig path `{s}` is not absolute, will look in path", .{exe_path}); + allocator.free(exe_path); + } + config.zig_exe_path = try setup.findZig(allocator); + } + + if (config.zig_exe_path) |exe_path| { + logger.info("Using zig executable {s}", .{exe_path}); + + if (config.zig_lib_path == null) find_lib_path: { + // Use `zig env` to find the lib path + const zig_env_result = try std.ChildProcess.exec(.{ + .allocator = allocator, + .argv = &[_][]const u8{ exe_path, "env" }, + }); + + defer { + allocator.free(zig_env_result.stdout); + allocator.free(zig_env_result.stderr); + } + + switch (zig_env_result.term) { + .Exited => |exit_code| { + if (exit_code == 0) { + const Env = struct { + zig_exe: []const u8, + lib_dir: ?[]const u8, + std_dir: []const u8, + global_cache_dir: []const u8, + version: []const u8, + }; + + var json_env = std.json.parse( + Env, + &std.json.TokenStream.init(zig_env_result.stdout), + .{ .allocator = allocator }, + ) catch { + logger.err("Failed to parse zig env JSON result", .{}); + break :find_lib_path; + }; + defer std.json.parseFree(Env, json_env, .{ .allocator = allocator }); + // We know this is allocated with `allocator`, we just steal it! + config.zig_lib_path = json_env.lib_dir.?; + json_env.lib_dir = null; + logger.info("Using zig lib path '{s}'", .{config.zig_lib_path}); + } + }, + else => logger.err("zig env invocation failed", .{}), + } + } + } else { + logger.warn("Zig executable path not specified in zls.json and could not be found in PATH", .{}); + } + + if (config.zig_lib_path == null) { + logger.warn("Zig standard library path not specified in zls.json and could not be resolved from the zig executable", .{}); + } + + if (config.builtin_path == null and config.zig_exe_path != null and builtin_creation_dir != null) blk: { + const result = try std.ChildProcess.exec(.{ + .allocator = allocator, + .argv = &.{ + config.zig_exe_path.?, + "build-exe", + "--show-builtin", + }, + .max_output_bytes = 1024 * 1024 * 50, + }); + defer allocator.free(result.stdout); + defer allocator.free(result.stderr); + + var d = try std.fs.cwd().openDir(builtin_creation_dir.?, .{}); + defer d.close(); + + const f = d.createFile("builtin.zig", .{}) catch |err| switch (err) { + error.AccessDenied => break :blk, + else => |e| return e, + }; + defer f.close(); + + try f.writer().writeAll(result.stdout); + + config.builtin_path = try std.fs.path.join(allocator, &.{ builtin_creation_dir.?, "builtin.zig" }); + } + + config.build_runner_path = if (config.build_runner_path) |p| + try allocator.dupe(u8, p) + else blk: { + var exe_dir_bytes: [std.fs.MAX_PATH_BYTES]u8 = undefined; + const exe_dir_path = try std.fs.selfExeDirPath(&exe_dir_bytes); + break :blk try std.fs.path.resolve(allocator, &[_][]const u8{ exe_dir_path, "build_runner.zig" }); + }; + + config.build_runner_cache_path = if (config.build_runner_cache_path) |p| + try allocator.dupe(u8, p) + else blk: { + const cache_dir_path = (try known_folders.getPath(allocator, .cache)) orelse { + logger.warn("Known-folders could not fetch the cache path", .{}); + return; + }; + defer allocator.free(cache_dir_path); + break :blk try std.fs.path.resolve(allocator, &[_][]const u8{ cache_dir_path, "zls" }); + }; +} diff --git a/src/DocumentStore.zig b/src/DocumentStore.zig index deb4a65..146c570 100644 --- a/src/DocumentStore.zig +++ b/src/DocumentStore.zig @@ -1,13 +1,13 @@ const std = @import("std"); -const types = @import("./types.zig"); -const URI = @import("./uri.zig"); -const analysis = @import("./analysis.zig"); -const offsets = @import("./offsets.zig"); +const types = @import("types.zig"); +const URI = @import("uri.zig"); +const analysis = @import("analysis.zig"); +const offsets = @import("offsets.zig"); const log = std.log.scoped(.doc_store); const Ast = std.zig.Ast; -const BuildAssociatedConfig = @import("./BuildAssociatedConfig.zig"); -const tracy = @import("./tracy.zig"); -const Config = @import("./Config.zig"); +const BuildAssociatedConfig = @import("BuildAssociatedConfig.zig"); +const tracy = @import("tracy.zig"); +const Config = @import("Config.zig"); const DocumentStore = @This(); @@ -75,6 +75,12 @@ pub fn init( }; } +fn updateStdUri(store: *DocumentStore) !void { + if (store.std_uri) |std_uri| + store.allocator.free(std_uri); + store.std_uri = try stdUriFromLibPath(store.allocator, store.config.zig_lib_path); +} + fn loadBuildAssociatedConfiguration(allocator: std.mem.Allocator, build_file: *BuildFile, build_file_path: []const u8) !void { const directory_path = build_file_path[0 .. build_file_path.len - "build.zig".len]; @@ -767,8 +773,6 @@ pub fn deinit(self: *DocumentStore) void { if (self.std_uri) |std_uri| { self.allocator.free(std_uri); } - self.allocator.free(self.config.build_runner_path.?); - self.allocator.free(self.config.build_runner_cache_path.?); self.build_files.deinit(self.allocator); } diff --git a/src/analysis.zig b/src/analysis.zig index cbf2147..dcc70f2 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -1,10 +1,10 @@ const std = @import("std"); -const DocumentStore = @import("./DocumentStore.zig"); +const DocumentStore = @import("DocumentStore.zig"); const Ast = std.zig.Ast; -const types = @import("./types.zig"); -const offsets = @import("./offsets.zig"); +const types = @import("types.zig"); +const offsets = @import("offsets.zig"); const log = std.log.scoped(.analysis); -const ast = @import("./ast.zig"); +const ast = @import("ast.zig"); var using_trail: std.ArrayList([*]const u8) = undefined; var resolve_trail: std.ArrayList(NodeWithHandle) = undefined; diff --git a/src/main.zig b/src/main.zig index 5fc8acc..b82891d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,23 +1,23 @@ const std = @import("std"); const zig_builtin = @import("builtin"); const build_options = @import("build_options"); -const Config = @import("./Config.zig"); -const DocumentStore = @import("./DocumentStore.zig"); -const readRequestHeader = @import("./header.zig").readRequestHeader; -const requests = @import("./requests.zig"); -const types = @import("./types.zig"); -const analysis = @import("./analysis.zig"); -const ast = @import("./ast.zig"); -const references = @import("./references.zig"); -const rename = @import("./rename.zig"); -const offsets = @import("./offsets.zig"); -const setup = @import("./setup.zig"); -const semantic_tokens = @import("./semantic_tokens.zig"); -const shared = @import("./shared.zig"); +const Config = @import("Config.zig"); +const DocumentStore = @import("DocumentStore.zig"); +const readRequestHeader = @import("header.zig").readRequestHeader; +const requests = @import("requests.zig"); +const types = @import("types.zig"); +const analysis = @import("analysis.zig"); +const ast = @import("ast.zig"); +const references = @import("references.zig"); +const rename = @import("rename.zig"); +const offsets = @import("offsets.zig"); +const setup = @import("setup.zig"); +const semantic_tokens = @import("semantic_tokens.zig"); +const shared = @import("shared.zig"); const Ast = std.zig.Ast; const known_folders = @import("known-folders"); -const tracy = @import("./tracy.zig"); -const uri_utils = @import("./uri.zig"); +const tracy = @import("tracy.zig"); +const uri_utils = @import("uri.zig"); const data = switch (build_options.data_version) { .master => @import("data/master.zig"), @@ -2191,6 +2191,8 @@ fn didChangeConfigurationHandler(arena: *std.heap.ArenaAllocator, id: types.Requ @field(config, field.name) = if (@TypeOf(value) == []const u8) try gpa_state.allocator().dupe(u8, value) else value; } } + + try config.configChanged(allocator, null); } else if (client_capabilities.supports_configuration) try requestConfiguration(arena); } @@ -2281,6 +2283,8 @@ fn processJsonRpc(arena: *std.heap.ArenaAllocator, parser: *std.json.Parser, jso } } + try config.configChanged(allocator, null); + return; } @@ -2475,116 +2479,7 @@ pub fn main() anyerror!void { config_path = null; } - // Find the zig executable in PATH - find_zig: { - if (config.zig_exe_path) |exe_path| { - if (std.fs.path.isAbsolute(exe_path)) not_valid: { - std.fs.cwd().access(exe_path, .{}) catch break :not_valid; - break :find_zig; - } - logger.debug("zig path `{s}` is not absolute, will look in path", .{exe_path}); - allocator.free(exe_path); - } - config.zig_exe_path = try setup.findZig(allocator); - } - - if (config.zig_exe_path) |exe_path| { - logger.info("Using zig executable {s}", .{exe_path}); - - if (config.zig_lib_path == null) find_lib_path: { - // Use `zig env` to find the lib path - const zig_env_result = try std.ChildProcess.exec(.{ - .allocator = allocator, - .argv = &[_][]const u8{ exe_path, "env" }, - }); - - defer { - allocator.free(zig_env_result.stdout); - allocator.free(zig_env_result.stderr); - } - - switch (zig_env_result.term) { - .Exited => |exit_code| { - if (exit_code == 0) { - const Env = struct { - zig_exe: []const u8, - lib_dir: ?[]const u8, - std_dir: []const u8, - global_cache_dir: []const u8, - version: []const u8, - }; - - var json_env = std.json.parse( - Env, - &std.json.TokenStream.init(zig_env_result.stdout), - .{ .allocator = allocator }, - ) catch { - logger.err("Failed to parse zig env JSON result", .{}); - break :find_lib_path; - }; - defer std.json.parseFree(Env, json_env, .{ .allocator = allocator }); - // We know this is allocated with `allocator`, we just steal it! - config.zig_lib_path = json_env.lib_dir.?; - json_env.lib_dir = null; - logger.info("Using zig lib path '{s}'", .{config.zig_lib_path}); - } - }, - else => logger.err("zig env invocation failed", .{}), - } - } - } else { - logger.warn("Zig executable path not specified in zls.json and could not be found in PATH", .{}); - } - - if (config.zig_lib_path == null) { - logger.warn("Zig standard library path not specified in zls.json and could not be resolved from the zig executable", .{}); - } - - if (config.builtin_path == null and config.zig_exe_path != null and config_path != null) blk: { - const result = try std.ChildProcess.exec(.{ - .allocator = allocator, - .argv = &.{ - config.zig_exe_path.?, - "build-exe", - "--show-builtin", - }, - .max_output_bytes = 1024 * 1024 * 50, - }); - defer allocator.free(result.stdout); - defer allocator.free(result.stderr); - - var d = try std.fs.cwd().openDir(config_path.?, .{}); - defer d.close(); - - const f = d.createFile("builtin.zig", .{}) catch |err| switch (err) { - error.AccessDenied => break :blk, - else => |e| return e, - }; - defer f.close(); - - try f.writer().writeAll(result.stdout); - - config.builtin_path = try std.fs.path.join(allocator, &.{ config_path.?, "builtin.zig" }); - } - - config.build_runner_path = if (config.build_runner_path) |p| - try allocator.dupe(u8, p) - else blk: { - var exe_dir_bytes: [std.fs.MAX_PATH_BYTES]u8 = undefined; - const exe_dir_path = try std.fs.selfExeDirPath(&exe_dir_bytes); - break :blk try std.fs.path.resolve(allocator, &[_][]const u8{ exe_dir_path, "build_runner.zig" }); - }; - - config.build_runner_cache_path = if (config.build_runner_cache_path) |p| - try allocator.dupe(u8, p) - else blk: { - const cache_dir_path = (try known_folders.getPath(allocator, .cache)) orelse { - logger.warn("Known-folders could not fetch the cache path", .{}); - return; - }; - defer allocator.free(cache_dir_path); - break :blk try std.fs.path.resolve(allocator, &[_][]const u8{ cache_dir_path, "zls" }); - }; + try config.configChanged(allocator, config_path); document_store = try DocumentStore.init( allocator, diff --git a/src/offsets.zig b/src/offsets.zig index 702f4b9..ea1d2d9 100644 --- a/src/offsets.zig +++ b/src/offsets.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const types = @import("./types.zig"); +const types = @import("types.zig"); const Ast = std.zig.Ast; pub const Encoding = enum { diff --git a/src/references.zig b/src/references.zig index 27cdd7b..b5f58f7 100644 --- a/src/references.zig +++ b/src/references.zig @@ -1,11 +1,11 @@ const std = @import("std"); const Ast = std.zig.Ast; -const DocumentStore = @import("./DocumentStore.zig"); -const analysis = @import("./analysis.zig"); -const types = @import("./types.zig"); -const offsets = @import("./offsets.zig"); +const DocumentStore = @import("DocumentStore.zig"); +const analysis = @import("analysis.zig"); +const types = @import("types.zig"); +const offsets = @import("offsets.zig"); const log = std.log.scoped(.references); -const ast = @import("./ast.zig"); +const ast = @import("ast.zig"); fn tokenReference(handle: *DocumentStore.Handle, tok: Ast.TokenIndex, encoding: offsets.Encoding, context: anytype, comptime handler: anytype) !void { const loc = offsets.tokenRelativeLocation(handle.tree, 0, handle.tree.tokens.items(.start)[tok], encoding) catch return; diff --git a/src/rename.zig b/src/rename.zig index b93d712..e5b4c18 100644 --- a/src/rename.zig +++ b/src/rename.zig @@ -1,9 +1,9 @@ const std = @import("std"); -const DocumentStore = @import("./DocumentStore.zig"); -const analysis = @import("./analysis.zig"); -const references = @import("./references.zig"); -const types = @import("./types.zig"); -const offsets = @import("./offsets.zig"); +const DocumentStore = @import("DocumentStore.zig"); +const analysis = @import("analysis.zig"); +const references = @import("references.zig"); +const types = @import("types.zig"); +const offsets = @import("offsets.zig"); // TODO Use a map to array lists and collect at the end instead? const RefHandlerContext = struct { diff --git a/src/requests.zig b/src/requests.zig index 7ca8295..92470a5 100644 --- a/src/requests.zig +++ b/src/requests.zig @@ -3,7 +3,7 @@ //! We only define what we actually use. const std = @import("std"); -const types = @import("./types.zig"); +const types = @import("types.zig"); /// Only check for the field's existence. const Exists = struct { diff --git a/src/semantic_tokens.zig b/src/semantic_tokens.zig index 93a05b8..3a91423 100644 --- a/src/semantic_tokens.zig +++ b/src/semantic_tokens.zig @@ -1,10 +1,10 @@ const std = @import("std"); -const offsets = @import("./offsets.zig"); -const DocumentStore = @import("./DocumentStore.zig"); -const analysis = @import("./analysis.zig"); +const offsets = @import("offsets.zig"); +const DocumentStore = @import("DocumentStore.zig"); +const analysis = @import("analysis.zig"); const Ast = std.zig.Ast; const log = std.log.scoped(.semantic_tokens); -const ast = @import("./ast.zig"); +const ast = @import("ast.zig"); pub const TokenType = enum(u32) { type, diff --git a/src/signature_help.zig b/src/signature_help.zig index a2ff65c..8ce9444 100644 --- a/src/signature_help.zig +++ b/src/signature_help.zig @@ -1,12 +1,12 @@ const std = @import("std"); -const analysis = @import("./analysis.zig"); -const offsets = @import("./offsets.zig"); -const DocumentStore = @import("./DocumentStore.zig"); -const types = @import("./types.zig"); +const analysis = @import("analysis.zig"); +const offsets = @import("offsets.zig"); +const DocumentStore = @import("DocumentStore.zig"); +const types = @import("types.zig"); const Ast = std.zig.Ast; const Token = std.zig.Token; -const identifierFromPosition = @import("./main.zig").identifierFromPosition; -const ast = @import("./ast.zig"); +const identifierFromPosition = @import("main.zig").identifierFromPosition; +const ast = @import("ast.zig"); fn fnProtoToSignatureInfo(document_store: *DocumentStore, arena: *std.heap.ArenaAllocator, commas: u32, skip_self_param: bool, handle: *DocumentStore.Handle, fn_node: Ast.Node.Index, proto: Ast.full.FnProto) !types.SignatureInformation { const ParameterInformation = types.SignatureInformation.ParameterInformation; diff --git a/src/unit_tests.zig b/src/unit_tests.zig index 59c1175..4a5867d 100644 --- a/src/unit_tests.zig +++ b/src/unit_tests.zig @@ -1,8 +1,8 @@ const std = @import("std"); -const analysis = @import("./analysis.zig"); -const types = @import("./types.zig"); -const offsets = @import("./offsets.zig"); -const URI = @import("./uri.zig"); +const analysis = @import("analysis.zig"); +const types = @import("types.zig"); +const offsets = @import("offsets.zig"); +const URI = @import("uri.zig"); const allocator = std.testing.allocator;