mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Update the ICommandPredictor interface to reduce boilerplate code from predictor implementation (#19414)
This commit is contained in:
-164
@@ -188,168 +188,4 @@ namespace System.Management.Automation.Subsystem.Feedback
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class UnixCommandNotFound : IFeedbackProvider, ICommandPredictor
|
||||
{
|
||||
private readonly Guid _guid;
|
||||
private List<string>? _candidates;
|
||||
|
||||
internal UnixCommandNotFound()
|
||||
{
|
||||
_guid = new Guid("47013747-CB9D-4EBC-9F02-F32B8AB19D48");
|
||||
}
|
||||
|
||||
Dictionary<string, string>? ISubsystem.FunctionsToDefine => null;
|
||||
|
||||
public Guid Id => _guid;
|
||||
|
||||
public string Name => "cmd-not-found";
|
||||
|
||||
public string Description => "The built-in feedback/prediction source for the Unix command utility.";
|
||||
|
||||
#region IFeedbackProvider
|
||||
|
||||
private static string? GetUtilityPath()
|
||||
{
|
||||
string cmd_not_found = "/usr/lib/command-not-found";
|
||||
bool exist = IsFileExecutable(cmd_not_found);
|
||||
|
||||
if (!exist)
|
||||
{
|
||||
cmd_not_found = "/usr/share/command-not-found/command-not-found";
|
||||
exist = IsFileExecutable(cmd_not_found);
|
||||
}
|
||||
|
||||
return exist ? cmd_not_found : null;
|
||||
|
||||
static bool IsFileExecutable(string path)
|
||||
{
|
||||
var file = new FileInfo(path);
|
||||
return file.Exists && file.UnixFileMode.HasFlag(UnixFileMode.OtherExecute);
|
||||
}
|
||||
}
|
||||
|
||||
public FeedbackItem? GetFeedback(string commandLine, ErrorRecord lastError, CancellationToken token)
|
||||
{
|
||||
if (Platform.IsWindows || lastError.FullyQualifiedErrorId != "CommandNotFoundException")
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var target = (string)lastError.TargetObject;
|
||||
if (target is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (target.EndsWith(".ps1", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string? cmd_not_found = GetUtilityPath();
|
||||
if (cmd_not_found is not null)
|
||||
{
|
||||
var startInfo = new ProcessStartInfo(cmd_not_found);
|
||||
startInfo.ArgumentList.Add(target);
|
||||
startInfo.RedirectStandardError = true;
|
||||
startInfo.RedirectStandardOutput = true;
|
||||
|
||||
using var process = Process.Start(startInfo);
|
||||
if (process is not null)
|
||||
{
|
||||
string? header = null;
|
||||
List<string>? actions = null;
|
||||
|
||||
while (true)
|
||||
{
|
||||
string? line = process.StandardError.ReadLine();
|
||||
if (line is null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (line == string.Empty)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.StartsWith("sudo ", StringComparison.Ordinal))
|
||||
{
|
||||
actions ??= new List<string>();
|
||||
actions.Add(line.TrimEnd());
|
||||
}
|
||||
else if (actions is null)
|
||||
{
|
||||
header = line;
|
||||
}
|
||||
}
|
||||
|
||||
if (actions is not null && header is not null)
|
||||
{
|
||||
_candidates = actions;
|
||||
|
||||
var footer = process.StandardOutput.ReadToEnd().Trim();
|
||||
return string.IsNullOrEmpty(footer)
|
||||
? new FeedbackItem(header, actions)
|
||||
: new FeedbackItem(header, actions, footer, FeedbackDisplayLayout.Portrait);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ICommandPredictor
|
||||
|
||||
public bool CanAcceptFeedback(PredictionClient client, PredictorFeedbackKind feedback)
|
||||
{
|
||||
return feedback switch
|
||||
{
|
||||
PredictorFeedbackKind.CommandLineAccepted => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
public SuggestionPackage GetSuggestion(PredictionClient client, PredictionContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
if (_candidates is not null)
|
||||
{
|
||||
string input = context.InputAst.Extent.Text;
|
||||
List<PredictiveSuggestion>? result = null;
|
||||
|
||||
foreach (string c in _candidates)
|
||||
{
|
||||
if (c.StartsWith(input, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
result ??= new List<PredictiveSuggestion>(_candidates.Count);
|
||||
result.Add(new PredictiveSuggestion(c));
|
||||
}
|
||||
}
|
||||
|
||||
if (result is not null)
|
||||
{
|
||||
return new SuggestionPackage(result);
|
||||
}
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public void OnCommandLineAccepted(PredictionClient client, IReadOnlyList<string> history)
|
||||
{
|
||||
// Reset the candidate state.
|
||||
_candidates = null;
|
||||
}
|
||||
|
||||
public void OnSuggestionDisplayed(PredictionClient client, uint session, int countOrIndex) { }
|
||||
|
||||
public void OnSuggestionAccepted(PredictionClient client, uint session, string acceptedSuggestion) { }
|
||||
|
||||
public void OnCommandLineExecuted(PredictionClient client, string commandLine, bool success) { }
|
||||
|
||||
#endregion;
|
||||
}
|
||||
}
|
||||
|
||||
-1
@@ -5,7 +5,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Management.Automation.Internal;
|
||||
using System.Management.Automation.Language;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
+11
-5
@@ -36,7 +36,7 @@ namespace System.Management.Automation.Subsystem.Prediction
|
||||
/// <param name="client">Represents the client that initiates the call.</param>
|
||||
/// <param name="feedback">A specific type of feedback.</param>
|
||||
/// <returns>True or false, to indicate whether the specific feedback is accepted.</returns>
|
||||
bool CanAcceptFeedback(PredictionClient client, PredictorFeedbackKind feedback);
|
||||
bool CanAcceptFeedback(PredictionClient client, PredictorFeedbackKind feedback) => false;
|
||||
|
||||
/// <summary>
|
||||
/// One or more suggestions provided by the predictor were displayed to the user.
|
||||
@@ -47,7 +47,7 @@ namespace System.Management.Automation.Subsystem.Prediction
|
||||
/// When the value is greater than 0, it's the number of displayed suggestions from the list returned in <paramref name="session"/>, starting from the index 0.
|
||||
/// When the value is less than or equal to 0, it means a single suggestion from the list got displayed, and the index is the absolute value.
|
||||
/// </param>
|
||||
void OnSuggestionDisplayed(PredictionClient client, uint session, int countOrIndex);
|
||||
void OnSuggestionDisplayed(PredictionClient client, uint session, int countOrIndex) { }
|
||||
|
||||
/// <summary>
|
||||
/// The suggestion provided by the predictor was accepted.
|
||||
@@ -55,7 +55,7 @@ namespace System.Management.Automation.Subsystem.Prediction
|
||||
/// <param name="client">Represents the client that initiates the call.</param>
|
||||
/// <param name="session">Represents the mini-session where the accepted suggestion came from.</param>
|
||||
/// <param name="acceptedSuggestion">The accepted suggestion text.</param>
|
||||
void OnSuggestionAccepted(PredictionClient client, uint session, string acceptedSuggestion);
|
||||
void OnSuggestionAccepted(PredictionClient client, uint session, string acceptedSuggestion) { }
|
||||
|
||||
/// <summary>
|
||||
/// A command line was accepted to execute.
|
||||
@@ -63,7 +63,7 @@ namespace System.Management.Automation.Subsystem.Prediction
|
||||
/// </summary>
|
||||
/// <param name="client">Represents the client that initiates the call.</param>
|
||||
/// <param name="history">History command lines provided as references for prediction.</param>
|
||||
void OnCommandLineAccepted(PredictionClient client, IReadOnlyList<string> history);
|
||||
void OnCommandLineAccepted(PredictionClient client, IReadOnlyList<string> history) { }
|
||||
|
||||
/// <summary>
|
||||
/// A command line was done execution.
|
||||
@@ -71,7 +71,7 @@ namespace System.Management.Automation.Subsystem.Prediction
|
||||
/// <param name="client">Represents the client that initiates the call.</param>
|
||||
/// <param name="commandLine">The last accepted command line.</param>
|
||||
/// <param name="success">Shows whether the execution was successful.</param>
|
||||
void OnCommandLineExecuted(PredictionClient client, string commandLine, bool success);
|
||||
void OnCommandLineExecuted(PredictionClient client, string commandLine, bool success) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -131,6 +131,12 @@ namespace System.Management.Automation.Subsystem.Prediction
|
||||
/// </summary>
|
||||
public PredictionClientKind Kind { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current location of the default session.
|
||||
/// It returns null if there is no default Runspace or if the default is a remote Runspace.
|
||||
/// </summary>
|
||||
public PathInfo? CurrentLocation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PredictionClient"/> class.
|
||||
/// </summary>
|
||||
|
||||
@@ -703,7 +703,7 @@ namespace System.Management.Automation.Runspaces
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidRunspaceStateException">Runspace is not opened.
|
||||
/// </exception>
|
||||
internal System.Management.Automation.ExecutionContext ExecutionContext
|
||||
internal ExecutionContext ExecutionContext
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -1577,7 +1577,7 @@ namespace System.Management.Automation.Runspaces
|
||||
/// <summary>
|
||||
/// Gets the execution context.
|
||||
/// </summary>
|
||||
internal abstract System.Management.Automation.ExecutionContext GetExecutionContext
|
||||
internal abstract ExecutionContext GetExecutionContext
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ namespace System.Management.Automation.Runspaces
|
||||
/// <summary>
|
||||
/// Gets the execution context.
|
||||
/// </summary>
|
||||
internal override System.Management.Automation.ExecutionContext GetExecutionContext
|
||||
internal override ExecutionContext GetExecutionContext
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
@@ -71,18 +71,8 @@ namespace PSTests.Sequential
|
||||
|
||||
#region ICommandPredictor
|
||||
|
||||
public bool CanAcceptFeedback(PredictionClient client, PredictorFeedbackKind feedback) => false;
|
||||
|
||||
public SuggestionPackage GetSuggestion(PredictionClient client, PredictionContext context, CancellationToken cancellationToken) => default;
|
||||
|
||||
public void OnCommandLineAccepted(PredictionClient client, IReadOnlyList<string> history) { }
|
||||
|
||||
public void OnSuggestionDisplayed(PredictionClient client, uint session, int countOrIndex) { }
|
||||
|
||||
public void OnSuggestionAccepted(PredictionClient client, uint session, string acceptedSuggestion) { }
|
||||
|
||||
public void OnCommandLineExecuted(PredictionClient client, string commandLine, bool success) { }
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user