Files
PowerToys/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/RecentCommandsManager.cs
Mike Griese f55c49e15b 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
2025-10-09 15:35:22 -04:00

90 lines
2.5 KiB
C#

// 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.Text.Json.Serialization;
using CommunityToolkit.Mvvm.ComponentModel;
namespace Microsoft.CmdPal.UI.ViewModels;
public partial class RecentCommandsManager : ObservableObject, IRecentCommandsManager
{
[JsonInclude]
internal List<HistoryItem> History { get; set; } = [];
private readonly Lock _lock = new();
public RecentCommandsManager()
{
}
public int GetCommandHistoryWeight(string commandId)
{
lock (_lock)
{
var entry = History
.Index()
.Where(item => item.Item.CommandId == commandId)
.FirstOrDefault();
// These numbers are vaguely scaled so that "VS" will make "Visual Studio" the
// match after one use.
// Usually it has a weight of 84, compared to 109 for the VS cmd prompt
if (entry.Item is not null)
{
var index = entry.Index;
// First, add some weight based on how early in the list this appears
var bucket = index switch
{
var i when index <= 2 => 35,
var i when index <= 10 => 25,
var i when index <= 15 => 15,
var i when index <= 35 => 10,
_ => 5,
};
// Then, add weight for how often this is used, but cap the weight from usage.
var uses = Math.Min(entry.Item.Uses * 5, 35);
return bucket + uses;
}
return 0;
}
}
public void AddHistoryItem(string commandId)
{
lock (_lock)
{
var entry = History
.Where(item => item.CommandId == commandId)
.FirstOrDefault();
if (entry is null)
{
var newitem = new HistoryItem() { CommandId = commandId, Uses = 1 };
History.Insert(0, newitem);
}
else
{
History.Remove(entry);
entry.Uses++;
History.Insert(0, entry);
}
if (History.Count > 50)
{
History.RemoveRange(50, History.Count - 50);
}
}
}
}
public interface IRecentCommandsManager
{
int GetCommandHistoryWeight(string commandId);
void AddHistoryItem(string commandId);
}