From 9e3f1af8582b78bdaa13411b33c58cd6e198e717 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 25 Mar 2025 09:41:36 +0100 Subject: [PATCH 1/3] feat: queue messages to LSP while waiting for initialize response --- src/LSP.zig | 51 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/src/LSP.zig b/src/LSP.zig index c3ae7dc..9d3dfed 100644 --- a/src/LSP.zig +++ b/src/LSP.zig @@ -125,6 +125,10 @@ const Process = struct { log_file: ?std.fs.File = null, next_id: i32 = 0, requests: std.StringHashMap(tp.pid), + state: enum { init, running } = .init, + init_queue: std.ArrayListUnmanaged(struct { tp.pid, []const u8, []const u8, InitQueueType }) = .empty, + + const InitQueueType = enum { request, notify }; const Receiver = tp.Receiver(*Process); @@ -161,6 +165,7 @@ const Process = struct { } fn deinit(self: *Process) void { + self.free_init_queue(); var i = self.requests.iterator(); while (i.next()) |req| { self.allocator.free(req.key_ptr.*); @@ -236,12 +241,24 @@ const Process = struct { var code: u32 = 0; var cbor_id: []const u8 = ""; - if (try cbor.match(m.buf, .{ "REQ", tp.extract(&method), tp.extract(&bytes) })) { - try self.send_request(from, method, bytes); + if (try cbor.match(m.buf, .{ "REQ", "initialize", tp.extract(&bytes) })) { + try self.send_request(from, "initialize", bytes); + } else if (try cbor.match(m.buf, .{ "REQ", tp.extract(&method), tp.extract(&bytes) })) { + switch (self.state) { + .init => try self.append_init_queue(from, method, bytes, .request), //queue requests + .running => try self.send_request(from, method, bytes), + } } else if (try cbor.match(m.buf, .{ "RSP", tp.extract_cbor(&cbor_id), tp.extract_cbor(&bytes) })) { try self.send_response(cbor_id, bytes); + } else if (try cbor.match(m.buf, .{ "NTFY", "initialized", tp.extract(&bytes) })) { + self.state = .running; + try self.send_notification("initialized", bytes); + try self.replay_init_queue(); } else if (try cbor.match(m.buf, .{ "NTFY", tp.extract(&method), tp.extract(&bytes) })) { - try self.send_notification(method, bytes); + switch (self.state) { + .init => try self.append_init_queue(from, method, bytes, .notify), //queue requests + .running => try self.send_notification(method, bytes), + } } else if (try cbor.match(m.buf, .{"close"})) { self.write_log("### LSP close ###\n", .{}); try self.close(); @@ -265,6 +282,34 @@ const Process = struct { } } + fn append_init_queue(self: *Process, from: tp.pid_ref, method: []const u8, bytes: []const u8, type_: InitQueueType) !void { + const p = try self.init_queue.addOne(self.allocator); + p.* = .{ + from.clone(), + try self.allocator.dupe(u8, method), + try self.allocator.dupe(u8, bytes), + type_, + }; + } + + fn replay_init_queue(self: *Process) !void { + defer self.free_init_queue(); + for (self.init_queue.items) |*p| + switch (p[3]) { + .request => try self.send_request(p[0].ref(), p[1], p[2]), + .notify => try self.send_notification(p[1], p[2]), + }; + } + + fn free_init_queue(self: *Process) void { + for (self.init_queue.items) |*p| { + p[0].deinit(); + self.allocator.free(p[1]); + self.allocator.free(p[2]); + } + self.init_queue.deinit(self.allocator); + } + fn receive_lsp_message(self: *Process, cb: []const u8) Error!void { var iter = cb; From 44c55dd4f2cab69ec242c5cf48bf6860ff617723 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 25 Mar 2025 09:42:13 +0100 Subject: [PATCH 2/3] feat: add command palette bindings to vim visual modes closes #212 --- src/keybind/builtin/vim.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/keybind/builtin/vim.json b/src/keybind/builtin/vim.json index 87438f8..b112d9f 100644 --- a/src/keybind/builtin/vim.json +++ b/src/keybind/builtin/vim.json @@ -116,6 +116,7 @@ ["0", "move_begin"], ["^", "smart_move_begin"], ["$", "move_end"], + [":", "open_command_palette"], ["p", ["paste_internal_vim"], ["enter_mode", "normal"]], ["P", ["paste_internal_vim"], ["enter_mode", "normal"]], @@ -153,6 +154,7 @@ ["0", "move_begin"], ["^", "smart_move_begin"], ["$", "move_end"], + [":", "open_command_palette"], ["p", ["paste_internal_vim"], ["enter_mode", "normal"]], ["P", ["paste_internal_vim"], ["enter_mode", "normal"]], From 739b2a776fd4dc734e1664845b0874feb3dad211 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 25 Mar 2025 09:49:46 +0100 Subject: [PATCH 3/3] fix: never double free LSP init queue --- src/LSP.zig | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/LSP.zig b/src/LSP.zig index 9d3dfed..99bf74f 100644 --- a/src/LSP.zig +++ b/src/LSP.zig @@ -126,7 +126,7 @@ const Process = struct { next_id: i32 = 0, requests: std.StringHashMap(tp.pid), state: enum { init, running } = .init, - init_queue: std.ArrayListUnmanaged(struct { tp.pid, []const u8, []const u8, InitQueueType }) = .empty, + init_queue: ?std.ArrayListUnmanaged(struct { tp.pid, []const u8, []const u8, InitQueueType }) = null, const InitQueueType = enum { request, notify }; @@ -283,7 +283,11 @@ const Process = struct { } fn append_init_queue(self: *Process, from: tp.pid_ref, method: []const u8, bytes: []const u8, type_: InitQueueType) !void { - const p = try self.init_queue.addOne(self.allocator); + const queue = if (self.init_queue) |*queue| queue else blk: { + self.init_queue = .empty; + break :blk &self.init_queue.?; + }; + const p = try queue.addOne(self.allocator); p.* = .{ from.clone(), try self.allocator.dupe(u8, method), @@ -294,20 +298,25 @@ const Process = struct { fn replay_init_queue(self: *Process) !void { defer self.free_init_queue(); - for (self.init_queue.items) |*p| - switch (p[3]) { - .request => try self.send_request(p[0].ref(), p[1], p[2]), - .notify => try self.send_notification(p[1], p[2]), - }; + if (self.init_queue) |*queue| { + for (queue.items) |*p| + switch (p[3]) { + .request => try self.send_request(p[0].ref(), p[1], p[2]), + .notify => try self.send_notification(p[1], p[2]), + }; + } } fn free_init_queue(self: *Process) void { - for (self.init_queue.items) |*p| { - p[0].deinit(); - self.allocator.free(p[1]); - self.allocator.free(p[2]); + if (self.init_queue) |*queue| { + for (queue.items) |*p| { + p[0].deinit(); + self.allocator.free(p[1]); + self.allocator.free(p[2]); + } + queue.deinit(self.allocator); } - self.init_queue.deinit(self.allocator); + self.init_queue = null; } fn receive_lsp_message(self: *Process, cb: []const u8) Error!void {