Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/System.Management.Automation/engine/CommandPathSearch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,17 @@ internal class CommandPathSearch : IEnumerable<string>, IEnumerator<string>
/// <param name="acceptableCommandNames">
/// The patterns to search for in the paths.
/// </param>
/// <param name="useFuzzyMatch">
/// Use likely relevant search.
/// <param name="fuzzyMatcher">
/// The fuzzy matcher to use for fuzzy searching.
/// </param>
internal CommandPathSearch(
string commandName,
LookupPathCollection lookupPaths,
ExecutionContext context,
Collection<string>? acceptableCommandNames,
bool useFuzzyMatch)
FuzzyMatcher? fuzzyMatcher)
{
_useFuzzyMatch = useFuzzyMatch;
_fuzzyMatcher = fuzzyMatcher;
string[] commandPatterns;
if (acceptableCommandNames != null)
{
Expand Down Expand Up @@ -434,13 +434,13 @@ private void GetNewDirectoryResults(string pattern, string directory)
// to forcefully use null if pattern is "."
if (pattern.Length != 1 || pattern[0] != '.')
{
if (_useFuzzyMatch)
if (_fuzzyMatcher is not null)
{
var files = new List<string>();
var matchingFiles = Directory.EnumerateFiles(directory);
foreach (string file in matchingFiles)
{
if (FuzzyMatcher.IsFuzzyMatch(Path.GetFileName(file), pattern))
if (_fuzzyMatcher.IsFuzzyMatch(Path.GetFileName(file), pattern))
{
files.Add(file);
}
Expand Down Expand Up @@ -589,7 +589,7 @@ private void GetNewDirectoryResults(string pattern, string directory)
private readonly string[] _orderedPathExt;
private readonly Collection<string>? _acceptableCommandNames;

private readonly bool _useFuzzyMatch = false;
private readonly FuzzyMatcher? _fuzzyMatcher;

#endregion private members
}
Expand Down
62 changes: 26 additions & 36 deletions src/System.Management.Automation/engine/CommandSearcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,29 +24,20 @@ internal class CommandSearcher : IEnumerable<CommandInfo>, IEnumerator<CommandIn
/// Constructs a command searching enumerator that resolves the location
/// to a command using a standard algorithm.
/// </summary>
/// <param name="commandName">
/// The name of the command to look for.
/// </param>
/// <param name="options">
/// Determines which types of commands glob resolution of the name will take place on.
/// </param>
/// <param name="commandTypes">
/// The types of commands to look for.
/// </param>
/// <param name="context">
/// The execution context for this engine instance...
/// </param>
/// <exception cref="ArgumentNullException">
/// If <paramref name="context"/> is null.
/// </exception>
/// <exception cref="PSArgumentException">
/// If <paramref name="commandName"/> is null or empty.
/// </exception>
/// <param name="commandName">The name of the command to look for.</param>
/// <param name="options">Determines which types of commands glob resolution of the name will take place on.</param>
/// <param name="commandTypes">The types of commands to look for.</param>
/// <param name="context">The execution context for this engine instance.</param>
/// <param name="fuzzyMatcher">The fuzzy matcher to use for fuzzy searching.</param>
///
/// <exception cref="ArgumentNullException">If <paramref name="context"/> is null.</exception>
/// <exception cref="PSArgumentException">If <paramref name="commandName"/> is null or empty.</exception>
internal CommandSearcher(
string commandName,
SearchResolutionOptions options,
CommandTypes commandTypes,
ExecutionContext context)
ExecutionContext context,
FuzzyMatcher? fuzzyMatcher = null)
{
Diagnostics.Assert(context != null, "caller to verify context is not null");
Diagnostics.Assert(!string.IsNullOrEmpty(commandName), "caller to verify commandName is valid");
Expand All @@ -55,6 +46,7 @@ internal CommandSearcher(
_context = context;
_commandResolutionOptions = options;
_commandTypes = commandTypes;
_fuzzyMatcher = fuzzyMatcher;

// Initialize the enumerators
this.Reset();
Expand Down Expand Up @@ -705,8 +697,7 @@ private static bool checkPath(string path, string commandName)
foreach (KeyValuePair<string, AliasInfo> aliasEntry in _context.EngineSessionState.GetAliasTable())
{
if (aliasMatcher.IsMatch(aliasEntry.Key) ||
(_commandResolutionOptions.HasFlag(SearchResolutionOptions.FuzzyMatch) &&
FuzzyMatcher.IsFuzzyMatch(aliasEntry.Key, _commandName)))
(_fuzzyMatcher is not null && _fuzzyMatcher.IsFuzzyMatch(aliasEntry.Key, _commandName)))
{
matchingAliases.Add(aliasEntry.Value);
}
Expand Down Expand Up @@ -785,8 +776,7 @@ private static bool checkPath(string path, string commandName)
foreach ((string functionName, FunctionInfo functionInfo) in _context.EngineSessionState.GetFunctionTable())
{
if (functionMatcher.IsMatch(functionName) ||
(_commandResolutionOptions.HasFlag(SearchResolutionOptions.FuzzyMatch) &&
FuzzyMatcher.IsFuzzyMatch(functionName, _commandName)))
(_fuzzyMatcher is not null && _fuzzyMatcher.IsFuzzyMatch(functionName, _commandName)))
{
matchingFunction.Add(functionInfo);
}
Expand Down Expand Up @@ -1018,10 +1008,8 @@ private static bool ShouldSkipCommandResolutionForConstrainedLanguage(CommandInf
{
foreach (CmdletInfo cmdlet in cmdletList)
{
if (cmdletMatcher != null &&
cmdletMatcher.IsMatch(cmdlet.Name) ||
(_commandResolutionOptions.HasFlag(SearchResolutionOptions.FuzzyMatch) &&
FuzzyMatcher.IsFuzzyMatch(cmdlet.Name, _commandName)))
if ((cmdletMatcher is not null && cmdletMatcher.IsMatch(cmdlet.Name)) ||
(_fuzzyMatcher is not null && _fuzzyMatcher.IsFuzzyMatch(cmdlet.Name, _commandName)))
{
if (string.IsNullOrEmpty(moduleName) || moduleName.Equals(cmdlet.ModuleName, StringComparison.OrdinalIgnoreCase))
{
Expand Down Expand Up @@ -1496,6 +1484,11 @@ private static CanDoPathLookupResult CanDoPathLookup(string possiblePath)
/// </summary>
private readonly ExecutionContext _context;

/// <summary>
/// The fuzzy matcher to use for fuzzy searching.
/// </summary>
private readonly FuzzyMatcher? _fuzzyMatcher;

/// <summary>
/// A routine to initialize the path searcher...
/// </summary>
Expand Down Expand Up @@ -1528,7 +1521,7 @@ private void setupPathSearcher()
_context.CommandDiscovery.GetLookupDirectoryPaths(),
_context,
acceptableCommandNames: null,
useFuzzyMatch: _commandResolutionOptions.HasFlag(SearchResolutionOptions.FuzzyMatch));
_fuzzyMatcher);
}
else
{
Expand All @@ -1544,7 +1537,7 @@ private void setupPathSearcher()
_context.CommandDiscovery.GetLookupDirectoryPaths(),
_context,
ConstructSearchPatternsFromName(_commandName, commandDiscovery: true),
useFuzzyMatch: false);
fuzzyMatcher: null);
}
else if (_canDoPathLookupResult == CanDoPathLookupResult.PathIsRooted)
{
Expand All @@ -1568,7 +1561,7 @@ private void setupPathSearcher()
directoryCollection,
_context,
ConstructSearchPatternsFromName(fileName, commandDiscovery: true),
useFuzzyMatch: false);
fuzzyMatcher: null);
}
else
{
Expand Down Expand Up @@ -1608,7 +1601,7 @@ private void setupPathSearcher()
directoryCollection,
_context,
ConstructSearchPatternsFromName(fileName, commandDiscovery: true),
useFuzzyMatch: false);
fuzzyMatcher: null);
}
else
{
Expand Down Expand Up @@ -1727,17 +1720,14 @@ internal enum SearchResolutionOptions
CommandNameIsPattern = 0x04,
SearchAllScopes = 0x08,

/// <summary>Use fuzzy matching.</summary>
FuzzyMatch = 0x10,

/// <summary>
/// Enable searching for cmdlets/functions by abbreviation expansion.
/// </summary>
UseAbbreviationExpansion = 0x20,
UseAbbreviationExpansion = 0x10,

/// <summary>
/// Enable resolving wildcard in paths.
/// </summary>
ResolveLiteralThenPathPatterns = 0x40
ResolveLiteralThenPathPatterns = 0x20
}
}
54 changes: 27 additions & 27 deletions src/System.Management.Automation/engine/GetCommandCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,8 @@ public PSTypeName[] ParameterType
[Parameter(ParameterSetName = "AllCommandSet")]
public uint FuzzyMinimumDistance { get; set; } = 5;

