From ede2e07794bd82b029062d8d29bd8fbec26d2918 Mon Sep 17 00:00:00 2001 From: WTEDST Date: Tue, 24 Feb 2026 13:52:06 +0100 Subject: [PATCH 1/2] impl --- src/features/completions.zig | 15 ++++++++- tests/lsp_features/completion.zig | 51 +++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/features/completions.zig b/src/features/completions.zig index d3ab90b51..3a8ef2c39 100644 --- a/src/features/completions.zig +++ b/src/features/completions.zig @@ -664,6 +664,19 @@ fn kindToSortScore(kind: types.completion.Item.Kind) []const u8 { }; } +fn itemSortScore(item: types.completion.Item) []const u8 { + const deprecated: bool = if (item.tags) |tags| + std.mem.findScalar(types.completion.Item.Tag, tags, .Deprecated) != null + else + false; + + if (deprecated) { + return "9"; + } else { + return kindToSortScore(item.kind.?); + } +} + fn collectUsedMembersSet(builder: *Builder, likely: EnumLiteralContext.Likely, dot_token_index: Ast.TokenIndex) error{OutOfMemory}!std.BufSet { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); @@ -993,7 +1006,7 @@ pub fn completionAtIndex( } if (item.sortText == null) { - const score = kindToSortScore(item.kind.?); + const score = itemSortScore(item.*); item.sortText = try std.fmt.allocPrint(arena, "{s}_{s}", .{ score, item.label }); } } diff --git a/tests/lsp_features/completion.zig b/tests/lsp_features/completion.zig index 352a88be1..072976807 100644 --- a/tests/lsp_features/completion.zig +++ b/tests/lsp_features/completion.zig @@ -2684,6 +2684,30 @@ test "deprecated" { }); } +test "deprecated sorting" { + try testCompletionWithOptions( + \\pub const Test = struct { + \\ pub const a = @compileError("Deprecated; some message"); + \\ pub const b = true; + \\}; + \\const foo = Test. + , &.{ + .{ + .label = "b", + .kind = .Constant, + .deprecated = false, + }, + .{ + .label = "a", + .kind = .Constant, + .documentation = "Deprecated; some message", + .deprecated = true, + }, + }, .{ + .check_order = true, + }); +} + test "declarations" { try testCompletion( \\const S = struct { @@ -4407,6 +4431,7 @@ fn testCompletionWithOptions( enable_argument_placeholders: bool = true, enable_snippets: bool = true, completion_label_details: bool = true, + check_order: bool = false, }, ) !void { const cursor_idx = std.mem.find(u8, source, "").?; @@ -4583,6 +4608,32 @@ fn testCompletionWithOptions( try error_builder.msgAtIndex("invalid completions\n{s}", test_uri.raw, cursor_idx, .err, .{buffer.items}); return error.MissingOrUnexpectedCompletions; } + + if (options.check_order) { + const Item = types.completion.Item; + + const items: []Item = try allocator.dupe(Item, completion_list.items); + defer allocator.free(items); + + std.mem.sort(Item, items, {}, struct { + fn sort(_: void, lhs: Item, rhs: Item) bool { + const lhs_text = lhs.sortText orelse return false; + const rhs_text = rhs.sortText orelse return false; + + switch (std.mem.order(u8, lhs_text, rhs_text)) { + .lt => return true, + .eq, .gt => return false, + } + } + }.sort); + + for (0..expected_completions.len) |i| { + const expected_completion = expected_completions[i]; + const actual_completion = items[i]; + + try std.testing.expectEqualStrings(expected_completion.label, actual_completion.label); + } + } } fn extractCompletionLabels(items: anytype) error{ DuplicateCompletionLabel, OutOfMemory }!std.StringArrayHashMapUnmanaged(void) { From 5b5e16da14f294a094c13e1422741b487e02ea5f Mon Sep 17 00:00:00 2001 From: WTEDST Date: Wed, 25 Feb 2026 21:45:00 +0100 Subject: [PATCH 2/2] apply pr feedback --- src/features/completions.zig | 3 ++- tests/lsp_features/completion.zig | 8 +------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/features/completions.zig b/src/features/completions.zig index 3a8ef2c39..03385e02f 100644 --- a/src/features/completions.zig +++ b/src/features/completions.zig @@ -665,7 +665,8 @@ fn kindToSortScore(kind: types.completion.Item.Kind) []const u8 { } fn itemSortScore(item: types.completion.Item) []const u8 { - const deprecated: bool = if (item.tags) |tags| + // Completion items have two ways to mark deprecation; we need to check both. + const deprecated: bool = item.deprecated orelse if (item.tags) |tags| std.mem.findScalar(types.completion.Item.Tag, tags, .Deprecated) != null else false; diff --git a/tests/lsp_features/completion.zig b/tests/lsp_features/completion.zig index 072976807..029564d9c 100644 --- a/tests/lsp_features/completion.zig +++ b/tests/lsp_features/completion.zig @@ -4617,13 +4617,7 @@ fn testCompletionWithOptions( std.mem.sort(Item, items, {}, struct { fn sort(_: void, lhs: Item, rhs: Item) bool { - const lhs_text = lhs.sortText orelse return false; - const rhs_text = rhs.sortText orelse return false; - - switch (std.mem.order(u8, lhs_text, rhs_text)) { - .lt => return true, - .eq, .gt => return false, - } + return std.mem.lessThan(u8, lhs.sortText.?, rhs.sortText.?); } }.sort);