Update the ICommandPredictor interface to reduce boilerplate code from predictor implementation (#19414)

This commit is contained in:
Dongbo Wang
2023-03-29 11:27:04 -07:00
committed by GitHub
parent 5a22587792
commit 51f55f716e
6 changed files with 14 additions and 183 deletions
@@ -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;
}
}
@@ -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;
@@ -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
{
-10
View File
@@ -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
}