| File: Utils\StringUtils.cs | Web Access |
| Project: src\src\Aspire.Cli\Aspire.Cli.csproj (aspire) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Text.RegularExpressions; namespace Aspire.Cli.Utils; internal static partial class StringUtils { public static string RemoveSpectreFormatting(this string input) { return RemoveSpectreFormattingRegex().Replace(input, string.Empty).Trim(); } [GeneratedRegex(@"\[[^\]]+\]")] private static partial Regex RemoveSpectreFormattingRegex(); /// <summary> /// Calculates a fuzzy match score between a search term and a target string. /// </summary> /// <param name="searchTerm">The search term.</param> /// <param name="target">The target string to match against.</param> /// <returns> /// A score between 0.0 and 1.0, where 1.0 is a perfect match and 0.0 is no match. /// Higher scores indicate better matches. The caller is responsible for filtering based on score thresholds. /// </returns> /// <remarks> /// The scoring prioritizes different match types: /// <list type="bullet"> /// <item>1.0 - Exact match (case-insensitive)</item> /// <item>0.95 - Target starts with search term</item> /// <item>0.85 - Target contains search term</item> /// <item>0.0-0.75 - Fuzzy match based on Levenshtein distance</item> /// </list> /// </remarks> public static double CalculateFuzzyScore(string searchTerm, string target) { if (string.IsNullOrWhiteSpace(searchTerm) || string.IsNullOrWhiteSpace(target)) { return 0.0; } var searchLower = searchTerm.ToLowerInvariant(); var targetLower = target.ToLowerInvariant(); // Exact match if (searchLower == targetLower) { return 1.0; } // Starts with (high priority) if (targetLower.StartsWith(searchLower)) { return 0.95; } // Contains (medium priority) if (targetLower.Contains(searchLower)) { return 0.85; } // Levenshtein distance for fuzzy matching (low priority) var distance = GetLevenshteinDistance(searchLower, targetLower); var maxLength = Math.Max(searchLower.Length, targetLower.Length); // Normalize score: closer to 0 distance = higher score return (1.0 - (double)distance / maxLength) * 0.75; } /// <summary> /// Calculates the Levenshtein distance between two strings. /// </summary> /// <param name="source">The source string.</param> /// <param name="target">The target string.</param> /// <returns>The minimum number of single-character edits required to change the source string into the target string.</returns> public static int GetLevenshteinDistance(string source, string target) { if (string.IsNullOrWhiteSpace(source)) { return string.IsNullOrWhiteSpace(target) ? 0 : target.Length; } if (string.IsNullOrWhiteSpace(target)) { return source.Length; } var sourceLength = source.Length; var targetLength = target.Length; var distance = new int[sourceLength + 1, targetLength + 1]; for (var i = 0; i <= sourceLength; i++) { distance[i, 0] = i; } for (var j = 0; j <= targetLength; j++) { distance[0, j] = j; } for (var i = 1; i <= sourceLength; i++) { for (var j = 1; j <= targetLength; j++) { var cost = target[j - 1] == source[i - 1] ? 0 : 1; distance[i, j] = Math.Min( Math.Min(distance[i - 1, j] + 1, distance[i, j - 1] + 1), distance[i - 1, j - 1] + cost); } } return distance[sourceLength, targetLength]; } }