private List<CommandScore> _commandScores = new List<CommandScore>();
private FuzzyMatcher _fuzzyMatcher;
private List<CommandScore> _commandScores;

/// <summary>
/// Gets or sets the parameter that determines if return cmdlets based on abbreviation expansion.
Expand All @@ -367,7 +368,11 @@ protected override void BeginProcessing()
#if LEGACYTELEMETRY
_timer.Start();
#endif
base.BeginProcessing();
if (UseFuzzyMatching)
{
_fuzzyMatcher = new FuzzyMatcher(FuzzyMinimumDistance);
_commandScores = new List<CommandScore>();
}

if (ShowCommandInfo.IsPresent && Syntax.IsPresent)
{
Expand Down Expand Up @@ -503,14 +508,11 @@ protected override void EndProcessing()

private void OutputResultsHelper(IEnumerable<CommandInfo> results)
{
CommandOrigin origin = this.MyInvocation.CommandOrigin;
CommandOrigin origin = MyInvocation.CommandOrigin;

if (UseFuzzyMatching)
{
_commandScores = _commandScores
.Where(x => x.Score <= FuzzyMinimumDistance)
.OrderBy(static x => x.Score)
.ToList();
_commandScores = _commandScores.OrderBy(static x => x.Score).ToList();
results = _commandScores.Select(static x => x.Command);
}

Expand Down Expand Up @@ -784,11 +786,6 @@ private void AccumulateMatchingCommands(IEnumerable<string> commandNames)
options |= SearchResolutionOptions.UseAbbreviationExpansion;
}

if (UseFuzzyMatching)
{
options |= SearchResolutionOptions.FuzzyMatch;
}

if ((this.CommandType & CommandTypes.Alias) != 0)
{
options |= SearchResolutionOptions.ResolveAliasPatterns;
Expand Down Expand Up @@ -861,24 +858,25 @@ private void AccumulateMatchingCommands(IEnumerable<string> commandNames)
IEnumerable<CommandInfo> commands;
if (UseFuzzyMatching)
{
foreach (var commandScore in System.Management.Automation.Internal.ModuleUtils.GetFuzzyMatchingCommands(
foreach (var commandScore in ModuleUtils.GetFuzzyMatchingCommands(
plainCommandName,
this.Context,
this.MyInvocation.CommandOrigin,
Context,
MyInvocation.CommandOrigin,
_fuzzyMatcher,
rediscoverImportedModules: true,
moduleVersionRequired: _isFullyQualifiedModuleSpecified))
{
_commandScores.Add(commandScore);
}

commands = _commandScores.Select(static x => x.Command).ToList();
commands = _commandScores.Select(static x => x.Command);
}
else
{
commands = System.Management.Automation.Internal.ModuleUtils.GetMatchingCommands(
commands = ModuleUtils.GetMatchingCommands(
plainCommandName,
this.Context,
this.MyInvocation.CommandOrigin,
Context,
MyInvocation.CommandOrigin,
rediscoverImportedModules: true,
moduleVersionRequired: _isFullyQualifiedModuleSpecified,
useAbbreviationExpansion: UseAbbreviationExpansion);
Expand Down Expand Up @@ -939,12 +937,12 @@ private void AccumulateMatchingCommands(IEnumerable<string> commandNames)

private bool FindCommandForName(SearchResolutionOptions options, string commandName, bool isPattern, bool emitErrors, ref int currentCount, out bool isDuplicate)
{
CommandSearcher searcher =
new CommandSearcher(
commandName,
options,
this.CommandType,
this.Context);
var searcher = new CommandSearcher(
commandName,
options,
CommandType,
Context,
_fuzzyMatcher);

bool resultFound = false;
isDuplicate = false;
Expand Down Expand Up @@ -1032,8 +1030,10 @@ private bool FindCommandForName(SearchResolutionOptions options, string commandN

if (UseFuzzyMatching)
{
int score = FuzzyMatcher.GetDamerauLevenshteinDistance(current.Name, commandName);
_commandScores.Add(new CommandScore(current, score));
if (_fuzzyMatcher.IsFuzzyMatch(current.Name, commandName, out int score))
{
_commandScores.Add(new CommandScore(current, score));
}
}

_accumulatedResults.Add(current);
Expand Down
Loading