CmdPal: adjust frecency weighting (#42242)

In #41959 we changed the string matcher's weighting. It ended up giving
us lower scores than before.

That meant that the weighting from recent commands was far too heavy,
and it was polluting the results. Basically any command that you'd run
would be like, 30 characters of weight heavier than anything you
haven't.


This increases the weight of all string matches by 10x. That means
something like
`Command Prompt` will get a string matched weight of `100` instead of
`10`. This balances better with the weighting from frecency (where the
MRU command gets +35, then `+min(5*uses,35)`, for up to 70 points of
weight)

It also adds a bunch of tests here, to try and catch this again in the
future.

Closes #42158
This commit is contained in:
Mike Griese
2025-10-09 14:35:22 -05:00
committed by GitHub
parent b06cd9f896
commit f55c49e15b
6 changed files with 507 additions and 11 deletions

View File

@@ -160,7 +160,7 @@ public partial class MainListPage : DynamicListPage,
{
lock (_tlcManager.TopLevelCommands)
{
List<Scored<IListItem>> limitedApps = new List<Scored<IListItem>>();
var limitedApps = new List<Scored<IListItem>>();
// Fuzzy matching can produce a lot of results, so we want to limit the
// number of apps we show at once if it's a large set.
@@ -273,9 +273,9 @@ public partial class MainListPage : DynamicListPage,
return;
}
IEnumerable<IListItem> newFilteredItems = Enumerable.Empty<IListItem>();
IEnumerable<IListItem> newFallbacks = Enumerable.Empty<IListItem>();
IEnumerable<IListItem> newApps = Enumerable.Empty<IListItem>();
var newFilteredItems = Enumerable.Empty<IListItem>();
var newFallbacks = Enumerable.Empty<IListItem>();
var newApps = Enumerable.Empty<IListItem>();
if (_filteredItems is not null)
{
@@ -339,8 +339,11 @@ public partial class MainListPage : DynamicListPage,
}
}
var history = _serviceProvider.GetService<AppStateModel>()!.RecentCommands!;
Func<string, IListItem, int> scoreItem = (a, b) => { return ScoreTopLevelItem(a, b, history); };
// Produce a list of everything that matches the current filter.
_filteredItems = [.. ListHelpers.FilterListWithScores<IListItem>(newFilteredItems ?? [], SearchText, ScoreTopLevelItem)];
_filteredItems = [.. ListHelpers.FilterListWithScores<IListItem>(newFilteredItems ?? [], SearchText, scoreItem)];
if (token.IsCancellationRequested)
{
@@ -358,7 +361,7 @@ public partial class MainListPage : DynamicListPage,
// Produce a list of filtered apps with the appropriate limit
if (newApps.Any())
{
var scoredApps = ListHelpers.FilterListWithScores<IListItem>(newApps, SearchText, ScoreTopLevelItem);
var scoredApps = ListHelpers.FilterListWithScores<IListItem>(newApps, SearchText, scoreItem);
if (token.IsCancellationRequested)
{
@@ -425,7 +428,7 @@ public partial class MainListPage : DynamicListPage,
// Almost verbatim ListHelpers.ScoreListItem, but also accounting for the
// fact that we want fallback handlers down-weighted, so that they don't
// _always_ show up first.
private int ScoreTopLevelItem(string query, IListItem topLevelOrAppItem)
internal static int ScoreTopLevelItem(string query, IListItem topLevelOrAppItem, IRecentCommandsManager history)
{
var title = topLevelOrAppItem.Title;
if (string.IsNullOrWhiteSpace(title))
@@ -501,10 +504,9 @@ public partial class MainListPage : DynamicListPage,
// here we add the recent command weight boost
//
// Otherwise something like `x` will still match everything you've run before
var finalScore = matchSomething;
var finalScore = matchSomething * 10;
if (matchSomething > 0)
{
var history = _serviceProvider.GetService<AppStateModel>()!.RecentCommands;
var recentWeightBoost = history.GetCommandHistoryWeight(id);
finalScore += recentWeightBoost;
}
@@ -521,7 +523,7 @@ public partial class MainListPage : DynamicListPage,
AppStateModel.SaveState(state);
}
private string IdForTopLevelOrAppItem(IListItem topLevelOrAppItem)
private static string IdForTopLevelOrAppItem(IListItem topLevelOrAppItem)
{
if (topLevelOrAppItem is TopLevelViewModel topLevel)
{

View File

@@ -0,0 +1,7 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.CmdPal.UI.ViewModels.UnitTests")]

View File

@@ -7,7 +7,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
namespace Microsoft.CmdPal.UI.ViewModels;
public partial class RecentCommandsManager : ObservableObject
public partial class RecentCommandsManager : ObservableObject, IRecentCommandsManager
{
[JsonInclude]
internal List<HistoryItem> History { get; set; } = [];
@@ -80,3 +80,10 @@ public partial class RecentCommandsManager : ObservableObject
}
}
}
public interface IRecentCommandsManager
{
int GetCommandHistoryWeight(string commandId);
void AddHistoryItem(string commandId);
}