Added workspace folder support, read configs from there, use known-folders
This commit is contained in:
		
							parent
							
								
									80e1a37282
								
							
						
					
					
						commit
						7dcbc39d59
					
				
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| [submodule "src/known-folders"] | ||||
| 	path = src/known-folders | ||||
| 	url = https://github.com/ziglibs/known-folders | ||||
| @ -1,7 +1,7 @@ | ||||
| // Configuration options for zls. | ||||
| 
 | ||||
| /// Whether to enable snippet completions | ||||
| enable_snippets: bool = true, | ||||
| enable_snippets: bool = false, | ||||
| 
 | ||||
| /// zig library path | ||||
| zig_lib_path: ?[]const u8 = null, | ||||
|  | ||||
| @ -22,29 +22,11 @@ pub const Handle = struct { | ||||
| 
 | ||||
| allocator: *std.mem.Allocator, | ||||
| handles: std.StringHashMap(*Handle), | ||||
| std_uri: ?[]const u8, | ||||
| 
 | ||||
| pub fn init(self: *DocumentStore, allocator: *std.mem.Allocator, zig_lib_path: ?[]const u8) !void { | ||||
| pub fn init(self: *DocumentStore, allocator: *std.mem.Allocator) !void { | ||||
|     self.allocator = allocator; | ||||
|     self.handles = std.StringHashMap(*Handle).init(allocator); | ||||
|     errdefer self.handles.deinit(); | ||||
| 
 | ||||
|     if (zig_lib_path) |zpath| { | ||||
|         const std_path = std.fs.path.resolve(allocator, &[_][]const u8{ | ||||
|             zpath, "./std/std.zig", | ||||
|         }) catch |err| block: { | ||||
|             std.debug.warn("Failed to resolve zig std library path, error: {}\n", .{err}); | ||||
|             self.std_uri = null; | ||||
|             return; | ||||
|         }; | ||||
| 
 | ||||
|         defer allocator.free(std_path); | ||||
|         // Get the std_path as a URI, so we can just append to it! | ||||
|         self.std_uri = try URI.fromPath(allocator, std_path); | ||||
|         std.debug.warn("Standard library base uri: {}\n", .{self.std_uri}); | ||||
|     } else { | ||||
|         self.std_uri = null; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// This function asserts the document is not open yet and takes ownership | ||||
| @ -120,7 +102,7 @@ pub fn getHandle(self: *DocumentStore, uri: []const u8) ?*Handle { | ||||
| } | ||||
| 
 | ||||
| // Check if the document text is now sane, move it to sane_text if so. | ||||
| fn removeOldImports(self: *DocumentStore, handle: *Handle) !void { | ||||
| fn removeOldImports(self: *DocumentStore, handle: *Handle, zig_lib_path: ?[]const u8) !void { | ||||
|     std.debug.warn("New text for document {}\n", .{handle.uri()}); | ||||
|     // TODO: Better algorithm or data structure? | ||||
|     // Removing the imports is costly since they live in an array list | ||||
| @ -144,7 +126,8 @@ fn removeOldImports(self: *DocumentStore, handle: *Handle) !void { | ||||
|     } | ||||
| 
 | ||||
|     for (import_strs.items) |str| { | ||||
|         const uri = (try uriFromImportStr(self, &arena.allocator, handle.*, str)) orelse continue; | ||||
|         const std_uri = try stdUriFromLibPath(&arena.allocator, zig_lib_path); | ||||
|         const uri = (try uriFromImportStr(self, &arena.allocator, handle.*, str, std_uri)) orelse continue; | ||||
| 
 | ||||
|         var idx: usize = 0; | ||||
|         exists_loop: while (idx < still_exist.len) : (idx += 1) { | ||||
| @ -172,7 +155,12 @@ fn removeOldImports(self: *DocumentStore, handle: *Handle) !void { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn applyChanges(self: *DocumentStore, handle: *Handle, content_changes: std.json.Array) !void { | ||||
| pub fn applyChanges( | ||||
|     self: *DocumentStore, | ||||
|     handle: *Handle, | ||||
|     content_changes: std.json.Array, | ||||
|     zig_lib_path: ?[]const u8, | ||||
| ) !void { | ||||
|     const document = &handle.document; | ||||
| 
 | ||||
|     for (content_changes.items) |change| { | ||||
| @ -225,12 +213,18 @@ pub fn applyChanges(self: *DocumentStore, handle: *Handle, content_changes: std. | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     try self.removeOldImports(handle); | ||||
|     try self.removeOldImports(handle, zig_lib_path); | ||||
| } | ||||
| 
 | ||||
| fn uriFromImportStr(store: *DocumentStore, allocator: *std.mem.Allocator, handle: Handle, import_str: []const u8) !?[]const u8 { | ||||
| fn uriFromImportStr( | ||||
|     store: *DocumentStore, | ||||
|     allocator: *std.mem.Allocator, | ||||
|     handle: Handle, | ||||
|     import_str: []const u8, | ||||
|     std_uri: ?[]const u8, | ||||
| ) !?[]const u8 { | ||||
|     return if (std.mem.eql(u8, import_str, "std")) | ||||
|         if (store.std_uri) |std_root_uri| try std.mem.dupe(allocator, u8, std_root_uri) else { | ||||
|         if (std_uri) |uri| try std.mem.dupe(allocator, u8, uri) else { | ||||
|             std.debug.warn("Cannot resolve std library import, path is null.\n", .{}); | ||||
|             return null; | ||||
|         } | ||||
| @ -259,6 +253,7 @@ pub const AnalysisContext = struct { | ||||
|     tree: *std.zig.ast.Tree, | ||||
|     scope_nodes: []*std.zig.ast.Node, | ||||
|     last_this_node: *std.zig.ast.Node, | ||||
|     std_uri: ?[]const u8, | ||||
| 
 | ||||
|     fn refreshScopeNodes(self: *AnalysisContext) !void { | ||||
|         var scope_nodes = std.ArrayList(*std.zig.ast.Node).init(&self.arena.allocator); | ||||
| @ -269,7 +264,13 @@ pub const AnalysisContext = struct { | ||||
| 
 | ||||
|     pub fn onImport(self: *AnalysisContext, import_str: []const u8) !?*std.zig.ast.Node { | ||||
|         const allocator = self.store.allocator; | ||||
|         const final_uri = (try uriFromImportStr(self.store, self.store.allocator, self.handle.*, import_str)) orelse return null; | ||||
|         const final_uri = (try uriFromImportStr( | ||||
|             self.store, | ||||
|             self.store.allocator, | ||||
|             self.handle.*, | ||||
|             import_str, | ||||
|             self.std_uri, | ||||
|         )) orelse return null; | ||||
| 
 | ||||
|         std.debug.warn("Import final URI: {}\n", .{final_uri}); | ||||
|         var consumed_final_uri = false; | ||||
| @ -351,6 +352,7 @@ pub const AnalysisContext = struct { | ||||
|             .tree = tree, | ||||
|             .scope_nodes = self.scope_nodes, | ||||
|             .last_this_node = &tree.root_node.base, | ||||
|             .std_uri = self.std_uri, | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
| @ -369,12 +371,36 @@ pub const AnalysisContext = struct { | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| pub fn analysisContext(self: *DocumentStore, handle: *Handle, arena: *std.heap.ArenaAllocator, position: types.Position) !AnalysisContext { | ||||
| fn stdUriFromLibPath(allocator: *std.mem.Allocator, zig_lib_path: ?[]const u8) !?[]const u8 { | ||||
|     if (zig_lib_path) |zpath| { | ||||
|         const std_path = std.fs.path.resolve(allocator, &[_][]const u8{ | ||||
|             zpath, "./std/std.zig", | ||||
|         }) catch |err| block: { | ||||
|             std.debug.warn("Failed to resolve zig std library path, error: {}\n", .{err}); | ||||
|             return null; | ||||
|         }; | ||||
| 
 | ||||
|         defer allocator.free(std_path); | ||||
|         // Get the std_path as a URI, so we can just append to it! | ||||
|         return try URI.fromPath(allocator, std_path); | ||||
|     } | ||||
| 
 | ||||
|     return null; | ||||
| } | ||||
| 
 | ||||
| pub fn analysisContext( | ||||
|     self: *DocumentStore, | ||||
|     handle: *Handle, | ||||
|     arena: *std.heap.ArenaAllocator, | ||||
|     position: types.Position, | ||||
|     zig_lib_path: ?[]const u8, | ||||
| ) !AnalysisContext { | ||||
|     const tree = try handle.tree(self.allocator); | ||||
| 
 | ||||
|     var scope_nodes = std.ArrayList(*std.zig.ast.Node).init(&arena.allocator); | ||||
|     try analysis.declsFromIndex(&scope_nodes, tree, try handle.document.positionToIndex(position)); | ||||
| 
 | ||||
|     const std_uri = try stdUriFromLibPath(&arena.allocator, zig_lib_path); | ||||
|     return AnalysisContext{ | ||||
|         .store = self, | ||||
|         .handle = handle, | ||||
| @ -382,6 +408,7 @@ pub fn analysisContext(self: *DocumentStore, handle: *Handle, arena: *std.heap.A | ||||
|         .tree = tree, | ||||
|         .scope_nodes = scope_nodes.items, | ||||
|         .last_this_node = &tree.root_node.base, | ||||
|         .std_uri = std_uri, | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| @ -400,7 +427,4 @@ pub fn deinit(self: *DocumentStore) void { | ||||
|     } | ||||
| 
 | ||||
|     self.handles.deinit(); | ||||
|     if (self.std_uri) |uri| { | ||||
|         self.allocator.free(uri); | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										1
									
								
								src/known-folders
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
								
							
						
						
									
										1
									
								
								src/known-folders
									
									
									
									
									
										Submodule
									
								
							| @ -0,0 +1 @@ | ||||
| Subproject commit 42a32b0241a5aaeaa09d7edeceefc5384b4eb466 | ||||
							
								
								
									
										182
									
								
								src/main.zig
									
									
									
									
									
								
							
							
						
						
									
										182
									
								
								src/main.zig
									
									
									
									
									
								
							| @ -8,6 +8,7 @@ const readRequestHeader = @import("header.zig").readRequestHeader; | ||||
| const data = @import("data/" ++ build_options.data_version ++ ".zig"); | ||||
| const types = @import("types.zig"); | ||||
| const analysis = @import("analysis.zig"); | ||||
| const URI = @import("uri.zig"); | ||||
| 
 | ||||
| // Code is largely based off of https://github.com/andersfr/zig-lsp/blob/master/server.zig | ||||
| 
 | ||||
| @ -15,9 +16,10 @@ var stdout: std.io.BufferedOutStream(4096, std.fs.File.OutStream) = undefined; | ||||
| var allocator: *std.mem.Allocator = undefined; | ||||
| 
 | ||||
| var document_store: DocumentStore = undefined; | ||||
| var workspace_folder_configs: std.StringHashMap(?Config) = undefined; | ||||
| 
 | ||||
| const initialize_response = | ||||
|     \\,"result":{"capabilities":{"signatureHelpProvider":{"triggerCharacters":["(",","]},"textDocumentSync":1,"completionProvider":{"resolveProvider":false,"triggerCharacters":[".",":","@"]},"documentHighlightProvider":false,"codeActionProvider":false,"declarationProvider":true,"definitionProvider":true,"typeDefinitionProvider":true,"workspace":{"workspaceFolders":{"supported":true}}}}} | ||||
|     \\,"result":{"capabilities":{"signatureHelpProvider":{"triggerCharacters":["(",","]},"textDocumentSync":1,"completionProvider":{"resolveProvider":false,"triggerCharacters":[".",":","@"]},"documentHighlightProvider":false,"codeActionProvider":false,"declarationProvider":true,"definitionProvider":true,"typeDefinitionProvider":true,"workspace":{"workspaceFolders":{"supported":true,"changeNotifications":true}}}}} | ||||
| ; | ||||
| 
 | ||||
| const not_implemented_response = | ||||
| @ -319,11 +321,17 @@ fn gotoDefinitionGlobal(id: i64, pos_index: usize, handle: DocumentStore.Handle) | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| fn gotoDefinitionFieldAccess(id: i64, handle: *DocumentStore.Handle, position: types.Position, line_start_idx: usize) !void { | ||||
| fn gotoDefinitionFieldAccess( | ||||
|     id: i64, | ||||
|     handle: *DocumentStore.Handle, | ||||
|     position: types.Position, | ||||
|     line_start_idx: usize, | ||||
|     config: Config, | ||||
| ) !void { | ||||
|     var arena = std.heap.ArenaAllocator.init(allocator); | ||||
|     defer arena.deinit(); | ||||
| 
 | ||||
|     var analysis_ctx = try document_store.analysisContext(handle, &arena, position); | ||||
|     var analysis_ctx = try document_store.analysisContext(handle, &arena, position, config.zig_lib_path); | ||||
|     defer analysis_ctx.deinit(); | ||||
| 
 | ||||
|     const pos_index = try handle.document.positionToIndex(position); | ||||
| @ -365,7 +373,7 @@ fn completeGlobal(id: i64, pos_index: usize, handle: *DocumentStore.Handle, conf | ||||
|     var analysis_ctx = try document_store.analysisContext(handle, &arena, types.Position{ | ||||
|         .line = 0, | ||||
|         .character = 0, | ||||
|     }); | ||||
|     }, config.zig_lib_path); | ||||
|     defer analysis_ctx.deinit(); | ||||
| 
 | ||||
|     var decl_nodes = std.ArrayList(*std.zig.ast.Node).init(&arena.allocator); | ||||
| @ -390,7 +398,7 @@ fn completeFieldAccess(id: i64, handle: *DocumentStore.Handle, position: types.P | ||||
|     var arena = std.heap.ArenaAllocator.init(allocator); | ||||
|     defer arena.deinit(); | ||||
| 
 | ||||
|     var analysis_ctx = try document_store.analysisContext(handle, &arena, position); | ||||
|     var analysis_ctx = try document_store.analysisContext(handle, &arena, position, config.zig_lib_path); | ||||
|     defer analysis_ctx.deinit(); | ||||
| 
 | ||||
|     var completions = std.ArrayList(types.CompletionItem).init(&arena.allocator); | ||||
| @ -573,27 +581,121 @@ fn documentPositionContext(doc: types.TextDocument, pos_index: usize) PositionCo | ||||
|     return context; | ||||
| } | ||||
| 
 | ||||
| fn loadConfig(folder_path: []const u8) ?Config { | ||||
|     var folder = std.fs.cwd().openDir(folder_path, .{}) catch return null; | ||||
|     defer folder.close(); | ||||
| 
 | ||||
|     const conf_file = folder.openFile("zls.json", .{}) catch return null; | ||||
|     defer conf_file.close(); | ||||
| 
 | ||||
|     // Max 1MB | ||||
|     const file_buf = conf_file.inStream().readAllAlloc(allocator, 0x1000000) catch return null; | ||||
|     defer allocator.free(file_buf); | ||||
| 
 | ||||
|     // TODO: Better errors? Doesn't seem like std.json can provide us positions or context. | ||||
|     var config = std.json.parse(Config, &std.json.TokenStream.init(file_buf), std.json.ParseOptions{ .allocator = allocator }) catch |err| { | ||||
|         std.debug.warn("Error while parsing configuration file: {}\nUsing default config.\n", .{err}); | ||||
|         return null; | ||||
|     }; | ||||
| 
 | ||||
|     if (config.zig_lib_path) |zig_lib_path| { | ||||
|         if (!std.fs.path.isAbsolute(zig_lib_path)) { | ||||
|             std.debug.warn("zig library path is not absolute, defaulting to null.\n", .{}); | ||||
|             allocator.free(zig_lib_path); | ||||
|             config.zig_lib_path = null; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return config; | ||||
| } | ||||
| 
 | ||||
| fn loadWorkspaceConfigs() !void { | ||||
|     var folder_config_it = workspace_folder_configs.iterator(); | ||||
|     while (folder_config_it.next()) |entry| { | ||||
|         if (entry.value) |_| continue; | ||||
| 
 | ||||
|         const folder_path = try URI.parse(allocator, entry.key); | ||||
|         defer allocator.free(folder_path); | ||||
| 
 | ||||
|         entry.value = loadConfig(folder_path); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn configFromUriOr(uri: []const u8, default: Config) Config { | ||||
|     var folder_config_it = workspace_folder_configs.iterator(); | ||||
|     while (folder_config_it.next()) |entry| { | ||||
|         if (std.mem.startsWith(u8, uri, entry.key)) { | ||||
|             return entry.value orelse default; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return default; | ||||
| } | ||||
| 
 | ||||
| fn processJsonRpc(parser: *std.json.Parser, json: []const u8, config: Config) !void { | ||||
|     var tree = try parser.parse(json); | ||||
|     defer tree.deinit(); | ||||
| 
 | ||||
|     const root = tree.root; | ||||
| 
 | ||||
|     std.debug.assert(root.Object.getValue("method") != null); | ||||
| 
 | ||||
|     const method = root.Object.getValue("method").?.String; | ||||
|     const id = if (root.Object.getValue("id")) |id| id.Integer else 0; | ||||
|     if (id == 1337 and (root.Object.getValue("method") == null or std.mem.eql(u8, root.Object.getValue("method").?.String, ""))) { | ||||
|         const result = (root.Object.getValue("result") orelse return).Array; | ||||
| 
 | ||||
|         for (result.items) |workspace_folder| { | ||||
|             const duped_uri = try std.mem.dupe(allocator, u8, workspace_folder.Object.getValue("uri").?.String); | ||||
|             try workspace_folder_configs.putNoClobber(duped_uri, null); | ||||
|         } | ||||
| 
 | ||||
|         try loadWorkspaceConfigs(); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     std.debug.assert(root.Object.getValue("method") != null); | ||||
|     const method = root.Object.getValue("method").?.String; | ||||
|     const params = root.Object.getValue("params").?.Object; | ||||
| 
 | ||||
|     // Core | ||||
|     if (std.mem.eql(u8, method, "initialize")) { | ||||
|         try respondGeneric(id, initialize_response); | ||||
|     } else if (std.mem.eql(u8, method, "initialized")) { | ||||
|         // noop | ||||
|         // Send the workspaceFolders request | ||||
|         try send(types.Request{ | ||||
|             .id = .{ .Integer = 1337 }, | ||||
|             .method = "workspace/workspaceFolders", | ||||
|             .params = {}, | ||||
|         }); | ||||
|     } else if (std.mem.eql(u8, method, "$/cancelRequest")) { | ||||
|         // noop | ||||
|     } | ||||
|     // Workspace folder changes | ||||
|     else if (std.mem.eql(u8, method, "workspace/didChangeWorkspaceFolders")) { | ||||
|         const event = params.getValue("event").?.Object; | ||||
|         const added = event.getValue("added").?.Array; | ||||
|         const removed = event.getValue("removed").?.Array; | ||||
| 
 | ||||
|         for (removed.items) |rem| { | ||||
|             const uri = rem.Object.getValue("uri").?.String; | ||||
|             if (workspace_folder_configs.remove(uri)) |entry| { | ||||
|                 allocator.free(entry.key); | ||||
|                 if (entry.value) |c| { | ||||
|                     std.json.parseFree(Config, c, std.json.ParseOptions{ .allocator = allocator }); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         for (added.items) |add| { | ||||
|             const duped_uri = try std.mem.dupe(allocator, u8, add.Object.getValue("uri").?.String); | ||||
|             if (try workspace_folder_configs.put(duped_uri, null)) |old| { | ||||
|                 allocator.free(old.key); | ||||
|                 if (old.value) |c| { | ||||
|                     std.json.parseFree(Config, c, std.json.ParseOptions{ .allocator = allocator }); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         try loadWorkspaceConfigs(); | ||||
|     } | ||||
|     // File changes | ||||
|     else if (std.mem.eql(u8, method, "textDocument/didOpen")) { | ||||
|         const document = params.getValue("textDocument").?.Object; | ||||
| @ -601,7 +703,7 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8, config: Config) !v | ||||
|         const text = document.getValue("text").?.String; | ||||
| 
 | ||||
|         const handle = try document_store.openDocument(uri, text); | ||||
|         try publishDiagnostics(handle.*, config); | ||||
|         try publishDiagnostics(handle.*, configFromUriOr(uri, config)); | ||||
|     } else if (std.mem.eql(u8, method, "textDocument/didChange")) { | ||||
|         const text_document = params.getValue("textDocument").?.Object; | ||||
|         const uri = text_document.getValue("uri").?.String; | ||||
| @ -612,8 +714,9 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8, config: Config) !v | ||||
|             return; | ||||
|         }; | ||||
| 
 | ||||
|         try document_store.applyChanges(handle, content_changes); | ||||
|         try publishDiagnostics(handle.*, config); | ||||
|         const local_config = configFromUriOr(uri, config); | ||||
|         try document_store.applyChanges(handle, content_changes, local_config.zig_lib_path); | ||||
|         try publishDiagnostics(handle.*, local_config); | ||||
|     } else if (std.mem.eql(u8, method, "textDocument/didSave")) { | ||||
|         // noop | ||||
|     } else if (std.mem.eql(u8, method, "textDocument/didClose")) { | ||||
| @ -641,18 +744,19 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8, config: Config) !v | ||||
|             const pos_index = try handle.document.positionToIndex(pos); | ||||
|             const pos_context = documentPositionContext(handle.document, pos_index); | ||||
| 
 | ||||
|             const this_config = configFromUriOr(uri, config); | ||||
|             switch (pos_context) { | ||||
|                 .builtin => try send(types.Response{ | ||||
|                     .id = .{ .Integer = id }, | ||||
|                     .result = .{ | ||||
|                         .CompletionList = .{ | ||||
|                             .isIncomplete = false, | ||||
|                             .items = builtin_completions[@boolToInt(config.enable_snippets)][0..], | ||||
|                             .items = builtin_completions[@boolToInt(this_config.enable_snippets)][0..], | ||||
|                         }, | ||||
|                     }, | ||||
|                 }), | ||||
|                 .var_access, .empty => try completeGlobal(id, pos_index, handle, config), | ||||
|                 .field_access => |start_idx| try completeFieldAccess(id, handle, pos, start_idx, config), | ||||
|                 .var_access, .empty => try completeGlobal(id, pos_index, handle, this_config), | ||||
|                 .field_access => |start_idx| try completeFieldAccess(id, handle, pos, start_idx, this_config), | ||||
|                 else => try respondGeneric(id, no_completions_response), | ||||
|             } | ||||
|         } else { | ||||
| @ -685,7 +789,13 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8, config: Config) !v | ||||
| 
 | ||||
|             switch (pos_context) { | ||||
|                 .var_access => try gotoDefinitionGlobal(id, pos_index, handle.*), | ||||
|                 .field_access => |start_idx| try gotoDefinitionFieldAccess(id, handle, pos, start_idx), | ||||
|                 .field_access => |start_idx| try gotoDefinitionFieldAccess( | ||||
|                     id, | ||||
|                     handle, | ||||
|                     pos, | ||||
|                     start_idx, | ||||
|                     configFromUriOr(uri, config), | ||||
|                 ), | ||||
|                 else => try respondGeneric(id, null_result_response), | ||||
|             } | ||||
|         } | ||||
| @ -723,39 +833,31 @@ pub fn main() anyerror!void { | ||||
|     var config = Config{}; | ||||
|     defer std.json.parseFree(Config, config, config_parse_options); | ||||
| 
 | ||||
|     // TODO: Investigate using std.fs.Watch to detect writes to the config and reload it. | ||||
|     config_read: { | ||||
|         const known_folders = @import("known-folders/known-folders.zig"); | ||||
|         const res = try known_folders.getPath(allocator, .local_configuration); | ||||
|         if (res) |local_config_path| { | ||||
|             defer allocator.free(local_config_path); | ||||
|             if (loadConfig(local_config_path)) |conf| { | ||||
|                 config = conf; | ||||
|                 break :config_read; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         var exec_dir_bytes: [std.fs.MAX_PATH_BYTES]u8 = undefined; | ||||
|         const exec_dir_path = std.fs.selfExeDirPath(&exec_dir_bytes) catch break :config_read; | ||||
| 
 | ||||
|         var exec_dir = std.fs.cwd().openDir(exec_dir_path, .{}) catch break :config_read; | ||||
|         defer exec_dir.close(); | ||||
| 
 | ||||
|         const conf_file = exec_dir.openFile("zls.json", .{}) catch break :config_read; | ||||
|         defer conf_file.close(); | ||||
| 
 | ||||
|         // Max 1MB | ||||
|         const file_buf = conf_file.inStream().readAllAlloc(allocator, 0x1000000) catch break :config_read; | ||||
|         defer allocator.free(file_buf); | ||||
| 
 | ||||
|         // TODO: Better errors? Doesn't seem like std.json can provide us positions or context. | ||||
|         config = std.json.parse(Config, &std.json.TokenStream.init(file_buf), config_parse_options) catch |err| { | ||||
|             std.debug.warn("Error while parsing configuration file: {}\nUsing default config.\n", .{err}); | ||||
|             break :config_read; | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     if (config.zig_lib_path) |zig_lib_path| { | ||||
|         if (!std.fs.path.isAbsolute(zig_lib_path)) { | ||||
|             std.debug.warn("zig library path is not absolute, defaulting to null.\n", .{}); | ||||
|             allocator.free(zig_lib_path); | ||||
|             config.zig_lib_path = null; | ||||
|         if (loadConfig(exec_dir_path)) |conf| { | ||||
|             config = conf; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     try document_store.init(allocator, config.zig_lib_path); | ||||
|     try document_store.init(allocator); | ||||
|     defer document_store.deinit(); | ||||
| 
 | ||||
|     workspace_folder_configs = std.StringHashMap(?Config).init(allocator); | ||||
|     defer workspace_folder_configs.deinit(); | ||||
| 
 | ||||
|     // This JSON parser is passed to processJsonRpc and reset. | ||||
|     var json_parser = std.json.Parser.init(allocator, false); | ||||
|     defer json_parser.deinit(); | ||||
|  | ||||
| @ -40,9 +40,7 @@ pub const RequestId = union(enum) { | ||||
| }; | ||||
| 
 | ||||
| /// Params of a request | ||||
| pub const RequestParams = union(enum) { | ||||
| 
 | ||||
| }; | ||||
| pub const RequestParams = void; | ||||
| 
 | ||||
| pub const NotificationParams = union(enum) { | ||||
|     LogMessageParams: LogMessageParams, | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user