From 8890ec749787ea4e8e54afe6f941fddbb97c1fca Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Sat, 13 Dec 2025 20:53:31 +0100 Subject: [PATCH] refactor: read lsp config from a separate file in the config directory --- build.zig | 8 +++++ src/file_type_config.zig | 2 -- src/lsp_config.zig | 75 ++++++++++++++++++++++++++++++++++++++++ src/project_manager.zig | 4 ++- 4 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 src/lsp_config.zig diff --git a/build.zig b/build.zig index 08dbc59..029e8cb 100644 --- a/build.zig +++ b/build.zig @@ -378,6 +378,13 @@ pub fn build_exe( }, }); + const lsp_config_mod = b.createModule(.{ + .root_source_file = b.path("src/lsp_config.zig"), + .imports = &.{ + .{ .name = "soft_root", .module = soft_root_mod }, + }, + }); + const log_mod = b.createModule(.{ .root_source_file = b.path("src/log.zig"), .imports = &.{ @@ -579,6 +586,7 @@ pub fn build_exe( .{ .name = "Buffer", .module = Buffer_mod }, .{ .name = "tracy", .module = tracy_mod }, .{ .name = "file_type_config", .module = file_type_config_mod }, + .{ .name = "lsp_config", .module = lsp_config_mod }, .{ .name = "dizzy", .module = dizzy_dep.module("dizzy") }, .{ .name = "fuzzig", .module = fuzzig_dep.module("fuzzig") }, .{ .name = "git", .module = git_mod }, diff --git a/src/file_type_config.zig b/src/file_type_config.zig index f934cab..a444d04 100644 --- a/src/file_type_config.zig +++ b/src/file_type_config.zig @@ -7,7 +7,6 @@ comment: ?[]const u8 = null, parser: ?[]const u8 = null, formatter: ?[]const []const u8 = null, language_server: ?[]const []const u8 = null, -language_server_options: ?[]const u8 = null, first_line_matches_prefix: ?[]const u8 = null, first_line_matches_content: ?[]const u8 = null, first_line_matches: ?[]const u8 = null, @@ -37,7 +36,6 @@ fn from_file_type(file_type: syntax.FileType) @This() { .comment = file_type.comment, .formatter = lsp_defaults.formatter, .language_server = lsp_defaults.language_server, - .language_server_options = "", }; } diff --git a/src/lsp_config.zig b/src/lsp_config.zig new file mode 100644 index 0000000..a321985 --- /dev/null +++ b/src/lsp_config.zig @@ -0,0 +1,75 @@ +pub fn get(project: []const u8, lsp_name: []const u8) ?[]const u8 { + if (project.len == 0) return get_global(lsp_name); + if (get_project(project, lsp_name)) |conf| return conf; + return get_global(lsp_name); +} + +fn get_project(project: []const u8, lsp_name: []const u8) ?[]const u8 { + const file_name = get_config_file_path(project, lsp_name, .project, .no_create) catch return null; + defer allocator.free(file_name); + const file: std.fs.File = std.fs.openFileAbsolute(file_name, .{ .mode = .read_only }) catch return null; + defer file.close(); + return file.readToEndAlloc(allocator, std.math.maxInt(usize)) catch return null; +} + +fn get_global(lsp_name: []const u8) ?[]const u8 { + const file_name = get_config_file_path(&.{}, lsp_name, .global, .no_create) catch return null; + defer allocator.free(file_name); + const file: std.fs.File = std.fs.openFileAbsolute(file_name, .{ .mode = .read_only }) catch return null; + defer file.close(); + return file.readToEndAlloc(allocator, std.math.maxInt(usize)) catch return null; +} + +pub fn get_config_file_path(project: ?[]const u8, lsp_name: []const u8, scope: Scope, mode: Mode) ![]u8 { + const config_dir_path = try get_config_dir_path(project, scope, mode); + defer allocator.free(config_dir_path); + var stream: std.Io.Writer.Allocating = .init(allocator); + defer stream.deinit(); + try stream.writer.print("{s}{s}.json", .{ config_dir_path, lsp_name }); + return stream.toOwnedSlice(); +} + +fn get_config_dir_path(project: ?[]const u8, scope: Scope, mode: Mode) ![]u8 { + var stream: std.Io.Writer.Allocating = .init(allocator); + defer stream.deinit(); + const writer = &stream.writer; + try writer.writeAll(try root.get_config_dir()); + try writer.writeByte(std.fs.path.sep); + switch (scope) { + .project => { + try writer.writeAll("project"); + try writer.writeByte(std.fs.path.sep); + if (mode == .mk_parents) std.fs.makeDirAbsolute(stream.written()) catch |e| switch (e) { + error.PathAlreadyExists => {}, + else => return e, + }; + if (project) |prj| { + for (prj) |c| { + _ = if (std.fs.path.isSep(c)) + try writer.write("__") + else if (c == ':') + try writer.write("___") + else + try writer.writeByte(c); + } + _ = try writer.writeByte(std.fs.path.sep); + } + }, + .global => { + try writer.writeAll("lsp"); + try writer.writeByte(std.fs.path.sep); + }, + } + if (mode == .mk_parents) std.fs.makeDirAbsolute(stream.written()) catch |e| switch (e) { + error.PathAlreadyExists => {}, + else => return e, + }; + return stream.toOwnedSlice(); +} + +pub const Scope = enum { project, global }; +pub const Mode = enum { mk_parents, no_create }; + +pub const allocator = std.heap.c_allocator; +const std = @import("std"); +const root = @import("soft_root").root; diff --git a/src/project_manager.zig b/src/project_manager.zig index 2ad5872..2ba42a9 100644 --- a/src/project_manager.zig +++ b/src/project_manager.zig @@ -4,6 +4,7 @@ const cbor = @import("cbor"); const log = @import("log"); const tracy = @import("tracy"); const file_type_config = @import("file_type_config"); +const lsp_config = @import("lsp_config"); const root = @import("soft_root").root; const Buffer = @import("Buffer"); const builtin = @import("builtin"); @@ -192,7 +193,8 @@ pub fn did_open(file_path: []const u8, file_type: file_type_config, version: usi return error.NoProject; const text_ptr: usize = if (text.len > 0) @intFromPtr(text.ptr) else 0; const language_server = file_type.language_server orelse return; - const language_server_options = file_type.language_server_options orelse return; + const language_server_options = if (file_type.language_server) |lsp| lsp_config.get(project, lsp[0]) orelse &.{} else &.{}; + defer lsp_config.allocator.free(language_server_options); return send(.{ "did_open", project, file_path, file_type.name, language_server, language_server_options, version, text_ptr, text.len }); }