From e8939a78298f357cb2c12c7987bad09a30f0d8ab Mon Sep 17 00:00:00 2001 From: Atirna <288419661+atirna@users.noreply.github.com> Date: Mon, 7 Sep 2026 01:56:28 +0530 Subject: [PATCH] fix: cancel stale completion requests --- src/Server.zig | 18 ++++++++++++++++++ src/features/completions.zig | 3 +++ 2 files changed, 21 insertions(+) diff --git a/src/Server.zig b/src/Server.zig index 5bd3e527e..91f537f53 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -1342,6 +1342,23 @@ fn isBlockingMessage(msg: Message) bool { } } +fn invalidatesPendingRequests(msg: Message) bool { + return switch (msg) { + .notification => |notification| switch (notification.params) { + .@"textDocument/didOpen", + .@"textDocument/didChange", + .@"textDocument/didSave", + .@"textDocument/didClose", + .@"workspace/didChangeWatchedFiles", + .@"workspace/didChangeWorkspaceFolders", + .@"workspace/didChangeConfiguration", + => true, + else => false, + }, + else => false, + }; +} + pub const CreateOptions = struct { /// An implementation that doesn't support `concurrent` is permitted but will not be able to provide some features like build on save. io: std.Io, @@ -1446,6 +1463,7 @@ pub fn loop(server: *Server) LoopError!void { }; if (isBlockingMessage(message)) { + if (invalidatesPendingRequests(message)) server.wait_group.cancel(server.io); try server.wait_group.await(server.io); server.wait_group = .init; try server.processMessageReportError(arena_allocator.state, message); diff --git a/src/features/completions.zig b/src/features/completions.zig index 618a28ecb..452512dbb 100644 --- a/src/features/completions.zig +++ b/src/features/completions.zig @@ -186,6 +186,7 @@ fn typeToCompletion(builder: *Builder, ty: Analyser.Type) Analyser.Error!void { try builder.analyser.collectDeclarationsOfContainer(ty, builder.orig_handle, !ty.is_type_val, &decls); for (decls.items) |decl_with_handle| { + try builder.server.io.checkCancel(); try declToCompletion(builder, decl_with_handle); } }, @@ -686,6 +687,7 @@ fn completeGlobal(builder: *Builder) Analyser.Error!void { var decls: std.ArrayList(Analyser.DeclWithHandle) = .empty; try builder.analyser.collectAllSymbolsAtSourceIndex(builder.orig_handle, builder.source_index, &decls); for (decls.items) |decl_with_handle| { + try builder.server.io.checkCancel(); try declToCompletion(builder, decl_with_handle); } try populateSnippedCompletions(builder, .generic); @@ -1497,6 +1499,7 @@ fn collectContainerFields( const scope_decls = document_scope.getScopeDeclarationsConst(scope_handle.scope); for (scope_decls) |decl_index| { + try builder.server.io.checkCancel(); const decl = document_scope.declarations.get(@backingInt(decl_index)); if (decl != .ast_node) continue; const decl_handle: Analyser.DeclWithHandle = .{ .decl = decl, .handle = scope_handle.handle, .container_type = container };